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

234 lines
5.9 KiB
Go

package main
import (
"encoding/base64"
"encoding/hex"
"encoding/json"
"fmt"
"io"
"log"
"net"
"net/http"
"os/exec"
"github.com/julienschmidt/httprouter"
"git.nemunai.re/srs/adlin/libadlin"
)
func init() {
router.GET("/api/collector_info", apiHandler(func(ps httprouter.Params, body []byte) (interface{}, error) {
return "\"" + base64.StdEncoding.EncodeToString(adlin.GetCollectorPublic()) + "\"", nil
}))
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 *adlin.Student, ps httprouter.Params, body []byte) (interface{}, error) {
return getTunnelInfo(student.Id, 0), nil
}))
router.POST("/api/wg/", apiAuthHandler(genWgToken))
router.GET("/api/wg/:token", getWgTunnelInfo)
router.POST("/api/wg/:token", getWgTunnelInfo)
router.PUT("/api/wg/:token", apiAuthHandler(updateWgTunnel))
router.DELETE("/api/wg/:token", apiAuthHandler(deleteWgTunnel))
}
func showWgTunnel(student *adlin.Student, ps httprouter.Params, body []byte) (interface{}, error) {
// Get tunnels assigned to the student
return student.GetTunnelTokens()
}
func genWgToken(student *adlin.Student, ps httprouter.Params, body []byte) (interface{}, error) {
// Generate a token to access related wg info
return student.NewTunnelToken(0)
}
type TunnelInfo struct {
Status string `json:"status"`
SrvPubKey []byte `json:"srv_pubkey"`
SrvPort uint16 `json:"srv_port"`
CltIPv6 net.IP `json:"clt_ipv6"`
CltRange uint8 `json:"clt_range"`
SrvGW6 string `json:"srv_gw6"`
}
func getTunnelInfo(student int64, idoverride int) TunnelInfo {
srv_pubkey, _ := base64.StdEncoding.DecodeString("uSpqyYovvP4OG6wDxZ0Qkq45MfyK58PMUuPaLesY8FI=")
return TunnelInfo{
Status: "OK",
SrvPubKey: srv_pubkey,
SrvPort: 42912,
CltIPv6: adlin.StudentIP(student, idoverride),
CltRange: adlin.StdNetmask,
SrvGW6: "2a01:e0a:518:833::1",
}
}
type PubTunnel struct {
PubKey []byte
}
func getWgTunnelInfo(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
if addr := r.Header.Get("X-Forwarded-For"); addr != "" {
r.RemoteAddr = addr
}
log.Printf("%s \"%s %s\" [%s]\n", r.RemoteAddr, r.Method, r.URL.Path, r.UserAgent())
// Read the body
if r.ContentLength < 0 || r.ContentLength > 6553600 {
http.Error(w, "{errmsg:\"Request too large or request size unknown\"}", http.StatusRequestEntityTooLarge)
return
}
// Access wg infos
tokenhex := []byte(ps.ByName("token"))
tokendec := make([]byte, hex.DecodedLen(len(tokenhex)))
n, err := hex.Decode(tokendec, tokenhex)
if err != nil {
http.Error(w, fmt.Sprintf("{errmsg:%q}", err), http.StatusBadRequest)
return
}
token, err := adlin.GetTunnelToken(tokendec[:n])
if err != nil {
http.Error(w, fmt.Sprintf("{errmsg:%q}", err), http.StatusBadRequest)
return
}
var pt PubTunnel
var version int
if r.Method == "POST" {
if err := json.NewDecoder(r.Body).Decode(&pt); err != nil {
http.Error(w, fmt.Sprintf("{errmsg:%q}", err), http.StatusBadRequest)
return
}
version = 2
} else if pubkey := r.Header.Get("X-WG-PubKey"); pubkey != "" {
pt.PubKey, _ = base64.StdEncoding.DecodeString(pubkey)
version = 3
} else {
http.Error(w, fmt.Sprintf("{errmsg:\"No public key given\"}"), http.StatusBadRequest)
return
}
token.PubKey = pt.PubKey
token.Version = version
_, err = token.Update()
if err != nil {
http.Error(w, fmt.Sprintf("{errmsg:%q}", err), http.StatusBadRequest)
return
}
// 0 is considered default for suffix, apply default now
if token.SuffixIP <= 0 {
token.SuffixIP = 1
}
syncWgConf()
tinfo := getTunnelInfo(token.IdStudent, token.OverrideID)
var student *adlin.Student
student, err = adlin.GetStudent(int(token.IdStudent))
if err != nil {
http.Error(w, fmt.Sprintf("{errmsg:%q}", err), http.StatusBadRequest)
return
}
w.Header().Set("Content-Type", "text/plain")
w.Write([]byte(fmt.Sprintf(`[Peer]
PublicKey = %s
Endpoint = %s:%d
AllowedIPs = ::/0
PersistentKeepalive = 5
# MyIPv6=%s%x/%d
# MyNetwork=%s/%d
# GWIPv6=%s
# MyLogin=%s
`, base64.StdEncoding.EncodeToString(tinfo.SrvPubKey), "82.64.151.41", tinfo.SrvPort, tinfo.CltIPv6, token.SuffixIP, 64, tinfo.CltIPv6, tinfo.CltRange, tinfo.SrvGW6, student.Login)))
if version > 2 {
w.Write([]byte(fmt.Sprintf(`# KeySign=%s`, base64.StdEncoding.EncodeToString(token.GenKeySign()))))
}
}
func updateWgTunnel(student *adlin.Student, ps httprouter.Params, body []byte) (interface{}, error) {
token, err := adlin.GetTunnelToken(adlin.TokenFromText(ps.ByName("token")))
if err != nil {
return nil, err
}
if token.IdStudent != student.Id {
return nil, fmt.Errorf("Unauthorized")
}
var newToken adlin.TunnelToken
if err := json.Unmarshal(body, &newToken); err != nil {
return nil, err
}
token.TokenText = newToken.TokenText
token.PubKey = newToken.PubKey
token.SuffixIP = newToken.SuffixIP
if _, err = token.Update(); err != nil {
return nil, err
}
syncWgConf()
return true, err
}
func deleteWgTunnel(student *adlin.Student, ps httprouter.Params, body []byte) (interface{}, error) {
token, err := adlin.GetTunnelToken(adlin.TokenFromText(ps.ByName("token")))
if err != nil {
return nil, err
}
if token.IdStudent != student.Id {
return nil, fmt.Errorf("Unauthorized")
}
if _, err = token.Delete(); err != nil {
return nil, err
}
syncWgConf()
return true, err
}
func GenWGConfig(w io.Writer) error {
ts, err := adlin.GetStudentsTunnels()
if err != nil {
return err
}
for _, t := range ts {
if t.PubKey == nil {
continue
}
w.Write([]byte(fmt.Sprintf(`[Peer]
#IdStudent = %d
PublicKey = %s
AllowedIPs = %s/%d
`, t.IdStudent, base64.StdEncoding.EncodeToString(t.PubKey), adlin.StudentIP(t.IdStudent, t.OverrideID), 80)))
}
return nil
}
func syncWgConf() (err error) {
_, err = exec.Command("sh", "/root/wg-sync.sh").Output()
return
}