split checker from token-validator
This commit is contained in:
parent
685dc0b0ea
commit
0c661f36f6
@ -9,6 +9,8 @@ import (
|
|||||||
|
|
||||||
"github.com/miekg/dns"
|
"github.com/miekg/dns"
|
||||||
"github.com/sparrc/go-ping"
|
"github.com/sparrc/go-ping"
|
||||||
|
|
||||||
|
"git.nemunai.re/lectures/adlin/libadlin"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ICMP
|
// ICMP
|
||||||
@ -29,11 +31,6 @@ func check_ping(ip string, cb func(pkt *ping.Packet)) (err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Student) onPong(state bool) (err error) {
|
|
||||||
_, err = DBExec("INSERT INTO student_pong (id_student, time, state) VALUES (?, ?, ?)", s.Id, time.Now(), state)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// PORT 53
|
// PORT 53
|
||||||
|
|
||||||
func check_dns(domain, ip string) (err error) {
|
func check_dns(domain, ip string) (err error) {
|
||||||
@ -75,7 +72,7 @@ func check_https(domain, ip string) (err error) {
|
|||||||
// Main
|
// Main
|
||||||
|
|
||||||
func studentsChecker() {
|
func studentsChecker() {
|
||||||
students, err := getStudents()
|
students, err := adlin.GetStudents()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println("Unable to check students:", err)
|
log.Println("Unable to check students:", err)
|
||||||
return
|
return
|
||||||
@ -85,9 +82,9 @@ func studentsChecker() {
|
|||||||
time.Sleep(250 * time.Millisecond)
|
time.Sleep(250 * time.Millisecond)
|
||||||
// Check ping
|
// Check ping
|
||||||
std := s
|
std := s
|
||||||
stdIP := studentIP(std.Id).String() + "1"
|
stdIP := adlin.StudentIP(std.Id).String() + "1"
|
||||||
go check_ping(stdIP, func(pkt *ping.Packet) {
|
go check_ping(stdIP, func(pkt *ping.Packet) {
|
||||||
std.onPong(true)
|
std.OnPong(true)
|
||||||
|
|
||||||
// Check HTTP
|
// Check HTTP
|
||||||
if err := check_http(stdIP); err == nil {
|
if err := check_http(stdIP); err == nil {
|
||||||
@ -99,7 +96,7 @@ func studentsChecker() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Check HTTPs
|
// Check HTTPs
|
||||||
if err := check_https(std.myAssociatedDomain(), stdIP); err == nil {
|
if err := check_https(std.MyAssociatedDomain(), stdIP); err == nil {
|
||||||
if _, err := std.UnlockNewChallenge(101, ""); err != nil {
|
if _, err := std.UnlockNewChallenge(101, ""); err != nil {
|
||||||
if _, err := std.UpdateUnlockedChallenge(101, ""); err != nil {
|
if _, err := std.UpdateUnlockedChallenge(101, ""); err != nil {
|
||||||
log.Printf("Unable to register challenge for %s: %s\n", std.Login, err.Error())
|
log.Printf("Unable to register challenge for %s: %s\n", std.Login, err.Error())
|
@ -1,4 +1,4 @@
|
|||||||
package main
|
package adlin
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"database/sql"
|
"database/sql"
|
27
libadlin/domain.go
Normal file
27
libadlin/domain.go
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
package adlin
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
AssociatedDomainSuffix = "adlin2021.p0m.fr."
|
||||||
|
DelegatedDomainSuffix = "srs.p0m.fr."
|
||||||
|
)
|
||||||
|
|
||||||
|
func (student Student) MyDelegatedDomain() string {
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (student Student) GetAssociatedDomains() (ds []string) {
|
||||||
|
studentDomain := student.MyAssociatedDomain()
|
||||||
|
|
||||||
|
ds = append(ds, studentDomain)
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
36
libadlin/ping.go
Normal file
36
libadlin/ping.go
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
package adlin
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Pong struct {
|
||||||
|
Date time.Time
|
||||||
|
State bool
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
if err = rows.Scan(&p.Date, &p.State); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
pongs = append(pongs, p)
|
||||||
|
}
|
||||||
|
if err = rows.Err(); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Student) OnPong(state bool) (err error) {
|
||||||
|
_, err = DBExec("INSERT INTO student_pong (id_student, time, state) VALUES (?, ?, ?)", s.Id, time.Now(), state)
|
||||||
|
return
|
||||||
|
}
|
3
libadlin/secret.go
Normal file
3
libadlin/secret.go
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
package adlin
|
||||||
|
|
||||||
|
var SharedSecret string
|
@ -1,4 +1,4 @@
|
|||||||
package main
|
package adlin
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
@ -11,7 +11,7 @@ type Session struct {
|
|||||||
Time time.Time `json:"time"`
|
Time time.Time `json:"time"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func getSession(id []byte) (s Session, err error) {
|
func GetSession(id []byte) (s Session, err error) {
|
||||||
err = DBQueryRow("SELECT id_session, id_student, time FROM student_sessions WHERE id_session=?", id).Scan(&s.Id, &s.IdStudent, &s.Time)
|
err = DBQueryRow("SELECT id_session, id_student, time FROM student_sessions WHERE id_session=?", id).Scan(&s.Id, &s.IdStudent, &s.Time)
|
||||||
return
|
return
|
||||||
}
|
}
|
135
libadlin/ssh.go
Normal file
135
libadlin/ssh.go
Normal file
@ -0,0 +1,135 @@
|
|||||||
|
package adlin
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"errors"
|
||||||
|
"os/exec"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type StudentKey struct {
|
||||||
|
Id int64 `json:"id"`
|
||||||
|
IdStudent int64 `json:"id_student"`
|
||||||
|
Key string `json:"key"`
|
||||||
|
Time time.Time `json:"time"`
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
if err = rows.Scan(&k.Id, &k.IdStudent, &k.Key, &k.Time); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
keys = append(keys, k)
|
||||||
|
}
|
||||||
|
if err = rows.Err(); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
if err = rows.Scan(&k.Id, &k.IdStudent, &k.Key, &k.Time); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
keys = append(keys, k)
|
||||||
|
}
|
||||||
|
if err = rows.Err(); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func getStudentKey(id int) (k StudentKey, err error) {
|
||||||
|
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) {
|
||||||
|
// Check key before importing it
|
||||||
|
cmd := exec.Command("ssh-keygen", "-l", "-f", "-")
|
||||||
|
cmd.Stdin = strings.NewReader(key)
|
||||||
|
var stdoutStderr []byte
|
||||||
|
stdoutStderr, err = cmd.CombinedOutput()
|
||||||
|
if err != nil {
|
||||||
|
if _, ok := err.(*exec.ExitError); ok {
|
||||||
|
err = errors.New(string(stdoutStderr))
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
chunks := bytes.Fields(stdoutStderr)
|
||||||
|
|
||||||
|
keytype := string(chunks[len(chunks)-1])
|
||||||
|
minkeysize := 2048
|
||||||
|
if keytype == "(ED25519)" || keytype == "(ECDSA)" {
|
||||||
|
minkeysize = 256
|
||||||
|
}
|
||||||
|
|
||||||
|
var bits int
|
||||||
|
if bits, err = strconv.Atoi(string(chunks[0])); err != nil {
|
||||||
|
return
|
||||||
|
} else if bits < minkeysize {
|
||||||
|
err = errors.New("Keysize too small")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sanitize the given key
|
||||||
|
keyf := strings.Fields(key)
|
||||||
|
if len(keyf) < 2 {
|
||||||
|
err = errors.New("Unexpected key file, this should never happen")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
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
|
||||||
|
} else if kid, err := res.LastInsertId(); err != nil {
|
||||||
|
return StudentKey{}, err
|
||||||
|
} else {
|
||||||
|
s.UnlockNewChallenge(9, "")
|
||||||
|
return StudentKey{kid, s.Id, key, time.Now()}, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (k StudentKey) GetStudent() (Student, error) {
|
||||||
|
return GetStudent(int(k.IdStudent))
|
||||||
|
}
|
||||||
|
|
||||||
|
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 {
|
||||||
|
return 0, err
|
||||||
|
} else {
|
||||||
|
return nb, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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 {
|
||||||
|
return 0, err
|
||||||
|
} else {
|
||||||
|
return nb, err
|
||||||
|
}
|
||||||
|
}
|
177
libadlin/students.go
Normal file
177
libadlin/students.go
Normal file
@ -0,0 +1,177 @@
|
|||||||
|
package adlin
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/hmac"
|
||||||
|
"crypto/sha512"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Student struct {
|
||||||
|
Id int64 `json:"id"`
|
||||||
|
Login string `json:"login"`
|
||||||
|
Time *time.Time `json:"time"`
|
||||||
|
IP *string `json:"ip"`
|
||||||
|
MAC *string `json:"mac"`
|
||||||
|
}
|
||||||
|
|
||||||
|
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 {
|
||||||
|
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 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
students = append(students, s)
|
||||||
|
}
|
||||||
|
if err = rows.Err(); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
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)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func StudentExists(login string) bool {
|
||||||
|
var z int
|
||||||
|
err := DBQueryRow("SELECT 1 FROM students WHERE login=?", login).Scan(&z)
|
||||||
|
return err == nil && z == 1
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
} else if sid, err := res.LastInsertId(); err != nil {
|
||||||
|
return Student{}, err
|
||||||
|
} else {
|
||||||
|
return Student{sid, login, &t, nil, nil}, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s Student) GetPKey() []byte {
|
||||||
|
return hmac.New(sha512.New512_224, []byte(SharedSecret)).Sum([]byte(s.Login))
|
||||||
|
}
|
||||||
|
|
||||||
|
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 {
|
||||||
|
return 0, err
|
||||||
|
} else if nb, err := res.RowsAffected(); err != nil {
|
||||||
|
return 0, err
|
||||||
|
} else {
|
||||||
|
return nb, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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 {
|
||||||
|
return 0, err
|
||||||
|
} else {
|
||||||
|
return nb, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func ClearStudents() (int64, error) {
|
||||||
|
if res, err := DBExec("DELETE FROM students"); err != nil {
|
||||||
|
return 0, err
|
||||||
|
} else if nb, err := res.RowsAffected(); err != nil {
|
||||||
|
return 0, err
|
||||||
|
} else {
|
||||||
|
return nb, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type UnlockedChallenge struct {
|
||||||
|
Id int64 `json:"id,omitempty"`
|
||||||
|
IdStudent int64 `json:"id_student"`
|
||||||
|
Challenge int `json:"challenge,omitempty"`
|
||||||
|
Time time.Time `json:"time"`
|
||||||
|
Value interface{} `json:"value,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
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.IdStudent = s.Id
|
||||||
|
if err = rows.Scan(&u.Id, &u.Challenge, &u.Time); 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.IdStudent = s.Id
|
||||||
|
if err = rows.Scan(&u.Id, &u.Challenge, &u.Time, &u.Value); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ucs = append(ucs, u)
|
||||||
|
}
|
||||||
|
if err = rows.Err(); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
} else if utid, err := res.LastInsertId(); err != nil {
|
||||||
|
return UnlockedChallenge{}, err
|
||||||
|
} else {
|
||||||
|
return UnlockedChallenge{utid, s.Id, challenge, time.Now(), value}, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
} else {
|
||||||
|
return UnlockedChallenge{0, s.Id, challenge, time.Now(), value}, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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 {
|
||||||
|
return err
|
||||||
|
} else {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
@ -8,6 +8,8 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/julienschmidt/httprouter"
|
"github.com/julienschmidt/httprouter"
|
||||||
|
|
||||||
|
"git.nemunai.re/lectures/adlin/libadlin"
|
||||||
)
|
)
|
||||||
|
|
||||||
var AuthFunc = checkAuth
|
var AuthFunc = checkAuth
|
||||||
@ -20,17 +22,17 @@ func init() {
|
|||||||
router.POST("/api/auth/logout", apiRawHandler(logout))
|
router.POST("/api/auth/logout", apiRawHandler(logout))
|
||||||
}
|
}
|
||||||
|
|
||||||
func validateAuthToken(s Student, _ httprouter.Params, _ []byte) (interface{}, error) {
|
func validateAuthToken(s adlin.Student, _ httprouter.Params, _ []byte) (interface{}, error) {
|
||||||
return s, nil
|
return s, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func logout(w http.ResponseWriter, ps httprouter.Params, body []byte) (interface{}, error) {
|
func logout(w http.ResponseWriter, ps httprouter.Params, body []byte) (interface{}, error) {
|
||||||
http.SetCookie(w, &http.Cookie{
|
http.SetCookie(w, &http.Cookie{
|
||||||
Name: "auth",
|
Name: "auth",
|
||||||
Value: "",
|
Value: "",
|
||||||
Path: baseURL,
|
Path: baseURL + "/",
|
||||||
Expires: time.Unix(0,0),
|
Expires: time.Unix(0, 0),
|
||||||
Secure: true,
|
Secure: true,
|
||||||
HttpOnly: true,
|
HttpOnly: true,
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -42,18 +44,18 @@ type loginForm struct {
|
|||||||
Password string
|
Password string
|
||||||
}
|
}
|
||||||
|
|
||||||
func completeAuth(w http.ResponseWriter, username string, session *Session) (err error) {
|
func completeAuth(w http.ResponseWriter, username string, session *adlin.Session) (err error) {
|
||||||
var std Student
|
var std adlin.Student
|
||||||
if !studentExists(username) {
|
if !adlin.StudentExists(username) {
|
||||||
if std, err = NewStudent(username); err != nil {
|
if std, err = adlin.NewStudent(username); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
} else if std, err = getStudentByLogin(username); err != nil {
|
} else if std, err = adlin.GetStudentByLogin(username); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if session == nil {
|
if session == nil {
|
||||||
var s Session
|
var s adlin.Session
|
||||||
s, err = std.NewSession()
|
s, err = std.NewSession()
|
||||||
session = &s
|
session = &s
|
||||||
} else {
|
} else {
|
||||||
@ -65,11 +67,11 @@ func completeAuth(w http.ResponseWriter, username string, session *Session) (err
|
|||||||
}
|
}
|
||||||
|
|
||||||
http.SetCookie(w, &http.Cookie{
|
http.SetCookie(w, &http.Cookie{
|
||||||
Name: "auth",
|
Name: "auth",
|
||||||
Value: base64.StdEncoding.EncodeToString(session.Id),
|
Value: base64.StdEncoding.EncodeToString(session.Id),
|
||||||
Path: baseURL,
|
Path: baseURL + "/",
|
||||||
Expires: time.Now().Add(30 * 24 * time.Hour),
|
Expires: time.Now().Add(30 * 24 * time.Hour),
|
||||||
Secure: true,
|
Secure: true,
|
||||||
HttpOnly: true,
|
HttpOnly: true,
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -91,7 +93,7 @@ func checkAuth(w http.ResponseWriter, _ httprouter.Params, body []byte) (interfa
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if r, err := http.NewRequest("GET", "https://fic.srs.epita.fr/2021/", nil); err != nil {
|
if r, err := http.NewRequest("GET", "https://owncloud.srs.epita.fr/remote.php/dav/", nil); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
} else {
|
} else {
|
||||||
r.SetBasicAuth(lf.Username, lf.Password)
|
r.SetBasicAuth(lf.Username, lf.Password)
|
||||||
|
@ -12,11 +12,13 @@ import (
|
|||||||
|
|
||||||
"github.com/coreos/go-oidc"
|
"github.com/coreos/go-oidc"
|
||||||
"github.com/julienschmidt/httprouter"
|
"github.com/julienschmidt/httprouter"
|
||||||
|
|
||||||
|
"git.nemunai.re/lectures/adlin/libadlin"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
oidcClientID = ""
|
oidcClientID = ""
|
||||||
oidcSecret = ""
|
oidcSecret = ""
|
||||||
oauth2Config oauth2.Config
|
oauth2Config oauth2.Config
|
||||||
oidcVerifier *oidc.IDTokenVerifier
|
oidcVerifier *oidc.IDTokenVerifier
|
||||||
)
|
)
|
||||||
@ -49,7 +51,7 @@ func initializeOIDC() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
oidcConfig := oidc.Config{
|
oidcConfig := oidc.Config{
|
||||||
ClientID: oidcClientID,
|
ClientID: oidcClientID,
|
||||||
}
|
}
|
||||||
oidcVerifier = provider.Verifier(&oidcConfig)
|
oidcVerifier = provider.Verifier(&oidcConfig)
|
||||||
}
|
}
|
||||||
@ -57,7 +59,7 @@ func initializeOIDC() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func redirectOIDC_CRI(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
|
func redirectOIDC_CRI(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
|
||||||
session, err := NewSession()
|
session, err := adlin.NewSession()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
http.Error(w, fmt.Sprintf("{'errmsg':%q}", err.Error()), http.StatusInternalServerError)
|
http.Error(w, fmt.Sprintf("{'errmsg':%q}", err.Error()), http.StatusInternalServerError)
|
||||||
} else {
|
} else {
|
||||||
@ -72,7 +74,7 @@ func OIDC_CRI_complete(w http.ResponseWriter, r *http.Request, ps httprouter.Par
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
session, err := getSession(idsession)
|
session, err := adlin.GetSession(idsession)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
http.Error(w, fmt.Sprintf("{'errmsg':%q}", err.Error()), http.StatusBadRequest)
|
http.Error(w, fmt.Sprintf("{'errmsg':%q}", err.Error()), http.StatusBadRequest)
|
||||||
return
|
return
|
||||||
|
@ -15,27 +15,29 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/julienschmidt/httprouter"
|
"github.com/julienschmidt/httprouter"
|
||||||
|
|
||||||
|
"git.nemunai.re/lectures/adlin/libadlin"
|
||||||
)
|
)
|
||||||
|
|
||||||
const IPgwDMZ = "172.23.200.1"
|
const IPgwDMZ = "172.23.200.1"
|
||||||
|
|
||||||
type Challenge struct {
|
type Challenge struct {
|
||||||
Accessible []func(*Student, *http.Request) error
|
Accessible []func(*adlin.Student, *http.Request) error
|
||||||
Check func(*Student, *givenToken, int) error
|
Check func(*adlin.Student, *givenToken, int) error
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Restrictions */
|
/* Restrictions */
|
||||||
|
|
||||||
func noAccessRestriction(*Student, *http.Request) error {
|
func noAccessRestriction(*adlin.Student, *http.Request) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func noAccess(*Student, *http.Request) error {
|
func noAccess(*adlin.Student, *http.Request) error {
|
||||||
return errors.New("This challenge cannot be accessed this way. ")
|
return errors.New("This challenge cannot be accessed this way. ")
|
||||||
}
|
}
|
||||||
|
|
||||||
func accessFrom(ip string) func(_ *Student, r *http.Request) error {
|
func accessFrom(ip string) func(_ *adlin.Student, r *http.Request) error {
|
||||||
return func(_ *Student, r *http.Request) error {
|
return func(_ *adlin.Student, r *http.Request) error {
|
||||||
if r.Header.Get("X-Forwarded-By") != ip {
|
if r.Header.Get("X-Forwarded-By") != ip {
|
||||||
return errors.New("This challenge is not accessible this way.")
|
return errors.New("This challenge is not accessible this way.")
|
||||||
}
|
}
|
||||||
@ -43,8 +45,8 @@ func accessFrom(ip string) func(_ *Student, r *http.Request) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func notAccessFrom(ip string) func(_ *Student, r *http.Request) error {
|
func notAccessFrom(ip string) func(_ *adlin.Student, r *http.Request) error {
|
||||||
return func(_ *Student, r *http.Request) error {
|
return func(_ *adlin.Student, r *http.Request) error {
|
||||||
if r.Header.Get("X-Forwarded-By") == ip {
|
if r.Header.Get("X-Forwarded-By") == ip {
|
||||||
return errors.New("This challenge is not accessible this way.")
|
return errors.New("This challenge is not accessible this way.")
|
||||||
}
|
}
|
||||||
@ -52,8 +54,8 @@ func notAccessFrom(ip string) func(_ *Student, r *http.Request) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func maxProxy(nb int) func(_ *Student, r *http.Request) error {
|
func maxProxy(nb int) func(_ *adlin.Student, r *http.Request) error {
|
||||||
return func(_ *Student, r *http.Request) error {
|
return func(_ *adlin.Student, r *http.Request) error {
|
||||||
if len(strings.Split(r.Header.Get("X-Forwarded-For"), ",")) > nb {
|
if len(strings.Split(r.Header.Get("X-Forwarded-For"), ",")) > nb {
|
||||||
return errors.New("This challenge is not accessible this way.")
|
return errors.New("This challenge is not accessible this way.")
|
||||||
}
|
}
|
||||||
@ -61,50 +63,49 @@ func maxProxy(nb int) func(_ *Student, r *http.Request) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func sslOnly(_ *Student, r *http.Request) error {
|
func sslOnly(_ *adlin.Student, r *http.Request) error {
|
||||||
if r.Header.Get("X-Forwarded-Proto") != "https" {
|
if r.Header.Get("X-Forwarded-Proto") != "https" {
|
||||||
return errors.New("This challenge should be performed over TLS.")
|
return errors.New("This challenge should be performed over TLS.")
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Challenges */
|
/* Challenges */
|
||||||
|
|
||||||
func challenge42(s *Student, t *givenToken, chid int) error {
|
func challenge42(s *adlin.Student, t *givenToken, chid int) error {
|
||||||
pkey := s.GetPKey()
|
pkey := s.GetPKey()
|
||||||
if expectedToken, err := GenerateToken(pkey, chid, []byte("42")); err != nil {
|
if expectedToken, err := GenerateToken(pkey, chid, []byte("42")); err != nil {
|
||||||
return err
|
return err
|
||||||
} else if ! hmac.Equal(expectedToken, t.token) {
|
} else if !hmac.Equal(expectedToken, t.token) {
|
||||||
return errors.New("This is not the expected token.")
|
return errors.New("This is not the expected token.")
|
||||||
} else {
|
} else {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func challengeDNS(s *Student, t *givenToken, chid int) error {
|
func challengeDNS(s *adlin.Student, t *givenToken, chid int) error {
|
||||||
pkey := s.GetPKey()
|
pkey := s.GetPKey()
|
||||||
if expectedToken, err := GenerateToken(pkey, chid, []byte("8dde678132d6c558fc6adaeb9f1d53bf6ec7b876308cf98c48604caa9138523c1ce58b672c87c7e7d9b7248b81804d3940dbf20bf263eeb683244f7c1143712d")); err != nil {
|
if expectedToken, err := GenerateToken(pkey, chid, []byte("8dde678132d6c558fc6adaeb9f1d53bf6ec7b876308cf98c48604caa9138523c1ce58b672c87c7e7d9b7248b81804d3940dbf20bf263eeb683244f7c1143712d")); err != nil {
|
||||||
return err
|
return err
|
||||||
} else if ! hmac.Equal(expectedToken, t.token) {
|
} else if !hmac.Equal(expectedToken, t.token) {
|
||||||
return errors.New("This is not the expected token.")
|
return errors.New("This is not the expected token.")
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func challengeTime(s *Student, t *givenToken, chid int) error {
|
func challengeTime(s *adlin.Student, t *givenToken, chid int) error {
|
||||||
pkey := s.GetPKey()
|
pkey := s.GetPKey()
|
||||||
if expectedToken, err := GenerateToken(pkey, chid, []byte(t.Data[0])); err != nil {
|
if expectedToken, err := GenerateToken(pkey, chid, []byte(t.Data[0])); err != nil {
|
||||||
return err
|
return err
|
||||||
} else if ! hmac.Equal(expectedToken, t.token) {
|
} else if !hmac.Equal(expectedToken, t.token) {
|
||||||
return errors.New("This is not the expected token.")
|
return errors.New("This is not the expected token.")
|
||||||
} else if t, err := strconv.ParseInt(t.Data[0], 10, 64); err != nil {
|
} else if t, err := strconv.ParseInt(t.Data[0], 10, 64); err != nil {
|
||||||
return err
|
return err
|
||||||
} else {
|
} else {
|
||||||
var rt time.Time
|
var rt time.Time
|
||||||
if t > 3000000000 {
|
if t > 3000000000 {
|
||||||
rt = time.Unix(t / 1000000000, t % 1000000000)
|
rt = time.Unix(t/1000000000, t%1000000000)
|
||||||
} else {
|
} else {
|
||||||
rt = time.Unix(t, 0)
|
rt = time.Unix(t, 0)
|
||||||
}
|
}
|
||||||
@ -117,7 +118,7 @@ func challengeTime(s *Student, t *givenToken, chid int) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func challengePing(s *Student, t *givenToken, chid int) error {
|
func challengePing(s *adlin.Student, t *givenToken, chid int) error {
|
||||||
var expected []byte
|
var expected []byte
|
||||||
switch s.Id % 5 {
|
switch s.Id % 5 {
|
||||||
case 1:
|
case 1:
|
||||||
@ -164,7 +165,7 @@ func challengePing(s *Student, t *givenToken, chid int) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func challengeDisk(s *Student, t *givenToken, chid int) error {
|
func challengeDisk(s *adlin.Student, t *givenToken, chid int) error {
|
||||||
pkey := fmt.Sprintf("%x", s.GetPKey())
|
pkey := fmt.Sprintf("%x", s.GetPKey())
|
||||||
|
|
||||||
n1, err := strconv.Atoi(t.Data[0][0:2])
|
n1, err := strconv.Atoi(t.Data[0][0:2])
|
||||||
@ -182,19 +183,19 @@ func challengeDisk(s *Student, t *givenToken, chid int) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if n1+n2 > len(pkey) {
|
if n1+n2 > len(pkey) {
|
||||||
n2 = len(pkey)-n1
|
n2 = len(pkey) - n1
|
||||||
}
|
}
|
||||||
|
|
||||||
expectedToken := sha512.Sum512([]byte(pkey[n1:n1+n2]))
|
expectedToken := sha512.Sum512([]byte(pkey[n1 : n1+n2]))
|
||||||
|
|
||||||
if ! hmac.Equal(expectedToken[:], sum) {
|
if !hmac.Equal(expectedToken[:], sum) {
|
||||||
return errors.New("This is not the expected token.")
|
return errors.New("This is not the expected token.")
|
||||||
} else {
|
} else {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func challengeEMail(s *Student, t *givenToken, chid int) error {
|
func challengeEMail(s *adlin.Student, t *givenToken, chid int) error {
|
||||||
return errors.New("This is not the expected token.")
|
return errors.New("This is not the expected token.")
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -204,61 +205,61 @@ func init() {
|
|||||||
challenges = []Challenge{
|
challenges = []Challenge{
|
||||||
/* Challenge 1 : 42 */
|
/* Challenge 1 : 42 */
|
||||||
Challenge{
|
Challenge{
|
||||||
Accessible: []func(*Student, *http.Request) error{noAccessRestriction},
|
Accessible: []func(*adlin.Student, *http.Request) error{noAccessRestriction},
|
||||||
Check: challenge42,
|
Check: challenge42,
|
||||||
},
|
},
|
||||||
|
|
||||||
/* Challenge 2 : 42 from DMZ */
|
/* Challenge 2 : 42 from DMZ */
|
||||||
Challenge{
|
Challenge{
|
||||||
Accessible: []func(*Student, *http.Request) error{accessFrom(IPgwDMZ)},
|
Accessible: []func(*adlin.Student, *http.Request) error{accessFrom(IPgwDMZ)},
|
||||||
Check: challenge42,
|
Check: challenge42,
|
||||||
},
|
},
|
||||||
|
|
||||||
/* Challenge 3 : ssl (+ ntp) */
|
/* Challenge 3 : ssl (+ ntp) */
|
||||||
Challenge{
|
Challenge{
|
||||||
Accessible: []func(*Student, *http.Request) error{accessFrom(IPgwDMZ), sslOnly},
|
Accessible: []func(*adlin.Student, *http.Request) error{accessFrom(IPgwDMZ), sslOnly},
|
||||||
Check: challengeTime,
|
Check: challengeTime,
|
||||||
},
|
},
|
||||||
|
|
||||||
/* Challenge 4 : DNS TXT */
|
/* Challenge 4 : DNS TXT */
|
||||||
Challenge{
|
Challenge{
|
||||||
Accessible: []func(*Student, *http.Request) error{accessFrom(IPgwDMZ), sslOnly},
|
Accessible: []func(*adlin.Student, *http.Request) error{accessFrom(IPgwDMZ), sslOnly},
|
||||||
Check: challengeDNS,
|
Check: challengeDNS,
|
||||||
},
|
},
|
||||||
|
|
||||||
/* Challenge 5 : time net */
|
/* Challenge 5 : time net */
|
||||||
Challenge{
|
Challenge{
|
||||||
Accessible: []func(*Student, *http.Request) error{maxProxy(1)},
|
Accessible: []func(*adlin.Student, *http.Request) error{maxProxy(1)},
|
||||||
Check: challengeTime,
|
Check: challengeTime,
|
||||||
},
|
},
|
||||||
|
|
||||||
/* Bonus 0 : toctoc (read in source code) */
|
/* Bonus 0 : toctoc (read in source code) */
|
||||||
Challenge{
|
Challenge{
|
||||||
Accessible: []func(*Student, *http.Request) error{noAccessRestriction},
|
Accessible: []func(*adlin.Student, *http.Request) error{noAccessRestriction},
|
||||||
Check: challenge42,
|
Check: challenge42,
|
||||||
},
|
},
|
||||||
|
|
||||||
/* Bonus 1 : echo request */
|
/* Bonus 1 : echo request */
|
||||||
Challenge{
|
Challenge{
|
||||||
Accessible: []func(*Student, *http.Request) error{noAccessRestriction},
|
Accessible: []func(*adlin.Student, *http.Request) error{noAccessRestriction},
|
||||||
Check: challengePing,
|
Check: challengePing,
|
||||||
},
|
},
|
||||||
|
|
||||||
/* Bonus 2 : disk */
|
/* Bonus 2 : disk */
|
||||||
Challenge{
|
Challenge{
|
||||||
Accessible: []func(*Student, *http.Request) error{noAccessRestriction},
|
Accessible: []func(*adlin.Student, *http.Request) error{noAccessRestriction},
|
||||||
Check: challengeDisk,
|
Check: challengeDisk,
|
||||||
},
|
},
|
||||||
|
|
||||||
/* Bonus 3 : mail */
|
/* Bonus 3 : mail */
|
||||||
Challenge{
|
Challenge{
|
||||||
Accessible: []func(*Student, *http.Request) error{noAccessRestriction},
|
Accessible: []func(*adlin.Student, *http.Request) error{noAccessRestriction},
|
||||||
Check: challengeEMail,
|
Check: challengeEMail,
|
||||||
},
|
},
|
||||||
|
|
||||||
/* Last : SSH key, see ssh.go:156 in NewKey function */
|
/* Last : SSH key, see ssh.go:156 in NewKey function */
|
||||||
Challenge{
|
Challenge{
|
||||||
Accessible: []func(*Student, *http.Request) error{noAccess},
|
Accessible: []func(*adlin.Student, *http.Request) error{noAccess},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -272,9 +273,9 @@ func init() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type givenToken struct {
|
type givenToken struct {
|
||||||
Login string `json:"login"`
|
Login string `json:"login"`
|
||||||
Challenge int `json:"challenge"`
|
Challenge int `json:"challenge"`
|
||||||
Token string `json:"token"`
|
Token string `json:"token"`
|
||||||
token []byte
|
token []byte
|
||||||
Data []string `json:"data"`
|
Data []string `json:"data"`
|
||||||
}
|
}
|
||||||
@ -293,7 +294,7 @@ func accessibleChallenge(r *http.Request, ps httprouter.Params, _ []byte) (inter
|
|||||||
} else if chid == 0 || chid >= len(challenges) {
|
} else if chid == 0 || chid >= len(challenges) {
|
||||||
return nil, errors.New("This challenge doesn't exist")
|
return nil, errors.New("This challenge doesn't exist")
|
||||||
} else {
|
} else {
|
||||||
for _, a := range challenges[chid - 1].Accessible {
|
for _, a := range challenges[chid-1].Accessible {
|
||||||
if err := a(nil, r); err != nil {
|
if err := a(nil, r); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -319,13 +320,13 @@ func receiveChallenge(r *http.Request, ps httprouter.Params, body []byte) (inter
|
|||||||
return nil, errors.New("This is not the expected token.")
|
return nil, errors.New("This is not the expected token.")
|
||||||
}
|
}
|
||||||
|
|
||||||
var std Student
|
var std adlin.Student
|
||||||
|
|
||||||
if stdid, err := strconv.Atoi(gt.Login); err == nil {
|
if stdid, err := strconv.Atoi(gt.Login); err == nil {
|
||||||
if std, err = getStudent(stdid); err != nil {
|
if std, err = adlin.GetStudent(stdid); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
} else if std, err = getStudentByLogin(gt.Login); err != nil {
|
} else if std, err = adlin.GetStudentByLogin(gt.Login); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -355,21 +356,21 @@ func receiveToken(r *http.Request, body []byte, chid int) (interface{}, error) {
|
|||||||
chid = gt.Challenge
|
chid = gt.Challenge
|
||||||
}
|
}
|
||||||
|
|
||||||
if chid <= 0 || chid - 1 > len(challenges) {
|
if chid <= 0 || chid-1 > len(challenges) {
|
||||||
return nil, errors.New("This challenge doesn't exist")
|
return nil, errors.New("This challenge doesn't exist")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Is the challenge accessible?
|
// Is the challenge accessible?
|
||||||
for _, a := range challenges[chid - 1].Accessible {
|
for _, a := range challenges[chid-1].Accessible {
|
||||||
if err := a(nil, r); err != nil {
|
if err := a(nil, r); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if std, err := getStudentByLogin(gt.Login); err != nil {
|
if std, err := adlin.GetStudentByLogin(gt.Login); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
} else {
|
} 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)
|
log.Printf("%s just try ch#%d: %s\n", std.Login, chid, err)
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -11,104 +11,104 @@ import (
|
|||||||
|
|
||||||
"github.com/julienschmidt/httprouter"
|
"github.com/julienschmidt/httprouter"
|
||||||
"github.com/miekg/dns"
|
"github.com/miekg/dns"
|
||||||
|
|
||||||
|
"git.nemunai.re/lectures/adlin/libadlin"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
AssociatedDomainSuffix = "adlin2021.p0m.fr."
|
ControlSocket = "[2a01:e0a:2b:2250::b]:53"
|
||||||
DelegatedDomainSuffix = "srs.p0m.fr."
|
|
||||||
ControlSocket = "[2a01:e0a:2b:2250::b]:53"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var tsigSecret = map[string]string{"ddns.": "so6ZGir4GPAqINNh9U5c3A=="}
|
var tsigSecret = map[string]string{"ddns.": "so6ZGir4GPAqINNh9U5c3A=="}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
router.GET("/api/adomains/", apiAuthHandler(func(student 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
|
return student.GetAssociatedDomains(), nil
|
||||||
}))
|
}))
|
||||||
router.POST("/api/adomains/", apiAuthHandler(func(student Student, ps httprouter.Params, body []byte) (interface{}, error) {
|
router.POST("/api/adomains/", apiAuthHandler(func(student adlin.Student, ps httprouter.Params, body []byte) (interface{}, error) {
|
||||||
return true, student.AddAssociatedDomains()
|
return true, AddAssociatedDomains(student)
|
||||||
}))
|
}))
|
||||||
router.GET("/api/adomains/:dn", apiAuthHandler(func(student 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 student.GetAssociatedDomain(ps.ByName("dn"))
|
return GetAssociatedDomain(student, ps.ByName("dn"))
|
||||||
}))
|
}))
|
||||||
|
|
||||||
router.GET("/api/ddomains/", apiAuthHandler(func(student 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
|
return []string{student.MyDelegatedDomain()}, nil
|
||||||
}))
|
}))
|
||||||
router.GET("/api/ddomains/:dn/", apiAuthHandler(func(student 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 student.getRRDelegatedDomain(ps.ByName("dn"), "")
|
return getRRDelegatedDomain(student, ps.ByName("dn"), "")
|
||||||
}))
|
}))
|
||||||
router.GET("/api/ddomains/:dn/NS", apiAuthHandler(func(student 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 student.getRRDelegatedDomain(ps.ByName("dn"), "NS")
|
return getRRDelegatedDomain(student, ps.ByName("dn"), "NS")
|
||||||
}))
|
}))
|
||||||
router.POST("/api/ddomains/:dn/NS", apiAuthHandler(func(student 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
|
var ue Entry
|
||||||
if err := json.Unmarshal(body, &ue); err != nil {
|
if err := json.Unmarshal(body, &ue); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return true, student.AddNSDelegatedDomain(ps.ByName("dn"), ue.TTL, strings.Join(ue.Values, " "))
|
return true, AddNSDelegatedDomain(student, ps.ByName("dn"), ue.TTL, strings.Join(ue.Values, " "))
|
||||||
}))
|
}))
|
||||||
router.PATCH("/api/ddomains/:dn/NS", apiAuthHandler(func(student 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
|
var ue Entry
|
||||||
if err := json.Unmarshal(body, &ue); err != nil {
|
if err := json.Unmarshal(body, &ue); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return true, student.UpdateNSDelegatedDomain(ps.ByName("dn"), ue.TTL, ue.ValuesFrom, strings.Join(ue.Values, ""))
|
return true, UpdateNSDelegatedDomain(student, ps.ByName("dn"), ue.TTL, ue.ValuesFrom, strings.Join(ue.Values, ""))
|
||||||
}))
|
}))
|
||||||
router.DELETE("/api/ddomains/:dn/NS", apiAuthHandler(func(student 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
|
var ue Entry
|
||||||
if err := json.Unmarshal(body, &ue); err != nil {
|
if err := json.Unmarshal(body, &ue); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return true, student.DeleteRRDelegatedDomain(ps.ByName("dn"), "NS", strings.Join(ue.Values, " "))
|
return true, DeleteRRDelegatedDomain(student, ps.ByName("dn"), "NS", strings.Join(ue.Values, " "))
|
||||||
}))
|
}))
|
||||||
router.GET("/api/ddomains/:dn/GLUE", apiAuthHandler(func(student 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 student.getRRDelegatedDomain(ps.ByName("dn"), "AAAA")
|
return getRRDelegatedDomain(student, ps.ByName("dn"), "AAAA")
|
||||||
}))
|
}))
|
||||||
router.POST("/api/ddomains/:dn/AAAA", apiAuthHandler(func(student 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
|
var ue Entry
|
||||||
if err := json.Unmarshal(body, &ue); err != nil {
|
if err := json.Unmarshal(body, &ue); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return true, student.AddGLUEDelegatedDomain(ps.ByName("dn"), ue.TTL, strings.Join(ue.Values, " "))
|
return true, AddGLUEDelegatedDomain(student, ps.ByName("dn"), ue.TTL, strings.Join(ue.Values, " "))
|
||||||
}))
|
}))
|
||||||
router.PATCH("/api/ddomains/:dn/AAAA", apiAuthHandler(func(student 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
|
var ue Entry
|
||||||
if err := json.Unmarshal(body, &ue); err != nil {
|
if err := json.Unmarshal(body, &ue); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return true, student.UpdateGLUEDelegatedDomain(ps.ByName("dn"), ue.TTL, ue.ValuesFrom, strings.Join(ue.Values, " "))
|
return true, UpdateGLUEDelegatedDomain(student, ps.ByName("dn"), ue.TTL, ue.ValuesFrom, strings.Join(ue.Values, " "))
|
||||||
}))
|
}))
|
||||||
router.POST("/api/ddomains/:dn/GLUE", apiAuthHandler(func(student 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
|
var ue Entry
|
||||||
if err := json.Unmarshal(body, &ue); err != nil {
|
if err := json.Unmarshal(body, &ue); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return true, student.UpdateGLUEDelegatedDomain(ps.ByName("dn"), ue.TTL, ue.ValuesFrom, strings.Join(ue.Values, " "))
|
return true, UpdateGLUEDelegatedDomain(student, ps.ByName("dn"), ue.TTL, ue.ValuesFrom, strings.Join(ue.Values, " "))
|
||||||
}))
|
}))
|
||||||
router.DELETE("/api/ddomains/:dn/AAAA", apiAuthHandler(func(student 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
|
var ue Entry
|
||||||
if err := json.Unmarshal(body, &ue); err != nil {
|
if err := json.Unmarshal(body, &ue); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return true, student.DeleteRRDelegatedDomain(ps.ByName("dn"), "AAAA", strings.Join(ue.Values, " "))
|
return true, DeleteRRDelegatedDomain(student, ps.ByName("dn"), "AAAA", strings.Join(ue.Values, " "))
|
||||||
}))
|
}))
|
||||||
router.GET("/api/ddomains/:dn/DS", apiAuthHandler(func(student 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 student.getRRDelegatedDomain(ps.ByName("dn"), "DS")
|
return getRRDelegatedDomain(student, ps.ByName("dn"), "DS")
|
||||||
}))
|
}))
|
||||||
router.POST("/api/ddomains/:dn/DS", apiAuthHandler(func(student 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
|
var ue Entry
|
||||||
if err := json.Unmarshal(body, &ue); err != nil {
|
if err := json.Unmarshal(body, &ue); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return true, student.AddDSDelegatedDomain(ps.ByName("dn"), ue.TTL, strings.Join(ue.Values, " "))
|
return true, AddDSDelegatedDomain(student, ps.ByName("dn"), ue.TTL, strings.Join(ue.Values, " "))
|
||||||
}))
|
}))
|
||||||
router.DELETE("/api/ddomains/:dn/DS", apiAuthHandler(func(student 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
|
var ue Entry
|
||||||
if err := json.Unmarshal(body, &ue); err != nil {
|
if err := json.Unmarshal(body, &ue); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return true, student.DeleteRRDelegatedDomain(ps.ByName("dn"), "DS", strings.Join(ue.Values, " "))
|
return true, DeleteRRDelegatedDomain(student, ps.ByName("dn"), "DS", strings.Join(ue.Values, " "))
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -160,19 +160,7 @@ func parseZoneRead(globalDomain string, domain string) (rr []Entry, err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (student Student) myAssociatedDomain() string {
|
func GetAssociatedDomain(student adlin.Student, dn string) (rrs []Entry, err error) {
|
||||||
return fmt.Sprintf("%s.%s", strings.Trim(strings.Replace(student.Login, "_", "-", -1), "-_"), AssociatedDomainSuffix)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (student Student) GetAssociatedDomains() (ds []string) {
|
|
||||||
studentDomain := student.myAssociatedDomain()
|
|
||||||
|
|
||||||
ds = append(ds, studentDomain)
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (student Student) GetAssociatedDomain(dn string) (rrs []Entry, err error) {
|
|
||||||
domains := student.GetAssociatedDomains()
|
domains := student.GetAssociatedDomains()
|
||||||
found := false
|
found := false
|
||||||
for _, d := range domains {
|
for _, d := range domains {
|
||||||
@ -185,7 +173,7 @@ func (student Student) GetAssociatedDomain(dn string) (rrs []Entry, err error) {
|
|||||||
err = errors.New(fmt.Sprintf("Unable to find domain %q.", dn))
|
err = errors.New(fmt.Sprintf("Unable to find domain %q.", dn))
|
||||||
}
|
}
|
||||||
|
|
||||||
if entries, errr := parseZoneRead(AssociatedDomainSuffix, dn); err != nil {
|
if entries, errr := parseZoneRead(adlin.AssociatedDomainSuffix, dn); err != nil {
|
||||||
return nil, errr
|
return nil, errr
|
||||||
} else {
|
} else {
|
||||||
for _, e := range entries {
|
for _, e := range entries {
|
||||||
@ -198,19 +186,19 @@ func (student Student) GetAssociatedDomain(dn string) (rrs []Entry, err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (student Student) AddAssociatedDomains() (err error) {
|
func AddAssociatedDomains(student adlin.Student) (err error) {
|
||||||
m1 := new(dns.Msg)
|
m1 := new(dns.Msg)
|
||||||
m1.Id = dns.Id()
|
m1.Id = dns.Id()
|
||||||
m1.Opcode = dns.OpcodeUpdate
|
m1.Opcode = dns.OpcodeUpdate
|
||||||
m1.Question = make([]dns.Question, 1)
|
m1.Question = make([]dns.Question, 1)
|
||||||
m1.Question[0] = dns.Question{AssociatedDomainSuffix, dns.TypeSOA, dns.ClassINET}
|
m1.Question[0] = dns.Question{adlin.AssociatedDomainSuffix, dns.TypeSOA, dns.ClassINET}
|
||||||
|
|
||||||
rrAd := new(dns.A)
|
rrAd := new(dns.A)
|
||||||
rrAd.Hdr = dns.RR_Header{Name: student.myAssociatedDomain(), Rrtype: dns.TypeA, Class: dns.ClassINET, Ttl: 0}
|
rrAd.Hdr = dns.RR_Header{Name: student.MyAssociatedDomain(), Rrtype: dns.TypeA, Class: dns.ClassINET, Ttl: 0}
|
||||||
m1.Remove([]dns.RR{rrAd})
|
m1.Remove([]dns.RR{rrAd})
|
||||||
|
|
||||||
rrAAAAd := new(dns.AAAA)
|
rrAAAAd := new(dns.AAAA)
|
||||||
rrAAAAd.Hdr = dns.RR_Header{Name: student.myAssociatedDomain(), Rrtype: dns.TypeAAAA, Class: dns.ClassINET, Ttl: 0}
|
rrAAAAd.Hdr = dns.RR_Header{Name: student.MyAssociatedDomain(), Rrtype: dns.TypeAAAA, Class: dns.ClassINET, Ttl: 0}
|
||||||
m1.Remove([]dns.RR{rrAAAAd})
|
m1.Remove([]dns.RR{rrAAAAd})
|
||||||
|
|
||||||
c := new(dns.Client)
|
c := new(dns.Client)
|
||||||
@ -226,16 +214,16 @@ func (student Student) AddAssociatedDomains() (err error) {
|
|||||||
m2.Id = dns.Id()
|
m2.Id = dns.Id()
|
||||||
m2.Opcode = dns.OpcodeUpdate
|
m2.Opcode = dns.OpcodeUpdate
|
||||||
m2.Question = make([]dns.Question, 1)
|
m2.Question = make([]dns.Question, 1)
|
||||||
m2.Question[0] = dns.Question{AssociatedDomainSuffix, dns.TypeSOA, dns.ClassINET}
|
m2.Question[0] = dns.Question{adlin.AssociatedDomainSuffix, dns.TypeSOA, dns.ClassINET}
|
||||||
|
|
||||||
rrA := new(dns.A)
|
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.MyAssociatedDomain(), Rrtype: dns.TypeA, Class: dns.ClassINET, Ttl: 3600}
|
||||||
rrA.A = net.IPv4(82, 64, 31, 248)
|
rrA.A = net.IPv4(82, 64, 31, 248)
|
||||||
m2.Insert([]dns.RR{rrA})
|
m2.Insert([]dns.RR{rrA})
|
||||||
|
|
||||||
rrAAAA := new(dns.AAAA)
|
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.MyAssociatedDomain(), Rrtype: dns.TypeAAAA, Class: dns.ClassINET, Ttl: 3600}
|
||||||
rrAAAA.AAAA = studentIP(student.Id)
|
rrAAAA.AAAA = adlin.StudentIP(student.Id)
|
||||||
rrAAAA.AAAA[15] = 1
|
rrAAAA.AAAA[15] = 1
|
||||||
m2.Insert([]dns.RR{rrAAAA})
|
m2.Insert([]dns.RR{rrAAAA})
|
||||||
|
|
||||||
@ -247,11 +235,7 @@ func (student Student) AddAssociatedDomains() (err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (student Student) MyDelegatedDomain() string {
|
func getRRDelegatedDomain(student adlin.Student, dn string, rr string) (rrs []Entry, err error) {
|
||||||
return fmt.Sprintf("%s.%s", strings.Trim(strings.Replace(student.Login, "_", "-", -1), "-_"), DelegatedDomainSuffix)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (student Student) getRRDelegatedDomain(dn string, rr string) (rrs []Entry, err error) {
|
|
||||||
domains := []string{student.MyDelegatedDomain()}
|
domains := []string{student.MyDelegatedDomain()}
|
||||||
found := false
|
found := false
|
||||||
for _, d := range domains {
|
for _, d := range domains {
|
||||||
@ -264,7 +248,7 @@ func (student Student) getRRDelegatedDomain(dn string, rr string) (rrs []Entry,
|
|||||||
err = errors.New(fmt.Sprintf("Unable to find domain %q.", dn))
|
err = errors.New(fmt.Sprintf("Unable to find domain %q.", dn))
|
||||||
}
|
}
|
||||||
|
|
||||||
if entries, errr := parseZoneRead(DelegatedDomainSuffix, dn); err != nil {
|
if entries, errr := parseZoneRead(adlin.DelegatedDomainSuffix, dn); err != nil {
|
||||||
return nil, errr
|
return nil, errr
|
||||||
} else {
|
} else {
|
||||||
for _, e := range entries {
|
for _, e := range entries {
|
||||||
@ -277,13 +261,13 @@ func (student Student) getRRDelegatedDomain(dn string, rr string) (rrs []Entry,
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (student Student) AddNSDelegatedDomain(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()} {
|
for _, d := range []string{student.MyDelegatedDomain()} {
|
||||||
m1 := new(dns.Msg)
|
m1 := new(dns.Msg)
|
||||||
m1.Id = dns.Id()
|
m1.Id = dns.Id()
|
||||||
m1.Opcode = dns.OpcodeUpdate
|
m1.Opcode = dns.OpcodeUpdate
|
||||||
m1.Question = make([]dns.Question, 1)
|
m1.Question = make([]dns.Question, 1)
|
||||||
m1.Question[0] = dns.Question{DelegatedDomainSuffix, dns.TypeSOA, dns.ClassINET}
|
m1.Question[0] = dns.Question{adlin.DelegatedDomainSuffix, dns.TypeSOA, dns.ClassINET}
|
||||||
|
|
||||||
rrNS := new(dns.NS)
|
rrNS := new(dns.NS)
|
||||||
rrNS.Hdr = dns.RR_Header{Name: d, Rrtype: dns.TypeNS, Class: dns.ClassINET, Ttl: ttl}
|
rrNS.Hdr = dns.RR_Header{Name: d, Rrtype: dns.TypeNS, Class: dns.ClassINET, Ttl: ttl}
|
||||||
@ -300,13 +284,13 @@ func (student Student) AddNSDelegatedDomain(dn string, ttl uint32, ns string) (e
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (student Student) UpdateNSDelegatedDomain(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()} {
|
for _, d := range []string{student.MyDelegatedDomain()} {
|
||||||
m1 := new(dns.Msg)
|
m1 := new(dns.Msg)
|
||||||
m1.Id = dns.Id()
|
m1.Id = dns.Id()
|
||||||
m1.Opcode = dns.OpcodeUpdate
|
m1.Opcode = dns.OpcodeUpdate
|
||||||
m1.Question = make([]dns.Question, 1)
|
m1.Question = make([]dns.Question, 1)
|
||||||
m1.Question[0] = dns.Question{DelegatedDomainSuffix, dns.TypeSOA, dns.ClassINET}
|
m1.Question[0] = dns.Question{adlin.DelegatedDomainSuffix, dns.TypeSOA, dns.ClassINET}
|
||||||
|
|
||||||
rrOldNS := new(dns.NS)
|
rrOldNS := new(dns.NS)
|
||||||
rrOldNS.Hdr = dns.RR_Header{Name: d, Rrtype: dns.TypeNS, Class: dns.ClassINET}
|
rrOldNS.Hdr = dns.RR_Header{Name: d, Rrtype: dns.TypeNS, Class: dns.ClassINET}
|
||||||
@ -328,7 +312,7 @@ func (student Student) UpdateNSDelegatedDomain(dn string, ttl uint32, oldns stri
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (student Student) AddGLUEDelegatedDomain(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()}
|
domains := []string{student.MyDelegatedDomain()}
|
||||||
found := false
|
found := false
|
||||||
for _, d := range domains {
|
for _, d := range domains {
|
||||||
@ -346,7 +330,7 @@ func (student Student) AddGLUEDelegatedDomain(dn string, ttl uint32, aaaa string
|
|||||||
m1.Id = dns.Id()
|
m1.Id = dns.Id()
|
||||||
m1.Opcode = dns.OpcodeUpdate
|
m1.Opcode = dns.OpcodeUpdate
|
||||||
m1.Question = make([]dns.Question, 1)
|
m1.Question = make([]dns.Question, 1)
|
||||||
m1.Question[0] = dns.Question{DelegatedDomainSuffix, dns.TypeSOA, dns.ClassINET}
|
m1.Question[0] = dns.Question{adlin.DelegatedDomainSuffix, dns.TypeSOA, dns.ClassINET}
|
||||||
|
|
||||||
var rr dns.RR
|
var rr dns.RR
|
||||||
rr, err = dns.NewRR(fmt.Sprintf("%s %d IN AAAA %s", dn, ttl, aaaa))
|
rr, err = dns.NewRR(fmt.Sprintf("%s %d IN AAAA %s", dn, ttl, aaaa))
|
||||||
@ -364,7 +348,7 @@ func (student Student) AddGLUEDelegatedDomain(dn string, ttl uint32, aaaa string
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (student Student) UpdateGLUEDelegatedDomain(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()}
|
domains := []string{student.MyDelegatedDomain()}
|
||||||
found := false
|
found := false
|
||||||
for _, d := range domains {
|
for _, d := range domains {
|
||||||
@ -382,7 +366,7 @@ func (student Student) UpdateGLUEDelegatedDomain(dn string, ttl uint32, oldaaaa
|
|||||||
m1.Id = dns.Id()
|
m1.Id = dns.Id()
|
||||||
m1.Opcode = dns.OpcodeUpdate
|
m1.Opcode = dns.OpcodeUpdate
|
||||||
m1.Question = make([]dns.Question, 1)
|
m1.Question = make([]dns.Question, 1)
|
||||||
m1.Question[0] = dns.Question{DelegatedDomainSuffix, dns.TypeSOA, dns.ClassINET}
|
m1.Question[0] = dns.Question{adlin.DelegatedDomainSuffix, dns.TypeSOA, dns.ClassINET}
|
||||||
|
|
||||||
var rr dns.RR
|
var rr dns.RR
|
||||||
|
|
||||||
@ -406,7 +390,7 @@ func (student Student) UpdateGLUEDelegatedDomain(dn string, ttl uint32, oldaaaa
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (student Student) AddDSDelegatedDomain(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()}
|
domains := []string{student.MyDelegatedDomain()}
|
||||||
found := false
|
found := false
|
||||||
for _, d := range domains {
|
for _, d := range domains {
|
||||||
@ -438,7 +422,7 @@ func (student Student) AddDSDelegatedDomain(dn string, ttl uint32, rdata string)
|
|||||||
m1.Id = dns.Id()
|
m1.Id = dns.Id()
|
||||||
m1.Opcode = dns.OpcodeUpdate
|
m1.Opcode = dns.OpcodeUpdate
|
||||||
m1.Question = make([]dns.Question, 1)
|
m1.Question = make([]dns.Question, 1)
|
||||||
m1.Question[0] = dns.Question{DelegatedDomainSuffix, dns.TypeSOA, dns.ClassINET}
|
m1.Question[0] = dns.Question{adlin.DelegatedDomainSuffix, dns.TypeSOA, dns.ClassINET}
|
||||||
|
|
||||||
var ds *dns.DS
|
var ds *dns.DS
|
||||||
ds = dnskey.ToDS(dns.SHA256)
|
ds = dnskey.ToDS(dns.SHA256)
|
||||||
@ -456,7 +440,7 @@ func (student Student) AddDSDelegatedDomain(dn string, ttl uint32, rdata string)
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (student Student) DeleteRRDelegatedDomain(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()}
|
domains := []string{student.MyDelegatedDomain()}
|
||||||
found := false
|
found := false
|
||||||
for _, d := range domains {
|
for _, d := range domains {
|
||||||
@ -474,7 +458,7 @@ func (student Student) DeleteRRDelegatedDomain(dn string, rr string, values ...s
|
|||||||
m1.Id = dns.Id()
|
m1.Id = dns.Id()
|
||||||
m1.Opcode = dns.OpcodeUpdate
|
m1.Opcode = dns.OpcodeUpdate
|
||||||
m1.Question = make([]dns.Question, 1)
|
m1.Question = make([]dns.Question, 1)
|
||||||
m1.Question[0] = dns.Question{DelegatedDomainSuffix, dns.TypeSOA, dns.ClassINET}
|
m1.Question[0] = dns.Question{adlin.DelegatedDomainSuffix, dns.TypeSOA, dns.ClassINET}
|
||||||
|
|
||||||
rrr, errr := dns.NewRR(fmt.Sprintf("%s %s %s", dn, rr, strings.Join(values, " ")))
|
rrr, errr := dns.NewRR(fmt.Sprintf("%s %s %s", dn, rr, strings.Join(values, " ")))
|
||||||
if errr != nil {
|
if errr != nil {
|
||||||
|
@ -2,6 +2,8 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/julienschmidt/httprouter"
|
"github.com/julienschmidt/httprouter"
|
||||||
|
|
||||||
|
"git.nemunai.re/lectures/adlin/libadlin"
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
@ -9,7 +11,7 @@ func init() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func computeGrades(_ httprouter.Params, _ []byte) (interface{}, error) {
|
func computeGrades(_ httprouter.Params, _ []byte) (interface{}, error) {
|
||||||
if stds, err := getStudents(); err != nil {
|
if stds, err := adlin.GetStudents(); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
} else {
|
} else {
|
||||||
res := map[string]map[string]float32{}
|
res := map[string]map[string]float32{}
|
||||||
@ -20,7 +22,7 @@ func computeGrades(_ httprouter.Params, _ []byte) (interface{}, error) {
|
|||||||
"TP2": 0,
|
"TP2": 0,
|
||||||
}
|
}
|
||||||
|
|
||||||
if states, err := std.getStatesByChallenge(); err != nil {
|
if states, err := std.GetStatesByChallenge(); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
} else {
|
} else {
|
||||||
for _, st := range states {
|
for _, st := range states {
|
||||||
|
@ -14,6 +14,8 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/julienschmidt/httprouter"
|
"github.com/julienschmidt/httprouter"
|
||||||
|
|
||||||
|
"git.nemunai.re/lectures/adlin/libadlin"
|
||||||
)
|
)
|
||||||
|
|
||||||
var router = httprouter.New()
|
var router = httprouter.New()
|
||||||
@ -26,8 +28,8 @@ type DispatchFunction func(httprouter.Params, []byte) (interface{}, error)
|
|||||||
|
|
||||||
func remoteValidatorHandler(f func(http.ResponseWriter, *http.Request, httprouter.Params)) func(http.ResponseWriter, *http.Request, httprouter.Params) {
|
func remoteValidatorHandler(f func(http.ResponseWriter, *http.Request, httprouter.Params)) func(http.ResponseWriter, *http.Request, httprouter.Params) {
|
||||||
return func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
|
return func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
|
||||||
expectedMAC := hmac.New(sha512.New, []byte(sharedSecret)).Sum([]byte(fmt.Sprintf("%d", time.Now().Unix()/10)))
|
expectedMAC := hmac.New(sha512.New, []byte(adlin.SharedSecret)).Sum([]byte(fmt.Sprintf("%d", time.Now().Unix()/10)))
|
||||||
previousMAC := hmac.New(sha512.New, []byte(sharedSecret)).Sum([]byte(fmt.Sprintf("%d", time.Now().Unix()/10-1)))
|
previousMAC := hmac.New(sha512.New, []byte(adlin.SharedSecret)).Sum([]byte(fmt.Sprintf("%d", time.Now().Unix()/10-1)))
|
||||||
|
|
||||||
if aauth, err := base64.StdEncoding.DecodeString(r.Header.Get("X-ADLIN-Authentication")); err != nil {
|
if aauth, err := base64.StdEncoding.DecodeString(r.Header.Get("X-ADLIN-Authentication")); err != nil {
|
||||||
http.Error(w, fmt.Sprintf("{\"errmsg\":%q}\n", err), http.StatusUnauthorized)
|
http.Error(w, fmt.Sprintf("{\"errmsg\":%q}\n", err), http.StatusUnauthorized)
|
||||||
@ -39,7 +41,7 @@ func remoteValidatorHandler(f func(http.ResponseWriter, *http.Request, httproute
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func rawHandler(f func(http.ResponseWriter, *http.Request, httprouter.Params, []byte), access ...func(*Student, *http.Request) error) func(http.ResponseWriter, *http.Request, httprouter.Params) {
|
func rawHandler(f func(http.ResponseWriter, *http.Request, httprouter.Params, []byte), access ...func(*adlin.Student, *http.Request) error) func(http.ResponseWriter, *http.Request, httprouter.Params) {
|
||||||
return func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
|
return func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
|
||||||
if addr := r.Header.Get("X-Forwarded-For"); addr != "" {
|
if addr := r.Header.Get("X-Forwarded-For"); addr != "" {
|
||||||
r.RemoteAddr = addr
|
r.RemoteAddr = addr
|
||||||
@ -49,17 +51,17 @@ func rawHandler(f func(http.ResponseWriter, *http.Request, httprouter.Params, []
|
|||||||
w.Header().Set("Content-Type", "application/json")
|
w.Header().Set("Content-Type", "application/json")
|
||||||
|
|
||||||
// Read Authorization header
|
// Read Authorization header
|
||||||
var student *Student = nil
|
var student *adlin.Student = nil
|
||||||
if cookie, err := r.Cookie("auth"); err == nil {
|
if cookie, err := r.Cookie("auth"); err == nil {
|
||||||
if sessionid, err := base64.StdEncoding.DecodeString(cookie.Value); err != nil {
|
if sessionid, err := base64.StdEncoding.DecodeString(cookie.Value); err != nil {
|
||||||
http.Error(w, fmt.Sprintf(`{"errmsg": %q}`, err), http.StatusNotAcceptable)
|
http.Error(w, fmt.Sprintf(`{"errmsg": %q}`, err), http.StatusNotAcceptable)
|
||||||
return
|
return
|
||||||
} else if session, err := getSession(sessionid); err != nil {
|
} else if session, err := adlin.GetSession(sessionid); err != nil {
|
||||||
http.Error(w, fmt.Sprintf(`{"errmsg": %q}`, err), http.StatusUnauthorized)
|
http.Error(w, fmt.Sprintf(`{"errmsg": %q}`, err), http.StatusUnauthorized)
|
||||||
return
|
return
|
||||||
} else if session.IdStudent == nil {
|
} else if session.IdStudent == nil {
|
||||||
student = nil
|
student = nil
|
||||||
} else if std, err := getStudent(int(*session.IdStudent)); err != nil {
|
} else if std, err := adlin.GetStudent(int(*session.IdStudent)); err != nil {
|
||||||
http.Error(w, fmt.Sprintf(`{"errmsg": %q}`, err), http.StatusUnauthorized)
|
http.Error(w, fmt.Sprintf(`{"errmsg": %q}`, err), http.StatusUnauthorized)
|
||||||
return
|
return
|
||||||
} else {
|
} else {
|
||||||
@ -132,41 +134,41 @@ func responseHandler(f func(*http.Request, httprouter.Params, []byte) (interface
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func challengeHandler(f func (*http.Request, []byte, int) (interface{}, error)) func(*http.Request, httprouter.Params, []byte) (interface{}, error) {
|
func challengeHandler(f func(*http.Request, []byte, int) (interface{}, error)) func(*http.Request, httprouter.Params, []byte) (interface{}, error) {
|
||||||
return func(r *http.Request, ps httprouter.Params, body []byte) (interface{}, error) {
|
return func(r *http.Request, ps httprouter.Params, body []byte) (interface{}, error) {
|
||||||
return f(r, body, 0)
|
return f(r, body, 0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func definedChallengeHandler(f func (*http.Request, []byte, int) (interface{}, error), chid int) func(*http.Request, httprouter.Params, []byte) (interface{}, error) {
|
func definedChallengeHandler(f func(*http.Request, []byte, int) (interface{}, error), chid int) func(*http.Request, httprouter.Params, []byte) (interface{}, error) {
|
||||||
return func(r *http.Request, ps httprouter.Params, body []byte) (interface{}, error) {
|
return func(r *http.Request, ps httprouter.Params, body []byte) (interface{}, error) {
|
||||||
return f(r, body, chid)
|
return f(r, body, chid)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func apiRawHandler(f func(http.ResponseWriter, httprouter.Params, []byte) (interface{}, error), access ...func(*Student, *http.Request) error) func(http.ResponseWriter, *http.Request, httprouter.Params) {
|
func apiRawHandler(f func(http.ResponseWriter, httprouter.Params, []byte) (interface{}, error), access ...func(*adlin.Student, *http.Request) error) func(http.ResponseWriter, *http.Request, httprouter.Params) {
|
||||||
return rawHandler(func (w http.ResponseWriter, r *http.Request, ps httprouter.Params, b []byte) {
|
return rawHandler(func(w http.ResponseWriter, r *http.Request, ps httprouter.Params, b []byte) {
|
||||||
responseHandler(func (_ *http.Request, ps httprouter.Params, b []byte)(interface{}, error) {
|
responseHandler(func(_ *http.Request, ps httprouter.Params, b []byte) (interface{}, error) {
|
||||||
return f(w, ps, b)
|
return f(w, ps, b)
|
||||||
})(w, r, ps, b)
|
})(w, r, ps, b)
|
||||||
}, access...)
|
}, access...)
|
||||||
}
|
}
|
||||||
|
|
||||||
func apiHandler(f DispatchFunction, access ...func(*Student, *http.Request) error) func(http.ResponseWriter, *http.Request, httprouter.Params) {
|
func apiHandler(f DispatchFunction, access ...func(*adlin.Student, *http.Request) error) func(http.ResponseWriter, *http.Request, httprouter.Params) {
|
||||||
return rawHandler(responseHandler(func (_ *http.Request, ps httprouter.Params, b []byte) (interface{}, error) { return f(ps, b) }), access...)
|
return rawHandler(responseHandler(func(_ *http.Request, ps httprouter.Params, b []byte) (interface{}, error) { return f(ps, b) }), access...)
|
||||||
}
|
}
|
||||||
|
|
||||||
func apiAuthHandler(f func(Student, httprouter.Params, []byte) (interface{}, error), access ...func(*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) {
|
return rawHandler(responseHandler(func(r *http.Request, ps httprouter.Params, b []byte) (interface{}, error) {
|
||||||
if cookie, err := r.Cookie("auth"); err != nil {
|
if cookie, err := r.Cookie("auth"); err != nil {
|
||||||
return nil, errors.New("Authorization required")
|
return nil, errors.New("Authorization required")
|
||||||
} else if sessionid, err := base64.StdEncoding.DecodeString(cookie.Value); err != nil {
|
} else if sessionid, err := base64.StdEncoding.DecodeString(cookie.Value); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
} else if session, err := getSession(sessionid); err != nil {
|
} else if session, err := adlin.GetSession(sessionid); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
} else if session.IdStudent == nil {
|
} else if session.IdStudent == nil {
|
||||||
return nil, errors.New("Authorization required")
|
return nil, errors.New("Authorization required")
|
||||||
} else if std, err := getStudent(int(*session.IdStudent)); err != nil {
|
} else if std, err := adlin.GetStudent(int(*session.IdStudent)); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
} else {
|
} else {
|
||||||
return f(std, ps, b)
|
return f(std, ps, b)
|
||||||
@ -174,15 +176,15 @@ func apiAuthHandler(f func(Student, httprouter.Params, []byte) (interface{}, err
|
|||||||
}), access...)
|
}), access...)
|
||||||
}
|
}
|
||||||
|
|
||||||
func studentHandler(f func(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) {
|
return func(ps httprouter.Params, body []byte) (interface{}, error) {
|
||||||
if sid, err := strconv.Atoi(string(ps.ByName("sid"))); err != nil {
|
if sid, err := strconv.Atoi(string(ps.ByName("sid"))); err != nil {
|
||||||
if student, err := getStudentByLogin(ps.ByName("sid")); err != nil {
|
if student, err := adlin.GetStudentByLogin(ps.ByName("sid")); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
} else {
|
} else {
|
||||||
return f(student, body)
|
return f(student, body)
|
||||||
}
|
}
|
||||||
} else if student, err := getStudent(sid); err != nil {
|
} else if student, err := adlin.GetStudent(sid); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
} else {
|
} else {
|
||||||
return f(student, body)
|
return f(student, body)
|
||||||
|
@ -4,23 +4,25 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/julienschmidt/httprouter"
|
"github.com/julienschmidt/httprouter"
|
||||||
|
|
||||||
|
"git.nemunai.re/lectures/adlin/libadlin"
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
router.GET("/api/ips", apiHandler(showIPs))
|
router.GET("/api/ips", apiHandler(showIPs))
|
||||||
router.GET("/api/students/:sid/ips", apiHandler(studentHandler(func(student 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
|
return getStudentIPs(student), nil
|
||||||
})))
|
})))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s Student) IPSuffix() int64 {
|
func IPSuffix(s adlin.Student) int64 {
|
||||||
return s.Id * 5 + 10
|
return s.Id*4 + 10
|
||||||
}
|
}
|
||||||
|
|
||||||
func showIPs(_ httprouter.Params, body []byte) (interface{}, error) {
|
func showIPs(_ httprouter.Params, body []byte) (interface{}, error) {
|
||||||
r := make(map[string]map[string]string)
|
r := make(map[string]map[string]string)
|
||||||
|
|
||||||
students, err := getStudents()
|
students, err := adlin.GetStudents()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -40,13 +42,14 @@ func showIPs(_ httprouter.Params, body []byte) (interface{}, error) {
|
|||||||
return r, nil
|
return r, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func getStudentIPs(student Student) (r map[string]string) {
|
func getStudentIPs(student adlin.Student) (r map[string]string) {
|
||||||
r = make(map[string]string)
|
r = make(map[string]string)
|
||||||
|
|
||||||
r["vlan0"] = fmt.Sprintf("172.23.0.%d", student.IPSuffix())
|
r["vlan0"] = fmt.Sprintf("172.23.0.%d", IPSuffix(student))
|
||||||
r["vlan7"] = fmt.Sprintf("172.23.142.%d", student.IPSuffix())
|
r["wg0"] = fmt.Sprintf("172.17.0.%d", IPSuffix(student))
|
||||||
r["wg"] = studentIP(student.Id).String()
|
r["vlan7"] = fmt.Sprintf("172.23.142.%d", IPSuffix(student))
|
||||||
r["adn"] = student.myAssociatedDomain()
|
r["wg"] = adlin.StudentIP(student.Id).String()
|
||||||
|
r["adn"] = student.MyAssociatedDomain()
|
||||||
r["ddn"] = student.MyDelegatedDomain()
|
r["ddn"] = student.MyDelegatedDomain()
|
||||||
|
|
||||||
return
|
return
|
||||||
|
@ -12,11 +12,11 @@ import (
|
|||||||
"path"
|
"path"
|
||||||
"strings"
|
"strings"
|
||||||
"syscall"
|
"syscall"
|
||||||
"time"
|
|
||||||
|
"git.nemunai.re/lectures/adlin/libadlin"
|
||||||
)
|
)
|
||||||
|
|
||||||
var baseURL string = "/"
|
var baseURL string = "/"
|
||||||
var sharedSecret string
|
|
||||||
|
|
||||||
type ResponseWriterPrefix struct {
|
type ResponseWriterPrefix struct {
|
||||||
real http.ResponseWriter
|
real http.ResponseWriter
|
||||||
@ -60,9 +60,9 @@ func StripPrefix(prefix string, h http.Handler) http.Handler {
|
|||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
var bind = flag.String("bind", ":8081", "Bind port/socket")
|
var bind = flag.String("bind", ":8081", "Bind port/socket")
|
||||||
var dsn = flag.String("dsn", DSNGenerator(), "DSN to connect to the MySQL server")
|
var dsn = flag.String("dsn", adlin.DSNGenerator(), "DSN to connect to the MySQL server")
|
||||||
flag.StringVar(&baseURL, "baseurl", baseURL, "URL prepended to each URL")
|
flag.StringVar(&baseURL, "baseurl", baseURL, "URL prepended to each URL")
|
||||||
flag.StringVar(&sharedSecret, "sharedsecret", "adelina", "secret used to communicate with remote validator")
|
flag.StringVar(&adlin.SharedSecret, "sharedsecret", "adelina", "secret used to communicate with remote validator")
|
||||||
flag.StringVar(&AuthorizedKeysLocation, "authorizedkeyslocation", AuthorizedKeysLocation, "File for allowing user to SSH to the machine")
|
flag.StringVar(&AuthorizedKeysLocation, "authorizedkeyslocation", AuthorizedKeysLocation, "File for allowing user to SSH to the machine")
|
||||||
flag.StringVar(&SshPiperLocation, "sshPiperLocation", SshPiperLocation, "Directory containing directories for sshpiperd")
|
flag.StringVar(&SshPiperLocation, "sshPiperLocation", SshPiperLocation, "Directory containing directories for sshpiperd")
|
||||||
var dummyauth = flag.Bool("dummyauth", false, "don't perform password check")
|
var dummyauth = flag.Bool("dummyauth", false, "don't perform password check")
|
||||||
@ -88,13 +88,13 @@ func main() {
|
|||||||
|
|
||||||
// Initialize contents
|
// Initialize contents
|
||||||
log.Println("Opening database...")
|
log.Println("Opening database...")
|
||||||
if err := DBInit(*dsn); err != nil {
|
if err := adlin.DBInit(*dsn); err != nil {
|
||||||
log.Fatal("Cannot open the database: ", err)
|
log.Fatal("Cannot open the database: ", err)
|
||||||
}
|
}
|
||||||
defer DBClose()
|
defer adlin.DBClose()
|
||||||
|
|
||||||
log.Println("Creating database...")
|
log.Println("Creating database...")
|
||||||
if err := DBCreate(); err != nil {
|
if err := adlin.DBCreate(); err != nil {
|
||||||
log.Fatal("Cannot create database: ", err)
|
log.Fatal("Cannot create database: ", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -107,14 +107,6 @@ func main() {
|
|||||||
Handler: StripPrefix(baseURL, Router()),
|
Handler: StripPrefix(baseURL, Router()),
|
||||||
}
|
}
|
||||||
|
|
||||||
// Launch checker
|
|
||||||
go func() {
|
|
||||||
for {
|
|
||||||
studentsChecker()
|
|
||||||
time.Sleep(500 * time.Millisecond)
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
// Serve content
|
// Serve content
|
||||||
go func() {
|
go func() {
|
||||||
log.Fatal(srv.ListenAndServe())
|
log.Fatal(srv.ListenAndServe())
|
||||||
|
@ -3,21 +3,22 @@ package main
|
|||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"time"
|
|
||||||
|
"git.nemunai.re/lectures/adlin/libadlin"
|
||||||
)
|
)
|
||||||
|
|
||||||
var PongSecret = "felixfixit"
|
var PongSecret = "felixfixit"
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
router.GET("/api/students/:sid/ping", apiHandler(studentHandler(lastPing)))
|
router.GET("/api/students/:sid/ping", apiHandler(studentHandler(lastPing)))
|
||||||
router.GET("/api/students/:sid/pong", apiHandler(studentHandler(func(student Student, body []byte) (interface{}, error) {
|
router.GET("/api/students/:sid/pong", apiHandler(studentHandler(func(student adlin.Student, body []byte) (interface{}, error) {
|
||||||
return student.lastPongs()
|
return student.LastPongs()
|
||||||
})))
|
})))
|
||||||
router.POST("/api/students/:sid/pong", apiHandler(studentHandler(stdPong), sslOnly))
|
router.POST("/api/students/:sid/pong", apiHandler(studentHandler(stdPong), sslOnly))
|
||||||
}
|
}
|
||||||
|
|
||||||
func lastPing(student Student, body []byte) (interface{}, error) {
|
func lastPing(student adlin.Student, body []byte) (interface{}, error) {
|
||||||
if pongs, err := student.lastPongs(); err != nil {
|
if pongs, err := student.LastPongs(); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
} else if len(pongs) <= 0 {
|
} else if len(pongs) <= 0 {
|
||||||
return false, nil
|
return false, nil
|
||||||
@ -26,33 +27,7 @@ func lastPing(student Student, body []byte) (interface{}, error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type Pong struct {
|
func stdPong(student adlin.Student, body []byte) (interface{}, error) {
|
||||||
Date time.Time
|
|
||||||
State bool
|
|
||||||
}
|
|
||||||
|
|
||||||
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
|
|
||||||
if err = rows.Scan(&p.Date, &p.State); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
pongs = append(pongs, p)
|
|
||||||
}
|
|
||||||
if err = rows.Err(); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func stdPong(student Student, body []byte) (interface{}, error) {
|
|
||||||
var gt givenToken
|
var gt givenToken
|
||||||
if err := json.Unmarshal(body, >); err != nil {
|
if err := json.Unmarshal(body, >); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -62,5 +37,5 @@ func stdPong(student Student, body []byte) (interface{}, error) {
|
|||||||
return nil, errors.New("This is not the expected token.")
|
return nil, errors.New("This is not the expected token.")
|
||||||
}
|
}
|
||||||
|
|
||||||
return true, student.onPong(gt.Challenge == 0)
|
return true, student.OnPong(gt.Challenge == 0)
|
||||||
}
|
}
|
||||||
|
@ -1,23 +1,21 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"crypto/hmac"
|
"crypto/hmac"
|
||||||
"encoding/json"
|
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
|
||||||
"path"
|
"path"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/julienschmidt/httprouter"
|
"github.com/julienschmidt/httprouter"
|
||||||
|
|
||||||
|
"git.nemunai.re/lectures/adlin/libadlin"
|
||||||
)
|
)
|
||||||
|
|
||||||
var AuthorizedKeysLocation = "/root/.ssh/authorized_keys"
|
var AuthorizedKeysLocation = "/root/.ssh/authorized_keys"
|
||||||
@ -26,7 +24,7 @@ var SshPiperLocation = "/var/sshpiper/"
|
|||||||
func init() {
|
func init() {
|
||||||
router.GET("/sshkeys", apiHandler(
|
router.GET("/sshkeys", apiHandler(
|
||||||
func(httprouter.Params, []byte) (interface{}, error) {
|
func(httprouter.Params, []byte) (interface{}, error) {
|
||||||
return getStudentKeys()
|
return adlin.GetStudentKeys()
|
||||||
}))
|
}))
|
||||||
router.POST("/sshkeys", rawHandler(responseHandler(receiveKey)))
|
router.POST("/sshkeys", rawHandler(responseHandler(receiveKey)))
|
||||||
router.GET("/sshkeys/authorizedkeys", func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
|
router.GET("/sshkeys/authorizedkeys", func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
|
||||||
@ -35,153 +33,28 @@ func init() {
|
|||||||
router.GET("/api/students/:sid/hassshkeys", apiHandler(studentHandler(hasSSHKeys)))
|
router.GET("/api/students/:sid/hassshkeys", apiHandler(studentHandler(hasSSHKeys)))
|
||||||
router.GET("/api/students/:sid/authorizedkeys", func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
|
router.GET("/api/students/:sid/authorizedkeys", func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
|
||||||
if sid, err := strconv.Atoi(string(ps.ByName("sid"))); err != nil {
|
if sid, err := strconv.Atoi(string(ps.ByName("sid"))); err != nil {
|
||||||
if student, err := getStudentByLogin(ps.ByName("sid")); err != nil {
|
if student, err := adlin.GetStudentByLogin(ps.ByName("sid")); err != nil {
|
||||||
http.Error(w, "Student doesn't exist.", http.StatusNotFound)
|
http.Error(w, "Student doesn't exist.", http.StatusNotFound)
|
||||||
} else {
|
} else {
|
||||||
student.dumpAuthorizedKeysFile(w)
|
dumpStdAuthorizedKeysFile(student, w)
|
||||||
}
|
}
|
||||||
} else if student, err := getStudent(sid); err != nil {
|
} else if student, err := adlin.GetStudent(sid); err != nil {
|
||||||
http.Error(w, "Student doesn't exist.", http.StatusNotFound)
|
http.Error(w, "Student doesn't exist.", http.StatusNotFound)
|
||||||
} else {
|
} else {
|
||||||
student.dumpAuthorizedKeysFile(w)
|
dumpStdAuthorizedKeysFile(student, w)
|
||||||
}
|
}
|
||||||
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func hasSSHKeys(student Student, body []byte) (interface{}, error) {
|
func hasSSHKeys(student adlin.Student, body []byte) (interface{}, error) {
|
||||||
if keys, err := student.getKeys(); err != nil {
|
if keys, err := student.GetKeys(); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
} else {
|
} else {
|
||||||
return len(keys) > 0, nil
|
return len(keys) > 0, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type StudentKey struct {
|
|
||||||
Id int64 `json:"id"`
|
|
||||||
IdStudent int64 `json:"id_student"`
|
|
||||||
Key string `json:"key"`
|
|
||||||
Time time.Time `json:"time"`
|
|
||||||
}
|
|
||||||
|
|
||||||
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
|
|
||||||
if err = rows.Scan(&k.Id, &k.IdStudent, &k.Key, &k.Time); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
keys = append(keys, k)
|
|
||||||
}
|
|
||||||
if err = rows.Err(); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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
|
|
||||||
if err = rows.Scan(&k.Id, &k.IdStudent, &k.Key, &k.Time); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
keys = append(keys, k)
|
|
||||||
}
|
|
||||||
if err = rows.Err(); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func getStudentKey(id int) (k StudentKey, err error) {
|
|
||||||
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) {
|
|
||||||
// Check key before importing it
|
|
||||||
cmd := exec.Command("ssh-keygen", "-l", "-f", "-")
|
|
||||||
cmd.Stdin = strings.NewReader(key)
|
|
||||||
var stdoutStderr []byte
|
|
||||||
stdoutStderr, err = cmd.CombinedOutput()
|
|
||||||
if err != nil {
|
|
||||||
if _, ok := err.(*exec.ExitError); ok {
|
|
||||||
err = errors.New(string(stdoutStderr))
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
chunks := bytes.Fields(stdoutStderr)
|
|
||||||
|
|
||||||
keytype := string(chunks[len(chunks)-1])
|
|
||||||
minkeysize := 2048
|
|
||||||
if keytype == "(ED25519)" || keytype == "(ECDSA)" {
|
|
||||||
minkeysize = 256
|
|
||||||
}
|
|
||||||
|
|
||||||
var bits int
|
|
||||||
if bits, err = strconv.Atoi(string(chunks[0])); err != nil {
|
|
||||||
return
|
|
||||||
} else if bits < minkeysize {
|
|
||||||
err = errors.New("Keysize too small")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Sanitize the given key
|
|
||||||
keyf := strings.Fields(key)
|
|
||||||
if len(keyf) < 2 {
|
|
||||||
err = errors.New("Unexpected key file, this should never happen")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
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
|
|
||||||
} else if kid, err := res.LastInsertId(); err != nil {
|
|
||||||
return StudentKey{}, err
|
|
||||||
} else {
|
|
||||||
s.UnlockNewChallenge(len(challenges), "")
|
|
||||||
return StudentKey{kid, s.Id, key, time.Now()}, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (k StudentKey) GetStudent() (Student, error) {
|
|
||||||
return getStudent(int(k.IdStudent))
|
|
||||||
}
|
|
||||||
|
|
||||||
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 {
|
|
||||||
return 0, err
|
|
||||||
} else {
|
|
||||||
return nb, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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 {
|
|
||||||
return 0, err
|
|
||||||
} else {
|
|
||||||
return nb, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func receiveKey(r *http.Request, ps httprouter.Params, body []byte) (interface{}, error) {
|
func receiveKey(r *http.Request, ps httprouter.Params, body []byte) (interface{}, error) {
|
||||||
var gt givenToken
|
var gt givenToken
|
||||||
if err := json.Unmarshal(body, >); err != nil {
|
if err := json.Unmarshal(body, >); err != nil {
|
||||||
@ -193,7 +66,7 @@ func receiveKey(r *http.Request, ps httprouter.Params, body []byte) (interface{}
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if std, err := getStudentByLogin(gt.Login); err != nil {
|
if std, err := adlin.GetStudentByLogin(gt.Login); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
} else if len(gt.Data) < 2 {
|
} else if len(gt.Data) < 2 {
|
||||||
return nil, errors.New("No key found!")
|
return nil, errors.New("No key found!")
|
||||||
@ -207,7 +80,7 @@ func receiveKey(r *http.Request, ps httprouter.Params, body []byte) (interface{}
|
|||||||
|
|
||||||
if expectedToken, err := GenerateToken(pkey, 0, data...); err != nil {
|
if expectedToken, err := GenerateToken(pkey, 0, data...); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
} else if ! hmac.Equal(expectedToken, gt.token) {
|
} else if !hmac.Equal(expectedToken, gt.token) {
|
||||||
return nil, errors.New("This is not the expected token.")
|
return nil, errors.New("This is not the expected token.")
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -240,7 +113,7 @@ func receiveKey(r *http.Request, ps httprouter.Params, body []byte) (interface{}
|
|||||||
}
|
}
|
||||||
defer file.Close()
|
defer file.Close()
|
||||||
|
|
||||||
std.dumpAuthorizedKeysFile(file)
|
dumpStdAuthorizedKeysFile(std, file)
|
||||||
os.Symlink(path.Join(SshPiperLocation, "sshpiper_upstream"), path.Join(SshPiperLocation, std.Login, "sshpiper_upstream"))
|
os.Symlink(path.Join(SshPiperLocation, "sshpiper_upstream"), path.Join(SshPiperLocation, std.Login, "sshpiper_upstream"))
|
||||||
os.Symlink(path.Join(SshPiperLocation, "id_rsa"), path.Join(SshPiperLocation, std.Login, "id_rsa"))
|
os.Symlink(path.Join(SshPiperLocation, "id_rsa"), path.Join(SshPiperLocation, std.Login, "id_rsa"))
|
||||||
}
|
}
|
||||||
@ -254,7 +127,7 @@ func receiveKey(r *http.Request, ps httprouter.Params, body []byte) (interface{}
|
|||||||
func dumpAuthorizedKeysFile(w io.Writer) {
|
func dumpAuthorizedKeysFile(w io.Writer) {
|
||||||
seen := map[string]interface{}{}
|
seen := map[string]interface{}{}
|
||||||
|
|
||||||
if keys, _ := getStudentKeys(); keys != nil {
|
if keys, _ := adlin.GetStudentKeys(); keys != nil {
|
||||||
for _, k := range keys {
|
for _, k := range keys {
|
||||||
if _, exists := seen[k.Key]; exists {
|
if _, exists := seen[k.Key]; exists {
|
||||||
continue
|
continue
|
||||||
@ -268,10 +141,10 @@ func dumpAuthorizedKeysFile(w io.Writer) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s Student) dumpAuthorizedKeysFile(w io.Writer) {
|
func dumpStdAuthorizedKeysFile(s adlin.Student, w io.Writer) {
|
||||||
seen := map[string]interface{}{}
|
seen := map[string]interface{}{}
|
||||||
|
|
||||||
if keys, _ := s.getKeys(); keys != nil {
|
if keys, _ := s.GetKeys(); keys != nil {
|
||||||
for _, k := range keys {
|
for _, k := range keys {
|
||||||
if _, exists := seen[k.Key]; exists {
|
if _, exists := seen[k.Key]; exists {
|
||||||
continue
|
continue
|
||||||
|
@ -1,37 +1,36 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/hmac"
|
|
||||||
"crypto/sha512"
|
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/julienschmidt/httprouter"
|
"github.com/julienschmidt/httprouter"
|
||||||
|
|
||||||
|
"git.nemunai.re/lectures/adlin/libadlin"
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
router.GET("/api/progress", apiHandler(
|
router.GET("/api/progress", apiHandler(
|
||||||
func(httprouter.Params, []byte) (interface{}, error) {
|
func(httprouter.Params, []byte) (interface{}, error) {
|
||||||
if stds, err := getStudents(); err != nil {
|
if stds, err := adlin.GetStudents(); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
} else {
|
} else {
|
||||||
ret := map[string]map[string]UnlockedChallenge{}
|
ret := map[string]map[string]adlin.UnlockedChallenge{}
|
||||||
for _, std := range stds {
|
for _, std := range stds {
|
||||||
if sts, err := std.getStates(); err == nil {
|
if sts, err := std.GetStates(); err == nil {
|
||||||
ret[std.Login] = map[string]UnlockedChallenge{}
|
ret[std.Login] = map[string]adlin.UnlockedChallenge{}
|
||||||
|
|
||||||
for _, s := range sts {
|
for _, s := range sts {
|
||||||
ret[std.Login][fmt.Sprintf("%d", s.Challenge)] = s
|
ret[std.Login][fmt.Sprintf("%d", s.Challenge)] = s
|
||||||
}
|
}
|
||||||
|
|
||||||
if pongs, err := std.lastPongs(); err == nil && len(pongs) > 0 {
|
if pongs, err := std.LastPongs(); err == nil && len(pongs) > 0 {
|
||||||
ret[std.Login]["ping"] = UnlockedChallenge{
|
ret[std.Login]["ping"] = adlin.UnlockedChallenge{
|
||||||
IdStudent: std.Id,
|
IdStudent: std.Id,
|
||||||
Time: pongs[0].Date,
|
Time: pongs[0].Date,
|
||||||
Value: pongs[0].State,
|
Value: pongs[0].State,
|
||||||
}
|
}
|
||||||
} else if err != nil {
|
} else if err != nil {
|
||||||
log.Println(err)
|
log.Println(err)
|
||||||
@ -43,23 +42,23 @@ func init() {
|
|||||||
}))
|
}))
|
||||||
router.GET("/api/students/", apiHandler(
|
router.GET("/api/students/", apiHandler(
|
||||||
func(httprouter.Params, []byte) (interface{}, error) {
|
func(httprouter.Params, []byte) (interface{}, error) {
|
||||||
return getStudents()
|
return adlin.GetStudents()
|
||||||
}))
|
}))
|
||||||
router.POST("/api/students/", remoteValidatorHandler(apiHandler(createStudent)))
|
router.POST("/api/students/", remoteValidatorHandler(apiHandler(createStudent)))
|
||||||
router.GET("/api/students/:sid/", apiHandler(studentHandler(
|
router.GET("/api/students/:sid/", apiHandler(studentHandler(
|
||||||
func(std Student, _ []byte) (interface{}, error) {
|
func(std adlin.Student, _ []byte) (interface{}, error) {
|
||||||
return std, nil
|
return std, nil
|
||||||
})))
|
})))
|
||||||
router.PUT("/api/students/:sid/", remoteValidatorHandler(apiHandler(studentHandler(updateStudent))))
|
router.PUT("/api/students/:sid/", remoteValidatorHandler(apiHandler(studentHandler(updateStudent))))
|
||||||
router.DELETE("/api/students/:sid/", remoteValidatorHandler(apiHandler(studentHandler(
|
router.DELETE("/api/students/:sid/", remoteValidatorHandler(apiHandler(studentHandler(
|
||||||
func(std Student, _ []byte) (interface{}, error) {
|
func(std adlin.Student, _ []byte) (interface{}, error) {
|
||||||
return std.Delete()
|
return std.Delete()
|
||||||
}))))
|
}))))
|
||||||
router.GET("/api/students/:sid/progress", apiHandler(studentHandler(
|
router.GET("/api/students/:sid/progress", apiHandler(studentHandler(
|
||||||
func(std Student, _ []byte) (interface{}, error) {
|
func(std adlin.Student, _ []byte) (interface{}, error) {
|
||||||
ret := map[string]UnlockedChallenge{}
|
ret := map[string]adlin.UnlockedChallenge{}
|
||||||
|
|
||||||
if sts, err := std.getStates(); err == nil {
|
if sts, err := std.GetStates(); err == nil {
|
||||||
for _, s := range sts {
|
for _, s := range sts {
|
||||||
ret[fmt.Sprintf("%d", s.Challenge)] = s
|
ret[fmt.Sprintf("%d", s.Challenge)] = s
|
||||||
}
|
}
|
||||||
@ -69,102 +68,12 @@ func init() {
|
|||||||
})))
|
})))
|
||||||
}
|
}
|
||||||
|
|
||||||
type Student struct {
|
|
||||||
Id int64 `json:"id"`
|
|
||||||
Login string `json:"login"`
|
|
||||||
Time *time.Time `json:"time"`
|
|
||||||
IP *string `json:"ip"`
|
|
||||||
MAC *string `json:"mac"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type uploadedStudent struct {
|
type uploadedStudent struct {
|
||||||
Login string `json:"login"`
|
Login string `json:"login"`
|
||||||
IP string `json:"ip"`
|
IP string `json:"ip"`
|
||||||
MAC string `json:"mac"`
|
MAC string `json:"mac"`
|
||||||
}
|
}
|
||||||
|
|
||||||
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 {
|
|
||||||
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 {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
students = append(students, s)
|
|
||||||
}
|
|
||||||
if err = rows.Err(); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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)
|
|
||||||
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)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func studentExists(login string) bool {
|
|
||||||
var z int
|
|
||||||
err := DBQueryRow("SELECT 1 FROM students WHERE login=?", login).Scan(&z)
|
|
||||||
return err == nil && z == 1
|
|
||||||
}
|
|
||||||
|
|
||||||
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
|
|
||||||
} else if sid, err := res.LastInsertId(); err != nil {
|
|
||||||
return Student{}, err
|
|
||||||
} else {
|
|
||||||
return Student{sid, login, &t, nil, nil}, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s Student) GetPKey() []byte {
|
|
||||||
return hmac.New(sha512.New512_224, []byte(sharedSecret)).Sum([]byte(s.Login))
|
|
||||||
}
|
|
||||||
|
|
||||||
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 {
|
|
||||||
return 0, err
|
|
||||||
} else if nb, err := res.RowsAffected(); err != nil {
|
|
||||||
return 0, err
|
|
||||||
} else {
|
|
||||||
return nb, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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 {
|
|
||||||
return 0, err
|
|
||||||
} else {
|
|
||||||
return nb, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func ClearStudents() (int64, error) {
|
|
||||||
if res, err := DBExec("DELETE FROM students"); err != nil {
|
|
||||||
return 0, err
|
|
||||||
} else if nb, err := res.RowsAffected(); err != nil {
|
|
||||||
return 0, err
|
|
||||||
} else {
|
|
||||||
return nb, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func createStudent(_ httprouter.Params, body []byte) (interface{}, error) {
|
func createStudent(_ httprouter.Params, body []byte) (interface{}, error) {
|
||||||
var err error
|
var err error
|
||||||
var std uploadedStudent
|
var std uploadedStudent
|
||||||
@ -172,22 +81,22 @@ func createStudent(_ httprouter.Params, body []byte) (interface{}, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
var exist Student
|
var exist adlin.Student
|
||||||
if exist, err = getStudentByLogin(strings.TrimSpace(std.Login)); err != nil {
|
if exist, err = adlin.GetStudentByLogin(strings.TrimSpace(std.Login)); err != nil {
|
||||||
if exist, err = NewStudent(strings.TrimSpace(std.Login)); err != nil {
|
if exist, err = adlin.NewStudent(strings.TrimSpace(std.Login)); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
exist.registerAccess(std.IP, std.MAC)
|
exist.RegisterAccess(std.IP, std.MAC)
|
||||||
|
|
||||||
ip := fmt.Sprintf("172.23.0.%d", exist.IPSuffix())
|
ip := fmt.Sprintf("172.23.0.%d", IPSuffix(exist))
|
||||||
exist.IP = &ip
|
exist.IP = &ip
|
||||||
|
|
||||||
return exist, nil
|
return exist, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func updateStudent(current Student, body []byte) (interface{}, error) {
|
func updateStudent(current adlin.Student, body []byte) (interface{}, error) {
|
||||||
var new Student
|
var new adlin.Student
|
||||||
if err := json.Unmarshal(body, &new); err != nil {
|
if err := json.Unmarshal(body, &new); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -196,83 +105,3 @@ func updateStudent(current Student, body []byte) (interface{}, error) {
|
|||||||
current.Time = new.Time
|
current.Time = new.Time
|
||||||
return current.Update()
|
return current.Update()
|
||||||
}
|
}
|
||||||
|
|
||||||
type UnlockedChallenge struct {
|
|
||||||
Id int64 `json:"id,omitempty"`
|
|
||||||
IdStudent int64 `json:"id_student"`
|
|
||||||
Challenge int `json:"challenge,omitempty"`
|
|
||||||
Time time.Time `json:"time"`
|
|
||||||
Value interface{} `json:"value,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
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.IdStudent = s.Id
|
|
||||||
if err = rows.Scan(&u.Id, &u.Challenge, &u.Time); 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.IdStudent = s.Id
|
|
||||||
if err = rows.Scan(&u.Id, &u.Challenge, &u.Time, &u.Value); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
ucs = append(ucs, u)
|
|
||||||
}
|
|
||||||
if err = rows.Err(); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
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
|
|
||||||
} else if utid, err := res.LastInsertId(); err != nil {
|
|
||||||
return UnlockedChallenge{}, err
|
|
||||||
} else {
|
|
||||||
return UnlockedChallenge{utid, s.Id, challenge, time.Now(), value}, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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
|
|
||||||
} else {
|
|
||||||
return UnlockedChallenge{0, s.Id, challenge, time.Now(), value}, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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 {
|
|
||||||
return err
|
|
||||||
} else {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -1,8 +1,6 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/rand"
|
|
||||||
"crypto/sha512"
|
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
@ -12,10 +10,10 @@ import (
|
|||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"strings"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/julienschmidt/httprouter"
|
"github.com/julienschmidt/httprouter"
|
||||||
|
|
||||||
|
"git.nemunai.re/lectures/adlin/libadlin"
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
@ -29,19 +27,19 @@ func init() {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
router.GET("/api/wg/", apiAuthHandler(showWgTunnel))
|
router.GET("/api/wg/", apiAuthHandler(showWgTunnel))
|
||||||
router.GET("/api/wginfo", apiAuthHandler(func (student 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
|
return getTunnelInfo(student.Id), nil
|
||||||
}))
|
}))
|
||||||
router.POST("/api/wg/", apiAuthHandler(genWgToken))
|
router.POST("/api/wg/", apiAuthHandler(genWgToken))
|
||||||
router.POST("/api/wg/:token", getWgTunnelInfo)
|
router.POST("/api/wg/:token", getWgTunnelInfo)
|
||||||
}
|
}
|
||||||
|
|
||||||
func showWgTunnel(student 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
|
// Get tunnels assigned to the student
|
||||||
return student.GetTunnelTokens()
|
return student.GetTunnelTokens()
|
||||||
}
|
}
|
||||||
|
|
||||||
func genWgToken(student 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
|
// Generate a token to access related wg info
|
||||||
return student.NewTunnelToken()
|
return student.NewTunnelToken()
|
||||||
}
|
}
|
||||||
@ -58,12 +56,12 @@ type TunnelInfo struct {
|
|||||||
func getTunnelInfo(student int64) TunnelInfo {
|
func getTunnelInfo(student int64) TunnelInfo {
|
||||||
srv_pubkey, _ := base64.StdEncoding.DecodeString("uSpqyYovvP4OG6wDxZ0Qkq45MfyK58PMUuPaLesY8FI=")
|
srv_pubkey, _ := base64.StdEncoding.DecodeString("uSpqyYovvP4OG6wDxZ0Qkq45MfyK58PMUuPaLesY8FI=")
|
||||||
return TunnelInfo{
|
return TunnelInfo{
|
||||||
Status: "OK",
|
Status: "OK",
|
||||||
SrvPubKey: srv_pubkey,
|
SrvPubKey: srv_pubkey,
|
||||||
SrvPort: 42912,
|
SrvPort: 42912,
|
||||||
CltIPv6: studentIP(student),
|
CltIPv6: adlin.StudentIP(student),
|
||||||
CltRange: 80,
|
CltRange: 80,
|
||||||
SrvGW6: "2a01:e0a:2b:2252::1",
|
SrvGW6: "2a01:e0a:2b:2252::1",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -92,7 +90,7 @@ func getWgTunnelInfo(w http.ResponseWriter, r *http.Request, ps httprouter.Param
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
token, err := GetTunnelToken(tokendec[:n])
|
token, err := adlin.GetTunnelToken(tokendec[:n])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
http.Error(w, fmt.Sprintf("{errmsg:%q}", err), http.StatusBadRequest)
|
http.Error(w, fmt.Sprintf("{errmsg:%q}", err), http.StatusBadRequest)
|
||||||
return
|
return
|
||||||
@ -127,127 +125,8 @@ PersistentKeepalive = 5
|
|||||||
`, base64.StdEncoding.EncodeToString(tinfo.SrvPubKey), "82.64.31.248", tinfo.SrvPort, tinfo.CltIPv6, 64, tinfo.CltIPv6, tinfo.CltRange, tinfo.SrvGW6)))
|
`, base64.StdEncoding.EncodeToString(tinfo.SrvPubKey), "82.64.31.248", tinfo.SrvPort, tinfo.CltIPv6, 64, tinfo.CltIPv6, tinfo.CltRange, tinfo.SrvGW6)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func GenWGConfig(w io.Writer) error {
|
||||||
type TunnelToken struct {
|
ts, err := adlin.GetStudentsTunnels()
|
||||||
token []byte
|
|
||||||
TokenText string
|
|
||||||
IdStudent int64
|
|
||||||
PubKey []byte
|
|
||||||
Time time.Time
|
|
||||||
Dump *WGDump
|
|
||||||
}
|
|
||||||
|
|
||||||
func GetTunnelToken(token []byte) (t TunnelToken, err error) {
|
|
||||||
err = DBQueryRow("SELECT token, token_text, id_student, pubkey, time FROM student_tunnel_tokens WHERE token=? ORDER BY time DESC", token).Scan(&t.token, &t.TokenText, &t.IdStudent, &t.PubKey, &t.Time)
|
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func tokenFromText(token string) []byte {
|
|
||||||
sha := sha512.Sum512([]byte(token))
|
|
||||||
return sha[:]
|
|
||||||
}
|
|
||||||
|
|
||||||
func (student Student) NewTunnelToken() (t TunnelToken, err error) {
|
|
||||||
tok := make([]byte, 7)
|
|
||||||
if _, err = rand.Read(tok); err != nil {
|
|
||||||
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.token = tokenFromText(t.TokenText)
|
|
||||||
t.IdStudent = student.Id
|
|
||||||
|
|
||||||
_, err = DBExec("INSERT INTO student_tunnel_tokens (token, token_text, id_student, time) VALUES (?, ?, ?, ?)", t.token, t.TokenText, student.Id, time.Now())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (student Student) GetTunnelTokens() (ts []TunnelToken, err error) {
|
|
||||||
if rows, errr := DBQuery("SELECT token, token_text, id_student, pubkey, time 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 {
|
|
||||||
return nil, errr
|
|
||||||
} else {
|
|
||||||
defer rows.Close()
|
|
||||||
|
|
||||||
for rows.Next() {
|
|
||||||
var t TunnelToken
|
|
||||||
if err = rows.Scan(&t.token, &t.TokenText, &t.IdStudent, &t.PubKey, &t.Time); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if t.PubKey != nil {
|
|
||||||
if v, ok := wgd[base64.StdEncoding.EncodeToString(t.PubKey)]; ok {
|
|
||||||
t.Dump = &v
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ts = append(ts, t)
|
|
||||||
}
|
|
||||||
if err = rows.Err(); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (student Student) GetTunnelToken(token []byte) (t TunnelToken, err error) {
|
|
||||||
err = DBQueryRow("SELECT token, token_text, id_student, pubkey, time 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)
|
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *TunnelToken) Update() (int64, error) {
|
|
||||||
newtoken := tokenFromText(t.TokenText)
|
|
||||||
tm := time.Now()
|
|
||||||
|
|
||||||
if res, err := DBExec("UPDATE student_tunnel_tokens SET token = ?, token_text = ?, id_student = ?, pubkey = ?, time = ? WHERE token = ?", newtoken, t.TokenText, t.IdStudent, t.PubKey, tm, t.token); err != nil {
|
|
||||||
return 0, err
|
|
||||||
} else if nb, err := res.RowsAffected(); err != nil {
|
|
||||||
return 0, err
|
|
||||||
} else {
|
|
||||||
t.token = newtoken
|
|
||||||
t.Time = tm
|
|
||||||
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 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
|
|
||||||
if err = rows.Scan(&t.token, &t.TokenText, &t.IdStudent, &t.PubKey, &t.Time); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
ts = append(ts, t)
|
|
||||||
}
|
|
||||||
|
|
||||||
err = rows.Err()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func studentIP(idstd int64) net.IP {
|
|
||||||
return net.ParseIP(fmt.Sprintf("2a01:e0a:2b:2252:%x::", idstd))
|
|
||||||
}
|
|
||||||
|
|
||||||
func GenWGConfig(w io.Writer) (error) {
|
|
||||||
ts, err := GetStudentsTunnels()
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -261,46 +140,13 @@ func GenWGConfig(w io.Writer) (error) {
|
|||||||
#IdStudent = %d
|
#IdStudent = %d
|
||||||
PublicKey = %s
|
PublicKey = %s
|
||||||
AllowedIPs = %s/%d
|
AllowedIPs = %s/%d
|
||||||
`, t.IdStudent, base64.StdEncoding.EncodeToString(t.PubKey), studentIP(t.IdStudent), 80)))
|
`, t.IdStudent, base64.StdEncoding.EncodeToString(t.PubKey), adlin.StudentIP(t.IdStudent), 80)))
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
func syncWgConf() (err error) {
|
func syncWgConf() (err error) {
|
||||||
_, err = exec.Command("sh", "/root/wg-sync.sh").Output()
|
_, err = exec.Command("sh", "/root/wg-sync.sh").Output()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
type WGDump struct {
|
|
||||||
PubKey string
|
|
||||||
PSK string
|
|
||||||
Endpoint string
|
|
||||||
AllowedIPs string
|
|
||||||
LastHandS string
|
|
||||||
RX string
|
|
||||||
TX string
|
|
||||||
KeepAlive string
|
|
||||||
}
|
|
||||||
|
|
||||||
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{}
|
|
||||||
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]}
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
Reference in New Issue
Block a user