This commit is contained in:
jdl
2025-04-06 07:51:47 +02:00
parent 03b1bbcbcf
commit d558ebbd14
38 changed files with 773 additions and 455 deletions

View File

@@ -54,17 +54,11 @@ func (a *API) ensurePassword() error {
log.Printf("Setting password: %s", pwd)
hashed, err := bcrypt.GenerateFromPassword([]byte(pwd), bcrypt.DefaultCost)
if err != nil {
return err
}
conf := &Config{
ConfigID: 1,
VPNNetwork: []byte{10, 1, 1, 0},
Password: hashed,
}
conf := &Config{ConfigID: 1, Password: hashed}
return db.Config_Insert(a.db, conf)
}
@@ -80,10 +74,6 @@ func (a *API) Config_Update(conf *Config) error {
return db.Config_Update(a.db, conf)
}
func (a *API) Config_UpdatePassword(pwdHash []byte) error {
return db.Config_UpdatePassword(a.db, pwdHash)
}
func (a *API) Session_Delete(sessionID string) error {
return db.Session_Delete(a.db, sessionID)
}
@@ -137,6 +127,24 @@ func (a *API) Session_SignIn(s *Session, pwd string) error {
return db.Session_SetSignedIn(a.db, s.SessionID)
}
func (a *API) Network_Create(n *Network) error {
n.NetworkID = idgen.NextID(0)
return db.Network_Insert(a.db, n)
}
func (a *API) Network_Delete(n *Network) error {
return db.Network_Delete(a.db, n.NetworkID)
}
func (a *API) Network_Get(id int64) (*Network, error) {
return db.Network_Get(a.db, id)
}
func (a *API) Network_List() ([]*Network, error) {
const query = db.Network_SelectQuery + ` ORDER BY Name ASC`
return db.Network_List(a.db, query)
}
func (a *API) Peer_CreateNew(p *Peer) error {
p.Version = idgen.NextID(0)
p.PubKey = []byte{}
@@ -146,7 +154,7 @@ func (a *API) Peer_CreateNew(p *Peer) error {
return db.Peer_Insert(a.db, p)
}
func (a *API) Peer_Init(peer *Peer, args m.PeerInitArgs) (*m.PeerConfig, error) {
func (a *API) Peer_Init(peer *Peer, args m.PeerInitArgs) error {
a.lock.Lock()
defer a.lock.Unlock()
@@ -154,19 +162,7 @@ func (a *API) Peer_Init(peer *Peer, args m.PeerInitArgs) (*m.PeerConfig, error)
peer.PubKey = args.EncPubKey
peer.PubSignKey = args.PubSignKey
if err := db.Peer_UpdateFull(a.db, peer); err != nil {
return nil, err
}
conf := a.Config_Get()
return &m.PeerConfig{
PeerIP: peer.PeerIP,
Network: conf.VPNNetwork,
PublicIP: peer.PublicIP,
Port: peer.Port,
Relay: peer.Relay,
}, nil
return db.Peer_UpdateFull(a.db, peer)
}
func (a *API) Peer_Update(p *Peer) error {
@@ -177,16 +173,16 @@ func (a *API) Peer_Update(p *Peer) error {
return db.Peer_Update(a.db, p)
}
func (a *API) Peer_Delete(ip byte) error {
return db.Peer_Delete(a.db, ip)
func (a *API) Peer_Delete(networkID int64, peerIP byte) error {
return db.Peer_Delete(a.db, networkID, peerIP)
}
func (a *API) Peer_List() ([]*Peer, error) {
return db.Peer_ListAll(a.db)
func (a *API) Peer_List(networkID int64) ([]*Peer, error) {
return db.Peer_ListAll(a.db, networkID)
}
func (a *API) Peer_Get(ip byte) (*Peer, error) {
return db.Peer_Get(a.db, ip)
func (a *API) Peer_Get(networkID int64, ip byte) (*Peer, error) {
return db.Peer_Get(a.db, networkID, ip)
}
func (a *API) Peer_GetByAPIKey(key string) (*Peer, error) {

View File

View File

@@ -16,13 +16,11 @@ type TX interface {
// ----------------------------------------------------------------------------
type Config struct {
ConfigID int64
HubAddress string
VPNNetwork []byte
Password []byte
ConfigID int64
Password []byte
}
const Config_SelectQuery = "SELECT ConfigID,HubAddress,VPNNetwork,Password FROM config"
const Config_SelectQuery = "SELECT ConfigID,Password FROM config"
func Config_Insert(
tx TX,
@@ -33,7 +31,7 @@ func Config_Insert(
return err
}
_, err = tx.Exec("INSERT INTO config(ConfigID,HubAddress,VPNNetwork,Password) VALUES(?,?,?,?)", row.ConfigID, row.HubAddress, row.VPNNetwork, row.Password)
_, err = tx.Exec("INSERT INTO config(ConfigID,Password) VALUES(?,?)", row.ConfigID, row.Password)
return err
}
@@ -46,7 +44,7 @@ func Config_Update(
return err
}
result, err := tx.Exec("UPDATE config SET HubAddress=?,VPNNetwork=? WHERE ConfigID=?", row.HubAddress, row.VPNNetwork, row.ConfigID)
result, err := tx.Exec("UPDATE config SET Password=? WHERE ConfigID=?", row.Password, row.ConfigID)
if err != nil {
return err
}
@@ -74,7 +72,7 @@ func Config_UpdateFull(
return err
}
result, err := tx.Exec("UPDATE config SET HubAddress=?,VPNNetwork=?,Password=? WHERE ConfigID=?", row.HubAddress, row.VPNNetwork, row.Password, row.ConfigID)
result, err := tx.Exec("UPDATE config SET Password=? WHERE ConfigID=?", row.Password, row.ConfigID)
if err != nil {
return err
}
@@ -124,8 +122,8 @@ func Config_Get(
err error,
) {
row = &Config{}
r := tx.QueryRow("SELECT ConfigID,HubAddress,VPNNetwork,Password FROM config WHERE ConfigID=?", ConfigID)
err = r.Scan(&row.ConfigID, &row.HubAddress, &row.VPNNetwork, &row.Password)
r := tx.QueryRow("SELECT ConfigID,Password FROM config WHERE ConfigID=?", ConfigID)
err = r.Scan(&row.ConfigID, &row.Password)
return
}
@@ -139,7 +137,7 @@ func Config_GetWhere(
) {
row = &Config{}
r := tx.QueryRow(query, args...)
err = r.Scan(&row.ConfigID, &row.HubAddress, &row.VPNNetwork, &row.Password)
err = r.Scan(&row.ConfigID, &row.Password)
return
}
@@ -159,7 +157,7 @@ func Config_Iterate(
defer rows.Close()
for rows.Next() {
row := &Config{}
err := rows.Scan(&row.ConfigID, &row.HubAddress, &row.VPNNetwork, &row.Password)
err := rows.Scan(&row.ConfigID, &row.Password)
if !yield(row, err) {
return
}
@@ -302,11 +300,156 @@ func Session_List(
return l, nil
}
// ----------------------------------------------------------------------------
// Table: networks
// ----------------------------------------------------------------------------
type Network struct {
NetworkID int64
Name string
Network []byte
}
const Network_SelectQuery = "SELECT NetworkID,Name,Network FROM networks"
func Network_Insert(
tx TX,
row *Network,
) (err error) {
Network_Sanitize(row)
if err = Network_Validate(row); err != nil {
return err
}
_, err = tx.Exec("INSERT INTO networks(NetworkID,Name,Network) VALUES(?,?,?)", row.NetworkID, row.Name, row.Network)
return err
}
func Network_UpdateFull(
tx TX,
row *Network,
) (err error) {
Network_Sanitize(row)
if err = Network_Validate(row); err != nil {
return err
}
result, err := tx.Exec("UPDATE networks SET Name=?,Network=? WHERE NetworkID=?", row.Name, row.Network, row.NetworkID)
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 updated")
}
}
func Network_Delete(
tx TX,
NetworkID int64,
) (err error) {
result, err := tx.Exec("DELETE FROM networks WHERE NetworkID=?", NetworkID)
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 Network_Get(
tx TX,
NetworkID int64,
) (
row *Network,
err error,
) {
row = &Network{}
r := tx.QueryRow("SELECT NetworkID,Name,Network FROM networks WHERE NetworkID=?", NetworkID)
err = r.Scan(&row.NetworkID, &row.Name, &row.Network)
return
}
func Network_GetWhere(
tx TX,
query string,
args ...any,
) (
row *Network,
err error,
) {
row = &Network{}
r := tx.QueryRow(query, args...)
err = r.Scan(&row.NetworkID, &row.Name, &row.Network)
return
}
func Network_Iterate(
tx TX,
query string,
args ...any,
) iter.Seq2[*Network, error] {
rows, err := tx.Query(query, args...)
if err != nil {
return func(yield func(*Network, error) bool) {
yield(nil, err)
}
}
return func(yield func(*Network, error) bool) {
defer rows.Close()
for rows.Next() {
row := &Network{}
err := rows.Scan(&row.NetworkID, &row.Name, &row.Network)
if !yield(row, err) {
return
}
}
}
}
func Network_List(
tx TX,
query string,
args ...any,
) (
l []*Network,
err error,
) {
for row, err := range Network_Iterate(tx, query, args...) {
if err != nil {
return nil, err
}
l = append(l, row)
}
return l, nil
}
// ----------------------------------------------------------------------------
// Table: peers
// ----------------------------------------------------------------------------
type Peer struct {
NetworkID int64
PeerIP byte
Version int64
APIKey string
@@ -318,7 +461,7 @@ type Peer struct {
PubSignKey []byte
}
const Peer_SelectQuery = "SELECT PeerIP,Version,APIKey,Name,PublicIP,Port,Relay,PubKey,PubSignKey FROM peers"
const Peer_SelectQuery = "SELECT NetworkID,PeerIP,Version,APIKey,Name,PublicIP,Port,Relay,PubKey,PubSignKey FROM peers"
func Peer_Insert(
tx TX,
@@ -329,7 +472,7 @@ func Peer_Insert(
return err
}
_, err = tx.Exec("INSERT INTO peers(PeerIP,Version,APIKey,Name,PublicIP,Port,Relay,PubKey,PubSignKey) VALUES(?,?,?,?,?,?,?,?,?)", 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,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)
return err
}
@@ -342,7 +485,7 @@ func Peer_Update(
return err
}
result, err := tx.Exec("UPDATE peers SET Version=?,Name=?,PublicIP=?,Port=?,Relay=? WHERE PeerIP=?", row.Version, row.Name, row.PublicIP, row.Port, row.Relay, row.PeerIP)
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)
if err != nil {
return err
}
@@ -370,7 +513,7 @@ func Peer_UpdateFull(
return err
}
result, err := tx.Exec("UPDATE peers SET Version=?,APIKey=?,Name=?,PublicIP=?,Port=?,Relay=?,PubKey=?,PubSignKey=? WHERE PeerIP=?", row.Version, row.APIKey, row.Name, row.PublicIP, row.Port, row.Relay, row.PubKey, row.PubSignKey, row.PeerIP)
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)
if err != nil {
return err
}
@@ -391,9 +534,10 @@ func Peer_UpdateFull(
func Peer_Delete(
tx TX,
NetworkID int64,
PeerIP byte,
) (err error) {
result, err := tx.Exec("DELETE FROM peers WHERE PeerIP=?", PeerIP)
result, err := tx.Exec("DELETE FROM peers WHERE NetworkID=? AND PeerIP=?", NetworkID, PeerIP)
if err != nil {
return err
}
@@ -414,14 +558,15 @@ func Peer_Delete(
func Peer_Get(
tx TX,
NetworkID int64,
PeerIP byte,
) (
row *Peer,
err error,
) {
row = &Peer{}
r := tx.QueryRow("SELECT PeerIP,Version,APIKey,Name,PublicIP,Port,Relay,PubKey,PubSignKey FROM peers WHERE PeerIP=?", PeerIP)
err = r.Scan(&row.PeerIP, &row.Version, &row.APIKey, &row.Name, &row.PublicIP, &row.Port, &row.Relay, &row.PubKey, &row.PubSignKey)
r := tx.QueryRow("SELECT NetworkID,PeerIP,Version,APIKey,Name,PublicIP,Port,Relay,PubKey,PubSignKey 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)
return
}
@@ -435,7 +580,7 @@ func Peer_GetWhere(
) {
row = &Peer{}
r := tx.QueryRow(query, args...)
err = r.Scan(&row.PeerIP, &row.Version, &row.APIKey, &row.Name, &row.PublicIP, &row.Port, &row.Relay, &row.PubKey, &row.PubSignKey)
err = r.Scan(&row.NetworkID, &row.PeerIP, &row.Version, &row.APIKey, &row.Name, &row.PublicIP, &row.Port, &row.Relay, &row.PubKey, &row.PubSignKey)
return
}
@@ -455,7 +600,7 @@ func Peer_Iterate(
defer rows.Close()
for rows.Next() {
row := &Peer{}
err := rows.Scan(&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.PublicIP, &row.Port, &row.Relay, &row.PubKey, &row.PubSignKey)
if !yield(row, err) {
return
}

View File

@@ -3,35 +3,21 @@ package db
import (
"errors"
"net/netip"
"net/url"
"strings"
)
var (
ErrInvalidIP = errors.New("invalid IP")
ErrInvalidPort = errors.New("invalid port")
ErrInvalidIP = errors.New("invalid IP")
ErrNonPrivateIP = errors.New("non-private IP")
ErrInvalidPort = errors.New("invalid port")
ErrInvalidNetName = errors.New("invalid network name")
ErrInvalidPeerName = errors.New("invalid peer name")
)
func Config_Sanitize(c *Config) {
if u, err := url.Parse(c.HubAddress); err == nil {
c.HubAddress = u.String()
}
if addr, ok := netip.AddrFromSlice(c.VPNNetwork); ok {
c.VPNNetwork = addr.AsSlice()
}
}
func Config_Validate(c *Config) error {
if _, err := url.Parse(c.HubAddress); err != nil {
return err
}
addr, ok := netip.AddrFromSlice(c.VPNNetwork)
if !ok || !addr.Is4() || addr.As4()[3] != 0 || addr.As4()[0] == 0 {
return ErrInvalidIP
}
return nil
}
@@ -42,6 +28,42 @@ func Session_Validate(s *Session) error {
return nil
}
func Network_Sanitize(n *Network) {
n.Name = strings.TrimSpace(n.Name)
if addr, ok := netip.AddrFromSlice(n.Network); ok {
n.Network = addr.AsSlice()
}
}
func Network_Validate(c *Network) error {
// 16 bytes is linux limit for network interface names.
if len(c.Name) == 0 || len(c.Name) > 16 {
return ErrInvalidNetName
}
for _, c := range c.Name {
if c >= 'a' && c <= 'z' {
continue
}
if c >= '0' && c <= '9' {
continue
}
return ErrInvalidNetName
}
addr, ok := netip.AddrFromSlice(c.Network)
if !ok || !addr.Is4() || addr.As4()[3] != 0 || addr.As4()[0] == 0 {
return ErrInvalidIP
}
if !addr.IsPrivate() {
return ErrNonPrivateIP
}
return nil
}
func Peer_Sanitize(p *Peer) {
p.Name = strings.TrimSpace(p.Name)
if len(p.PublicIP) != 0 {
@@ -65,5 +87,20 @@ func Peer_Validate(p *Peer) error {
if p.Port == 0 {
return ErrInvalidPort
}
for _, c := range p.Name {
if c >= 'a' && c <= 'z' {
continue
}
if c >= '0' && c <= '9' {
continue
}
if c == '.' || c == '-' || c == '_' {
continue
}
return ErrInvalidPeerName
}
return nil
}

View File

@@ -1,8 +1,6 @@
TABLE config OF Config (
ConfigID int64 PK,
HubAddress string,
VPNNetwork []byte,
Password []byte NoUpdate
Password []byte
);
TABLE sessions OF Session NoUpdate (
@@ -13,7 +11,14 @@ TABLE sessions OF Session NoUpdate (
LastSeenAt int64
);
TABLE networks OF Network (
NetworkID int64 PK,
Name string NoUpdate,
Network []byte NoUpdate
);
TABLE peers OF Peer (
NetworkID int64 PK,
PeerIP byte PK,
Version int64,
APIKey string NoUpdate,

View File

@@ -26,16 +26,9 @@ func Session_DeleteBefore(
return err
}
func Config_UpdatePassword(
tx TX,
pwdHash []byte,
) (err error) {
_, err = tx.Exec("UPDATE config SET Password=? WHERE ConfigID=1", pwdHash)
return err
}
func Peer_ListAll(tx TX) ([]*Peer, error) {
return Peer_List(tx, Peer_SelectQuery)
func Peer_ListAll(tx TX, networkID int64) ([]*Peer, error) {
const query = Peer_SelectQuery + ` WHERE NetworkID=? ORDER BY PeerIP ASC`
return Peer_List(tx, query, networkID)
}
func Peer_GetByAPIKey(tx TX, apiKey string) (*Peer, error) {
@@ -45,7 +38,8 @@ func Peer_GetByAPIKey(tx TX, apiKey string) (*Peer, error) {
apiKey)
}
func Peer_Exists(tx TX, ip byte) (exists bool, err error) {
err = tx.QueryRow(`SELECT EXISTS(SELECT 1 FROM peers WHERE PeerIP=?)`, ip).Scan(&exists)
func Peer_Exists(tx TX, networkID int64, ip byte) (exists bool, err error) {
const query = `SELECT EXISTS(SELECT 1 FROM peers WHERE NetworkID=? AND PeerIP=?)`
err = tx.QueryRow(query, networkID, ip).Scan(&exists)
return
}

View File

@@ -1,7 +1,5 @@
CREATE TABLE config (
ConfigID INTEGER NOT NULL PRIMARY KEY, -- Always 1.
HubAddress TEXT NOT NULL, -- https://for.example.com
VPNNetwork BLOB NOT NULL, -- Network (/24), example 10.51.50.0
Password BLOB NOT NULL -- bcrypt password for web interface
) WITHOUT ROWID;
@@ -15,13 +13,22 @@ CREATE TABLE sessions (
CREATE INDEX sessions_last_seen_index ON sessions(LastSeenAt);
CREATE TABLE networks (
NetworkID INTEGER NOT NULL PRIMARY KEY,
Name TEXT NOT NULL UNIQUE, -- Network/interface name.
Network BLOB NOT NULL UNIQUE -- Network (/24), example 10.51.50.0
) WITHOUT ROWID;
CREATE TABLE peers (
PeerIP INTEGER NOT NULL PRIMARY KEY, -- Final byte.
Version INTEGER NOT NULL,
APIKey TEXT NOT NULL UNIQUE,
NetworkID INTEGER NOT NULL,
PeerIP INTEGER NOT NULL, -- Final byte of IP.
Version INTEGER NOT NULL, -- Changes when updated.
APIKey TEXT NOT NULL UNIQUE, -- Peer's secret API key.
Name TEXT NOT NULL UNIQUE, -- For humans.
PublicIP BLOB NOT NULL,
Port INTEGER NOT NULL,
Relay INTEGER NOT NULL DEFAULT 0, -- Boolean if peer will forward packets. Must also have public address.
PubKey BLOB NOT NULL
PubKey BLOB NOT NULL,
PubSignKey BLOB NOT NULL,
PRIMARY KEY(NetworkID, PeerIP)
) WITHOUT ROWID;

View File

@@ -1 +0,0 @@
ALTER TABLE peers ADD COLUMN PubSignKey BLOB NOT NULL DEFAULT '';

View File

@@ -4,4 +4,5 @@ import "vppn/hub/api/db"
type Config = db.Config
type Session = db.Session
type Network = db.Network
type Peer = db.Peer