This commit is contained in:
jdl
2026-06-04 16:00:21 +02:00
parent 5ae075647d
commit 8b2c9709fc
22 changed files with 217 additions and 317 deletions

View File

@@ -19,8 +19,10 @@ import (
var migrations embed.FS var migrations embed.FS
type API struct { type API struct {
db *sql.DB db *sql.DB
lock sync.Mutex lock sync.Mutex
sessionsMu sync.Mutex
sessions map[string]*Session
} }
func New(dbPath string) (*API, error) { func New(dbPath string) (*API, error) {
@@ -34,7 +36,8 @@ func New(dbPath string) (*API, error) {
} }
a := &API{ a := &API{
db: sqlDB, db: sqlDB,
sessions: make(map[string]*Session),
} }
return a, a.ensurePassword() return a, a.ensurePassword()
@@ -75,47 +78,41 @@ func (a *API) Config_Update(conf *Config) error {
} }
func (a *API) Session_Delete(sessionID string) error { func (a *API) Session_Delete(sessionID string) error {
return db.Session_Delete(a.db, sessionID) a.sessionsMu.Lock()
defer a.sessionsMu.Unlock()
delete(a.sessions, sessionID)
return nil
} }
func (a *API) Session_Get(sessionID string) (*Session, error) { func (a *API) Session_Get(sessionID string) (*Session, error) {
if sessionID == "" { a.sessionsMu.Lock()
return a.session_CreatePub() defer a.sessionsMu.Unlock()
}
session, err := db.Session_Get(a.db, sessionID) if sessionID != "" {
if err != nil { s, ok := a.sessions[sessionID]
return a.session_CreatePub() if ok {
} if timeSince(s.LastSeenAt) <= 86400*21 {
if timeSince(s.LastSeenAt) > 86400*7 {
if timeSince(session.LastSeenAt) > 86400*21 { s.LastSeenAt = time.Now().Unix()
return a.session_CreatePub() }
} return s, nil
}
if timeSince(session.LastSeenAt) > 86400*7 { delete(a.sessions, sessionID)
session.LastSeenAt = time.Now().Unix()
if err := db.Session_UpdateLastSeenAt(a.db, session.SessionID); err != nil {
log.Printf("Failed to update session: %v", err)
} }
} }
return session, nil return a.session_Create(), nil
} }
func (a *API) session_CreatePub() (*Session, error) { // caller must hold sessionsMu
func (a *API) session_Create() *Session {
s := &Session{ s := &Session{
SessionID: idgen.NewToken(), SessionID: idgen.NewToken(),
CSRF: idgen.NewToken(),
SignedIn: false,
CreatedAt: time.Now().Unix(), CreatedAt: time.Now().Unix(),
LastSeenAt: time.Now().Unix(), LastSeenAt: time.Now().Unix(),
} }
err := db.Session_Insert(a.db, s) a.sessions[s.SessionID] = s
return s, err return s
}
func (a *API) Session_DeleteBefore(timestamp int64) error {
return db.Session_DeleteBefore(a.db, timestamp)
} }
func (a *API) Session_SignIn(s *Session, pwd string) error { func (a *API) Session_SignIn(s *Session, pwd string) error {
@@ -123,8 +120,10 @@ func (a *API) Session_SignIn(s *Session, pwd string) error {
if err := bcrypt.CompareHashAndPassword(conf.Password, []byte(pwd)); err != nil { if err := bcrypt.CompareHashAndPassword(conf.Password, []byte(pwd)); err != nil {
return ErrNotAuthorized return ErrNotAuthorized
} }
a.sessionsMu.Lock()
return db.Session_SetSignedIn(a.db, s.SessionID) s.SignedIn = true
a.sessionsMu.Unlock()
return nil
} }
func (a *API) Network_Create(n *Network) error { func (a *API) Network_Create(n *Network) error {
@@ -147,8 +146,7 @@ func (a *API) Network_List() ([]*Network, error) {
func (a *API) Peer_CreateNew(p *Peer) error { func (a *API) Peer_CreateNew(p *Peer) error {
p.Version = idgen.NextID(0) p.Version = idgen.NextID(0)
p.PubKey = []byte{} p.WGPubKey = []byte{}
p.PubSignKey = []byte{}
p.APIKey = idgen.NewToken() p.APIKey = idgen.NewToken()
return db.Peer_Insert(a.db, p) return db.Peer_Insert(a.db, p)
@@ -159,8 +157,7 @@ func (a *API) Peer_Init(peer *Peer, args m.PeerInitArgs) error {
defer a.lock.Unlock() defer a.lock.Unlock()
peer.Version = idgen.NextID(0) peer.Version = idgen.NextID(0)
peer.PubKey = args.EncPubKey peer.WGPubKey = args.WGPubKey
peer.PubSignKey = args.PubSignKey
return db.Peer_UpdateFull(a.db, peer) return db.Peer_UpdateFull(a.db, peer)
} }

View File

@@ -123,7 +123,9 @@ func Config_Get(
) { ) {
row = &Config{} row = &Config{}
r := tx.QueryRow("SELECT ConfigID,Password FROM config WHERE ConfigID=?", ConfigID) r := tx.QueryRow("SELECT ConfigID,Password FROM config WHERE ConfigID=?", ConfigID)
err = r.Scan(&row.ConfigID, &row.Password) if err = r.Scan(&row.ConfigID, &row.Password); err != nil {
row = nil
}
return return
} }
@@ -137,7 +139,9 @@ func Config_GetWhere(
) { ) {
row = &Config{} row = &Config{}
r := tx.QueryRow(query, args...) r := tx.QueryRow(query, args...)
err = r.Scan(&row.ConfigID, &row.Password) if err = r.Scan(&row.ConfigID, &row.Password); err != nil {
row = nil
}
return return
} }
@@ -182,124 +186,6 @@ func Config_List(
return l, nil return l, nil
} }
// ----------------------------------------------------------------------------
// Table: sessions
// ----------------------------------------------------------------------------
type Session struct {
SessionID string
CSRF string
SignedIn bool
CreatedAt int64
LastSeenAt int64
}
const Session_SelectQuery = "SELECT SessionID,CSRF,SignedIn,CreatedAt,LastSeenAt FROM sessions"
func Session_Insert(
tx TX,
row *Session,
) (err error) {
Session_Sanitize(row)
if err = Session_Validate(row); err != nil {
return err
}
_, err = tx.Exec("INSERT INTO sessions(SessionID,CSRF,SignedIn,CreatedAt,LastSeenAt) VALUES(?,?,?,?,?)", row.SessionID, row.CSRF, row.SignedIn, row.CreatedAt, row.LastSeenAt)
return err
}
func Session_Delete(
tx TX,
SessionID string,
) (err error) {
result, err := tx.Exec("DELETE FROM sessions WHERE SessionID=?", SessionID)
if err != nil {
return err
}
n, err := result.RowsAffected()
if err != nil {
panic(err)
}
switch n {
case 0:
return sql.ErrNoRows
case 1:
return nil
default:
panic("multiple rows deleted")
}
}
func Session_Get(
tx TX,
SessionID string,
) (
row *Session,
err error,
) {
row = &Session{}
r := tx.QueryRow("SELECT SessionID,CSRF,SignedIn,CreatedAt,LastSeenAt FROM sessions WHERE SessionID=?", SessionID)
err = r.Scan(&row.SessionID, &row.CSRF, &row.SignedIn, &row.CreatedAt, &row.LastSeenAt)
return
}
func Session_GetWhere(
tx TX,
query string,
args ...any,
) (
row *Session,
err error,
) {
row = &Session{}
r := tx.QueryRow(query, args...)
err = r.Scan(&row.SessionID, &row.CSRF, &row.SignedIn, &row.CreatedAt, &row.LastSeenAt)
return
}
func Session_Iterate(
tx TX,
query string,
args ...any,
) iter.Seq2[*Session, error] {
rows, err := tx.Query(query, args...)
if err != nil {
return func(yield func(*Session, error) bool) {
yield(nil, err)
}
}
return func(yield func(*Session, error) bool) {
defer rows.Close()
for rows.Next() {
row := &Session{}
err := rows.Scan(&row.SessionID, &row.CSRF, &row.SignedIn, &row.CreatedAt, &row.LastSeenAt)
if !yield(row, err) {
return
}
}
}
}
func Session_List(
tx TX,
query string,
args ...any,
) (
l []*Session,
err error,
) {
for row, err := range Session_Iterate(tx, query, args...) {
if err != nil {
return nil, err
}
l = append(l, row)
}
return l, nil
}
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
// Table: networks // Table: networks
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
@@ -385,7 +271,9 @@ func Network_Get(
) { ) {
row = &Network{} row = &Network{}
r := tx.QueryRow("SELECT NetworkID,Name,Network FROM networks WHERE NetworkID=?", NetworkID) r := tx.QueryRow("SELECT NetworkID,Name,Network FROM networks WHERE NetworkID=?", NetworkID)
err = r.Scan(&row.NetworkID, &row.Name, &row.Network) if err = r.Scan(&row.NetworkID, &row.Name, &row.Network); err != nil {
row = nil
}
return return
} }
@@ -399,7 +287,9 @@ func Network_GetWhere(
) { ) {
row = &Network{} row = &Network{}
r := tx.QueryRow(query, args...) r := tx.QueryRow(query, args...)
err = r.Scan(&row.NetworkID, &row.Name, &row.Network) if err = r.Scan(&row.NetworkID, &row.Name, &row.Network); err != nil {
row = nil
}
return return
} }
@@ -449,19 +339,20 @@ func Network_List(
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
type Peer struct { type Peer struct {
NetworkID int64 NetworkID int64
PeerIP byte PeerIP byte
Version int64 Version int64
APIKey string APIKey string
Name string Name string
PublicIP []byte PublicIP1 []byte
Port uint16 Port1 uint16
Relay bool PublicIP2 []byte
PubKey []byte Port2 uint16
PubSignKey []byte Relay bool
WGPubKey []byte
} }
const Peer_SelectQuery = "SELECT NetworkID,PeerIP,Version,APIKey,Name,PublicIP,Port,Relay,PubKey,PubSignKey FROM peers" const Peer_SelectQuery = "SELECT NetworkID,PeerIP,Version,APIKey,Name,PublicIP1,Port1,PublicIP2,Port2,Relay,WGPubKey FROM peers"
func Peer_Insert( func Peer_Insert(
tx TX, tx TX,
@@ -472,7 +363,7 @@ func Peer_Insert(
return err return err
} }
_, err = tx.Exec("INSERT INTO peers(NetworkID,PeerIP,Version,APIKey,Name,PublicIP,Port,Relay,PubKey,PubSignKey) VALUES(?,?,?,?,?,?,?,?,?,?)", row.NetworkID, row.PeerIP, row.Version, row.APIKey, row.Name, row.PublicIP, row.Port, row.Relay, row.PubKey, row.PubSignKey) _, err = tx.Exec("INSERT INTO peers(NetworkID,PeerIP,Version,APIKey,Name,PublicIP1,Port1,PublicIP2,Port2,Relay,WGPubKey) VALUES(?,?,?,?,?,?,?,?,?,?,?)", row.NetworkID, row.PeerIP, row.Version, row.APIKey, row.Name, row.PublicIP1, row.Port1, row.PublicIP2, row.Port2, row.Relay, row.WGPubKey)
return err return err
} }
@@ -485,7 +376,7 @@ func Peer_Update(
return err return err
} }
result, err := tx.Exec("UPDATE peers SET Version=?,Name=?,PublicIP=?,Port=?,Relay=? WHERE NetworkID=? AND PeerIP=?", row.Version, row.Name, row.PublicIP, row.Port, row.Relay, row.NetworkID, row.PeerIP) result, err := tx.Exec("UPDATE peers SET Version=?,Name=?,PublicIP1=?,Port1=?,PublicIP2=?,Port2=?,Relay=? WHERE NetworkID=? AND PeerIP=?", row.Version, row.Name, row.PublicIP1, row.Port1, row.PublicIP2, row.Port2, row.Relay, row.NetworkID, row.PeerIP)
if err != nil { if err != nil {
return err return err
} }
@@ -513,7 +404,7 @@ func Peer_UpdateFull(
return err return err
} }
result, err := tx.Exec("UPDATE peers SET Version=?,APIKey=?,Name=?,PublicIP=?,Port=?,Relay=?,PubKey=?,PubSignKey=? WHERE NetworkID=? AND PeerIP=?", row.Version, row.APIKey, row.Name, row.PublicIP, row.Port, row.Relay, row.PubKey, row.PubSignKey, row.NetworkID, row.PeerIP) result, err := tx.Exec("UPDATE peers SET Version=?,APIKey=?,Name=?,PublicIP1=?,Port1=?,PublicIP2=?,Port2=?,Relay=?,WGPubKey=? WHERE NetworkID=? AND PeerIP=?", row.Version, row.APIKey, row.Name, row.PublicIP1, row.Port1, row.PublicIP2, row.Port2, row.Relay, row.WGPubKey, row.NetworkID, row.PeerIP)
if err != nil { if err != nil {
return err return err
} }
@@ -565,8 +456,10 @@ func Peer_Get(
err error, err error,
) { ) {
row = &Peer{} row = &Peer{}
r := tx.QueryRow("SELECT NetworkID,PeerIP,Version,APIKey,Name,PublicIP,Port,Relay,PubKey,PubSignKey FROM peers WHERE NetworkID=? AND PeerIP=?", NetworkID, PeerIP) r := tx.QueryRow("SELECT NetworkID,PeerIP,Version,APIKey,Name,PublicIP1,Port1,PublicIP2,Port2,Relay,WGPubKey FROM peers WHERE NetworkID=? AND PeerIP=?", NetworkID, PeerIP)
err = r.Scan(&row.NetworkID, &row.PeerIP, &row.Version, &row.APIKey, &row.Name, &row.PublicIP, &row.Port, &row.Relay, &row.PubKey, &row.PubSignKey) if err = r.Scan(&row.NetworkID, &row.PeerIP, &row.Version, &row.APIKey, &row.Name, &row.PublicIP1, &row.Port1, &row.PublicIP2, &row.Port2, &row.Relay, &row.WGPubKey); err != nil {
row = nil
}
return return
} }
@@ -580,7 +473,9 @@ func Peer_GetWhere(
) { ) {
row = &Peer{} row = &Peer{}
r := tx.QueryRow(query, args...) r := tx.QueryRow(query, args...)
err = r.Scan(&row.NetworkID, &row.PeerIP, &row.Version, &row.APIKey, &row.Name, &row.PublicIP, &row.Port, &row.Relay, &row.PubKey, &row.PubSignKey) if err = r.Scan(&row.NetworkID, &row.PeerIP, &row.Version, &row.APIKey, &row.Name, &row.PublicIP1, &row.Port1, &row.PublicIP2, &row.Port2, &row.Relay, &row.WGPubKey); err != nil {
row = nil
}
return return
} }
@@ -600,7 +495,7 @@ func Peer_Iterate(
defer rows.Close() defer rows.Close()
for rows.Next() { for rows.Next() {
row := &Peer{} row := &Peer{}
err := rows.Scan(&row.NetworkID, &row.PeerIP, &row.Version, &row.APIKey, &row.Name, &row.PublicIP, &row.Port, &row.Relay, &row.PubKey, &row.PubSignKey) err := rows.Scan(&row.NetworkID, &row.PeerIP, &row.Version, &row.APIKey, &row.Name, &row.PublicIP1, &row.Port1, &row.PublicIP2, &row.Port2, &row.Relay, &row.WGPubKey)
if !yield(row, err) { if !yield(row, err) {
return return
} }

View File

@@ -21,13 +21,6 @@ func Config_Validate(c *Config) error {
return nil return nil
} }
func Session_Sanitize(s *Session) {
}
func Session_Validate(s *Session) error {
return nil
}
func Network_Sanitize(n *Network) { func Network_Sanitize(n *Network) {
n.Name = strings.TrimSpace(n.Name) n.Name = strings.TrimSpace(n.Name)
@@ -66,25 +59,39 @@ func Network_Validate(c *Network) error {
func Peer_Sanitize(p *Peer) { func Peer_Sanitize(p *Peer) {
p.Name = strings.TrimSpace(p.Name) p.Name = strings.TrimSpace(p.Name)
if len(p.PublicIP) != 0 { if len(p.PublicIP1) != 0 {
addr, ok := netip.AddrFromSlice(p.PublicIP) if addr, ok := netip.AddrFromSlice(p.PublicIP1); ok {
if ok && addr.Is4() { p.PublicIP1 = addr.AsSlice()
p.PublicIP = addr.AsSlice()
} }
} }
if p.Port == 0 { if len(p.PublicIP2) != 0 {
p.Port = 456 if addr, ok := netip.AddrFromSlice(p.PublicIP2); ok {
p.PublicIP2 = addr.AsSlice()
}
}
if p.Port1 == 0 {
p.Port1 = 456
}
if len(p.PublicIP2) != 0 && p.Port2 == 0 {
p.Port2 = 456
} }
} }
func Peer_Validate(p *Peer) error { func Peer_Validate(p *Peer) error {
if len(p.PublicIP) > 0 { if len(p.PublicIP1) > 0 {
_, ok := netip.AddrFromSlice(p.PublicIP) if _, ok := netip.AddrFromSlice(p.PublicIP1); !ok {
if !ok {
return ErrInvalidIP return ErrInvalidIP
} }
} }
if p.Port == 0 { if len(p.PublicIP2) > 0 {
if _, ok := netip.AddrFromSlice(p.PublicIP2); !ok {
return ErrInvalidIP
}
if p.Port2 == 0 {
return ErrInvalidPort
}
}
if p.Port1 == 0 {
return ErrInvalidPort return ErrInvalidPort
} }
@@ -98,7 +105,6 @@ func Peer_Validate(p *Peer) error {
if c == '.' || c == '-' || c == '_' { if c == '.' || c == '-' || c == '_' {
continue continue
} }
return ErrInvalidPeerName return ErrInvalidPeerName
} }

View File

@@ -3,14 +3,6 @@ TABLE config OF Config (
Password []byte Password []byte
); );
TABLE sessions OF Session NoUpdate (
SessionID string PK,
CSRF string,
SignedIn bool,
CreatedAt int64,
LastSeenAt int64
);
TABLE networks OF Network ( TABLE networks OF Network (
NetworkID int64 PK, NetworkID int64 PK,
Name string NoUpdate, Name string NoUpdate,
@@ -23,9 +15,10 @@ TABLE peers OF Peer (
Version int64, Version int64,
APIKey string NoUpdate, APIKey string NoUpdate,
Name string, Name string,
PublicIP []byte, PublicIP1 []byte,
Port uint16, Port1 uint16,
PublicIP2 []byte,
Port2 uint16,
Relay bool, Relay bool,
PubKey []byte NoUpdate, WGPubKey []byte NoUpdate
PubSignKey []byte NoUpdate
); );

View File

@@ -1,31 +1,5 @@
package db package db
import "time"
func Session_UpdateLastSeenAt(
tx TX,
id string,
) (err error) {
_, err = tx.Exec("UPDATE sessions SET LastSeenAt=? WHERE SessionID=?", time.Now().Unix(), id)
return err
}
func Session_SetSignedIn(
tx TX,
id string,
) (err error) {
_, err = tx.Exec("UPDATE sessions SET SignedIn=1 WHERE SessionID=?", id)
return err
}
func Session_DeleteBefore(
tx TX,
timestamp int64,
) (err error) {
_, err = tx.Exec("DELETE FROM sessions WHERE LastSeenAt<?", timestamp)
return err
}
func Peer_ListAll(tx TX, networkID int64) ([]*Peer, error) { func Peer_ListAll(tx TX, networkID int64) ([]*Peer, error) {
const query = Peer_SelectQuery + ` WHERE NetworkID=? ORDER BY PeerIP ASC` const query = Peer_SelectQuery + ` WHERE NetworkID=? ORDER BY PeerIP ASC`
return Peer_List(tx, query, networkID) return Peer_List(tx, query, networkID)

View File

@@ -3,16 +3,6 @@ CREATE TABLE config (
Password BLOB NOT NULL -- bcrypt password for web interface Password BLOB NOT NULL -- bcrypt password for web interface
) WITHOUT ROWID; ) WITHOUT ROWID;
CREATE TABLE sessions (
SessionID TEXT NOT NULL PRIMARY KEY,
CSRF TEXT NOT NULL,
SignedIn INTEGER NOT NULL,
CreatedAt INTEGER NOT NULL,
LastSeenAt INTEGER NOT NULL
) WITHOUT ROWID;
CREATE INDEX sessions_last_seen_index ON sessions(LastSeenAt);
CREATE TABLE networks ( CREATE TABLE networks (
NetworkID INTEGER NOT NULL PRIMARY KEY, NetworkID INTEGER NOT NULL PRIMARY KEY,
Name TEXT NOT NULL UNIQUE, -- Network/interface name. Name TEXT NOT NULL UNIQUE, -- Network/interface name.
@@ -21,14 +11,15 @@ CREATE TABLE networks (
CREATE TABLE peers ( CREATE TABLE peers (
NetworkID INTEGER NOT NULL, NetworkID INTEGER NOT NULL,
PeerIP INTEGER NOT NULL, -- Final byte of IP. PeerIP INTEGER NOT NULL, -- Final byte of IP.
Version INTEGER NOT NULL, -- Changes when updated. Version INTEGER NOT NULL, -- Changes when updated.
APIKey TEXT NOT NULL UNIQUE, -- Peer's secret API key. APIKey TEXT NOT NULL UNIQUE, -- Peer's secret API key.
Name TEXT NOT NULL UNIQUE, -- For humans. Name TEXT NOT NULL UNIQUE, -- For humans.
PublicIP BLOB NOT NULL, PublicIP1 BLOB NOT NULL,
Port INTEGER NOT NULL, Port1 INTEGER NOT NULL,
Relay INTEGER NOT NULL DEFAULT 0, -- Boolean if peer will forward packets. Must also have public address. PublicIP2 BLOB NOT NULL,
PubKey BLOB NOT NULL, Port2 INTEGER NOT NULL,
PubSignKey BLOB NOT NULL, Relay INTEGER NOT NULL DEFAULT 0, -- Boolean if peer will forward packets.
WGPubKey BLOB NOT NULL,
PRIMARY KEY(NetworkID, PeerIP) PRIMARY KEY(NetworkID, PeerIP)
) WITHOUT ROWID; ) WITHOUT ROWID;

View File

@@ -3,6 +3,12 @@ package api
import "vppn/hub/api/db" import "vppn/hub/api/db"
type Config = db.Config type Config = db.Config
type Session = db.Session
type Network = db.Network type Network = db.Network
type Peer = db.Peer type Peer = db.Peer
type Session struct {
SessionID string
SignedIn bool
CreatedAt int64
LastSeenAt int64
}

View File

@@ -2,6 +2,7 @@ package hub
import ( import (
"embed" "embed"
"encoding/base64"
"html/template" "html/template"
"net/http" "net/http"
"path/filepath" "path/filepath"
@@ -47,6 +48,19 @@ func NewApp(conf Config) (*App, error) {
return app, nil return app, nil
} }
var templateFuncs = template.FuncMap{ func (app *App) Handler() http.Handler {
"ipToString": ipBytesTostring, cop := http.NewCrossOriginProtection()
return cop.Handler(app.mux)
}
var templateFuncs = template.FuncMap{
"ipToString": ipBytesTostring,
"wgKeyString": wgKeyString,
}
func wgKeyString(key []byte) string {
if len(key) == 0 {
return "not set"
}
return base64.StdEncoding.EncodeToString(key)
} }

View File

@@ -26,11 +26,6 @@ func (app *App) handlePub(pattern string, fn handlerFunc) {
if r.Method == http.MethodPost { if r.Method == http.MethodPost {
r.ParseMultipartForm(64 * 1024) r.ParseMultipartForm(64 * 1024)
if r.FormValue("CSRF") != s.CSRF {
log.Printf("%s != %s", r.FormValue("CSRF"), s.CSRF)
http.Error(w, "CSRF mismatch", http.StatusBadRequest)
return
}
} else { } else {
r.ParseForm() r.ParseForm()
} }

View File

@@ -142,22 +142,27 @@ func (a *App) _adminPeerCreate(s *api.Session, w http.ResponseWriter, r *http.Re
} }
func (a *App) _adminPeerCreateSubmit(s *api.Session, w http.ResponseWriter, r *http.Request) error { func (a *App) _adminPeerCreateSubmit(s *api.Session, w http.ResponseWriter, r *http.Request) error {
var ipStr string var ip1Str, ip2Str string
p := &api.Peer{} p := &api.Peer{}
err := webutil.NewFormScanner(r.Form). err := webutil.NewFormScanner(r.Form).
Scan("NetworkID", &p.NetworkID). Scan("NetworkID", &p.NetworkID).
Scan("IP", &p.PeerIP). Scan("IP", &p.PeerIP).
Scan("Name", &p.Name). Scan("Name", &p.Name).
Scan("PublicIP", &ipStr). Scan("PublicIP1", &ip1Str).
Scan("Port", &p.Port). Scan("Port1", &p.Port1).
Scan("PublicIP2", &ip2Str).
Scan("Port2", &p.Port2).
Scan("Relay", &p.Relay). Scan("Relay", &p.Relay).
Error() Error()
if err != nil { if err != nil {
return err return err
} }
if p.PublicIP, err = stringToIP(ipStr); err != nil { if p.PublicIP1, err = stringToIP(ip1Str); err != nil {
return err
}
if p.PublicIP2, err = stringToIP(ip2Str); err != nil {
return err return err
} }
@@ -199,19 +204,24 @@ func (a *App) _adminPeerEditSubmit(s *api.Session, w http.ResponseWriter, r *htt
return err return err
} }
var ipStr string var ip1Str, ip2Str string
err = webutil.NewFormScanner(r.Form). err = webutil.NewFormScanner(r.Form).
Scan("Name", &peer.Name). Scan("Name", &peer.Name).
Scan("PublicIP", &ipStr). Scan("PublicIP1", &ip1Str).
Scan("Port", &peer.Port). Scan("Port1", &peer.Port1).
Scan("PublicIP2", &ip2Str).
Scan("Port2", &peer.Port2).
Scan("Relay", &peer.Relay). Scan("Relay", &peer.Relay).
Error() Error()
if err != nil { if err != nil {
return err return err
} }
if peer.PublicIP, err = stringToIP(ipStr); err != nil { if peer.PublicIP1, err = stringToIP(ip1Str); err != nil {
return err
}
if peer.PublicIP2, err = stringToIP(ip2Str); err != nil {
return err return err
} }
@@ -295,6 +305,11 @@ func (a *App) _adminPasswordSubmit(s *api.Session, w http.ResponseWriter, r *htt
} }
func (a *App) _peerInit(peer *api.Peer, w http.ResponseWriter, r *http.Request) error { func (a *App) _peerInit(peer *api.Peer, w http.ResponseWriter, r *http.Request) error {
if len(peer.WGPubKey) != 0 {
http.Error(w, "Already initialized", http.StatusConflict)
return nil
}
args := m.PeerInitArgs{} args := m.PeerInitArgs{}
if err := json.NewDecoder(r.Body).Decode(&args); err != nil { if err := json.NewDecoder(r.Body).Decode(&args); err != nil {
return err return err
@@ -338,16 +353,17 @@ func (a *App) peersArray(networkID int64) (peers [256]*m.Peer, err error) {
} }
for _, p := range l { for _, p := range l {
if len(p.PubKey) != 0 { if len(p.WGPubKey) != 0 {
peers[p.PeerIP] = &m.Peer{ peers[p.PeerIP] = &m.Peer{
PeerIP: p.PeerIP, PeerIP: p.PeerIP,
Version: p.Version, Version: p.Version,
Name: p.Name, Name: p.Name,
PublicIP: p.PublicIP, PublicIP1: p.PublicIP1,
Port: p.Port, Port1: p.Port1,
Relay: p.Relay, PublicIP2: p.PublicIP2,
PubKey: p.PubKey, Port2: p.Port2,
PubSignKey: p.PubSignKey, Relay: p.Relay,
WGPubKey: p.WGPubKey,
} }
} }
} }

View File

@@ -31,7 +31,7 @@ func Main() {
srv := &http.Server{ srv := &http.Server{
Addr: conf.ListenAddr, Addr: conf.ListenAddr,
Handler: app.mux, Handler: app.Handler(),
} }
log.Fatal(webutil.ListenAndServe(srv)) log.Fatal(webutil.ListenAndServe(srv))

View File

@@ -2,8 +2,7 @@
<h2>Create Network</h2> <h2>Create Network</h2>
<form method="POST"> <form method="POST">
<input type="hidden" name="CSRF" value="{{.Session.CSRF}}"> <p>
<p>
<label>Name</label><br> <label>Name</label><br>
<input type="text" name="Name"> <input type="text" name="Name">
</p> </p>

View File

@@ -2,8 +2,7 @@
<h2>Change Password</h2> <h2>Change Password</h2>
<form method="POST"> <form method="POST">
<input type="hidden" name="CSRF" value="{{.Session.CSRF}}"> <p>
<p>
<label>Current Password</label><br> <label>Current Password</label><br>
<input type="password" name="CurrentPassword"> <input type="password" name="CurrentPassword">
</p> </p>

View File

@@ -2,8 +2,7 @@
<h2>Sign Out</h2> <h2>Sign Out</h2>
<form method="POST"> <form method="POST">
<input type="hidden" name="CSRF" value="{{.Session.CSRF}}"> <p>
<p>
<button type="submit">Sign Out</button> <button type="submit">Sign Out</button>
<a href="/">Cancel</a> <a href="/">Cancel</a>
</p> </p>

View File

@@ -5,8 +5,7 @@
<p>You must first delete all peers.</p> <p>You must first delete all peers.</p>
{{- else -}} {{- else -}}
<form method="POST"> <form method="POST">
<input type="hidden" name="CSRF" value="{{.Session.CSRF}}"> <input type="hidden" name="NetworkID" value="{{.Network.NetworkID}}">
<input type="hidden" name="NetworkID" value="{{.Network.NetworkID}}">
<p> <p>
<button type="submit">Delete</button> <button type="submit">Delete</button>
<a href="/admin/network/view/?NetworkID={{.Network.NetworkID}}">Cancel</a> <a href="/admin/network/view/?NetworkID={{.Network.NetworkID}}">Cancel</a>

View File

@@ -22,8 +22,10 @@
<tr> <tr>
<th>PeerIP</th> <th>PeerIP</th>
<th>Name</th> <th>Name</th>
<th>Public IP</th> <th>Public IP 1</th>
<th>Port</th> <th>Port 1</th>
<th>Public IP 2</th>
<th>Port 2</th>
<th>Relay</th> <th>Relay</th>
</tr> </tr>
</thead> </thead>
@@ -36,8 +38,10 @@
</a> </a>
</td> </td>
<td>{{.Name}}</td> <td>{{.Name}}</td>
<td>{{ipToString .PublicIP}}</td> <td>{{ipToString .PublicIP1}}</td>
<td>{{.Port}}</td> <td>{{.Port1}}</td>
<td>{{ipToString .PublicIP2}}</td>
<td>{{.Port2}}</td>
<td>{{if .Relay}}T{{else}}F{{end}}</td> <td>{{if .Relay}}T{{else}}F{{end}}</td>
</tr> </tr>
</tbody> </tbody>

View File

@@ -2,7 +2,6 @@
<h3>New Peer</h3> <h3>New Peer</h3>
<form method="POST"> <form method="POST">
<input type="hidden" name="CSRF" value="{{.Session.CSRF}}">
<input type="hidden" name="NetworkID" value="{{.Network.NetworkID}}"> <input type="hidden" name="NetworkID" value="{{.Network.NetworkID}}">
<p> <p>
<label>IP</label><br> <label>IP</label><br>
@@ -13,12 +12,20 @@
<input type="text" name="Name"> <input type="text" name="Name">
</p> </p>
<p> <p>
<label>Public IP</label><br> <label>Public IP 1</label><br>
<input type="text" name="PublicIP"> <input type="text" name="PublicIP1">
</p> </p>
<p> <p>
<label>Port</label><br> <label>Port 1</label><br>
<input type="number" name="Port" value="456"> <input type="number" name="Port1" value="456">
</p>
<p>
<label>Public IP 2 (optional)</label><br>
<input type="text" name="PublicIP2">
</p>
<p>
<label>Port 2</label><br>
<input type="number" name="Port2" value="0">
</p> </p>
<p> <p>
<label> <label>

View File

@@ -3,8 +3,7 @@
{{with .Peer -}} {{with .Peer -}}
<form method="POST"> <form method="POST">
<input type="hidden" name="CSRF" value="{{$.Session.CSRF}}"> <input type="hidden" name="NetworkID" value="{{.NetworkID}}">
<input type="hidden" name="NetworkID" value="{{.NetworkID}}">
<input type="hidden" name="NetworkID" value="{{.PeerIP}}"> <input type="hidden" name="NetworkID" value="{{.PeerIP}}">
<p> <p>
<button type="submit">Delete</button> <button type="submit">Delete</button>

View File

@@ -3,7 +3,6 @@
{{with .Peer -}} {{with .Peer -}}
<form method="POST"> <form method="POST">
<input type="hidden" name="CSRF" value="{{$.Session.CSRF}}">
<p> <p>
<label>Peer IP</label><br> <label>Peer IP</label><br>
<input type="text" value="{{.PeerIP}}" disabled> <input type="text" value="{{.PeerIP}}" disabled>
@@ -13,12 +12,20 @@
<input type="text" name="Name" value="{{.Name}}"> <input type="text" name="Name" value="{{.Name}}">
</p> </p>
<p> <p>
<label>Public IP</label><br> <label>Public IP 1</label><br>
<input type="text" name="PublicIP" value="{{ipToString .PublicIP}}"> <input type="text" name="PublicIP1" value="{{ipToString .PublicIP1}}">
</p> </p>
<p> <p>
<label>Port</label><br> <label>Port 1</label><br>
<input type="number" name="Port" value="{{.Port}}"> <input type="number" name="Port1" value="{{.Port1}}">
</p>
<p>
<label>Public IP 2 (optional)</label><br>
<input type="text" name="PublicIP2" value="{{ipToString .PublicIP2}}">
</p>
<p>
<label>Port 2</label><br>
<input type="number" name="Port2" value="{{.Port2}}">
</p> </p>
<p> <p>
<label> <label>

View File

@@ -8,10 +8,12 @@
{{with .Peer -}} {{with .Peer -}}
<table class="def-list"> <table class="def-list">
<tr><td>Peer IP</td><td>{{.PeerIP}}</td></tr> <tr><td>Peer IP</td><td>{{.PeerIP}}</td></tr>
<tr><td>Public IP</td><td>{{ipToString .PublicIP}}</td></tr> <tr><td>Public IP 1</td><td>{{ipToString .PublicIP1}}</td></tr>
<tr><td>Port</td><td>{{.Port}}</td></tr> <tr><td>Port 1</td><td>{{.Port1}}</td></tr>
<tr><td>Public IP 2</td><td>{{ipToString .PublicIP2}}</td></tr>
<tr><td>Port 2</td><td>{{.Port2}}</td></tr>
<tr><td>Relay</td><td>{{if .Relay}}T{{else}}F{{end}}</td></tr> <tr><td>Relay</td><td>{{if .Relay}}T{{else}}F{{end}}</td></tr>
</td></tr> <tr><td>WG Public Key</td><td>{{wgKeyString .WGPubKey}}</td></tr>
</table> </table>
<details> <details>
@@ -19,7 +21,6 @@
<p>{{.APIKey}}</p> <p>{{.APIKey}}</p>
</details> </details>
{{- end}} {{- end}}
{{- end}} {{- end}}

View File

@@ -2,8 +2,7 @@
<h2>Sign In</h2> <h2>Sign In</h2>
<form method="POST"> <form method="POST">
<input type="hidden" name="CSRF" value="{{.Session.CSRF}}"> <p>
<p>
<label>Password</label><br> <label>Password</label><br>
<input type="password" name="Password"> <input type="password" name="Password">
</p> </p>

View File

@@ -2,8 +2,7 @@
package m package m
type PeerInitArgs struct { type PeerInitArgs struct {
EncPubKey []byte WGPubKey []byte
PubSignKey []byte
} }
type PeerInitResp struct { type PeerInitResp struct {
@@ -13,14 +12,15 @@ type PeerInitResp struct {
} }
type Peer struct { type Peer struct {
PeerIP byte PeerIP byte
Version int64 Version int64
Name string Name string
PublicIP []byte PublicIP1 []byte
Port uint16 Port1 uint16
Relay bool PublicIP2 []byte
PubKey []byte Port2 uint16
PubSignKey []byte Relay bool
WGPubKey []byte
} }
type NetworkState struct { type NetworkState struct {