This repository has been archived on 2024-03-03. You can view files and clone it, but cannot push or open issues or pull requests.
adlin/token-validator/wg.go

208 lines
5.3 KiB
Go

package main
import (
"crypto/rand"
"crypto/sha512"
"encoding/base64"
"encoding/hex"
"encoding/json"
"fmt"
"io"
"net/http"
"time"
"github.com/julienschmidt/httprouter"
)
func init() {
router.GET("/api/wg.conf", func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
w.Header().Set("Content-Type", "text/plain")
err := GenWGConfig(w)
if err != nil {
w.Write([]byte(err.Error()))
}
})
router.GET("/api/wg/", apiAuthHandler(showWgTunnel))
router.GET("/api/wginfo", apiAuthHandler(func (student Student, ps httprouter.Params, body []byte) (interface{}, error) {
return getTunnelInfo(student), nil
}))
router.POST("/api/wg/", apiAuthHandler(genWgToken))
router.GET("/api/wg/:token", apiAuthHandler(getWgTunnelInfo))
}
func showWgTunnel(student Student, ps httprouter.Params, body []byte) (interface{}, error) {
// Get tunnels assigned to the student
return student.GetTunnelTokens()
}
func genWgToken(student Student, ps httprouter.Params, body []byte) (interface{}, error) {
// Generate a token to access related wg info
return student.NewTunnelToken()
}
type TunnelInfo struct {
Status string `json:"status"`
SrvPubKey []byte `json:"srv_pubkey"`
SrvPort uint16 `json:"srv_port"`
CltIPv6 string `json:"clt_ipv6"`
CltRange uint8 `json:"clt_range"`
SrvGW6 string `json:"srv_gw6"`
}
func getTunnelInfo(student Student) TunnelInfo {
return TunnelInfo{
Status: "OK",
SrvPubKey: []byte{'T', 'B', 'D'},
SrvPort: 42912,
CltIPv6: studentIP(student.Id),
CltRange: 80,
SrvGW6: "2a01:e0a:2b:2252::1",
}
}
type PubTunnel struct {
PubKey []byte
}
func getWgTunnelInfo(student Student, ps httprouter.Params, body []byte) (interface{}, error) {
// Access wg infos
tokenhex := []byte(ps.ByName("token"))
tokendec := make([]byte, hex.DecodedLen(len(tokenhex)))
n, err := hex.Decode(tokendec, tokenhex)
if err != nil {
return nil, err
}
token, err := student.GetTunnelToken(tokendec[:n])
if err != nil {
return nil, err
}
var pt PubTunnel
if err := json.Unmarshal(body, &pt); err != nil {
return nil, err
}
token.PubKey = pt.PubKey
_, err = token.Update()
if err != nil {
return nil, err
}
return getTunnelInfo(student), nil
}
type TunnelToken struct {
token []byte
TokenText string
IdStudent int64
PubKey []byte
Time time.Time
}
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)
return
}
func tokenFromText(token string) []byte {
sha := sha512.Sum512_256([]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 = base64.RawStdEncoding.EncodeToString(tok)
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 {
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)
}
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)
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 token, token_text, id_student, pubkey, time FROM student_tunnel_tokens T INNER JOIN (SELECT B.token, B.id_student, MAX(B.time) FROM student_tunnel_tokens B GROUP BY id_student) L ON T.token = L.token"); 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) string {
return fmt.Sprintf("2a01:e0a:2b:2252:%x::", idstd)
}
func GenWGConfig(w io.Writer) (error) {
ts, err := GetStudentsTunnels()
if err != nil {
return err
}
for _, t := range ts {
w.Write([]byte(fmt.Sprintf(`[Peer]
PublicKey = %s
AllowedIPs = %s/%d`, base64.StdEncoding.EncodeToString(t.PubKey), studentIP(t.IdStudent), 80)))
}
return nil
}