231 lines
5.8 KiB
Go
231 lines
5.8 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), 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) TunnelInfo {
|
|
srv_pubkey, _ := base64.StdEncoding.DecodeString("uSpqyYovvP4OG6wDxZ0Qkq45MfyK58PMUuPaLesY8FI=")
|
|
return TunnelInfo{
|
|
Status: "OK",
|
|
SrvPubKey: srv_pubkey,
|
|
SrvPort: 42912,
|
|
CltIPv6: adlin.StudentIP(student),
|
|
CltRange: adlin.StdNetmask,
|
|
SrvGW6: "2a01:e0a:2b:2252::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)
|
|
|
|
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
|
|
# KeySign=%s
|
|
`, base64.StdEncoding.EncodeToString(tinfo.SrvPubKey), "82.64.31.248", tinfo.SrvPort, tinfo.CltIPv6, token.SuffixIP, 64, tinfo.CltIPv6, tinfo.CltRange, tinfo.SrvGW6, student.Login, 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), 80)))
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func syncWgConf() (err error) {
|
|
_, err = exec.Command("sh", "/root/wg-sync.sh").Output()
|
|
return
|
|
}
|