From da1920673d2f4ba42d8bc865dd09f26b7d52c362 Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Tue, 2 Mar 2021 19:08:42 +0100 Subject: [PATCH 01/23] token-validator: use SuffixIP, can modify it and can delete tunnels --- libadlin/tunnel.go | 16 +++++-- token-validator/htdocs/js/adlin-main.js | 55 ++++++++++++++++++++- token-validator/htdocs/views/tunnels.html | 17 ++++++- token-validator/wg.go | 58 ++++++++++++++++++++++- 4 files changed, 137 insertions(+), 9 deletions(-) diff --git a/libadlin/tunnel.go b/libadlin/tunnel.go index 5a177b0..333903e 100644 --- a/libadlin/tunnel.go +++ b/libadlin/tunnel.go @@ -94,7 +94,7 @@ type TunnelToken struct { Dump *WGDump } -func tokenFromText(token string) []byte { +func TokenFromText(token string) []byte { sha := sha512.Sum512([]byte(token)) return sha[:] } @@ -118,7 +118,7 @@ func (student Student) NewTunnelToken(suffixip int) (t TunnelToken, err error) { } t.TokenText = strings.Replace(strings.Replace(strings.Replace(strings.Replace(strings.Replace(base64.RawStdEncoding.EncodeToString(tok), "/", ".", -1), "+", "_", -1), "O", "#", -1), "l", "$", -1), "I", ">", -1) - t.token = tokenFromText(t.TokenText) + t.token = TokenFromText(t.TokenText) t.IdStudent = student.Id _, err = DBExec("INSERT INTO student_tunnel_tokens (token, token_text, id_student, time, suffixip, version) VALUES (?, ?, ?, ?, ?, 0)", t.token, t.TokenText, student.Id, time.Now(), suffixip) @@ -194,7 +194,7 @@ func (student Student) GetTunnelToken(token []byte) (t TunnelToken, err error) { } func (t *TunnelToken) Update() (int64, error) { - newtoken := tokenFromText(t.TokenText) + newtoken := TokenFromText(t.TokenText) tm := time.Now() if res, err := DBExec("UPDATE student_tunnel_tokens SET token = ?, token_text = ?, id_student = ?, pubkey = ?, time = ?, suffixip = ?, version = ? WHERE token = ?", newtoken, t.TokenText, t.IdStudent, t.PubKey, tm, t.SuffixIP, t.Version, t.token); err != nil { @@ -208,6 +208,16 @@ func (t *TunnelToken) Update() (int64, error) { } } +func (t *TunnelToken) Delete() (int64, error) { + if res, err := DBExec("DELETE FROM student_tunnel_tokens WHERE token = ? AND id_student = ?", t.token, t.IdStudent); err != nil { + return 0, err + } else if nb, err := res.RowsAffected(); err != nil { + return 0, err + } else { + return nb, err + } +} + func GetStudentsTunnels() (ts []TunnelToken, err error) { if rows, errr := DBQuery("SELECT T.token, T.token_text, T.id_student, T.pubkey, T.time, T.suffixip, T.version FROM student_tunnel_tokens T INNER JOIN (SELECT B.id_student, MAX(B.time) AS time FROM student_tunnel_tokens B WHERE B.pubkey IS NOT NULL GROUP BY id_student) L ON T.id_student = L.id_student AND T.time = L.time"); errr != nil { return nil, errr diff --git a/token-validator/htdocs/js/adlin-main.js b/token-validator/htdocs/js/adlin-main.js index bdf72f9..ef2822f 100644 --- a/token-validator/htdocs/js/adlin-main.js +++ b/token-validator/htdocs/js/adlin-main.js @@ -44,6 +44,17 @@ angular.module("AdLinApp") } }) + .directive('integer', function() { + return { + require: 'ngModel', + link: function(scope, ele, attr, ctrl){ + ctrl.$parsers.unshift(function(viewValue){ + return parseInt(viewValue, 10); + }); + } + }; + }) + .component('toast', { bindings: { date: '=', @@ -150,7 +161,10 @@ angular.module("AdLinApp") }; $scope.updateTunnelInfo(); - $scope.updateTunnelsList = function() { + var noUpdate = 0 + + $scope.updateTunnelsList = function() { + if (noUpdate == 0) $http({ method: 'GET', url: "api/wg/", @@ -211,11 +225,48 @@ angular.module("AdLinApp") }); } + $scope.editTunnel = function(tunnel) { + tunnel.edit = true; + noUpdate++; + tunnel.newData = { + TokenText: tunnel.TokenText, + SuffixIP: tunnel.SuffixIP, + } + }; + + $scope.updateTunnel = function(tunnel) { + tunnel.pleaseWaitUpdate = true; + $http({ + method: 'PUT', + url: "api/wg/" + encodeURIComponent(tunnel.TokenText), + data: tunnel.newData + }).then(function(response) { + noUpdate--; + tunnel.SuffixIP = tunnel.newData.SuffixIP; + tunnel.TokenText = tunnel.newData.TokenText; + tunnel.edit = false; + tunnel.pleaseWaitUpdate = false; + $scope.updateTunnelsList(); + $scope.addToast({ + variant: "success", + title: "Maatma Tunnels", + msg: "Tunnel mise à jour avec succès !", + }); + }, function(response) { + tunnel.pleaseWaitUpdate = false; + $scope.addToast({ + variant: "danger", + title: "Maatma Tunnels", + msg: (response.data ? response.data.errmsg : "Impossible de contacter le serveur"), + }); + }); + } + $scope.dropTunnel = function(tunnel) { tunnel.pleaseWaitDrop = true; $http({ method: 'DELETE', - url: "api/wg/" + tunnel.TokenText, + url: "api/wg/" + encodeURIComponent(tunnel.TokenText), data: {} }).then(function(response) { $scope.updateTunnelsList(); diff --git a/token-validator/htdocs/views/tunnels.html b/token-validator/htdocs/views/tunnels.html index 784c981..60e29c6 100644 --- a/token-validator/htdocs/views/tunnels.html +++ b/token-validator/htdocs/views/tunnels.html @@ -7,6 +7,7 @@ Token + Suffix Dernière utilisation Clef publique @@ -18,10 +19,22 @@ {{ tunnel.TokenText }} + + + + {{ tunnel.SuffixIP }} + Par défaut {{ tunnel.Time | date:"medium" }} (VM TP {{ tunnel.Version }}) - {{ tunnel.PubKey }}(none) + {{ tunnel.PubKey }}(none) - + + diff --git a/token-validator/wg.go b/token-validator/wg.go index 544dd81..d68992f 100644 --- a/token-validator/wg.go +++ b/token-validator/wg.go @@ -33,6 +33,8 @@ func init() { router.POST("/api/wg/", apiAuthHandler(genWgToken)) router.GET("/api/wg/:token", getWgTunnelInfo) router.POST("/api/wg/:token", getWgTunnelInfo) + router.PUT("/api/wg/:token", apiAuthHandler(updateWgTunnel)) + router.DELETE("/api/wg/:token", apiAuthHandler(deleteWgTunnel)) } func showWgTunnel(student adlin.Student, ps httprouter.Params, body []byte) (interface{}, error) { @@ -121,6 +123,11 @@ func getWgTunnelInfo(w http.ResponseWriter, r *http.Request, ps httprouter.Param return } + // 0 is considered default for suffix, apply default now + if token.SuffixIP <= 0 { + token.SuffixIP = 1 + } + syncWgConf() tinfo := getTunnelInfo(token.IdStudent) @@ -138,11 +145,58 @@ PublicKey = %s Endpoint = %s:%d AllowedIPs = ::/0 PersistentKeepalive = 5 -# MyIPv6=%s1/%d +# MyIPv6=%s%x/%d # MyNetwork=%s/%d # GWIPv6=%s # MyLogin=%s -`, base64.StdEncoding.EncodeToString(tinfo.SrvPubKey), "82.64.31.248", tinfo.SrvPort, tinfo.CltIPv6, 64, tinfo.CltIPv6, tinfo.CltRange, tinfo.SrvGW6, student.Login))) +`, base64.StdEncoding.EncodeToString(tinfo.SrvPubKey), "82.64.31.248", tinfo.SrvPort, tinfo.CltIPv6, token.SuffixIP, 64, tinfo.CltIPv6, tinfo.CltRange, tinfo.SrvGW6, student.Login))) +} + +func updateWgTunnel(student adlin.Student, ps httprouter.Params, body []byte) (interface{}, error) { + token, err := adlin.GetTunnelToken(adlin.TokenFromText(ps.ByName("token"))) + if err != nil { + return nil, err + } + + if token.IdStudent != student.Id { + return nil, fmt.Errorf("Unauthorized") + } + + var newToken adlin.TunnelToken + if err := json.Unmarshal(body, &newToken); err != nil { + return nil, err + } + + token.TokenText = newToken.TokenText + token.PubKey = newToken.PubKey + token.SuffixIP = newToken.SuffixIP + + if _, err = token.Update(); err != nil { + return nil, err + } + + syncWgConf() + + return true, err +} + +func deleteWgTunnel(student adlin.Student, ps httprouter.Params, body []byte) (interface{}, error) { + token, err := adlin.GetTunnelToken(adlin.TokenFromText(ps.ByName("token"))) + if err != nil { + return nil, err + } + + if token.IdStudent != student.Id { + return nil, fmt.Errorf("Unauthorized") + } + + if _, err = token.Delete(); err != nil { + return nil, err + } + + syncWgConf() + + return true, err } func GenWGConfig(w io.Writer) error { From 54e1505db96df7e7ccf9f24d0aae11c75e40435c Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Wed, 3 Mar 2021 18:42:41 +0100 Subject: [PATCH 02/23] Update tuto2 for 2022 --- libadlin/domain.go | 2 +- tuto2.yml | 6 +++--- tutorial/ansible/ansible.md | 2 +- tutorial/ansible/tutorial.md | 8 ++++---- tutorial/ansible/vitrine.md | 2 +- tutorial/ansible/what.md | 2 +- 6 files changed, 11 insertions(+), 11 deletions(-) diff --git a/libadlin/domain.go b/libadlin/domain.go index 4a88dd2..7df578e 100644 --- a/libadlin/domain.go +++ b/libadlin/domain.go @@ -6,7 +6,7 @@ import ( ) const ( - AssociatedDomainSuffix = "adlin2021.p0m.fr." + AssociatedDomainSuffix = "adlin2022.p0m.fr." DelegatedDomainSuffix = "srs.p0m.fr." ) diff --git a/tuto2.yml b/tuto2.yml index efb3ebf..747a5a4 100644 --- a/tuto2.yml +++ b/tuto2.yml @@ -1,9 +1,9 @@ kernel: - image: linuxkit/kernel:4.19.104 + image: linuxkit/kernel:4.19.121 cmdline: "console=tty0 console=ttyS0 root=/dev/sda1 root=/dev/sr0 adlin.format=/dev/sda quiet" init: - - nemunaire/adlin-tuto2:41e341472a4a1b27dcf61c7d364f1f0a5f76fbe7 + - nemunaire/adlin-tuto2:a68d5f224331628dc525edf383ec7429dfe001b0 files: - path: etc/hostname @@ -170,7 +170,7 @@ files: - path: etc/shadow contents: | - root:$6$QNuPvO59Xk4UO3le$3P0V2ef6dHlKgO1FHsKcPPgOvL.YeCOPFqfIVTtpYn5eEn3xkgGYeM1RMCQ9l/eTc6rRc.l.WeRe1iJVznVGj/:18336:0:99999:7::: + root:$6$dQXVLB.662ob0XJL$wRhh73Q.Z3mBRHhM0rSw96dE0bOFykfIXa2Z2ncu6WVSOpFLdv5J6Br9AHhalO4wwG3xgPqqhvCdEMdroR2r50:18336:0:99999:7::: daemon:*:18316:0:99999:7::: bin:*:18316:0:99999:7::: sys:*:18316:0:99999:7::: diff --git a/tutorial/ansible/ansible.md b/tutorial/ansible/ansible.md index 924bafd..cdf8706 100644 --- a/tutorial/ansible/ansible.md +++ b/tutorial/ansible/ansible.md @@ -48,7 +48,7 @@ système et des utilisateurs. Un deuxième playbook est à rendre : `login-x-TP2/vitrine.yml`, celui-ci doit permettre de déployer (en parallèle de tous les autres), une page vitrine typique d'une entreprise (cf. la 4e question de cours ;)). Cette page doit être -accessible depuis votre domaine . +accessible depuis votre domaine . Mon première commande diff --git a/tutorial/ansible/tutorial.md b/tutorial/ansible/tutorial.md index d471639..48b0bcb 100644 --- a/tutorial/ansible/tutorial.md +++ b/tutorial/ansible/tutorial.md @@ -3,18 +3,18 @@ title: Administration Linux avancée -- TP n^o^ 2 subtitle: "Maatma : l'hébergeur DIY" author: Pierre-Olivier *nemunaire* [Mercier]{.smallcaps} institute: EPITA -date: Lundi 16 mars 2020 +date: Jeudi 4 mars 2021 abstract: | Durant ce deuxième TP, nous allons apprendre à déployer des services sur un serveur, de manière industrielle ! \vspace{1em} - La partie 4 de ce TP est un projet à rendre à au plus tard - le **lundi 30 mars 2020 à 00 h 42 du matin**. Consultez la dernière + La partie 5 de ce TP est un projet à rendre à au plus tard + le **jeudi 18 mars 2021 à 12 h 42**. Consultez la dernière section de chaque partie pour plus d'information sur les éléments à rendre. Et n'oubliez pas de répondre aux [questions de - cours](https://adlin.nemunai.re/quiz/2). + cours](https://adlin.nemunai.re/quiz/9). En tant que personnes sensibilisées à la sécurité des échanges électroniques, vous devrez m'envoyer vos rendus signés avec votre clef PGP. Pensez à diff --git a/tutorial/ansible/vitrine.md b/tutorial/ansible/vitrine.md index 833cbc3..b4b66cb 100644 --- a/tutorial/ansible/vitrine.md +++ b/tutorial/ansible/vitrine.md @@ -20,7 +20,7 @@ reporter dans un fichiers au chapitre suivant ! Ma première vitrine ------------------- -Sur le domaine `login_x.adlin2021.p0m.fr`, déployez une vitrine d'entreprise +Sur le domaine `login_x.adlin2022.p0m.fr`, déployez une vitrine d'entreprise basique (pas besoin d'un Wordpress, un simple lot de pages HTML fera l'affaire). Vous aurez pour cela besoin d'un serveur web, dont le choix est laissé à votre diff --git a/tutorial/ansible/what.md b/tutorial/ansible/what.md index 58ee80a..33a05b9 100644 --- a/tutorial/ansible/what.md +++ b/tutorial/ansible/what.md @@ -7,7 +7,7 @@ Accéder à la machine virtuelle ------------------------------ Une fois la machine virtuelle démarrée, vous pouvez vous y connecter en `root` -avec le mot de passe `adlin2021`. +avec le mot de passe `adlin2022`. Vous pouvez également démarrer en mode *single user*, mais comme votre disque n'est sans doute pas encore utilisable à ce stade, vous ne pourrez pas changer From 6fcdc449527b4ed093d0edfa1775d086235af328 Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Wed, 3 Mar 2021 18:43:11 +0100 Subject: [PATCH 03/23] Add missing deps for tuto2 --- Makefile | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 278134f..1359610 100644 --- a/Makefile +++ b/Makefile @@ -50,6 +50,10 @@ pkg/wg-manager: pkg/wg-manager/cmd/register.go pkg/wg-manager/cmd/main.go pkg/wg server.iso: server.yml students.csv ssl/fullchain.pem ssl/privkey.pem challenge-initrd.img pkg/arp-spoofer pkg/login-validator pkg/monit pkg/postfix pkg/tftpd pkg/unbound pkg/wg-manager challenge-kernel login-initrd.img linuxkit build -docker -format iso-bios $< +pkg/debian-tuto2: pkg/debian-tuto2/sshd_config pkg/debian-tuto2/gai.conf pkg/debian-tuto2/isolinux.cfg pkg/debian-tuto2/build.yml pkg/debian-tuto2/default.script pkg/debian-tuto2/issue pkg/debian-tuto2/Dockerfile + linuxkit pkg build -org nemunaire pkg/debian-tuto2/ + touch pkg/debian-tuto2 + tuto2-kernel: tuto2.yml linuxkit build -docker $< tuto2-initrd.img: tuto2.yml @@ -57,7 +61,7 @@ tuto2-initrd.img: tuto2.yml tuto2-cmdline: tuto2.yml linuxkit build -docker $< -tuto2.iso: tuto2.yml tuto2-kernel tuto2-initrd.img tuto2-cmdline +tuto2.iso: tuto2.yml pkg/debian-tuto2 tuto2-kernel tuto2-initrd.img tuto2-cmdline linuxkit build -docker -format iso-bios $< tuto2-srs.iso: tuto2.iso pkg/debian-tuto2/isolinux.cfg From d28b14fa504a78bdaa9834328e4510d63cc18cee Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Wed, 3 Mar 2021 18:44:26 +0100 Subject: [PATCH 04/23] token-validator: better calculate IP contained --- libadlin/tunnel.go | 9 +++++++++ token-validator/domain.go | 2 +- token-validator/wg.go | 2 +- 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/libadlin/tunnel.go b/libadlin/tunnel.go index 333903e..df0772b 100644 --- a/libadlin/tunnel.go +++ b/libadlin/tunnel.go @@ -12,10 +12,19 @@ import ( "time" ) +const StdNetmask = 80 + func StudentIP(idstd int64) net.IP { return net.ParseIP(fmt.Sprintf("2a01:e0a:2b:2252:%x::", idstd)) } +func StudentNet(idstd int64) *net.IPNet { + return &net.IPNet{ + IP: StudentIP(idstd), + Mask: net.CIDRMask(StdNetmask, 128), + } +} + type WGDump struct { PubKey string PSK string diff --git a/token-validator/domain.go b/token-validator/domain.go index 2497a72..a4c0078 100644 --- a/token-validator/domain.go +++ b/token-validator/domain.go @@ -246,7 +246,7 @@ func AddAssociatedDomains(student adlin.Student, aaaa net.IP) (err error) { if aaaa == nil { aaaa = net.ParseIP(adlin.StudentIP(student.Id).String() + "1") - } else if !strings.HasPrefix(aaaa.String(), adlin.StudentIP(student.Id).String()) { + } else if !adlin.StudentNet(student.Id).Contains(aaaa) { return errors.New("The associated IP has to be in your IP range.") } diff --git a/token-validator/wg.go b/token-validator/wg.go index d68992f..437f870 100644 --- a/token-validator/wg.go +++ b/token-validator/wg.go @@ -63,7 +63,7 @@ func getTunnelInfo(student int64) TunnelInfo { SrvPubKey: srv_pubkey, SrvPort: 42912, CltIPv6: adlin.StudentIP(student), - CltRange: 80, + CltRange: adlin.StdNetmask, SrvGW6: "2a01:e0a:2b:2252::1", } } From 8427a0adb85d817f1c4c4ae99b9eeaa11c07f146 Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Thu, 4 Mar 2021 01:04:58 +0100 Subject: [PATCH 05/23] token-validator: display user associated domain and delegation --- checker/checker.go | 6 ++++-- libadlin/db.go | 4 +++- libadlin/domain.go | 12 ++++++++++-- libadlin/students.go | 24 +++++++++++++----------- 4 files changed, 30 insertions(+), 16 deletions(-) diff --git a/checker/checker.go b/checker/checker.go index ddddaad..79212b4 100644 --- a/checker/checker.go +++ b/checker/checker.go @@ -86,8 +86,10 @@ func check_dns(domain, ip string) (aaaa net.IP, err error) { err = errors.New("failed to get a valid answer") } - if len(r.Answer) > 0 { - aaaa = r.Answer[0].(*dns.AAAA).AAAA + for _, answer := range r.Answer { + if t, ok := answer.(*dns.AAAA); ok { + aaaa = t.AAAA + } } return diff --git a/libadlin/db.go b/libadlin/db.go index 44551f1..ed04e46 100644 --- a/libadlin/db.go +++ b/libadlin/db.go @@ -56,7 +56,9 @@ func DBCreate() (err error) { CREATE TABLE IF NOT EXISTS students( id_student INTEGER NOT NULL PRIMARY KEY AUTO_INCREMENT, login VARCHAR(255) NOT NULL UNIQUE, - time TIMESTAMP NOT NULL + time TIMESTAMP NOT NULL, + associatedDomain VARCHAR(255) UNIQUE, + delegatedDomain VARCHAR(255) UNIQUE ) DEFAULT CHARACTER SET = utf8 COLLATE = utf8_bin; `); err != nil { return diff --git a/libadlin/domain.go b/libadlin/domain.go index 7df578e..113e5f1 100644 --- a/libadlin/domain.go +++ b/libadlin/domain.go @@ -11,11 +11,19 @@ const ( ) func (student Student) MyDelegatedDomain() string { - return fmt.Sprintf("%s.%s", strings.Trim(strings.Replace(student.Login, "_", "-", -1), "-_"), DelegatedDomainSuffix) + if student.DelegatedDomain != nil { + return *student.DelegatedDomain + } else { + return fmt.Sprintf("%s.%s", strings.Trim(strings.Replace(student.Login, "_", "-", -1), "-_"), DelegatedDomainSuffix) + } } func (student Student) MyAssociatedDomain() string { - return fmt.Sprintf("%s.%s", strings.Trim(strings.Replace(student.Login, "_", "-", -1), "-_"), AssociatedDomainSuffix) + if student.AssociatedDomain != nil { + return *student.AssociatedDomain + } else { + return fmt.Sprintf("%s.%s", strings.Trim(strings.Replace(student.Login, "_", "-", -1), "-_"), AssociatedDomainSuffix) + } } func (student Student) GetAssociatedDomains() (ds []string) { diff --git a/libadlin/students.go b/libadlin/students.go index c4790da..23a18e9 100644 --- a/libadlin/students.go +++ b/libadlin/students.go @@ -7,22 +7,24 @@ import ( ) type Student struct { - Id int64 `json:"id"` - Login string `json:"login"` - Time *time.Time `json:"time"` - IP *string `json:"ip"` - MAC *string `json:"mac"` + Id int64 `json:"id"` + Login string `json:"login"` + Time *time.Time `json:"time"` + IP *string `json:"ip"` + MAC *string `json:"mac"` + AssociatedDomain *string `json:"associated_domain,omitempty"` + DelegatedDomain *string `json:"delegated_domain,omitempty"` } func GetStudents() (students []Student, err error) { - if rows, errr := DBQuery("SELECT S.id_student, S.login, MAX(L.time), L.ip, L.mac FROM students S INNER JOIN (SELECT a.id_student, a.time, a.ip, a.mac FROM student_login a INNER JOIN (SELECT id_student, MAX(time) AS time FROM student_login GROUP BY id_student) b ON a.id_student = b.id_student AND a.time = b.time) L ON S.id_student = L.id_student GROUP BY id_student"); errr != nil { + if rows, errr := DBQuery("SELECT S.id_student, S.login, MAX(L.time), L.ip, L.mac, S.associatedDomain, S.delegatedDomain FROM students S INNER JOIN (SELECT a.id_student, a.time, a.ip, a.mac FROM student_login a INNER JOIN (SELECT id_student, MAX(time) AS time FROM student_login GROUP BY id_student) b ON a.id_student = b.id_student AND a.time = b.time) L ON S.id_student = L.id_student GROUP BY id_student"); errr != nil { return nil, errr } else { defer rows.Close() for rows.Next() { var s Student - if err = rows.Scan(&s.Id, &s.Login, &s.Time, &s.IP, &s.MAC); err != nil { + if err = rows.Scan(&s.Id, &s.Login, &s.Time, &s.IP, &s.MAC, &s.AssociatedDomain, &s.DelegatedDomain); err != nil { return } students = append(students, s) @@ -36,12 +38,12 @@ func GetStudents() (students []Student, err error) { } func GetStudent(id int) (s Student, err error) { - err = DBQueryRow("SELECT S.id_student, S.login, MAX(L.time), L.ip, L.mac FROM students S INNER JOIN (SELECT a.id_student, a.time, a.ip, a.mac FROM student_login a INNER JOIN (SELECT id_student, MAX(time) AS time FROM student_login GROUP BY id_student) b ON a.id_student = b.id_student AND a.time = b.time) L ON S.id_student = L.id_student WHERE S.id_student=?", id).Scan(&s.Id, &s.Login, &s.Time, &s.IP, &s.MAC) + err = DBQueryRow("SELECT S.id_student, S.login, MAX(L.time), L.ip, L.mac, S.associatedDomain, S.delegatedDomain FROM students S INNER JOIN (SELECT a.id_student, a.time, a.ip, a.mac FROM student_login a INNER JOIN (SELECT id_student, MAX(time) AS time FROM student_login GROUP BY id_student) b ON a.id_student = b.id_student AND a.time = b.time) L ON S.id_student = L.id_student WHERE S.id_student=?", id).Scan(&s.Id, &s.Login, &s.Time, &s.IP, &s.MAC, &s.AssociatedDomain, &s.DelegatedDomain) return } func GetStudentByLogin(login string) (s Student, err error) { - err = DBQueryRow("SELECT S.id_student, S.login, MAX(L.time), L.ip, L.mac FROM students S INNER JOIN (SELECT a.id_student, a.time, a.ip, a.mac FROM student_login a INNER JOIN (SELECT id_student, MAX(time) AS time FROM student_login GROUP BY id_student) b ON a.id_student = b.id_student AND a.time = b.time) L ON S.id_student = L.id_student WHERE login=?", login).Scan(&s.Id, &s.Login, &s.Time, &s.IP, &s.MAC) + err = DBQueryRow("SELECT S.id_student, S.login, MAX(L.time), L.ip, L.mac, S.associatedDomain, S.delegatedDomain FROM students S INNER JOIN (SELECT a.id_student, a.time, a.ip, a.mac FROM student_login a INNER JOIN (SELECT id_student, MAX(time) AS time FROM student_login GROUP BY id_student) b ON a.id_student = b.id_student AND a.time = b.time) L ON S.id_student = L.id_student WHERE login=?", login).Scan(&s.Id, &s.Login, &s.Time, &s.IP, &s.MAC, &s.AssociatedDomain, &s.DelegatedDomain) return } @@ -58,7 +60,7 @@ func NewStudent(login string) (Student, error) { } else if sid, err := res.LastInsertId(); err != nil { return Student{}, err } else { - return Student{sid, login, &t, nil, nil}, nil + return Student{sid, login, &t, nil, nil, nil, nil}, nil } } @@ -67,7 +69,7 @@ func (s Student) GetPKey() []byte { } func (s Student) Update() (int64, error) { - if res, err := DBExec("UPDATE students SET login = ?, time = ? WHERE id_student = ?", s.Login, s.Time, s.Id); err != nil { + if res, err := DBExec("UPDATE students SET login = ?, time = ?, associatedDomain = ?, delegatedDomain = ? WHERE id_student = ?", s.Login, s.Time, s.AssociatedDomain, s.DelegatedDomain, s.Id); err != nil { return 0, err } else if nb, err := res.RowsAffected(); err != nil { return 0, err From b6eb652929943745e6b4b6867b8e9806a0361545 Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Thu, 4 Mar 2021 01:05:24 +0100 Subject: [PATCH 06/23] token-validator: display custom IP --- libadlin/tunnel.go | 4 ++++ token-validator/htdocs/dashboard.html | 2 +- token-validator/ip.go | 22 +++++++++++++++++++++- 3 files changed, 26 insertions(+), 2 deletions(-) diff --git a/libadlin/tunnel.go b/libadlin/tunnel.go index df0772b..9f8ff77 100644 --- a/libadlin/tunnel.go +++ b/libadlin/tunnel.go @@ -103,6 +103,10 @@ type TunnelToken struct { Dump *WGDump } +func (tt *TunnelToken) GetStudentIP() string { + return fmt.Sprintf("%s%x", StudentIP(tt.IdStudent).String(), tt.SuffixIP) +} + func TokenFromText(token string) []byte { sha := sha512.Sum512([]byte(token)) return sha[:] diff --git a/token-validator/htdocs/dashboard.html b/token-validator/htdocs/dashboard.html index 8815e14..19e708d 100644 --- a/token-validator/htdocs/dashboard.html +++ b/token-validator/htdocs/dashboard.html @@ -72,7 +72,7 @@

diff --git a/token-validator/ip.go b/token-validator/ip.go index aca9a79..838ae33 100644 --- a/token-validator/ip.go +++ b/token-validator/ip.go @@ -3,6 +3,7 @@ package main import ( "fmt" "net" + "strconv" "github.com/julienschmidt/httprouter" @@ -54,13 +55,32 @@ func showIPs(_ httprouter.Params, body []byte) (interface{}, error) { return r, nil } +func GetStudentTunnelIPs(student adlin.Student) (ips []string) { + if ts, err := student.GetActivesTunnels(); err != nil || len(ts) == 0 || ts[0].SuffixIP == 0 { + ips = append(ips, adlin.StudentIP(student.Id).String()+"1") + } else { + for _, t := range ts { + ips = append(ips, t.GetTunnelIP()) + } + } + return +} + func getStudentIPs(student adlin.Student) (r map[string]string) { r = make(map[string]string) r["vlan0"] = IPSuffix(student, net.IPNet{net.ParseIP("172.23.0.0"), net.CIDRMask(17, 32)}).String() r["wg0"] = IPSuffix(student, net.IPNet{net.ParseIP("172.17.0.0"), net.CIDRMask(16, 32)}).String() r["vlan7"] = IPSuffix(student, net.IPNet{net.ParseIP("172.23.142.0"), net.CIDRMask(23, 32)}).String() - r["wg"] = adlin.StudentIP(student.Id).String() + + for d, ip := range GetStudentTunnelIPs(student) { + key := "wg" + if d > 0 { + key += strconv.Itoa(d) + } + r[key] = ip + } + r["adn"] = student.MyAssociatedDomain() r["ddn"] = student.MyDelegatedDomain() From 0af743769349594e4a49c24574597b9707437e38 Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Thu, 4 Mar 2021 01:05:46 +0100 Subject: [PATCH 07/23] libadlin: avoid # char, anchor... --- libadlin/tunnel.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libadlin/tunnel.go b/libadlin/tunnel.go index 9f8ff77..877c139 100644 --- a/libadlin/tunnel.go +++ b/libadlin/tunnel.go @@ -130,7 +130,7 @@ func (student Student) NewTunnelToken(suffixip int) (t TunnelToken, err error) { return } - t.TokenText = strings.Replace(strings.Replace(strings.Replace(strings.Replace(strings.Replace(base64.RawStdEncoding.EncodeToString(tok), "/", ".", -1), "+", "_", -1), "O", "#", -1), "l", "$", -1), "I", ">", -1) + t.TokenText = strings.Replace(strings.Replace(strings.Replace(strings.Replace(strings.Replace(base64.RawStdEncoding.EncodeToString(tok), "/", ".", -1), "+", "_", -1), "O", "<", -1), "l", "$", -1), "I", ">", -1) t.token = TokenFromText(t.TokenText) t.IdStudent = student.Id From 9cd237daff25abc948def6be96362f0cbd68ac21 Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Thu, 4 Mar 2021 01:32:09 +0100 Subject: [PATCH 08/23] checker: for each tun IP --- checker/checker.go | 205 ++++++++++++++++++++++-------------------- libadlin/tunnel.go | 6 +- token-validator/ip.go | 2 +- 3 files changed, 112 insertions(+), 101 deletions(-) diff --git a/checker/checker.go b/checker/checker.go index 79212b4..4cf16c7 100644 --- a/checker/checker.go +++ b/checker/checker.go @@ -129,7 +129,7 @@ func check_https(domain, ip string) (err error) { // Main -func minTunnelVersion(std adlin.Student) (int, error) { +func minTunnelVersion(std adlin.Student, suffixip int) (int, error) { tunnels, err := std.GetTunnelTokens() if err != nil { return 0, err @@ -141,7 +141,7 @@ func minTunnelVersion(std adlin.Student) (int, error) { continue } - if tunnel.Dump != nil && tunnel.Version < minversion { + if tunnel.Dump != nil && tunnel.Version < minversion && suffixip == tunnel.SuffixIP { minversion = tunnel.Version } } @@ -161,104 +161,111 @@ func studentsChecker() { time.Sleep(250 * time.Millisecond) // Check ping std := s - stdIP := adlin.StudentIP(std.Id).String() + "1" - go check_ping(stdIP, func(pkt *ping.Packet) { - tunnel_version, err := minTunnelVersion(std) - if verbose { - log.Printf("%s PONG; version=%d (%v)\n", std.Login, tunnel_version, err) - } - std.OnPong(true) + tuns, err := std.GetActivesTunnels() + if err != nil { + continue + } + + for _, tun := range tuns { + stdIP := tun.GetStudentIP() + go check_ping(stdIP, func(pkt *ping.Packet) { + tunnel_version, err := minTunnelVersion(std, tun.SuffixIP) + if verbose { + log.Printf("%s PONG (on %x); version=%d (%v)\n", std.Login, tun.SuffixIP, tunnel_version, err) + } + std.OnPong(true) + + if tunnel_version == 2147483647 || tunnel_version == 0 { + log.Printf("%s unknown tunnel version: %d skipping tests (%v)", std.Login, tunnel_version, err) + return + } + + dnsIP := stdIP + // Is GLUE defined? + if glueIP, err := get_GLUE(std.MyDelegatedDomain()); glueIP != nil { + dnsIP = glueIP.String() + + if verbose { + log.Printf("%s has defined GLUE: %s\n", std.Login, dnsIP) + } + } else if err != nil { + log.Printf("%s and GLUE: %s\n", std.Login, err) + } + + // Check DNS + if addr, err := check_dns(std.MyDelegatedDomain(), dnsIP); err == nil { + if verbose { + log.Printf("%s just unlocked DNS challenge\n", std.Login) + } + if _, err := std.UnlockNewChallenge(100*(tunnel_version-1)+2, ""); err != nil { + if _, err := std.UpdateUnlockedChallenge(100*(tunnel_version-1)+2, ""); err != nil { + log.Printf("Unable to register challenge for %s: %s\n", std.Login, err.Error()) + } + } + + // Check HTTP with DNS + if addr == nil { + log.Printf("%s and HTTP (with DNS ip=%s): skipped due to empty response\n", std.Login, addr.String()) + } else if err := check_http(addr.String()); err == nil { + if verbose { + log.Printf("%s just unlocked HTTP challenge\n", std.Login) + } + if _, err := std.UnlockNewChallenge(100*(tunnel_version-1)+0, ""); err != nil { + if _, err := std.UpdateUnlockedChallenge(100*(tunnel_version-1)+0, ""); err != nil { + log.Printf("Unable to register challenge for %s: %s\n", std.Login, err.Error()) + } + } + } else if verbose { + log.Printf("%s and HTTP (with DNS ip=%s): %s\n", std.Login, addr.String(), err) + } + + // Check HTTPs with DNS + if addr == nil { + log.Printf("%s and HTTPS (with DNS ip=%s): skipped due to empty response\n", std.Login, addr.String()) + } else if err := check_https(std.MyDelegatedDomain(), addr.String()); err == nil { + if verbose { + log.Printf("%s just unlocked HTTPS challenge\n", std.Login) + } + if _, err := std.UnlockNewChallenge(100*(tunnel_version-1)+1, ""); err != nil { + if _, err := std.UpdateUnlockedChallenge(100*(tunnel_version-1)+1, ""); err != nil { + log.Printf("Unable to register challenge for %s: %s\n", std.Login, err.Error()) + } + } + } else if verbose { + log.Printf("%s and HTTPS (with DNS ip=%s): %s\n", std.Login, addr.String(), err) + } + } else { + // Check HTTP without DNS + if err := check_http(stdIP); err == nil { + if verbose { + log.Printf("%s just unlocked HTTP challenge\n", std.Login) + } + if _, err := std.UnlockNewChallenge(100*(tunnel_version-1)+0, ""); err != nil { + if _, err := std.UpdateUnlockedChallenge(100*(tunnel_version-1)+0, ""); err != nil { + log.Printf("Unable to register challenge for %s: %s\n", std.Login, err.Error()) + } + } + } else if verbose { + log.Printf("%s and HTTP (without DNS): %s\n", std.Login, err) + } + + // Check HTTPs without DNS + if err := check_https(std.MyAssociatedDomain(), stdIP); err == nil { + if verbose { + log.Printf("%s just unlocked HTTPS challenge\n", std.Login) + } + if _, err := std.UnlockNewChallenge(100*(tunnel_version-1)+1, ""); err != nil { + if _, err := std.UpdateUnlockedChallenge(100*(tunnel_version-1)+1, ""); err != nil { + log.Printf("Unable to register challenge for %s: %s\n", std.Login, err.Error()) + } + } + } else if verbose { + log.Printf("%s and HTTPS (without DNS): %s\n", std.Login, err) + } + } - if tunnel_version == 2147483647 || tunnel_version == 0 { - log.Printf("%s unknown tunnel version: %d skipping tests (%v)", std.Login, tunnel_version, err) return - } - - dnsIP := stdIP - // Is GLUE defined? - if glueIP, err := get_GLUE(std.MyDelegatedDomain()); glueIP != nil { - dnsIP = glueIP.String() - - if verbose { - log.Printf("%s has defined GLUE: %s\n", std.Login, dnsIP) - } - } else if err != nil { - log.Printf("%s and GLUE: %s\n", std.Login, err) - } - - // Check DNS - if addr, err := check_dns(std.MyDelegatedDomain(), dnsIP); err == nil { - if verbose { - log.Printf("%s just unlocked DNS challenge\n", std.Login) - } - if _, err := std.UnlockNewChallenge(100*(tunnel_version-1)+2, ""); err != nil { - if _, err := std.UpdateUnlockedChallenge(100*(tunnel_version-1)+2, ""); err != nil { - log.Printf("Unable to register challenge for %s: %s\n", std.Login, err.Error()) - } - } - - // Check HTTP with DNS - if addr == nil { - log.Printf("%s and HTTP (with DNS ip=%s): skipped due to empty response\n", std.Login, addr.String()) - } else if err := check_http(addr.String()); err == nil { - if verbose { - log.Printf("%s just unlocked HTTP challenge\n", std.Login) - } - if _, err := std.UnlockNewChallenge(100*(tunnel_version-1)+0, ""); err != nil { - if _, err := std.UpdateUnlockedChallenge(100*(tunnel_version-1)+0, ""); err != nil { - log.Printf("Unable to register challenge for %s: %s\n", std.Login, err.Error()) - } - } - } else if verbose { - log.Printf("%s and HTTP (with DNS ip=%s): %s\n", std.Login, addr.String(), err) - } - - // Check HTTPs with DNS - if addr == nil { - log.Printf("%s and HTTPS (with DNS ip=%s): skipped due to empty response\n", std.Login, addr.String()) - } else if err := check_https(std.MyDelegatedDomain(), addr.String()); err == nil { - if verbose { - log.Printf("%s just unlocked HTTPS challenge\n", std.Login) - } - if _, err := std.UnlockNewChallenge(100*(tunnel_version-1)+1, ""); err != nil { - if _, err := std.UpdateUnlockedChallenge(100*(tunnel_version-1)+1, ""); err != nil { - log.Printf("Unable to register challenge for %s: %s\n", std.Login, err.Error()) - } - } - } else if verbose { - log.Printf("%s and HTTPS (with DNS ip=%s): %s\n", std.Login, addr.String(), err) - } - } else { - // Check HTTP without DNS - if err := check_http(stdIP); err == nil { - if verbose { - log.Printf("%s just unlocked HTTP challenge\n", std.Login) - } - if _, err := std.UnlockNewChallenge(100*(tunnel_version-1)+0, ""); err != nil { - if _, err := std.UpdateUnlockedChallenge(100*(tunnel_version-1)+0, ""); err != nil { - log.Printf("Unable to register challenge for %s: %s\n", std.Login, err.Error()) - } - } - } else if verbose { - log.Printf("%s and HTTP (without DNS): %s\n", std.Login, err) - } - - // Check HTTPs without DNS - if err := check_https(std.MyAssociatedDomain(), stdIP); err == nil { - if verbose { - log.Printf("%s just unlocked HTTPS challenge\n", std.Login) - } - if _, err := std.UnlockNewChallenge(100*(tunnel_version-1)+1, ""); err != nil { - if _, err := std.UpdateUnlockedChallenge(100*(tunnel_version-1)+1, ""); err != nil { - log.Printf("Unable to register challenge for %s: %s\n", std.Login, err.Error()) - } - } - } else if verbose { - log.Printf("%s and HTTPS (without DNS): %s\n", std.Login, err) - } - } - - return - }) + }) + } } } diff --git a/libadlin/tunnel.go b/libadlin/tunnel.go index 877c139..db96e47 100644 --- a/libadlin/tunnel.go +++ b/libadlin/tunnel.go @@ -104,7 +104,11 @@ type TunnelToken struct { } func (tt *TunnelToken) GetStudentIP() string { - return fmt.Sprintf("%s%x", StudentIP(tt.IdStudent).String(), tt.SuffixIP) + if tt.SuffixIP == 0 { + return fmt.Sprintf("%s%x", StudentIP(tt.IdStudent).String(), 1) + } else { + return fmt.Sprintf("%s%x", StudentIP(tt.IdStudent).String(), tt.SuffixIP) + } } func TokenFromText(token string) []byte { diff --git a/token-validator/ip.go b/token-validator/ip.go index 838ae33..7036cc4 100644 --- a/token-validator/ip.go +++ b/token-validator/ip.go @@ -60,7 +60,7 @@ func GetStudentTunnelIPs(student adlin.Student) (ips []string) { ips = append(ips, adlin.StudentIP(student.Id).String()+"1") } else { for _, t := range ts { - ips = append(ips, t.GetTunnelIP()) + ips = append(ips, t.GetStudentIP()) } } return From efab34d551699b2acb10d141946cd584059ded59 Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Thu, 4 Mar 2021 02:01:26 +0100 Subject: [PATCH 09/23] checker: don't validate http challenge if error status returns --- checker/checker.go | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/checker/checker.go b/checker/checker.go index 4cf16c7..d8b252a 100644 --- a/checker/checker.go +++ b/checker/checker.go @@ -110,6 +110,11 @@ func check_http(ip string) (err error) { return } defer resp.Body.Close() + + if resp.StatusCode >= 400 { + return fmt.Errorf("Bad status, got: %d (%s)", resp.StatusCode, resp.Status) + } + _, err = ioutil.ReadAll(resp.Body) return } @@ -123,6 +128,11 @@ func check_https(domain, ip string) (err error) { return } defer resp.Body.Close() + + if resp.StatusCode >= 300 { + return fmt.Errorf("Bad status, got: %d (%s)", resp.StatusCode, resp.Status) + } + _, err = ioutil.ReadAll(resp.Body) return } From 5a4650f70e6e4184397ca44be0e0832e5122eb57 Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Fri, 5 Mar 2021 14:52:14 +0100 Subject: [PATCH 10/23] token-validator: update to handle custom domains --- libadlin/domain.go | 13 ++- token-validator/domain.go | 64 +++++++++-- token-validator/htdocs/js/adlin-main.js | 117 ++++++++++++++++++++ token-validator/htdocs/views/domains.html | 128 +++++++++++++++++++++- 4 files changed, 307 insertions(+), 15 deletions(-) diff --git a/libadlin/domain.go b/libadlin/domain.go index 113e5f1..f5d7f7d 100644 --- a/libadlin/domain.go +++ b/libadlin/domain.go @@ -18,18 +18,27 @@ func (student Student) MyDelegatedDomain() string { } } +func (student Student) DefaultAssociatedDomain() string { + return fmt.Sprintf("%s.%s", strings.Trim(strings.Replace(student.Login, "_", "-", -1), "-_"), AssociatedDomainSuffix) +} + func (student Student) MyAssociatedDomain() string { if student.AssociatedDomain != nil { return *student.AssociatedDomain } else { - return fmt.Sprintf("%s.%s", strings.Trim(strings.Replace(student.Login, "_", "-", -1), "-_"), AssociatedDomainSuffix) + return student.DefaultAssociatedDomain() } } func (student Student) GetAssociatedDomains() (ds []string) { + defdn := student.DefaultAssociatedDomain() + ds = append(ds, defdn) + studentDomain := student.MyAssociatedDomain() - ds = append(ds, studentDomain) + if defdn != studentDomain { + ds = append(ds, studentDomain) + } return } diff --git a/token-validator/domain.go b/token-validator/domain.go index a4c0078..9f789d7 100644 --- a/token-validator/domain.go +++ b/token-validator/domain.go @@ -30,18 +30,38 @@ func init() { Domain string `json:"domain"` A string `json:"a"` AAAA string `json:"aaaa"` + CNAME string `json:"cname,omitempty"` }{} if err := json.Unmarshal(body, &ue); err != nil { return nil, err } - var aaaa net.IP - if ue != nil && len(ue.AAAA) > 0 { - aaaa = net.ParseIP(ue.AAAA) - } + if ue.Domain != "" && ue.A == "" && ue.AAAA == "" && ue.CNAME == "" { + student.AssociatedDomain = nil - return true, AddAssociatedDomains(student, aaaa) + if _, err := student.Update(); err != nil { + return nil, err + } + + return true, nil + } else if ue.CNAME != "" { + cname := dns.Fqdn(ue.CNAME) + student.AssociatedDomain = &cname + + if _, err := student.Update(); err != nil { + return nil, err + } + + return true, nil + } else { + var aaaa net.IP + if ue != nil && len(ue.AAAA) > 0 { + aaaa = net.ParseIP(ue.AAAA) + } + + return true, AddAssociatedDomains(student, aaaa) + } })) router.GET("/api/adomains/:dn", apiAuthHandler(func(student adlin.Student, ps httprouter.Params, body []byte) (interface{}, error) { return GetAssociatedDomain(student, ps.ByName("dn")) @@ -50,6 +70,34 @@ func init() { router.GET("/api/ddomains/", apiAuthHandler(func(student adlin.Student, ps httprouter.Params, body []byte) (interface{}, error) { return []string{student.MyDelegatedDomain()}, nil })) + router.POST("/api/ddomains/", apiAuthHandler(func(student adlin.Student, ps httprouter.Params, body []byte) (interface{}, error) { + ue := &struct { + NS string `json:"ns"` + }{} + + if err := json.Unmarshal(body, &ue); err != nil { + return nil, err + } + + if ue.NS == "" { + student.DelegatedDomain = nil + + if _, err := student.Update(); err != nil { + return nil, err + } + + return true, nil + } else { + ns := dns.Fqdn(ue.NS) + student.DelegatedDomain = &ns + + if _, err := student.Update(); err != nil { + return nil, err + } + + return true, nil + } + })) router.GET("/api/ddomains/:dn/", apiAuthHandler(func(student adlin.Student, ps httprouter.Params, body []byte) (interface{}, error) { return getRRDelegatedDomain(student, ps.ByName("dn"), "") })) @@ -239,7 +287,7 @@ func delAssociatedDomains(student adlin.Student, dn string) (err error) { } func AddAssociatedDomains(student adlin.Student, aaaa net.IP) (err error) { - err = delAssociatedDomains(student, student.MyAssociatedDomain()) + err = delAssociatedDomains(student, student.DefaultAssociatedDomain()) if err != nil { return } @@ -257,12 +305,12 @@ func AddAssociatedDomains(student adlin.Student, aaaa net.IP) (err error) { m2.Question[0] = dns.Question{adlin.AssociatedDomainSuffix, dns.TypeSOA, dns.ClassINET} rrA := new(dns.A) - rrA.Hdr = dns.RR_Header{Name: student.MyAssociatedDomain(), Rrtype: dns.TypeA, Class: dns.ClassINET, Ttl: 3600} + rrA.Hdr = dns.RR_Header{Name: student.DefaultAssociatedDomain(), Rrtype: dns.TypeA, Class: dns.ClassINET, Ttl: 3600} rrA.A = net.IPv4(82, 64, 31, 248) m2.Insert([]dns.RR{rrA}) rrAAAA := new(dns.AAAA) - rrAAAA.Hdr = dns.RR_Header{Name: student.MyAssociatedDomain(), Rrtype: dns.TypeAAAA, Class: dns.ClassINET, Ttl: 3600} + rrAAAA.Hdr = dns.RR_Header{Name: student.DefaultAssociatedDomain(), Rrtype: dns.TypeAAAA, Class: dns.ClassINET, Ttl: 3600} rrAAAA.AAAA = aaaa m2.Insert([]dns.RR{rrAAAA}) diff --git a/token-validator/htdocs/js/adlin-main.js b/token-validator/htdocs/js/adlin-main.js index ef2822f..b553ea1 100644 --- a/token-validator/htdocs/js/adlin-main.js +++ b/token-validator/htdocs/js/adlin-main.js @@ -381,6 +381,123 @@ angular.module("AdLinApp") }); } + $scope.useMyAssociationD = function() { + $scope.assoc = { + "domain": $scope.adomains[0].domain, + "cname": $scope.student.associated_domain?$scope.student.associated_domain:"", + } + $('#AssocMyDomainModal').modal('show'); + } + + $scope.newMyDomainAssociationD = function(assoc) { + $('#AssocMyDomainModal').modal('hide'); + $scope.pleaseWaitNewAssociation = true; + $http({ + method: 'POST', + url: "api/adomains/", + data: assoc, + }).then(function(response) { + $scope.updateAssociationD(); + $scope.checkLoginState(); + $scope.pleaseWaitNewAssociation = false; + $scope.addToast({ + variant: "success", + title: "Maatma Domain Names", + msg: "Votre domaine a bien été associé !", + }); + }, function(response) { + $scope.pleaseWaitNewAssociation = false; + $scope.addToast({ + variant: "danger", + title: "Maatma Domain Names", + msg: "Erreur durant l'association du domaine : " + response.data.errmsg, + }); + }); + } + + $scope.delMyDomainAssociationD = function(assoc) { + $('#AssocMyDomainModal').modal('hide'); + $scope.pleaseWaitNewAssociation = true; + assoc.cname = '' + $http({ + method: 'POST', + url: "api/adomains/", + data: assoc, + }).then(function(response) { + $scope.updateAssociationD(); + $scope.checkLoginState(); + $scope.pleaseWaitNewAssociation = false; + $scope.addToast({ + variant: "success", + title: "Maatma Domain Names", + msg: "Votre domaine n'est plus pris en compte. Vous devez utiliser l'association qui vous a été attribuée sous adlin20xx.p0m.fr.", + }); + }, function(response) { + $scope.pleaseWaitNewAssociation = false; + $scope.addToast({ + variant: "danger", + title: "Maatma Domain Names", + msg: "Erreur durant l'association du domaine : " + response.data.errmsg, + }); + }); + } + + $scope.useMyDelegationD = function() { + $scope.assoc = { + "ns": $scope.student.delegated_domain?$scope.student.delegated_domain:"", + } + $('#DelegateMyDomainModal').modal('show'); + } + + $scope.newMyDomainDelegationD = function(assoc) { + $('#DelegateMyDomainModal').modal('hide'); + $scope.pleaseWaitNewDelegation = true; + $http({ + method: 'POST', + url: "api/ddomains/", + data: assoc, + }).then(function(response) { + $scope.checkLoginState(); + $scope.pleaseWaitNewDelegation = false; + $scope.addToast({ + variant: "success", + title: "Maatma Domain Names", + msg: "Votre sous-domaine de délégation a bien été enregistré !", + }); + }, function(response) { + $scope.pleaseWaitNewDelegation = false; + $scope.addToast({ + variant: "danger", + title: "Maatma Domain Names", + msg: "Erreur durant la délégation du domaine : " + response.data.errmsg, + }); + }); + } + + $scope.delMyDomainDelegatedD = function() { + $scope.pleaseWaitNewDelegation = true; + $http({ + method: 'POST', + url: "api/ddomains/", + data: {}, + }).then(function(response) { + $scope.checkLoginState(); + $scope.pleaseWaitNewDelegation = false; + $scope.addToast({ + variant: "success", + title: "Maatma Domain Names", + msg: "Votre domaine n'est plus pris en compte. Vous devez utiliser la délégation qui vous a été attribuée sous srs.p0m.fr.", + }); + }, function(response) { + $scope.pleaseWaitNewDelegation = false; + $scope.addToast({ + variant: "danger", + title: "Maatma Domain Names", + msg: "Erreur durant la délégation du domaine : " + response.data.errmsg, + }); + }); + } + $scope.addNS = function(domain) { $scope.nsrr = { "domain": domain, diff --git a/token-validator/htdocs/views/domains.html b/token-validator/htdocs/views/domains.html index cd2161a..fe02e77 100644 --- a/token-validator/htdocs/views/domains.html +++ b/token-validator/htdocs/views/domains.html @@ -2,7 +2,10 @@ Noms de domaine -

Association simple

+

+ Association simple + ? +

@@ -28,6 +31,9 @@ Demander une nouvelle association + @@ -35,7 +41,10 @@
-

Délégation

+

+ Délégation + ? +

-
+
-

{{ domain }}

+

+ {{ domain }} + +

@@ -65,7 +80,7 @@ - - - - + + + +
{{ val }} - Not implemented yet + @@ -180,7 +195,31 @@ +
+

+ {{ student.delegated_domain }} + +

+

+ Vous avez choisi d'utiliser votre propre domaine pour réaliser la délégation. +

+

+ L'interface de maatma ne vous est plus utile, car pour réaliser la délégation, vous devez passer par l'interface de votre bureau d'enregistrement. +

+

+ Pour rappel, voici les enregistrements à rajouter : +

+
+;; Delegation {{ student.delegated_domain }} to the given name server
+{{ student.delegated_domain }} 300 IN NS ns.{{ student.delegated_domain }}
 
+;; GLUE record to serve along with the previous record
+ns.{{ student.delegated_domain }} 300 IN AAAA [your NS ip]
+  
+
+ + + +
TokenSuffixDernière utilisationClef publique + Token + ? + + Suffix + ? + + Dernière utilisation + ? + + Clef publique + ? +
- > - + > + {{ tunnel.TokenText }} @@ -55,7 +67,10 @@
-

Paramètres du tunnel

+

+ Paramètres du tunnel + ? +

  • Statut : {{ wginfo.status }}
  • Clef publique du serveur : {{ wginfo.srv_pubkey }}
  • @@ -64,10 +79,11 @@
  • Gateway/passerelle IPv6 : {{ wginfo.srv_gw6 }}
-
+

État de mon tunnel - 💻 + 💻 + ?

  • Clef publique pair : {{ tunnel.Dump.PubKey }}
  • From 059e3cabecd8f8164ca79708e8426ff104c10520 Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Fri, 5 Mar 2021 15:13:34 +0100 Subject: [PATCH 15/23] checker: fix problematic file descriptor leak --- checker/checker.go | 1 + 1 file changed, 1 insertion(+) diff --git a/checker/checker.go b/checker/checker.go index d8b252a..946acae 100644 --- a/checker/checker.go +++ b/checker/checker.go @@ -25,6 +25,7 @@ func check_ping(ip string, cb func(pkt *ping.Packet)) (err error) { if err != nil { return } + defer pinger.Stop() pinger.Timeout = time.Second * 5 pinger.Count = 1 From 4a27c9367e46c18c70bbc06919083b50b48d4423 Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Fri, 5 Mar 2021 15:13:59 +0100 Subject: [PATCH 16/23] checker: default NS as const --- checker/checker.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/checker/checker.go b/checker/checker.go index 946acae..dead7f7 100644 --- a/checker/checker.go +++ b/checker/checker.go @@ -15,6 +15,10 @@ import ( "git.nemunai.re/lectures/adlin/libadlin" ) +const ( + DEFAULT_RESOLVER = "2a01:e0a:2b:2250::1" +) + var verbose = false // ICMP @@ -47,7 +51,7 @@ func get_GLUE(domain string) (aaaa net.IP, err error) { m.SetEdns0(4096, true) var r *dns.Msg - r, _, err = client.Exchange(m, "[2a01:e0a:25a:9160::2]:53") + r, _, err = client.Exchange(m, "[2a01:e0a:2b:2250::b]:53") if err != nil { return } From 853477e54a575a138b3751492a4243166c8bd42c Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Fri, 5 Mar 2021 15:14:45 +0100 Subject: [PATCH 17/23] checker: deep redesign --- checker/checker.go | 122 ++++++++++++++++++-------- libadlin/db.go | 13 +++ libadlin/students.go | 31 +++++++ token-validator/htdocs/dashboard.html | 4 +- 4 files changed, 132 insertions(+), 38 deletions(-) diff --git a/checker/checker.go b/checker/checker.go index dead7f7..c85ef00 100644 --- a/checker/checker.go +++ b/checker/checker.go @@ -7,6 +7,7 @@ import ( "log" "net" "net/http" + "strings" "time" "github.com/miekg/dns" @@ -102,21 +103,30 @@ func check_dns(domain, ip string) (aaaa net.IP, err error) { // PORT 80 -func check_http(ip string) (err error) { +func check_http(ip, dn string) (err error) { client := &http.Client{ CheckRedirect: func(req *http.Request, via []*http.Request) error { return http.ErrUseLastResponse }, } + req, errr := http.NewRequest("GET", fmt.Sprintf("http://[%s]/", ip), nil) + if errr != nil { + return errr + } + + if dn != "" { + req.Header.Add("Host", strings.TrimSuffix(dn, ".")) + } + var resp *http.Response - resp, err = client.Get(fmt.Sprintf("http://[%s]/", ip)) + resp, err = client.Do(req) if err != nil { return } defer resp.Body.Close() - if resp.StatusCode >= 400 { + if dn != "" && resp.StatusCode >= 400 { return fmt.Errorf("Bad status, got: %d (%s)", resp.StatusCode, resp.Status) } @@ -128,12 +138,25 @@ func check_http(ip string) (err error) { func check_https(domain, ip string) (err error) { var resp *http.Response - resp, err = http.Get(fmt.Sprintf("https://%s/", domain)) + resp, err = http.Get(fmt.Sprintf("https://%s/", strings.TrimSuffix(domain, "."))) if err != nil { return } defer resp.Body.Close() + if resp.StatusCode >= 300 && resp.StatusCode < 400 { + loc := resp.Header.Get("Location") + if loc != "" && strings.HasSuffix(dns.Fqdn(loc), domain) { + if dns.Fqdn(loc) == domain { + return fmt.Errorf("Redirection loop %s redirect to %s", domain, loc) + } else if err = check_https(dns.Fqdn(loc), ip); err != nil { + return fmt.Errorf("Error after following redirection to %s: %w", loc, err) + } else { + return + } + } + } + if resp.StatusCode >= 300 { return fmt.Errorf("Bad status, got: %d (%s)", resp.StatusCode, resp.Status) } @@ -212,26 +235,25 @@ func studentsChecker() { if verbose { log.Printf("%s just unlocked DNS challenge\n", std.Login) } - if _, err := std.UnlockNewChallenge(100*(tunnel_version-1)+2, ""); err != nil { - if _, err := std.UpdateUnlockedChallenge(100*(tunnel_version-1)+2, ""); err != nil { - log.Printf("Unable to register challenge for %s: %s\n", std.Login, err.Error()) - } + if _, err := std.UnlockChallenge(100*(tunnel_version-1)+3, ""); err != nil { + log.Printf("Unable to register challenge for %s: %s\n", std.Login, err.Error()) } // Check HTTP with DNS if addr == nil { log.Printf("%s and HTTP (with DNS ip=%s): skipped due to empty response\n", std.Login, addr.String()) - } else if err := check_http(addr.String()); err == nil { + } else if err := check_http(addr.String(), std.MyDelegatedDomain()); err == nil { if verbose { log.Printf("%s just unlocked HTTP challenge\n", std.Login) } - if _, err := std.UnlockNewChallenge(100*(tunnel_version-1)+0, ""); err != nil { - if _, err := std.UpdateUnlockedChallenge(100*(tunnel_version-1)+0, ""); err != nil { - log.Printf("Unable to register challenge for %s: %s\n", std.Login, err.Error()) - } + if _, err := std.UnlockChallenge(100*(tunnel_version-1)+4, ""); err != nil { + log.Printf("Unable to register challenge for %s: %s\n", std.Login, err.Error()) + } + } else { + std.RegisterChallengeError(100*(tunnel_version-1)+4, err) + if verbose { + log.Printf("%s and HTTP (with DNS ip=%s): %s\n", std.Login, addr.String(), err) } - } else if verbose { - log.Printf("%s and HTTP (with DNS ip=%s): %s\n", std.Login, addr.String(), err) } // Check HTTPs with DNS @@ -241,27 +263,54 @@ func studentsChecker() { if verbose { log.Printf("%s just unlocked HTTPS challenge\n", std.Login) } - if _, err := std.UnlockNewChallenge(100*(tunnel_version-1)+1, ""); err != nil { - if _, err := std.UpdateUnlockedChallenge(100*(tunnel_version-1)+1, ""); err != nil { - log.Printf("Unable to register challenge for %s: %s\n", std.Login, err.Error()) - } + if _, err := std.UnlockChallenge(100*(tunnel_version-1)+5, ""); err != nil { + log.Printf("Unable to register challenge for %s: %s\n", std.Login, err.Error()) + } + } else { + std.RegisterChallengeError(100*(tunnel_version-1)+5, err) + if verbose { + log.Printf("%s and HTTPS (with DNS ip=%s): %s\n", std.Login, addr.String(), err) } - } else if verbose { - log.Printf("%s and HTTPS (with DNS ip=%s): %s\n", std.Login, addr.String(), err) } } else { - // Check HTTP without DNS - if err := check_http(stdIP); err == nil { + if errreg := std.RegisterChallengeError(100*(tunnel_version-1)+3, err); errreg != nil { + log.Printf("Unable to register challenge error for %s: %s\n", std.Login, errreg) + } + if verbose { + log.Printf("%s and DNS: %s\n", std.Login, err) + } + } + + // Check HTTP without DNS + if err := check_http(stdIP, ""); err == nil { + if verbose { + log.Printf("%s just unlocked HTTP IP (without DNS) challenge\n", std.Login) + } + if _, err := std.UnlockChallenge(100*(tunnel_version-1)+0, ""); err != nil { + log.Printf("Unable to register challenge for %s: %s\n", std.Login, err.Error()) + } + } else { + std.RegisterChallengeError(100*(tunnel_version-1)+0, err) + if verbose { + log.Printf("%s and HTTP IP (without DNS): %s\n", std.Login, err) + } + } + + // Check DNS for association + if addr, err := check_dns(std.MyAssociatedDomain(), DEFAULT_RESOLVER); err == nil { + // Check HTTP on delegated domain + if err := check_http(addr.String(), std.MyAssociatedDomain()); err == nil { if verbose { - log.Printf("%s just unlocked HTTP challenge\n", std.Login) + log.Printf("%s just unlocked HTTP (without DNS) challenge\n", std.Login) } - if _, err := std.UnlockNewChallenge(100*(tunnel_version-1)+0, ""); err != nil { - if _, err := std.UpdateUnlockedChallenge(100*(tunnel_version-1)+0, ""); err != nil { - log.Printf("Unable to register challenge for %s: %s\n", std.Login, err.Error()) - } + if _, err := std.UnlockChallenge(100*(tunnel_version-1)+1, ""); err != nil { + log.Printf("Unable to register challenge for %s: %s\n", std.Login, err.Error()) + } + } else { + std.RegisterChallengeError(100*(tunnel_version-1)+1, err) + if verbose { + log.Printf("%s and HTTP (without DNS): %s\n", std.Login, err) } - } else if verbose { - log.Printf("%s and HTTP (without DNS): %s\n", std.Login, err) } // Check HTTPs without DNS @@ -269,13 +318,14 @@ func studentsChecker() { if verbose { log.Printf("%s just unlocked HTTPS challenge\n", std.Login) } - if _, err := std.UnlockNewChallenge(100*(tunnel_version-1)+1, ""); err != nil { - if _, err := std.UpdateUnlockedChallenge(100*(tunnel_version-1)+1, ""); err != nil { - log.Printf("Unable to register challenge for %s: %s\n", std.Login, err.Error()) - } + if _, err := std.UnlockChallenge(100*(tunnel_version-1)+2, ""); err != nil { + log.Printf("Unable to register challenge for %s: %s\n", std.Login, err.Error()) + } + } else { + std.RegisterChallengeError(100*(tunnel_version-1)+2, err) + if verbose { + log.Printf("%s and HTTPS (without DNS): %s\n", std.Login, err) } - } else if verbose { - log.Printf("%s and HTTPS (without DNS): %s\n", std.Login, err) } } diff --git a/libadlin/db.go b/libadlin/db.go index ed04e46..5fafcc9 100644 --- a/libadlin/db.go +++ b/libadlin/db.go @@ -95,6 +95,19 @@ CREATE TABLE IF NOT EXISTS student_challenges( CONSTRAINT token_found UNIQUE (id_student,challenge), FOREIGN KEY(id_student) REFERENCES students(id_student) ) DEFAULT CHARACTER SET = utf8 COLLATE = utf8_bin; +`); err != nil { + return err + } + if _, err := db.Exec(` +CREATE TABLE IF NOT EXISTS student_challenge_errors( + id_st INTEGER NOT NULL PRIMARY KEY AUTO_INCREMENT, + id_student INTEGER NOT NULL, + challenge INTEGER NOT NULL, + time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + error VARCHAR(255) NOT NULL, + CONSTRAINT token_found UNIQUE (id_student,challenge), + FOREIGN KEY(id_student) REFERENCES students(id_student) +) DEFAULT CHARACTER SET = utf8 COLLATE = utf8_bin; `); err != nil { return err } diff --git a/libadlin/students.go b/libadlin/students.go index 23a18e9..6471335 100644 --- a/libadlin/students.go +++ b/libadlin/students.go @@ -3,6 +3,7 @@ package adlin import ( "crypto/hmac" "crypto/sha512" + "fmt" "time" ) @@ -150,6 +151,18 @@ func (s Student) GetStatesByChallenge() (ucs []UnlockedChallenge, err error) { } } +func (s Student) UnlockChallenge(challenge int, value string) (uc UnlockedChallenge, err error) { + if uc, err = s.UnlockNewChallenge(challenge, value); err != nil { + if uc, err = s.UpdateUnlockedChallenge(challenge, value); err != nil { + return + } + } + + s.RegisterChallengeError(challenge, fmt.Errorf("OK")) + + return +} + func (s Student) UnlockNewChallenge(challenge int, value string) (UnlockedChallenge, error) { if res, err := DBExec("INSERT INTO student_challenges (id_student, challenge, time, value) VALUES (?, ?, ?, ?)", s.Id, challenge, time.Now(), value); err != nil { return UnlockedChallenge{}, err @@ -168,6 +181,24 @@ func (s Student) UpdateUnlockedChallenge(challenge int, value string) (UnlockedC } } +type ErroredChallenge struct { + Id int64 `json:"id,omitempty"` + IdStudent int64 `json:"id_student"` + Challenge int `json:"challenge,omitempty"` + Time time.Time `json:"time"` + Error string `json:"error,omitempty"` +} + +func (s Student) RegisterChallengeError(challenge int, err error) error { + if _, errr := DBExec("INSERT INTO student_challenge_errors (id_student, challenge, time, error) VALUES (?, ?, ?, ?)", s.Id, challenge, time.Now(), err.Error()); errr == nil { + return nil + } else if _, errr := DBExec("UPDATE student_challenge_errors SET time = ?, error = ? WHERE id_student = ? AND challenge = ?", time.Now(), err.Error(), s.Id, challenge); errr != nil { + return errr + } else { + return nil + } +} + func (s Student) RegisterAccess(ip, mac string) error { if res, err := DBExec("INSERT INTO student_login (id_student, ip, mac, time) VALUES (?, ?, ?, ?)", s.Id, ip, mac, time.Now()); err != nil { return err diff --git a/token-validator/htdocs/dashboard.html b/token-validator/htdocs/dashboard.html index 1898257..8783eb6 100644 --- a/token-validator/htdocs/dashboard.html +++ b/token-validator/htdocs/dashboard.html @@ -77,7 +77,7 @@

    TP {{tutoid+1}}
    - +

@@ -116,7 +116,7 @@ {{ login }}
- +
From 6d8f38d74958d03ae0941eb40360706450b401f5 Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Sun, 7 Mar 2021 12:39:38 +0100 Subject: [PATCH 18/23] Use pointer instead of struct --- checker/checker.go | 2 +- libadlin/domain.go | 8 ++-- libadlin/ping.go | 4 +- libadlin/session.go | 25 +++++------ libadlin/ssh.go | 25 +++++------ libadlin/students.go | 80 ++++++++++++++++++++++++------------ libadlin/tunnel.go | 39 ++++++++++-------- token-validator/auth.go | 8 ++-- token-validator/auth_oidc.go | 2 +- token-validator/challenge.go | 4 +- token-validator/check.go | 4 +- token-validator/domain.go | 56 ++++++++++++------------- token-validator/handler.go | 6 +-- token-validator/ip.go | 8 ++-- token-validator/ping.go | 6 +-- token-validator/ssh.go | 4 +- token-validator/students.go | 36 +++++++++++----- token-validator/wg.go | 12 +++--- 18 files changed, 187 insertions(+), 142 deletions(-) diff --git a/checker/checker.go b/checker/checker.go index c85ef00..3b98cae 100644 --- a/checker/checker.go +++ b/checker/checker.go @@ -167,7 +167,7 @@ func check_https(domain, ip string) (err error) { // Main -func minTunnelVersion(std adlin.Student, suffixip int) (int, error) { +func minTunnelVersion(std *adlin.Student, suffixip int) (int, error) { tunnels, err := std.GetTunnelTokens() if err != nil { return 0, err diff --git a/libadlin/domain.go b/libadlin/domain.go index f5d7f7d..4553012 100644 --- a/libadlin/domain.go +++ b/libadlin/domain.go @@ -10,7 +10,7 @@ const ( DelegatedDomainSuffix = "srs.p0m.fr." ) -func (student Student) MyDelegatedDomain() string { +func (student *Student) MyDelegatedDomain() string { if student.DelegatedDomain != nil { return *student.DelegatedDomain } else { @@ -18,11 +18,11 @@ func (student Student) MyDelegatedDomain() string { } } -func (student Student) DefaultAssociatedDomain() string { +func (student *Student) DefaultAssociatedDomain() string { return fmt.Sprintf("%s.%s", strings.Trim(strings.Replace(student.Login, "_", "-", -1), "-_"), AssociatedDomainSuffix) } -func (student Student) MyAssociatedDomain() string { +func (student *Student) MyAssociatedDomain() string { if student.AssociatedDomain != nil { return *student.AssociatedDomain } else { @@ -30,7 +30,7 @@ func (student Student) MyAssociatedDomain() string { } } -func (student Student) GetAssociatedDomains() (ds []string) { +func (student *Student) GetAssociatedDomains() (ds []string) { defdn := student.DefaultAssociatedDomain() ds = append(ds, defdn) diff --git a/libadlin/ping.go b/libadlin/ping.go index c8bef5a..a93fc9c 100644 --- a/libadlin/ping.go +++ b/libadlin/ping.go @@ -9,14 +9,14 @@ type Pong struct { State bool } -func (s Student) LastPongs() (pongs []Pong, err error) { +func (s *Student) LastPongs() (pongs []*Pong, err error) { if rows, errr := DBQuery("SELECT time, state FROM student_pong WHERE id_student = ? ORDER BY time DESC", s.Id); errr != nil { return nil, errr } else { defer rows.Close() for rows.Next() { - var p Pong + p := &Pong{} if err = rows.Scan(&p.Date, &p.State); err != nil { return } diff --git a/libadlin/session.go b/libadlin/session.go index 933f425..2665987 100644 --- a/libadlin/session.go +++ b/libadlin/session.go @@ -11,40 +11,41 @@ type Session struct { Time time.Time `json:"time"` } -func GetSession(id []byte) (s Session, err error) { +func GetSession(id []byte) (s *Session, err error) { + s = new(Session) err = DBQueryRow("SELECT id_session, id_student, time FROM student_sessions WHERE id_session=?", id).Scan(&s.Id, &s.IdStudent, &s.Time) return } -func NewSession() (Session, error) { +func NewSession() (*Session, error) { session_id := make([]byte, 255) if _, err := rand.Read(session_id); err != nil { - return Session{}, err + return nil, err } else if _, err := DBExec("INSERT INTO student_sessions (id_session, time) VALUES (?, ?)", session_id, time.Now()); err != nil { - return Session{}, err + return nil, err } else { - return Session{session_id, nil, time.Now()}, nil + return &Session{session_id, nil, time.Now()}, nil } } -func (student Student) NewSession() (Session, error) { +func (student *Student) NewSession() (*Session, error) { session_id := make([]byte, 255) if _, err := rand.Read(session_id); err != nil { - return Session{}, err + return nil, err } else if _, err := DBExec("INSERT INTO student_sessions (id_session, id_student, time) VALUES (?, ?, ?)", session_id, student.Id, time.Now()); err != nil { - return Session{}, err + return nil, err } else { - return Session{session_id, &student.Id, time.Now()}, nil + return &Session{session_id, &student.Id, time.Now()}, nil } } -func (s Session) SetStudent(student Student) (Session, error) { +func (s *Session) SetStudent(student *Student) (*Session, error) { s.IdStudent = &student.Id _, err := s.Update() return s, err } -func (s Session) Update() (int64, error) { +func (s *Session) Update() (int64, error) { if res, err := DBExec("UPDATE student_sessions SET id_student = ?, time = ? WHERE id_session = ?", s.IdStudent, s.Time, s.Id); err != nil { return 0, err } else if nb, err := res.RowsAffected(); err != nil { @@ -54,7 +55,7 @@ func (s Session) Update() (int64, error) { } } -func (s Session) Delete() (int64, error) { +func (s *Session) Delete() (int64, error) { if res, err := DBExec("DELETE FROM student_sessions WHERE id_session = ?", s.Id); err != nil { return 0, err } else if nb, err := res.RowsAffected(); err != nil { diff --git a/libadlin/ssh.go b/libadlin/ssh.go index 9e8f1be..f699860 100644 --- a/libadlin/ssh.go +++ b/libadlin/ssh.go @@ -16,14 +16,14 @@ type StudentKey struct { Time time.Time `json:"time"` } -func GetStudentKeys() (keys []StudentKey, err error) { +func GetStudentKeys() (keys []*StudentKey, err error) { if rows, errr := DBQuery("SELECT id_key, id_student, sshkey, time FROM student_keys"); errr != nil { return nil, errr } else { defer rows.Close() for rows.Next() { - var k StudentKey + k := &StudentKey{} if err = rows.Scan(&k.Id, &k.IdStudent, &k.Key, &k.Time); err != nil { return } @@ -37,14 +37,14 @@ func GetStudentKeys() (keys []StudentKey, err error) { } } -func (s Student) GetKeys() (keys []StudentKey, err error) { +func (s *Student) GetKeys() (keys []*StudentKey, err error) { if rows, errr := DBQuery("SELECT id_key, id_student, sshkey, time FROM student_keys WHERE id_student = ?", s.Id); errr != nil { return nil, errr } else { defer rows.Close() for rows.Next() { - var k StudentKey + k := &StudentKey{} if err = rows.Scan(&k.Id, &k.IdStudent, &k.Key, &k.Time); err != nil { return } @@ -58,12 +58,13 @@ func (s Student) GetKeys() (keys []StudentKey, err error) { } } -func getStudentKey(id int) (k StudentKey, err error) { +func getStudentKey(id int) (k *StudentKey, err error) { + k = new(StudentKey) err = DBQueryRow("SELECT id_key, id_student, sshkey, time FROM student_keys WHERE id_key=?", id).Scan(&k.Id, &k.IdStudent, &k.Key, &k.Time) return } -func (s Student) NewKey(key string) (k StudentKey, err error) { +func (s *Student) NewKey(key string) (k *StudentKey, err error) { // Check key before importing it cmd := exec.Command("ssh-keygen", "-l", "-f", "-") cmd.Stdin = strings.NewReader(key) @@ -101,20 +102,20 @@ func (s Student) NewKey(key string) (k StudentKey, err error) { key = keyf[0] + " " + keyf[1] if res, err := DBExec("INSERT INTO student_keys (id_student, sshkey, time) VALUES (?, ?, ?)", s.Id, key, time.Now()); err != nil { - return StudentKey{}, err + return nil, err } else if kid, err := res.LastInsertId(); err != nil { - return StudentKey{}, err + return nil, err } else { s.UnlockNewChallenge(11, "") - return StudentKey{kid, s.Id, key, time.Now()}, nil + return &StudentKey{kid, s.Id, key, time.Now()}, nil } } -func (k StudentKey) GetStudent() (Student, error) { +func (k *StudentKey) GetStudent() (*Student, error) { return GetStudent(int(k.IdStudent)) } -func (k StudentKey) Update() (int64, error) { +func (k *StudentKey) Update() (int64, error) { if res, err := DBExec("UPDATE student_keys SET id_student = ?, sshkey = ?, time = ? WHERE id_key = ?", k.IdStudent, k.Key, k.Time, k.Id); err != nil { return 0, err } else if nb, err := res.RowsAffected(); err != nil { @@ -124,7 +125,7 @@ func (k StudentKey) Update() (int64, error) { } } -func (k StudentKey) Delete() (int64, error) { +func (k *StudentKey) Delete() (int64, error) { if res, err := DBExec("DELETE FROM student_keys WHERE id_key = ?", k.Id); err != nil { return 0, err } else if nb, err := res.RowsAffected(); err != nil { diff --git a/libadlin/students.go b/libadlin/students.go index 6471335..9903cc0 100644 --- a/libadlin/students.go +++ b/libadlin/students.go @@ -17,14 +17,14 @@ type Student struct { DelegatedDomain *string `json:"delegated_domain,omitempty"` } -func GetStudents() (students []Student, err error) { +func GetStudents() (students []*Student, err error) { if rows, errr := DBQuery("SELECT S.id_student, S.login, MAX(L.time), L.ip, L.mac, S.associatedDomain, S.delegatedDomain FROM students S INNER JOIN (SELECT a.id_student, a.time, a.ip, a.mac FROM student_login a INNER JOIN (SELECT id_student, MAX(time) AS time FROM student_login GROUP BY id_student) b ON a.id_student = b.id_student AND a.time = b.time) L ON S.id_student = L.id_student GROUP BY id_student"); errr != nil { return nil, errr } else { defer rows.Close() for rows.Next() { - var s Student + s := &Student{} if err = rows.Scan(&s.Id, &s.Login, &s.Time, &s.IP, &s.MAC, &s.AssociatedDomain, &s.DelegatedDomain); err != nil { return } @@ -38,12 +38,14 @@ func GetStudents() (students []Student, err error) { } } -func GetStudent(id int) (s Student, err error) { +func GetStudent(id int) (s *Student, err error) { + s = new(Student) err = DBQueryRow("SELECT S.id_student, S.login, MAX(L.time), L.ip, L.mac, S.associatedDomain, S.delegatedDomain FROM students S INNER JOIN (SELECT a.id_student, a.time, a.ip, a.mac FROM student_login a INNER JOIN (SELECT id_student, MAX(time) AS time FROM student_login GROUP BY id_student) b ON a.id_student = b.id_student AND a.time = b.time) L ON S.id_student = L.id_student WHERE S.id_student=?", id).Scan(&s.Id, &s.Login, &s.Time, &s.IP, &s.MAC, &s.AssociatedDomain, &s.DelegatedDomain) return } -func GetStudentByLogin(login string) (s Student, err error) { +func GetStudentByLogin(login string) (s *Student, err error) { + s = new(Student) err = DBQueryRow("SELECT S.id_student, S.login, MAX(L.time), L.ip, L.mac, S.associatedDomain, S.delegatedDomain FROM students S INNER JOIN (SELECT a.id_student, a.time, a.ip, a.mac FROM student_login a INNER JOIN (SELECT id_student, MAX(time) AS time FROM student_login GROUP BY id_student) b ON a.id_student = b.id_student AND a.time = b.time) L ON S.id_student = L.id_student WHERE login=?", login).Scan(&s.Id, &s.Login, &s.Time, &s.IP, &s.MAC, &s.AssociatedDomain, &s.DelegatedDomain) return } @@ -54,22 +56,22 @@ func StudentExists(login string) bool { return err == nil && z == 1 } -func NewStudent(login string) (Student, error) { +func NewStudent(login string) (*Student, error) { t := time.Now() if res, err := DBExec("INSERT INTO students (login, time) VALUES (?, ?)", login, t); err != nil { - return Student{}, err + return nil, err } else if sid, err := res.LastInsertId(); err != nil { - return Student{}, err + return nil, err } else { - return Student{sid, login, &t, nil, nil, nil, nil}, nil + return &Student{sid, login, &t, nil, nil, nil, nil}, nil } } -func (s Student) GetPKey() []byte { +func (s *Student) GetPKey() []byte { return hmac.New(sha512.New512_224, []byte(SharedSecret)).Sum([]byte(s.Login)) } -func (s Student) Update() (int64, error) { +func (s *Student) Update() (int64, error) { if res, err := DBExec("UPDATE students SET login = ?, time = ?, associatedDomain = ?, delegatedDomain = ? WHERE id_student = ?", s.Login, s.Time, s.AssociatedDomain, s.DelegatedDomain, s.Id); err != nil { return 0, err } else if nb, err := res.RowsAffected(); err != nil { @@ -79,7 +81,7 @@ func (s Student) Update() (int64, error) { } } -func (s Student) Delete() (int64, error) { +func (s *Student) Delete() (int64, error) { if res, err := DBExec("DELETE FROM students WHERE id_student = ?", s.Id); err != nil { return 0, err } else if nb, err := res.RowsAffected(); err != nil { @@ -103,18 +105,20 @@ type UnlockedChallenge struct { Id int64 `json:"id,omitempty"` IdStudent int64 `json:"id_student"` Challenge int `json:"challenge,omitempty"` - Time time.Time `json:"time"` + Time *time.Time `json:"time,omitempty"` Value interface{} `json:"value,omitempty"` + LastCheck *time.Time `json:"last_check,omitempty"` + Error string `json:"error,omitempty"` } -func (s Student) GetStates() (ucs []UnlockedChallenge, err error) { +func (s *Student) GetStates() (ucs []*UnlockedChallenge, err error) { if rows, errr := DBQuery("SELECT id_st, challenge, time FROM student_challenges WHERE id_student = ?", s.Id); errr != nil { return nil, errr } else { defer rows.Close() for rows.Next() { - var u UnlockedChallenge + u := &UnlockedChallenge{} u.IdStudent = s.Id if err = rows.Scan(&u.Id, &u.Challenge, &u.Time); err != nil { return @@ -129,14 +133,36 @@ func (s Student) GetStates() (ucs []UnlockedChallenge, err error) { } } -func (s Student) GetStatesByChallenge() (ucs []UnlockedChallenge, err error) { +func (s *Student) GetChallengeErrors() (ucs []*ErroredChallenge, err error) { + if rows, errr := DBQuery("SELECT id_st, challenge, time, error FROM student_challenge_errors WHERE id_student = ?", s.Id); errr != nil { + return nil, errr + } else { + defer rows.Close() + + for rows.Next() { + u := &ErroredChallenge{} + u.IdStudent = s.Id + if err = rows.Scan(&u.Id, &u.Challenge, &u.Time, &u.Error); err != nil { + return + } + ucs = append(ucs, u) + } + if err = rows.Err(); err != nil { + return + } + + return + } +} + +func (s *Student) GetStatesByChallenge() (ucs []*UnlockedChallenge, err error) { if rows, errr := DBQuery("SELECT id_st, challenge, MIN(time), value FROM student_challenges WHERE id_student = ? GROUP BY challenge, id_student", s.Id); errr != nil { return nil, errr } else { defer rows.Close() for rows.Next() { - var u UnlockedChallenge + u := &UnlockedChallenge{} u.IdStudent = s.Id if err = rows.Scan(&u.Id, &u.Challenge, &u.Time, &u.Value); err != nil { return @@ -151,7 +177,7 @@ func (s Student) GetStatesByChallenge() (ucs []UnlockedChallenge, err error) { } } -func (s Student) UnlockChallenge(challenge int, value string) (uc UnlockedChallenge, err error) { +func (s *Student) UnlockChallenge(challenge int, value string) (uc *UnlockedChallenge, err error) { if uc, err = s.UnlockNewChallenge(challenge, value); err != nil { if uc, err = s.UpdateUnlockedChallenge(challenge, value); err != nil { return @@ -163,21 +189,23 @@ func (s Student) UnlockChallenge(challenge int, value string) (uc UnlockedChalle return } -func (s Student) UnlockNewChallenge(challenge int, value string) (UnlockedChallenge, error) { +func (s *Student) UnlockNewChallenge(challenge int, value string) (*UnlockedChallenge, error) { if res, err := DBExec("INSERT INTO student_challenges (id_student, challenge, time, value) VALUES (?, ?, ?, ?)", s.Id, challenge, time.Now(), value); err != nil { - return UnlockedChallenge{}, err + return nil, err } else if utid, err := res.LastInsertId(); err != nil { - return UnlockedChallenge{}, err + return nil, err } else { - return UnlockedChallenge{utid, s.Id, challenge, time.Now(), value}, err + now := time.Now() + return &UnlockedChallenge{utid, s.Id, challenge, &now, value, nil, ""}, err } } -func (s Student) UpdateUnlockedChallenge(challenge int, value string) (UnlockedChallenge, error) { +func (s *Student) UpdateUnlockedChallenge(challenge int, value string) (*UnlockedChallenge, error) { if _, err := DBExec("UPDATE student_challenges SET time = ?, value = ? WHERE id_student = ? AND challenge = ?", time.Now(), value, s.Id, challenge); err != nil { - return UnlockedChallenge{}, err + return nil, err } else { - return UnlockedChallenge{0, s.Id, challenge, time.Now(), value}, err + now := time.Now() + return &UnlockedChallenge{0, s.Id, challenge, &now, value, nil, ""}, err } } @@ -189,7 +217,7 @@ type ErroredChallenge struct { Error string `json:"error,omitempty"` } -func (s Student) RegisterChallengeError(challenge int, err error) error { +func (s *Student) RegisterChallengeError(challenge int, err error) error { if _, errr := DBExec("INSERT INTO student_challenge_errors (id_student, challenge, time, error) VALUES (?, ?, ?, ?)", s.Id, challenge, time.Now(), err.Error()); errr == nil { return nil } else if _, errr := DBExec("UPDATE student_challenge_errors SET time = ?, error = ? WHERE id_student = ? AND challenge = ?", time.Now(), err.Error(), s.Id, challenge); errr != nil { @@ -199,7 +227,7 @@ func (s Student) RegisterChallengeError(challenge int, err error) error { } } -func (s Student) RegisterAccess(ip, mac string) error { +func (s *Student) RegisterAccess(ip, mac string) error { if res, err := DBExec("INSERT INTO student_login (id_student, ip, mac, time) VALUES (?, ?, ?, ?)", s.Id, ip, mac, time.Now()); err != nil { return err } else if _, err := res.LastInsertId(); err != nil { diff --git a/libadlin/tunnel.go b/libadlin/tunnel.go index db96e47..c22a520 100644 --- a/libadlin/tunnel.go +++ b/libadlin/tunnel.go @@ -37,32 +37,32 @@ type WGDump struct { } var ( - wgDumpCache_data map[string]WGDump = nil + wgDumpCache_data map[string]*WGDump = nil wgDumpCache_time time.Time wgDumpCache_mutex sync.RWMutex ) -func _readWgDump() (wgd map[string]WGDump, err error) { +func _readWgDump() (wgd map[string]*WGDump, err error) { out, errr := exec.Command("wg", "show", "wg-adlin", "dump").Output() if errr != nil { return nil, errr } - wgd = map[string]WGDump{} + wgd = map[string]*WGDump{} for _, line := range strings.Split(string(out), "\n") { cols := strings.Fields(line) if len(cols) != 8 { continue } - wgd[cols[0]] = WGDump{cols[0], cols[1], cols[2], cols[3], cols[4], cols[5], cols[6], cols[7]} + wgd[cols[0]] = &WGDump{cols[0], cols[1], cols[2], cols[3], cols[4], cols[5], cols[6], cols[7]} } return } -func readWgDump() (wgd map[string]WGDump, err error) { +func readWgDump() (wgd map[string]*WGDump, err error) { wgDumpCache_mutex.RLock() defer wgDumpCache_mutex.RUnlock() @@ -116,24 +116,26 @@ func TokenFromText(token string) []byte { return sha[:] } -func GetTunnelToken(token []byte) (t TunnelToken, err error) { +func GetTunnelToken(token []byte) (t *TunnelToken, err error) { + t = new(TunnelToken) err = DBQueryRow("SELECT token, token_text, id_student, pubkey, time, suffixip, version FROM student_tunnel_tokens WHERE token=? ORDER BY time DESC", token).Scan(&t.token, &t.TokenText, &t.IdStudent, &t.PubKey, &t.Time, &t.SuffixIP, &t.Version) if err == nil && t.PubKey != nil { if wgd, errr := readWgDump(); errr == nil { if v, ok := wgd[base64.StdEncoding.EncodeToString(t.PubKey)]; ok { - t.Dump = &v + t.Dump = v } } } return } -func (student Student) NewTunnelToken(suffixip int) (t TunnelToken, err error) { +func (student *Student) NewTunnelToken(suffixip int) (t *TunnelToken, err error) { tok := make([]byte, 7) if _, err = rand.Read(tok); err != nil { return } + t = new(TunnelToken) t.TokenText = strings.Replace(strings.Replace(strings.Replace(strings.Replace(strings.Replace(base64.RawStdEncoding.EncodeToString(tok), "/", ".", -1), "+", "_", -1), "O", "<", -1), "l", "$", -1), "I", ">", -1) t.token = TokenFromText(t.TokenText) t.IdStudent = student.Id @@ -142,7 +144,7 @@ func (student Student) NewTunnelToken(suffixip int) (t TunnelToken, err error) { return } -func (student Student) GetTunnelTokens() (ts []TunnelToken, err error) { +func (student *Student) GetTunnelTokens() (ts []*TunnelToken, err error) { if rows, errr := DBQuery("SELECT token, token_text, id_student, pubkey, time, suffixip, version FROM student_tunnel_tokens WHERE id_student = ? ORDER BY time DESC", student.Id); errr != nil { return nil, errr } else if wgd, errr := readWgDump(); errr != nil { @@ -151,13 +153,13 @@ func (student Student) GetTunnelTokens() (ts []TunnelToken, err error) { defer rows.Close() for rows.Next() { - var t TunnelToken + t := &TunnelToken{} if err = rows.Scan(&t.token, &t.TokenText, &t.IdStudent, &t.PubKey, &t.Time, &t.SuffixIP, &t.Version); err != nil { return } if t.PubKey != nil { if v, ok := wgd[base64.StdEncoding.EncodeToString(t.PubKey)]; ok { - t.Dump = &v + t.Dump = v } } ts = append(ts, t) @@ -170,7 +172,7 @@ func (student Student) GetTunnelTokens() (ts []TunnelToken, err error) { } } -func (student Student) GetActivesTunnels() (ts []TunnelToken, err error) { +func (student *Student) GetActivesTunnels() (ts []*TunnelToken, err error) { if rows, errr := DBQuery("SELECT token, token_text, id_student, pubkey, time, suffixip, version FROM student_tunnel_tokens WHERE id_student = ? ORDER BY time DESC", student.Id); errr != nil { return nil, errr } else if wgd, errr := readWgDump(); errr != nil { @@ -179,13 +181,13 @@ func (student Student) GetActivesTunnels() (ts []TunnelToken, err error) { defer rows.Close() for rows.Next() { - var t TunnelToken + t := &TunnelToken{} if err = rows.Scan(&t.token, &t.TokenText, &t.IdStudent, &t.PubKey, &t.Time, &t.SuffixIP, &t.Version); err != nil { return } if t.PubKey != nil { if v, ok := wgd[base64.StdEncoding.EncodeToString(t.PubKey)]; ok { - t.Dump = &v + t.Dump = v ts = append(ts, t) } } @@ -198,12 +200,13 @@ func (student Student) GetActivesTunnels() (ts []TunnelToken, err error) { } } -func (student Student) GetTunnelToken(token []byte) (t TunnelToken, err error) { +func (student *Student) GetTunnelToken(token []byte) (t *TunnelToken, err error) { + t = new(TunnelToken) err = DBQueryRow("SELECT token, token_text, id_student, pubkey, time, suffixip, version FROM student_tunnel_tokens WHERE token = ? AND id_student = ? ORDER BY time DESC", token, student.Id).Scan(&t.token, &t.TokenText, &t.IdStudent, &t.PubKey, &t.Time, &t.SuffixIP, &t.Version) if err == nil && t.PubKey != nil { if wgd, errr := readWgDump(); errr == nil { if v, ok := wgd[base64.StdEncoding.EncodeToString(t.PubKey)]; ok { - t.Dump = &v + t.Dump = v } } } @@ -235,14 +238,14 @@ func (t *TunnelToken) Delete() (int64, error) { } } -func GetStudentsTunnels() (ts []TunnelToken, err error) { +func GetStudentsTunnels() (ts []*TunnelToken, err error) { if rows, errr := DBQuery("SELECT T.token, T.token_text, T.id_student, T.pubkey, T.time, T.suffixip, T.version FROM student_tunnel_tokens T INNER JOIN (SELECT B.id_student, MAX(B.time) AS time FROM student_tunnel_tokens B WHERE B.pubkey IS NOT NULL GROUP BY id_student) L ON T.id_student = L.id_student AND T.time = L.time"); errr != nil { return nil, errr } else { defer rows.Close() for rows.Next() { - var t TunnelToken + t := &TunnelToken{} if err = rows.Scan(&t.token, &t.TokenText, &t.IdStudent, &t.PubKey, &t.Time, &t.SuffixIP, &t.Version); err != nil { return } diff --git a/token-validator/auth.go b/token-validator/auth.go index f90fec9..97be90b 100644 --- a/token-validator/auth.go +++ b/token-validator/auth.go @@ -27,7 +27,7 @@ func init() { router.POST("/api/auth/logout", apiRawHandler(logout)) } -func validateAuthToken(s adlin.Student, _ httprouter.Params, _ []byte) (interface{}, error) { +func validateAuthToken(s *adlin.Student, _ httprouter.Params, _ []byte) (interface{}, error) { return s, nil } @@ -50,7 +50,7 @@ type loginForm struct { } func completeAuth(w http.ResponseWriter, username string, session *adlin.Session) (err error) { - var std adlin.Student + var std *adlin.Student if !adlin.StudentExists(username) { if std, err = adlin.NewStudent(username); err != nil { return err @@ -60,9 +60,7 @@ func completeAuth(w http.ResponseWriter, username string, session *adlin.Session } if session == nil { - var s adlin.Session - s, err = std.NewSession() - session = &s + session, err = std.NewSession() } else { _, err = session.SetStudent(std) } diff --git a/token-validator/auth_oidc.go b/token-validator/auth_oidc.go index 9118aea..590676a 100644 --- a/token-validator/auth_oidc.go +++ b/token-validator/auth_oidc.go @@ -111,7 +111,7 @@ func OIDC_CRI_complete(w http.ResponseWriter, r *http.Request, ps httprouter.Par return } - if err := completeAuth(w, claims.Username, &session); err != nil { + if err := completeAuth(w, claims.Username, session); err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } diff --git a/token-validator/challenge.go b/token-validator/challenge.go index d21db5d..51144f1 100644 --- a/token-validator/challenge.go +++ b/token-validator/challenge.go @@ -338,7 +338,7 @@ func receiveChallenge(r *http.Request, ps httprouter.Params, body []byte) (inter return nil, errors.New("This is not the expected token.") } - var std adlin.Student + var std *adlin.Student if stdid, err := strconv.Atoi(gt.Login); err == nil { if std, err = adlin.GetStudent(stdid); err != nil { @@ -388,7 +388,7 @@ func receiveToken(r *http.Request, body []byte, chid int) (interface{}, error) { if std, err := adlin.GetStudentByLogin(gt.Login); err != nil { return nil, err } else { - if err := challenges[chid-1].Check(&std, >, chid); err != nil { + if err := challenges[chid-1].Check(std, >, chid); err != nil { log.Printf("%s just try ch#%d: %s\n", std.Login, chid, err) return nil, err } diff --git a/token-validator/check.go b/token-validator/check.go index 8e18faf..722e54f 100644 --- a/token-validator/check.go +++ b/token-validator/check.go @@ -19,7 +19,7 @@ type checkGLUE struct { } func init() { - router.POST("/api/check/GLUE", apiAuthHandler(func(student adlin.Student, ps httprouter.Params, body []byte) (interface{}, error) { + router.POST("/api/check/GLUE", apiAuthHandler(func(student *adlin.Student, ps httprouter.Params, body []byte) (interface{}, error) { var uc checkGLUE if err := json.Unmarshal(body, &uc); err != nil { return nil, err @@ -27,7 +27,7 @@ func init() { return true, check_GLUE_respond(student, uc.Domain, uc.IP) })) } -func check_GLUE_respond(student adlin.Student, domain string, ip string) (err error) { +func check_GLUE_respond(student *adlin.Student, domain string, ip string) (err error) { if !strings.HasPrefix(ip, adlin.StudentIP(student.Id).String()) { return fmt.Errorf("%q is not your IP range") } diff --git a/token-validator/domain.go b/token-validator/domain.go index 9f789d7..596f3bd 100644 --- a/token-validator/domain.go +++ b/token-validator/domain.go @@ -22,10 +22,10 @@ var ( ) func init() { - router.GET("/api/adomains/", apiAuthHandler(func(student adlin.Student, ps httprouter.Params, body []byte) (interface{}, error) { + router.GET("/api/adomains/", apiAuthHandler(func(student *adlin.Student, ps httprouter.Params, body []byte) (interface{}, error) { return student.GetAssociatedDomains(), nil })) - router.POST("/api/adomains/", apiAuthHandler(func(student adlin.Student, ps httprouter.Params, body []byte) (interface{}, error) { + router.POST("/api/adomains/", apiAuthHandler(func(student *adlin.Student, ps httprouter.Params, body []byte) (interface{}, error) { ue := &struct { Domain string `json:"domain"` A string `json:"a"` @@ -63,14 +63,14 @@ func init() { return true, AddAssociatedDomains(student, aaaa) } })) - router.GET("/api/adomains/:dn", apiAuthHandler(func(student adlin.Student, ps httprouter.Params, body []byte) (interface{}, error) { + router.GET("/api/adomains/:dn", apiAuthHandler(func(student *adlin.Student, ps httprouter.Params, body []byte) (interface{}, error) { return GetAssociatedDomain(student, ps.ByName("dn")) })) - router.GET("/api/ddomains/", apiAuthHandler(func(student adlin.Student, ps httprouter.Params, body []byte) (interface{}, error) { + router.GET("/api/ddomains/", apiAuthHandler(func(student *adlin.Student, ps httprouter.Params, body []byte) (interface{}, error) { return []string{student.MyDelegatedDomain()}, nil })) - router.POST("/api/ddomains/", apiAuthHandler(func(student adlin.Student, ps httprouter.Params, body []byte) (interface{}, error) { + router.POST("/api/ddomains/", apiAuthHandler(func(student *adlin.Student, ps httprouter.Params, body []byte) (interface{}, error) { ue := &struct { NS string `json:"ns"` }{} @@ -98,75 +98,75 @@ func init() { return true, nil } })) - router.GET("/api/ddomains/:dn/", apiAuthHandler(func(student adlin.Student, ps httprouter.Params, body []byte) (interface{}, error) { + router.GET("/api/ddomains/:dn/", apiAuthHandler(func(student *adlin.Student, ps httprouter.Params, body []byte) (interface{}, error) { return getRRDelegatedDomain(student, ps.ByName("dn"), "") })) - router.GET("/api/ddomains/:dn/NS", apiAuthHandler(func(student adlin.Student, ps httprouter.Params, body []byte) (interface{}, error) { + router.GET("/api/ddomains/:dn/NS", apiAuthHandler(func(student *adlin.Student, ps httprouter.Params, body []byte) (interface{}, error) { return getRRDelegatedDomain(student, ps.ByName("dn"), "NS") })) - router.POST("/api/ddomains/:dn/NS", apiAuthHandler(func(student adlin.Student, ps httprouter.Params, body []byte) (interface{}, error) { + router.POST("/api/ddomains/:dn/NS", apiAuthHandler(func(student *adlin.Student, ps httprouter.Params, body []byte) (interface{}, error) { var ue Entry if err := json.Unmarshal(body, &ue); err != nil { return nil, err } return true, AddNSDelegatedDomain(student, ps.ByName("dn"), ue.TTL, strings.Join(ue.Values, " ")) })) - router.PATCH("/api/ddomains/:dn/NS", apiAuthHandler(func(student adlin.Student, ps httprouter.Params, body []byte) (interface{}, error) { + router.PATCH("/api/ddomains/:dn/NS", apiAuthHandler(func(student *adlin.Student, ps httprouter.Params, body []byte) (interface{}, error) { var ue Entry if err := json.Unmarshal(body, &ue); err != nil { return nil, err } return true, UpdateNSDelegatedDomain(student, ps.ByName("dn"), ue.TTL, ue.ValuesFrom, strings.Join(ue.Values, "")) })) - router.DELETE("/api/ddomains/:dn/NS", apiAuthHandler(func(student adlin.Student, ps httprouter.Params, body []byte) (interface{}, error) { + router.DELETE("/api/ddomains/:dn/NS", apiAuthHandler(func(student *adlin.Student, ps httprouter.Params, body []byte) (interface{}, error) { var ue Entry if err := json.Unmarshal(body, &ue); err != nil { return nil, err } return true, DeleteRRDelegatedDomain(student, ps.ByName("dn"), "NS", strings.Join(ue.Values, " ")) })) - router.GET("/api/ddomains/:dn/GLUE", apiAuthHandler(func(student adlin.Student, ps httprouter.Params, body []byte) (interface{}, error) { + router.GET("/api/ddomains/:dn/GLUE", apiAuthHandler(func(student *adlin.Student, ps httprouter.Params, body []byte) (interface{}, error) { return getRRDelegatedDomain(student, ps.ByName("dn"), "AAAA") })) - router.POST("/api/ddomains/:dn/AAAA", apiAuthHandler(func(student adlin.Student, ps httprouter.Params, body []byte) (interface{}, error) { + router.POST("/api/ddomains/:dn/AAAA", apiAuthHandler(func(student *adlin.Student, ps httprouter.Params, body []byte) (interface{}, error) { var ue Entry if err := json.Unmarshal(body, &ue); err != nil { return nil, err } return true, AddGLUEDelegatedDomain(student, ps.ByName("dn"), ue.TTL, strings.Join(ue.Values, " ")) })) - router.PATCH("/api/ddomains/:dn/AAAA", apiAuthHandler(func(student adlin.Student, ps httprouter.Params, body []byte) (interface{}, error) { + router.PATCH("/api/ddomains/:dn/AAAA", apiAuthHandler(func(student *adlin.Student, ps httprouter.Params, body []byte) (interface{}, error) { var ue Entry if err := json.Unmarshal(body, &ue); err != nil { return nil, err } return true, UpdateGLUEDelegatedDomain(student, ps.ByName("dn"), ue.TTL, ue.ValuesFrom, strings.Join(ue.Values, " ")) })) - router.POST("/api/ddomains/:dn/GLUE", apiAuthHandler(func(student adlin.Student, ps httprouter.Params, body []byte) (interface{}, error) { + router.POST("/api/ddomains/:dn/GLUE", apiAuthHandler(func(student *adlin.Student, ps httprouter.Params, body []byte) (interface{}, error) { var ue Entry if err := json.Unmarshal(body, &ue); err != nil { return nil, err } return true, UpdateGLUEDelegatedDomain(student, ps.ByName("dn"), ue.TTL, ue.ValuesFrom, strings.Join(ue.Values, " ")) })) - router.DELETE("/api/ddomains/:dn/AAAA", apiAuthHandler(func(student adlin.Student, ps httprouter.Params, body []byte) (interface{}, error) { + router.DELETE("/api/ddomains/:dn/AAAA", apiAuthHandler(func(student *adlin.Student, ps httprouter.Params, body []byte) (interface{}, error) { var ue Entry if err := json.Unmarshal(body, &ue); err != nil { return nil, err } return true, DeleteRRDelegatedDomain(student, ps.ByName("dn"), "AAAA", strings.Join(ue.Values, " ")) })) - router.GET("/api/ddomains/:dn/DS", apiAuthHandler(func(student adlin.Student, ps httprouter.Params, body []byte) (interface{}, error) { + router.GET("/api/ddomains/:dn/DS", apiAuthHandler(func(student *adlin.Student, ps httprouter.Params, body []byte) (interface{}, error) { return getRRDelegatedDomain(student, ps.ByName("dn"), "DS") })) - router.POST("/api/ddomains/:dn/DS", apiAuthHandler(func(student adlin.Student, ps httprouter.Params, body []byte) (interface{}, error) { + router.POST("/api/ddomains/:dn/DS", apiAuthHandler(func(student *adlin.Student, ps httprouter.Params, body []byte) (interface{}, error) { var ue Entry if err := json.Unmarshal(body, &ue); err != nil { return nil, err } return true, AddDSDelegatedDomain(student, ps.ByName("dn"), ue.TTL, strings.Join(ue.Values, " ")) })) - router.DELETE("/api/ddomains/:dn/DS", apiAuthHandler(func(student adlin.Student, ps httprouter.Params, body []byte) (interface{}, error) { + router.DELETE("/api/ddomains/:dn/DS", apiAuthHandler(func(student *adlin.Student, ps httprouter.Params, body []byte) (interface{}, error) { var ue Entry if err := json.Unmarshal(body, &ue); err != nil { return nil, err @@ -223,7 +223,7 @@ func parseZoneRead(globalDomain string, domain string) (rr []Entry, err error) { return } -func GetAssociatedDomain(student adlin.Student, dn string) (rrs []Entry, err error) { +func GetAssociatedDomain(student *adlin.Student, dn string) (rrs []Entry, err error) { domains := student.GetAssociatedDomains() found := false for _, d := range domains { @@ -249,7 +249,7 @@ func GetAssociatedDomain(student adlin.Student, dn string) (rrs []Entry, err err return } -func delAssociatedDomains(student adlin.Student, dn string) (err error) { +func delAssociatedDomains(student *adlin.Student, dn string) (err error) { var adomains []Entry adomains, err = GetAssociatedDomain(student, dn) if err != nil { @@ -286,7 +286,7 @@ func delAssociatedDomains(student adlin.Student, dn string) (err error) { return } -func AddAssociatedDomains(student adlin.Student, aaaa net.IP) (err error) { +func AddAssociatedDomains(student *adlin.Student, aaaa net.IP) (err error) { err = delAssociatedDomains(student, student.DefaultAssociatedDomain()) if err != nil { return @@ -322,7 +322,7 @@ func AddAssociatedDomains(student adlin.Student, aaaa net.IP) (err error) { return } -func getRRDelegatedDomain(student adlin.Student, dn string, rr string) (rrs []Entry, err error) { +func getRRDelegatedDomain(student *adlin.Student, dn string, rr string) (rrs []Entry, err error) { domains := []string{student.MyDelegatedDomain()} found := false for _, d := range domains { @@ -348,7 +348,7 @@ func getRRDelegatedDomain(student adlin.Student, dn string, rr string) (rrs []En return } -func AddNSDelegatedDomain(student adlin.Student, dn string, ttl uint32, ns string) (err error) { +func AddNSDelegatedDomain(student *adlin.Student, dn string, ttl uint32, ns string) (err error) { for _, d := range []string{student.MyDelegatedDomain()} { m1 := new(dns.Msg) m1.Id = dns.Id() @@ -371,7 +371,7 @@ func AddNSDelegatedDomain(student adlin.Student, dn string, ttl uint32, ns strin return } -func UpdateNSDelegatedDomain(student adlin.Student, dn string, ttl uint32, oldns string, ns string) (err error) { +func UpdateNSDelegatedDomain(student *adlin.Student, dn string, ttl uint32, oldns string, ns string) (err error) { for _, d := range []string{student.MyDelegatedDomain()} { m1 := new(dns.Msg) m1.Id = dns.Id() @@ -399,7 +399,7 @@ func UpdateNSDelegatedDomain(student adlin.Student, dn string, ttl uint32, oldns return } -func AddGLUEDelegatedDomain(student adlin.Student, dn string, ttl uint32, aaaa string) (err error) { +func AddGLUEDelegatedDomain(student *adlin.Student, dn string, ttl uint32, aaaa string) (err error) { domains := []string{student.MyDelegatedDomain()} found := false for _, d := range domains { @@ -435,7 +435,7 @@ func AddGLUEDelegatedDomain(student adlin.Student, dn string, ttl uint32, aaaa s return } -func UpdateGLUEDelegatedDomain(student adlin.Student, dn string, ttl uint32, oldaaaa string, aaaa string) (err error) { +func UpdateGLUEDelegatedDomain(student *adlin.Student, dn string, ttl uint32, oldaaaa string, aaaa string) (err error) { domains := []string{student.MyDelegatedDomain()} found := false for _, d := range domains { @@ -477,7 +477,7 @@ func UpdateGLUEDelegatedDomain(student adlin.Student, dn string, ttl uint32, old return } -func AddDSDelegatedDomain(student adlin.Student, dn string, ttl uint32, rdata string) (err error) { +func AddDSDelegatedDomain(student *adlin.Student, dn string, ttl uint32, rdata string) (err error) { domains := []string{student.MyDelegatedDomain()} found := false for _, d := range domains { @@ -527,7 +527,7 @@ func AddDSDelegatedDomain(student adlin.Student, dn string, ttl uint32, rdata st return } -func DeleteRRDelegatedDomain(student adlin.Student, dn string, rr string, values ...string) (err error) { +func DeleteRRDelegatedDomain(student *adlin.Student, dn string, rr string, values ...string) (err error) { domains := []string{student.MyDelegatedDomain()} found := false for _, d := range domains { diff --git a/token-validator/handler.go b/token-validator/handler.go index c93c842..d40be85 100644 --- a/token-validator/handler.go +++ b/token-validator/handler.go @@ -65,7 +65,7 @@ func rawHandler(f func(http.ResponseWriter, *http.Request, httprouter.Params, [] http.Error(w, fmt.Sprintf(`{"errmsg": %q}`, err), http.StatusUnauthorized) return } else { - student = &std + student = std } } @@ -158,7 +158,7 @@ func apiHandler(f DispatchFunction, access ...func(*adlin.Student, *http.Request return rawHandler(responseHandler(func(_ *http.Request, ps httprouter.Params, b []byte) (interface{}, error) { return f(ps, b) }), access...) } -func apiAuthHandler(f func(adlin.Student, httprouter.Params, []byte) (interface{}, error), access ...func(*adlin.Student, *http.Request) error) func(http.ResponseWriter, *http.Request, httprouter.Params) { +func apiAuthHandler(f func(*adlin.Student, httprouter.Params, []byte) (interface{}, error), access ...func(*adlin.Student, *http.Request) error) func(http.ResponseWriter, *http.Request, httprouter.Params) { return rawHandler(responseHandler(func(r *http.Request, ps httprouter.Params, b []byte) (interface{}, error) { if cookie, err := r.Cookie("auth"); err != nil { return nil, errors.New("Authorization required") @@ -176,7 +176,7 @@ func apiAuthHandler(f func(adlin.Student, httprouter.Params, []byte) (interface{ }), access...) } -func studentHandler(f func(adlin.Student, []byte) (interface{}, error)) func(httprouter.Params, []byte) (interface{}, error) { +func studentHandler(f func(*adlin.Student, []byte) (interface{}, error)) func(httprouter.Params, []byte) (interface{}, error) { return func(ps httprouter.Params, body []byte) (interface{}, error) { if sid, err := strconv.Atoi(string(ps.ByName("sid"))); err != nil { if student, err := adlin.GetStudentByLogin(ps.ByName("sid")); err != nil { diff --git a/token-validator/ip.go b/token-validator/ip.go index 7036cc4..885cd0f 100644 --- a/token-validator/ip.go +++ b/token-validator/ip.go @@ -12,12 +12,12 @@ import ( func init() { router.GET("/api/ips", apiHandler(showIPs)) - router.GET("/api/students/:sid/ips", apiHandler(studentHandler(func(student adlin.Student, body []byte) (interface{}, error) { + router.GET("/api/students/:sid/ips", apiHandler(studentHandler(func(student *adlin.Student, body []byte) (interface{}, error) { return getStudentIPs(student), nil }))) } -func IPSuffix(s adlin.Student, network net.IPNet) net.IP { +func IPSuffix(s *adlin.Student, network net.IPNet) net.IP { ipshift := s.Id*4 + 10 myIP := network.IP @@ -55,7 +55,7 @@ func showIPs(_ httprouter.Params, body []byte) (interface{}, error) { return r, nil } -func GetStudentTunnelIPs(student adlin.Student) (ips []string) { +func GetStudentTunnelIPs(student *adlin.Student) (ips []string) { if ts, err := student.GetActivesTunnels(); err != nil || len(ts) == 0 || ts[0].SuffixIP == 0 { ips = append(ips, adlin.StudentIP(student.Id).String()+"1") } else { @@ -66,7 +66,7 @@ func GetStudentTunnelIPs(student adlin.Student) (ips []string) { return } -func getStudentIPs(student adlin.Student) (r map[string]string) { +func getStudentIPs(student *adlin.Student) (r map[string]string) { r = make(map[string]string) r["vlan0"] = IPSuffix(student, net.IPNet{net.ParseIP("172.23.0.0"), net.CIDRMask(17, 32)}).String() diff --git a/token-validator/ping.go b/token-validator/ping.go index b6441c5..37a6290 100644 --- a/token-validator/ping.go +++ b/token-validator/ping.go @@ -11,13 +11,13 @@ var PongSecret = "felixfixit" func init() { router.GET("/api/students/:sid/ping", apiHandler(studentHandler(lastPing))) - router.GET("/api/students/:sid/pong", apiHandler(studentHandler(func(student adlin.Student, body []byte) (interface{}, error) { + router.GET("/api/students/:sid/pong", apiHandler(studentHandler(func(student *adlin.Student, body []byte) (interface{}, error) { return student.LastPongs() }))) router.POST("/api/students/:sid/pong", apiHandler(studentHandler(stdPong), sslOnly)) } -func lastPing(student adlin.Student, body []byte) (interface{}, error) { +func lastPing(student *adlin.Student, body []byte) (interface{}, error) { if pongs, err := student.LastPongs(); err != nil { return nil, err } else if len(pongs) <= 0 { @@ -27,7 +27,7 @@ func lastPing(student adlin.Student, body []byte) (interface{}, error) { } } -func stdPong(student adlin.Student, body []byte) (interface{}, error) { +func stdPong(student *adlin.Student, body []byte) (interface{}, error) { var gt givenToken if err := json.Unmarshal(body, >); err != nil { return nil, err diff --git a/token-validator/ssh.go b/token-validator/ssh.go index 666db4c..f810571 100644 --- a/token-validator/ssh.go +++ b/token-validator/ssh.go @@ -47,7 +47,7 @@ func init() { }) } -func hasSSHKeys(student adlin.Student, body []byte) (interface{}, error) { +func hasSSHKeys(student *adlin.Student, body []byte) (interface{}, error) { if keys, err := student.GetKeys(); err != nil { return nil, err } else { @@ -141,7 +141,7 @@ func dumpAuthorizedKeysFile(w io.Writer) { } } -func dumpStdAuthorizedKeysFile(s adlin.Student, w io.Writer) { +func dumpStdAuthorizedKeysFile(s *adlin.Student, w io.Writer) { seen := map[string]interface{}{} if keys, _ := s.GetKeys(); keys != nil { diff --git a/token-validator/students.go b/token-validator/students.go index ac101fa..cee1e47 100644 --- a/token-validator/students.go +++ b/token-validator/students.go @@ -18,19 +18,19 @@ func init() { if stds, err := adlin.GetStudents(); err != nil { return nil, err } else { - ret := map[string]map[string]adlin.UnlockedChallenge{} + ret := map[string]map[string]*adlin.UnlockedChallenge{} for _, std := range stds { if sts, err := std.GetStates(); err == nil { - ret[std.Login] = map[string]adlin.UnlockedChallenge{} + ret[std.Login] = map[string]*adlin.UnlockedChallenge{} for _, s := range sts { ret[std.Login][fmt.Sprintf("%d", s.Challenge)] = s } if pongs, err := std.LastPongs(); err == nil && len(pongs) > 0 { - ret[std.Login]["ping"] = adlin.UnlockedChallenge{ + ret[std.Login]["ping"] = &adlin.UnlockedChallenge{ IdStudent: std.Id, - Time: pongs[0].Date, + Time: &pongs[0].Date, Value: pongs[0].State, } } else if err != nil { @@ -47,17 +47,17 @@ func init() { })) router.POST("/api/students/", remoteValidatorHandler(apiHandler(createStudent))) router.GET("/api/students/:sid/", apiHandler(studentHandler( - func(std adlin.Student, _ []byte) (interface{}, error) { + func(std *adlin.Student, _ []byte) (interface{}, error) { return std, nil }))) router.PUT("/api/students/:sid/", remoteValidatorHandler(apiHandler(studentHandler(updateStudent)))) router.DELETE("/api/students/:sid/", remoteValidatorHandler(apiHandler(studentHandler( - func(std adlin.Student, _ []byte) (interface{}, error) { + func(std *adlin.Student, _ []byte) (interface{}, error) { return std.Delete() })))) router.GET("/api/students/:sid/progress", apiHandler(studentHandler( - func(std adlin.Student, _ []byte) (interface{}, error) { - ret := map[string]adlin.UnlockedChallenge{} + func(std *adlin.Student, _ []byte) (interface{}, error) { + ret := map[string]*adlin.UnlockedChallenge{} if sts, err := std.GetStates(); err == nil { for _, s := range sts { @@ -65,6 +65,20 @@ func init() { } } + if cerrors, err := std.GetChallengeErrors(); err == nil { + for _, cerr := range cerrors { + if _, ok := ret[fmt.Sprintf("%d", cerr.Challenge)]; ok { + ret[fmt.Sprintf("%d", cerr.Challenge)].Error = cerr.Error + ret[fmt.Sprintf("%d", cerr.Challenge)].LastCheck = &cerr.Time + } else { + ret[fmt.Sprintf("%d", cerr.Challenge)] = &adlin.UnlockedChallenge{ + LastCheck: &cerr.Time, + Error: cerr.Error, + } + } + } + } + return ret, nil }))) } @@ -82,7 +96,7 @@ func createStudent(_ httprouter.Params, body []byte) (interface{}, error) { return nil, err } - var exist adlin.Student + var exist *adlin.Student if exist, err = adlin.GetStudentByLogin(strings.TrimSpace(std.Login)); err != nil { if exist, err = adlin.NewStudent(strings.TrimSpace(std.Login)); err != nil { return nil, err @@ -96,8 +110,8 @@ func createStudent(_ httprouter.Params, body []byte) (interface{}, error) { return exist, nil } -func updateStudent(current adlin.Student, body []byte) (interface{}, error) { - var new adlin.Student +func updateStudent(current *adlin.Student, body []byte) (interface{}, error) { + new := &adlin.Student{} if err := json.Unmarshal(body, &new); err != nil { return nil, err } diff --git a/token-validator/wg.go b/token-validator/wg.go index 437f870..195a3f7 100644 --- a/token-validator/wg.go +++ b/token-validator/wg.go @@ -27,7 +27,7 @@ func init() { } }) router.GET("/api/wg/", apiAuthHandler(showWgTunnel)) - router.GET("/api/wginfo", apiAuthHandler(func(student adlin.Student, ps httprouter.Params, body []byte) (interface{}, error) { + router.GET("/api/wginfo", apiAuthHandler(func(student *adlin.Student, ps httprouter.Params, body []byte) (interface{}, error) { return getTunnelInfo(student.Id), nil })) router.POST("/api/wg/", apiAuthHandler(genWgToken)) @@ -37,12 +37,12 @@ func init() { router.DELETE("/api/wg/:token", apiAuthHandler(deleteWgTunnel)) } -func showWgTunnel(student adlin.Student, ps httprouter.Params, body []byte) (interface{}, error) { +func showWgTunnel(student *adlin.Student, ps httprouter.Params, body []byte) (interface{}, error) { // Get tunnels assigned to the student return student.GetTunnelTokens() } -func genWgToken(student adlin.Student, ps httprouter.Params, body []byte) (interface{}, error) { +func genWgToken(student *adlin.Student, ps httprouter.Params, body []byte) (interface{}, error) { // Generate a token to access related wg info return student.NewTunnelToken(0) } @@ -132,7 +132,7 @@ func getWgTunnelInfo(w http.ResponseWriter, r *http.Request, ps httprouter.Param tinfo := getTunnelInfo(token.IdStudent) - var student adlin.Student + var student *adlin.Student student, err = adlin.GetStudent(int(token.IdStudent)) if err != nil { http.Error(w, fmt.Sprintf("{errmsg:%q}", err), http.StatusBadRequest) @@ -152,7 +152,7 @@ PersistentKeepalive = 5 `, base64.StdEncoding.EncodeToString(tinfo.SrvPubKey), "82.64.31.248", tinfo.SrvPort, tinfo.CltIPv6, token.SuffixIP, 64, tinfo.CltIPv6, tinfo.CltRange, tinfo.SrvGW6, student.Login))) } -func updateWgTunnel(student adlin.Student, ps httprouter.Params, body []byte) (interface{}, error) { +func updateWgTunnel(student *adlin.Student, ps httprouter.Params, body []byte) (interface{}, error) { token, err := adlin.GetTunnelToken(adlin.TokenFromText(ps.ByName("token"))) if err != nil { return nil, err @@ -180,7 +180,7 @@ func updateWgTunnel(student adlin.Student, ps httprouter.Params, body []byte) (i return true, err } -func deleteWgTunnel(student adlin.Student, ps httprouter.Params, body []byte) (interface{}, error) { +func deleteWgTunnel(student *adlin.Student, ps httprouter.Params, body []byte) (interface{}, error) { token, err := adlin.GetTunnelToken(adlin.TokenFromText(ps.ByName("token"))) if err != nil { return nil, err From 456694c33071df4aa0e265a6f5cf73174743d690 Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Sun, 7 Mar 2021 12:40:50 +0100 Subject: [PATCH 19/23] maatma: add mistake indications on home page --- token-validator/htdocs/views/home.html | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/token-validator/htdocs/views/home.html b/token-validator/htdocs/views/home.html index ab643f5..8199ba3 100644 --- a/token-validator/htdocs/views/home.html +++ b/token-validator/htdocs/views/home.html @@ -15,9 +15,14 @@
-
- TP {{tutoid+1}} - +
+ TP {{tutoid+1}} +
+
+ +
{{ mychallenges[ch].error }}
+
+
From 07e91271f4fdcf3f0ff75be40c3ce0f2881e4f17 Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Sun, 7 Mar 2021 12:42:05 +0100 Subject: [PATCH 20/23] checker: better handle DNS checks --- checker/checker.go | 72 +++++++++++++++++++++++++++------------------- 1 file changed, 42 insertions(+), 30 deletions(-) diff --git a/checker/checker.go b/checker/checker.go index 3b98cae..4a51817 100644 --- a/checker/checker.go +++ b/checker/checker.go @@ -219,6 +219,7 @@ func studentsChecker() { } dnsIP := stdIP + var glueErr error // Is GLUE defined? if glueIP, err := get_GLUE(std.MyDelegatedDomain()); glueIP != nil { dnsIP = glueIP.String() @@ -228,48 +229,59 @@ func studentsChecker() { } } else if err != nil { log.Printf("%s and GLUE: %s\n", std.Login, err) + glueErr = err } // Check DNS if addr, err := check_dns(std.MyDelegatedDomain(), dnsIP); err == nil { - if verbose { - log.Printf("%s just unlocked DNS challenge\n", std.Login) - } - if _, err := std.UnlockChallenge(100*(tunnel_version-1)+3, ""); err != nil { - log.Printf("Unable to register challenge for %s: %s\n", std.Login, err.Error()) - } - - // Check HTTP with DNS if addr == nil { - log.Printf("%s and HTTP (with DNS ip=%s): skipped due to empty response\n", std.Login, addr.String()) - } else if err := check_http(addr.String(), std.MyDelegatedDomain()); err == nil { - if verbose { - log.Printf("%s just unlocked HTTP challenge\n", std.Login) + dnsAt := " at " + dnsIP + if glueErr != nil { + dnsAt = " + there is a problem with the GLUE record: " + glueErr.Error() } - if _, err := std.UnlockChallenge(100*(tunnel_version-1)+4, ""); err != nil { - log.Printf("Unable to register challenge for %s: %s\n", std.Login, err.Error()) + if errreg := std.RegisterChallengeError(100*(tunnel_version-1)+3, fmt.Errorf("%s: empty response from the server%s", std.MyDelegatedDomain(), dnsAt)); errreg != nil { + log.Printf("Unable to register challenge error for %s: %s\n", std.Login, errreg) } } else { - std.RegisterChallengeError(100*(tunnel_version-1)+4, err) if verbose { - log.Printf("%s and HTTP (with DNS ip=%s): %s\n", std.Login, addr.String(), err) + log.Printf("%s just unlocked DNS challenge\n", std.Login) } - } - - // Check HTTPs with DNS - if addr == nil { - log.Printf("%s and HTTPS (with DNS ip=%s): skipped due to empty response\n", std.Login, addr.String()) - } else if err := check_https(std.MyDelegatedDomain(), addr.String()); err == nil { - if verbose { - log.Printf("%s just unlocked HTTPS challenge\n", std.Login) - } - if _, err := std.UnlockChallenge(100*(tunnel_version-1)+5, ""); err != nil { + if _, err := std.UnlockChallenge(100*(tunnel_version-1)+3, addr.String()); err != nil { log.Printf("Unable to register challenge for %s: %s\n", std.Login, err.Error()) } - } else { - std.RegisterChallengeError(100*(tunnel_version-1)+5, err) - if verbose { - log.Printf("%s and HTTPS (with DNS ip=%s): %s\n", std.Login, addr.String(), err) + + // Check HTTP with DNS + if glueErr != nil { + std.RegisterChallengeError(100*(tunnel_version-1)+4, fmt.Errorf("Unable to perform the test due to GLUE problem: %w", err)) + } else if err := check_http(addr.String(), std.MyDelegatedDomain()); err == nil { + if verbose { + log.Printf("%s just unlocked HTTP challenge\n", std.Login) + } + if _, err := std.UnlockChallenge(100*(tunnel_version-1)+4, ""); err != nil { + log.Printf("Unable to register challenge for %s: %s\n", std.Login, err.Error()) + } + } else { + std.RegisterChallengeError(100*(tunnel_version-1)+4, err) + if verbose { + log.Printf("%s and HTTP (with DNS ip=%s): %s\n", std.Login, addr.String(), err) + } + } + + // Check HTTPs with DNS + if glueErr != nil { + std.RegisterChallengeError(100*(tunnel_version-1)+5, fmt.Errorf("Unable to perform the test due to GLUE problem: %w", err)) + } else if err := check_https(std.MyDelegatedDomain(), addr.String()); err == nil { + if verbose { + log.Printf("%s just unlocked HTTPS challenge\n", std.Login) + } + if _, err := std.UnlockChallenge(100*(tunnel_version-1)+5, ""); err != nil { + log.Printf("Unable to register challenge for %s: %s\n", std.Login, err.Error()) + } + } else { + std.RegisterChallengeError(100*(tunnel_version-1)+5, err) + if verbose { + log.Printf("%s and HTTPS (with DNS ip=%s): %s\n", std.Login, addr.String(), err) + } } } } else { From 78c42e312c37376886e928f5525abd1bf6e2f16e Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Sun, 7 Mar 2021 16:06:53 +0100 Subject: [PATCH 21/23] checker: add test for Matrix --- checker/checker.go | 84 ++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 82 insertions(+), 2 deletions(-) diff --git a/checker/checker.go b/checker/checker.go index 4a51817..7408180 100644 --- a/checker/checker.go +++ b/checker/checker.go @@ -1,6 +1,7 @@ package main import ( + "encoding/json" "errors" "fmt" "io/ioutil" @@ -165,6 +166,66 @@ func check_https(domain, ip string) (err error) { return } +// MATRIX + +type matrix_result struct { + WellKnownResult struct { + Result string `json:"result"` + } + DNSResult struct { + SRVError *struct { + Message string + } + } + ConnectionReports map[string]struct { + Errors []string + } + ConnectionErrors map[string]struct { + Message string + } + Version struct { + Name string `json:"name"` + Version string `json:"version"` + } + FederationOK bool +} + +func check_matrix(domain string) (err error) { + var resp *http.Response + resp, err = http.Get(fmt.Sprintf("https://federation-tester.adlin.nemunai.re/api/report?server_name=%s", strings.TrimSuffix(domain, "."))) + if err != nil { + return + } + defer resp.Body.Close() + + if resp.StatusCode >= 300 { + return nil + } + + var federationTest matrix_result + if federationTest.FederationOK { + return nil + } else if err = json.NewDecoder(resp.Body).Decode(&federationTest); err != nil { + log.Printf("Error in chech_matrix, when decoding json:", err.Error()) + return nil + } else if federationTest.DNSResult.SRVError != nil && federationTest.WellKnownResult.Result != "" { + return fmt.Errorf("%s OR %s", federationTest.DNSResult.SRVError.Message, federationTest.WellKnownResult.Result) + } else if len(federationTest.ConnectionErrors) > 0 { + var msg strings.Builder + for srv, cerr := range federationTest.ConnectionErrors { + if msg.Len() > 0 { + msg.WriteString("; ") + } + msg.WriteString(srv) + msg.WriteString(": ") + msg.WriteString(cerr.Message) + } + return fmt.Errorf("Connection errors: %s", msg.String()) + } else { + return fmt.Errorf("An unimplemented error occurs. Please report to nemunaire. But know that federation seems to be broken. Check https://federationtester.matrix.org/#%s", domain) + } +} + // Main func minTunnelVersion(std *adlin.Student, suffixip int) (int, error) { @@ -193,9 +254,11 @@ func studentsChecker() { log.Println("Unable to check students:", err) return } - log.Println("Checking students...") + check_matrix_for := (time.Now().Second()/30)*5 + time.Now().Minute()%5 - for _, s := range students { + log.Printf("Checking students... (std_matrix%%10=%d)\n", check_matrix_for) + + for istd, s := range students { time.Sleep(250 * time.Millisecond) // Check ping std := s @@ -283,6 +346,23 @@ func studentsChecker() { log.Printf("%s and HTTPS (with DNS ip=%s): %s\n", std.Login, addr.String(), err) } } + + // Check Matrix (only if GLUE Ok and) + if glueErr == nil && istd%10 == check_matrix_for { + if err := check_matrix(std.MyDelegatedDomain()); err == nil { + if verbose { + log.Printf("%s just unlocked Matrix challenge\n", std.Login) + } + if _, err := std.UnlockChallenge(100*(tunnel_version-1)+6, ""); err != nil { + log.Printf("Unable to register challenge for %s: %s\n", std.Login, err.Error()) + } + } else { + std.RegisterChallengeError(100*(tunnel_version-1)+6, err) + if verbose { + log.Printf("%s and Matrix: %s\n", std.Login, err) + } + } + } } } else { if errreg := std.RegisterChallengeError(100*(tunnel_version-1)+3, err); errreg != nil { From 405f3d63957917e4282adb11cc48f88c77bbfbe1 Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Mon, 8 Mar 2021 13:44:23 +0100 Subject: [PATCH 22/23] checker: improve matrix check --- checker/checker.go | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/checker/checker.go b/checker/checker.go index 7408180..5d510aa 100644 --- a/checker/checker.go +++ b/checker/checker.go @@ -190,7 +190,7 @@ type matrix_result struct { FederationOK bool } -func check_matrix(domain string) (err error) { +func check_matrix(domain string) (version string, err error) { var resp *http.Response resp, err = http.Get(fmt.Sprintf("https://federation-tester.adlin.nemunai.re/api/report?server_name=%s", strings.TrimSuffix(domain, "."))) if err != nil { @@ -199,17 +199,18 @@ func check_matrix(domain string) (err error) { defer resp.Body.Close() if resp.StatusCode >= 300 { - return nil + return "", fmt.Errorf("Sorry, the federation tester is broken. Check on https://federationtester.matrix.org/#%s", domain) } var federationTest matrix_result if federationTest.FederationOK { - return nil + version = federationTest.Version.Name + " " + federationTest.Version.Version + return version, nil } else if err = json.NewDecoder(resp.Body).Decode(&federationTest); err != nil { log.Printf("Error in chech_matrix, when decoding json:", err.Error()) - return nil + return "", fmt.Errorf("Sorry, the federation tester is broken. Check on https://federationtester.matrix.org/#%s", domain) } else if federationTest.DNSResult.SRVError != nil && federationTest.WellKnownResult.Result != "" { - return fmt.Errorf("%s OR %s", federationTest.DNSResult.SRVError.Message, federationTest.WellKnownResult.Result) + return "", fmt.Errorf("%s OR %s", federationTest.DNSResult.SRVError.Message, federationTest.WellKnownResult.Result) } else if len(federationTest.ConnectionErrors) > 0 { var msg strings.Builder for srv, cerr := range federationTest.ConnectionErrors { @@ -220,9 +221,9 @@ func check_matrix(domain string) (err error) { msg.WriteString(": ") msg.WriteString(cerr.Message) } - return fmt.Errorf("Connection errors: %s", msg.String()) + return "", fmt.Errorf("Connection errors: %s", msg.String()) } else { - return fmt.Errorf("An unimplemented error occurs. Please report to nemunaire. But know that federation seems to be broken. Check https://federationtester.matrix.org/#%s", domain) + return "", fmt.Errorf("An unimplemented error occurs. Please report to nemunaire. But know that federation seems to be broken. Check https://federationtester.matrix.org/#%s", domain) } } @@ -349,11 +350,11 @@ func studentsChecker() { // Check Matrix (only if GLUE Ok and) if glueErr == nil && istd%10 == check_matrix_for { - if err := check_matrix(std.MyDelegatedDomain()); err == nil { + if v, err := check_matrix(std.MyDelegatedDomain()); err == nil { if verbose { log.Printf("%s just unlocked Matrix challenge\n", std.Login) } - if _, err := std.UnlockChallenge(100*(tunnel_version-1)+6, ""); err != nil { + if _, err := std.UnlockChallenge(100*(tunnel_version-1)+6, v); err != nil { log.Printf("Unable to register challenge for %s: %s\n", std.Login, err.Error()) } } else { From caaa30c7478b70c7402c429771dffde03ece6888 Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Mon, 8 Mar 2021 13:44:42 +0100 Subject: [PATCH 23/23] tuto2: fix dead URL --- tutorial/ansible/deploiement-svc.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tutorial/ansible/deploiement-svc.md b/tutorial/ansible/deploiement-svc.md index af1b56c..953725c 100644 --- a/tutorial/ansible/deploiement-svc.md +++ b/tutorial/ansible/deploiement-svc.md @@ -34,7 +34,7 @@ dédié lorsque c'est possible (pas de `root` !), droits d'accès et permissions des répertoires, etc. Profitez des [modules de base de -données](http://docs.ansible.com/ansible/latest/list_of_database_modules.html) +données](https://docs.ansible.com/ansible/2.9/modules/list_of_database_modules.html) pour l'initialiser correctement. Et bien entendu de l'ensemble des modules décrits dans la documentation standard !