Cleanup - WIP
This commit is contained in:
@@ -1,8 +1,48 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import "log"
|
import (
|
||||||
|
"flag"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
|
||||||
|
"vppn/peer"
|
||||||
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
// TODO: construct peer.App and call Run (audit C1).
|
log.SetFlags(0)
|
||||||
log.Fatal("not yet implemented")
|
|
||||||
|
name := flag.String("name", "", "network name (required)")
|
||||||
|
hub := flag.String("hub", "", "hub base URL (required)")
|
||||||
|
apiKey := flag.String("api-key", "", "API key (required)")
|
||||||
|
flag.Parse()
|
||||||
|
|
||||||
|
if *name == "" || *hub == "" || *apiKey == "" {
|
||||||
|
flag.Usage()
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
statePath := networkStatePath(*name)
|
||||||
|
|
||||||
|
state, err := peer.LoadOrInit(statePath, *hub, *apiKey)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("init: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
app, err := peer.New(state, *hub, *apiKey, *name)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("start: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := app.Run(); err != nil {
|
||||||
|
log.Fatalf("run: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func networkStatePath(name string) string {
|
||||||
|
home, err := os.UserHomeDir()
|
||||||
|
if err != nil {
|
||||||
|
return filepath.Join(".vppn", name, "state.json")
|
||||||
|
}
|
||||||
|
return filepath.Join(home, ".vppn", name, "state.json")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -345,14 +345,13 @@ type Peer struct {
|
|||||||
APIKey string
|
APIKey string
|
||||||
Name string
|
Name string
|
||||||
Addr4 []byte
|
Addr4 []byte
|
||||||
Port4 uint16
|
|
||||||
Addr6 []byte
|
Addr6 []byte
|
||||||
Port6 uint16
|
Port uint16
|
||||||
Relay bool
|
Relay bool
|
||||||
WGPubKey []byte
|
WGPubKey []byte
|
||||||
}
|
}
|
||||||
|
|
||||||
const Peer_SelectQuery = "SELECT NetworkID,PeerIP,Version,APIKey,Name,Addr4,Port4,Addr6,Port6,Relay,WGPubKey FROM peers"
|
const Peer_SelectQuery = "SELECT NetworkID,PeerIP,Version,APIKey,Name,Addr4,Addr6,Port,Relay,WGPubKey FROM peers"
|
||||||
|
|
||||||
func Peer_Insert(
|
func Peer_Insert(
|
||||||
tx TX,
|
tx TX,
|
||||||
@@ -363,7 +362,7 @@ func Peer_Insert(
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = tx.Exec("INSERT INTO peers(NetworkID,PeerIP,Version,APIKey,Name,Addr4,Port4,Addr6,Port6,Relay,WGPubKey) VALUES(?,?,?,?,?,?,?,?,?,?,?)", row.NetworkID, row.PeerIP, row.Version, row.APIKey, row.Name, row.Addr4, row.Port4, row.Addr6, row.Port6, row.Relay, row.WGPubKey)
|
_, err = tx.Exec("INSERT INTO peers(NetworkID,PeerIP,Version,APIKey,Name,Addr4,Addr6,Port,Relay,WGPubKey) VALUES(?,?,?,?,?,?,?,?,?,?)", row.NetworkID, row.PeerIP, row.Version, row.APIKey, row.Name, row.Addr4, row.Addr6, row.Port, row.Relay, row.WGPubKey)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -376,7 +375,7 @@ func Peer_Update(
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
result, err := tx.Exec("UPDATE peers SET Version=?,Name=?,Addr4=?,Port4=?,Addr6=?,Port6=?,Relay=? WHERE NetworkID=? AND PeerIP=?", row.Version, row.Name, row.Addr4, row.Port4, row.Addr6, row.Port6, row.Relay, row.NetworkID, row.PeerIP)
|
result, err := tx.Exec("UPDATE peers SET Version=?,Name=?,Addr4=?,Addr6=?,Port=?,Relay=? WHERE NetworkID=? AND PeerIP=?", row.Version, row.Name, row.Addr4, row.Addr6, row.Port, row.Relay, row.NetworkID, row.PeerIP)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -404,7 +403,7 @@ func Peer_UpdateFull(
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
result, err := tx.Exec("UPDATE peers SET Version=?,APIKey=?,Name=?,Addr4=?,Port4=?,Addr6=?,Port6=?,Relay=?,WGPubKey=? WHERE NetworkID=? AND PeerIP=?", row.Version, row.APIKey, row.Name, row.Addr4, row.Port4, row.Addr6, row.Port6, row.Relay, row.WGPubKey, row.NetworkID, row.PeerIP)
|
result, err := tx.Exec("UPDATE peers SET Version=?,APIKey=?,Name=?,Addr4=?,Addr6=?,Port=?,Relay=?,WGPubKey=? WHERE NetworkID=? AND PeerIP=?", row.Version, row.APIKey, row.Name, row.Addr4, row.Addr6, row.Port, row.Relay, row.WGPubKey, row.NetworkID, row.PeerIP)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -456,8 +455,8 @@ func Peer_Get(
|
|||||||
err error,
|
err error,
|
||||||
) {
|
) {
|
||||||
row = &Peer{}
|
row = &Peer{}
|
||||||
r := tx.QueryRow("SELECT NetworkID,PeerIP,Version,APIKey,Name,Addr4,Port4,Addr6,Port6,Relay,WGPubKey FROM peers WHERE NetworkID=? AND PeerIP=?", NetworkID, PeerIP)
|
r := tx.QueryRow("SELECT NetworkID,PeerIP,Version,APIKey,Name,Addr4,Addr6,Port,Relay,WGPubKey FROM peers WHERE NetworkID=? AND PeerIP=?", NetworkID, PeerIP)
|
||||||
if err = r.Scan(&row.NetworkID, &row.PeerIP, &row.Version, &row.APIKey, &row.Name, &row.Addr4, &row.Port4, &row.Addr6, &row.Port6, &row.Relay, &row.WGPubKey); err != nil {
|
if err = r.Scan(&row.NetworkID, &row.PeerIP, &row.Version, &row.APIKey, &row.Name, &row.Addr4, &row.Addr6, &row.Port, &row.Relay, &row.WGPubKey); err != nil {
|
||||||
row = nil
|
row = nil
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
@@ -473,7 +472,7 @@ func Peer_GetWhere(
|
|||||||
) {
|
) {
|
||||||
row = &Peer{}
|
row = &Peer{}
|
||||||
r := tx.QueryRow(query, args...)
|
r := tx.QueryRow(query, args...)
|
||||||
if err = r.Scan(&row.NetworkID, &row.PeerIP, &row.Version, &row.APIKey, &row.Name, &row.Addr4, &row.Port4, &row.Addr6, &row.Port6, &row.Relay, &row.WGPubKey); err != nil {
|
if err = r.Scan(&row.NetworkID, &row.PeerIP, &row.Version, &row.APIKey, &row.Name, &row.Addr4, &row.Addr6, &row.Port, &row.Relay, &row.WGPubKey); err != nil {
|
||||||
row = nil
|
row = nil
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
@@ -495,7 +494,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.Addr4, &row.Port4, &row.Addr6, &row.Port6, &row.Relay, &row.WGPubKey)
|
err := rows.Scan(&row.NetworkID, &row.PeerIP, &row.Version, &row.APIKey, &row.Name, &row.Addr4, &row.Addr6, &row.Port, &row.Relay, &row.WGPubKey)
|
||||||
if !yield(row, err) {
|
if !yield(row, err) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -69,11 +69,8 @@ func Peer_Sanitize(p *Peer) {
|
|||||||
p.Addr6 = addr.AsSlice()
|
p.Addr6 = addr.AsSlice()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if p.Port4 == 0 {
|
if p.Port == 0 {
|
||||||
p.Port4 = 456
|
p.Port = 51820
|
||||||
}
|
|
||||||
if len(p.Addr6) != 0 && p.Port6 == 0 {
|
|
||||||
p.Port6 = 456
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -87,11 +84,8 @@ func Peer_Validate(p *Peer) error {
|
|||||||
if _, ok := netip.AddrFromSlice(p.Addr6); !ok {
|
if _, ok := netip.AddrFromSlice(p.Addr6); !ok {
|
||||||
return ErrInvalidIP
|
return ErrInvalidIP
|
||||||
}
|
}
|
||||||
if p.Port6 == 0 {
|
|
||||||
return ErrInvalidPort
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if p.Port4 == 0 {
|
if p.Port == 0 {
|
||||||
return ErrInvalidPort
|
return ErrInvalidPort
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -16,9 +16,8 @@ TABLE peers OF Peer (
|
|||||||
APIKey string NoUpdate,
|
APIKey string NoUpdate,
|
||||||
Name string,
|
Name string,
|
||||||
Addr4 []byte,
|
Addr4 []byte,
|
||||||
Port4 uint16,
|
|
||||||
Addr6 []byte,
|
Addr6 []byte,
|
||||||
Port6 uint16,
|
Port uint16,
|
||||||
Relay bool,
|
Relay bool,
|
||||||
WGPubKey []byte NoUpdate
|
WGPubKey []byte NoUpdate
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -16,9 +16,8 @@ CREATE TABLE peers (
|
|||||||
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.
|
||||||
Addr4 BLOB NOT NULL,
|
Addr4 BLOB NOT NULL,
|
||||||
Port4 INTEGER NOT NULL,
|
|
||||||
Addr6 BLOB NOT NULL,
|
Addr6 BLOB NOT NULL,
|
||||||
Port6 INTEGER NOT NULL,
|
Port INTEGER NOT NULL,
|
||||||
Relay INTEGER NOT NULL DEFAULT 0, -- Boolean if peer will forward packets.
|
Relay INTEGER NOT NULL DEFAULT 0, -- Boolean if peer will forward packets.
|
||||||
WGPubKey BLOB NOT NULL,
|
WGPubKey BLOB NOT NULL,
|
||||||
PRIMARY KEY(NetworkID, PeerIP)
|
PRIMARY KEY(NetworkID, PeerIP)
|
||||||
|
|||||||
@@ -150,9 +150,8 @@ func (a *App) _adminPeerCreateSubmit(s *api.Session, w http.ResponseWriter, r *h
|
|||||||
Scan("IP", &p.PeerIP).
|
Scan("IP", &p.PeerIP).
|
||||||
Scan("Name", &p.Name).
|
Scan("Name", &p.Name).
|
||||||
Scan("Addr4", &addr4Str).
|
Scan("Addr4", &addr4Str).
|
||||||
Scan("Port4", &p.Port4).
|
|
||||||
Scan("Addr6", &addr6Str).
|
Scan("Addr6", &addr6Str).
|
||||||
Scan("Port6", &p.Port6).
|
Scan("Port", &p.Port).
|
||||||
Scan("Relay", &p.Relay).
|
Scan("Relay", &p.Relay).
|
||||||
Error()
|
Error()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -209,9 +208,8 @@ func (a *App) _adminPeerEditSubmit(s *api.Session, w http.ResponseWriter, r *htt
|
|||||||
err = webutil.NewFormScanner(r.Form).
|
err = webutil.NewFormScanner(r.Form).
|
||||||
Scan("Name", &peer.Name).
|
Scan("Name", &peer.Name).
|
||||||
Scan("Addr4", &addr4Str).
|
Scan("Addr4", &addr4Str).
|
||||||
Scan("Port4", &peer.Port4).
|
|
||||||
Scan("Addr6", &addr6Str).
|
Scan("Addr6", &addr6Str).
|
||||||
Scan("Port6", &peer.Port6).
|
Scan("Port", &peer.Port).
|
||||||
Scan("Relay", &peer.Relay).
|
Scan("Relay", &peer.Relay).
|
||||||
Error()
|
Error()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -358,10 +356,9 @@ func (a *App) peersArray(networkID int64) (peers [256]*m.Peer, err error) {
|
|||||||
PeerIP: p.PeerIP,
|
PeerIP: p.PeerIP,
|
||||||
Version: p.Version,
|
Version: p.Version,
|
||||||
Name: p.Name,
|
Name: p.Name,
|
||||||
Addr4: p.Addr4,
|
Addr4: p.Addr4,
|
||||||
Port4: p.Port4,
|
Addr6: p.Addr6,
|
||||||
Addr6: p.Addr6,
|
Port: p.Port,
|
||||||
Port6: p.Port6,
|
|
||||||
Relay: p.Relay,
|
Relay: p.Relay,
|
||||||
WGPubKey: p.WGPubKey,
|
WGPubKey: p.WGPubKey,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,10 +15,9 @@ type Peer struct {
|
|||||||
PeerIP byte
|
PeerIP byte
|
||||||
Version int64
|
Version int64
|
||||||
Name string
|
Name string
|
||||||
Addr4 []byte
|
Addr4 []byte
|
||||||
Port4 uint16
|
Addr6 []byte
|
||||||
Addr6 []byte
|
Port uint16
|
||||||
Port6 uint16
|
|
||||||
Relay bool
|
Relay bool
|
||||||
WGPubKey []byte
|
WGPubKey []byte
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -130,12 +130,12 @@ func hubPeerFrom(pubKey wgtypes.Key, vpnIP netip.Addr, p *m.Peer) HubPeer {
|
|||||||
var ep4, ep6 netip.AddrPort
|
var ep4, ep6 netip.AddrPort
|
||||||
if len(p.Addr4) > 0 {
|
if len(p.Addr4) > 0 {
|
||||||
if addr, ok := netip.AddrFromSlice(p.Addr4); ok {
|
if addr, ok := netip.AddrFromSlice(p.Addr4); ok {
|
||||||
ep4 = netip.AddrPortFrom(addr.Unmap(), p.Port4)
|
ep4 = netip.AddrPortFrom(addr.Unmap(), p.Port)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if len(p.Addr6) > 0 {
|
if len(p.Addr6) > 0 {
|
||||||
if addr, ok := netip.AddrFromSlice(p.Addr6); ok {
|
if addr, ok := netip.AddrFromSlice(p.Addr6); ok {
|
||||||
ep6 = netip.AddrPortFrom(addr, p.Port6)
|
ep6 = netip.AddrPortFrom(addr, p.Port)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return HubPeer{
|
return HubPeer{
|
||||||
|
|||||||
@@ -116,7 +116,7 @@ func (hp *HubPoller) applyPeerConfig(peer *m.Peer) {
|
|||||||
if peer == nil || len(peer.WGPubKey) != wgtypes.KeyLen {
|
if peer == nil || len(peer.WGPubKey) != wgtypes.KeyLen {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if len(peer.Addr4) == 0 || peer.Port4 == 0 {
|
if len(peer.Addr4) == 0 || peer.Port == 0 {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -131,7 +131,7 @@ func (hp *HubPoller) applyPeerConfig(peer *m.Peer) {
|
|||||||
hp.logf("Invalid public IP for peer %d", peer.PeerIP)
|
hp.logf("Invalid public IP for peer %d", peer.PeerIP)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
endpoint := netip.AddrPortFrom(ip.Unmap(), peer.Port4)
|
endpoint := netip.AddrPortFrom(ip.Unmap(), peer.Port)
|
||||||
|
|
||||||
if peer.Relay {
|
if peer.Relay {
|
||||||
if err := applyBaseConfig(hp.WGClient, hp.WGDevName, pubKey, endpoint, hp.Network); err != nil {
|
if err := applyBaseConfig(hp.WGClient, hp.WGDevName, pubKey, endpoint, hp.Network); err != nil {
|
||||||
|
|||||||
156
peer/init.go
Normal file
156
peer/init.go
Normal file
@@ -0,0 +1,156 @@
|
|||||||
|
package peer
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/base64"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"net/netip"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
|
||||||
|
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
|
||||||
|
|
||||||
|
"vppn/m"
|
||||||
|
)
|
||||||
|
|
||||||
|
// LocalState is the persisted identity for this peer, written on first run and
|
||||||
|
// loaded on every subsequent run.
|
||||||
|
type LocalState struct {
|
||||||
|
PrivKey wgtypes.Key
|
||||||
|
VPNIP netip.Addr
|
||||||
|
VPNNet netip.Prefix
|
||||||
|
WGPort uint16
|
||||||
|
IsRelay bool
|
||||||
|
IsPublic bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// localStateJSON is the on-disk representation.
|
||||||
|
type localStateJSON struct {
|
||||||
|
PrivKey string `json:"priv_key"` // standard base64
|
||||||
|
VPNIP netip.Addr `json:"vpn_ip"`
|
||||||
|
VPNNet netip.Prefix `json:"vpn_net"`
|
||||||
|
WGPort uint16 `json:"wg_port"`
|
||||||
|
IsRelay bool `json:"is_relay"`
|
||||||
|
IsPublic bool `json:"is_public"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// LoadOrInit loads LocalState from path, or registers with the hub and creates
|
||||||
|
// the file if it doesn't exist.
|
||||||
|
func LoadOrInit(statePath, hubURL, apiKey string) (LocalState, error) {
|
||||||
|
if data, err := os.ReadFile(statePath); err == nil {
|
||||||
|
return parseLocalState(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
privKey, err := wgtypes.GeneratePrivateKey()
|
||||||
|
if err != nil {
|
||||||
|
return LocalState{}, fmt.Errorf("generate key: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
state, err := initFromHub(hubURL, apiKey, privKey)
|
||||||
|
if err != nil {
|
||||||
|
return LocalState{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := saveLocalState(statePath, state); err != nil {
|
||||||
|
return LocalState{}, fmt.Errorf("save state: %w", err)
|
||||||
|
}
|
||||||
|
return state, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func initFromHub(hubURL, apiKey string, privKey wgtypes.Key) (LocalState, error) {
|
||||||
|
pubKey := privKey.PublicKey()
|
||||||
|
body, _ := json.Marshal(m.PeerInitArgs{WGPubKey: pubKey[:]})
|
||||||
|
|
||||||
|
req, err := http.NewRequest(http.MethodPost, hubURL+"/peer/init/", bytes.NewReader(body))
|
||||||
|
if err != nil {
|
||||||
|
return LocalState{}, err
|
||||||
|
}
|
||||||
|
req.SetBasicAuth("", apiKey)
|
||||||
|
req.Header.Set("Content-Type", "application/json")
|
||||||
|
|
||||||
|
resp, err := http.DefaultClient.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
return LocalState{}, fmt.Errorf("hub init: %w", err)
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
if resp.StatusCode != http.StatusOK {
|
||||||
|
return LocalState{}, fmt.Errorf("hub init: HTTP %d", resp.StatusCode)
|
||||||
|
}
|
||||||
|
|
||||||
|
var r m.PeerInitResp
|
||||||
|
if err := json.NewDecoder(resp.Body).Decode(&r); err != nil {
|
||||||
|
return LocalState{}, fmt.Errorf("hub init decode: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(r.Network) != 4 {
|
||||||
|
return LocalState{}, fmt.Errorf("hub init: invalid network %v", r.Network)
|
||||||
|
}
|
||||||
|
|
||||||
|
netAddr := netip.AddrFrom4([4]byte(r.Network))
|
||||||
|
octets := netAddr.As4()
|
||||||
|
octets[3] = r.PeerIP
|
||||||
|
vpnIP := netip.AddrFrom4(octets)
|
||||||
|
vpnNet := netip.PrefixFrom(netAddr, 24)
|
||||||
|
|
||||||
|
var isRelay, isPublic bool
|
||||||
|
var wgPort uint16
|
||||||
|
if self := r.NetworkState.Peers[r.PeerIP]; self != nil {
|
||||||
|
isRelay = self.Relay
|
||||||
|
isPublic = len(self.Addr4) > 0 || len(self.Addr6) > 0
|
||||||
|
wgPort = self.Port
|
||||||
|
}
|
||||||
|
|
||||||
|
return LocalState{
|
||||||
|
PrivKey: privKey,
|
||||||
|
VPNIP: vpnIP,
|
||||||
|
VPNNet: vpnNet,
|
||||||
|
WGPort: wgPort,
|
||||||
|
IsRelay: isRelay,
|
||||||
|
IsPublic: isPublic,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseLocalState(data []byte) (LocalState, error) {
|
||||||
|
var j localStateJSON
|
||||||
|
if err := json.Unmarshal(data, &j); err != nil {
|
||||||
|
return LocalState{}, fmt.Errorf("parse state: %w", err)
|
||||||
|
}
|
||||||
|
keyBytes, err := base64.StdEncoding.DecodeString(j.PrivKey)
|
||||||
|
if err != nil {
|
||||||
|
return LocalState{}, fmt.Errorf("decode key: %w", err)
|
||||||
|
}
|
||||||
|
key, err := wgtypes.NewKey(keyBytes)
|
||||||
|
if err != nil {
|
||||||
|
return LocalState{}, fmt.Errorf("invalid key: %w", err)
|
||||||
|
}
|
||||||
|
return LocalState{
|
||||||
|
PrivKey: key,
|
||||||
|
VPNIP: j.VPNIP,
|
||||||
|
VPNNet: j.VPNNet,
|
||||||
|
WGPort: j.WGPort,
|
||||||
|
IsRelay: j.IsRelay,
|
||||||
|
IsPublic: j.IsPublic,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func saveLocalState(path string, s LocalState) error {
|
||||||
|
j := localStateJSON{
|
||||||
|
PrivKey: base64.StdEncoding.EncodeToString(s.PrivKey[:]),
|
||||||
|
VPNIP: s.VPNIP,
|
||||||
|
VPNNet: s.VPNNet,
|
||||||
|
WGPort: s.WGPort,
|
||||||
|
IsRelay: s.IsRelay,
|
||||||
|
IsPublic: s.IsPublic,
|
||||||
|
}
|
||||||
|
data, err := json.MarshalIndent(j, "", " ")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := os.MkdirAll(filepath.Dir(path), 0700); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return os.WriteFile(path, data, 0600)
|
||||||
|
}
|
||||||
85
peer/new.go
Normal file
85
peer/new.go
Normal file
@@ -0,0 +1,85 @@
|
|||||||
|
package peer
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net/netip"
|
||||||
|
|
||||||
|
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
|
||||||
|
|
||||||
|
"vppn/peer/wginterface"
|
||||||
|
)
|
||||||
|
|
||||||
|
// New constructs an App, creates the WireGuard interface, and starts the
|
||||||
|
// background goroutines (hub poller, multicast, control conn reader).
|
||||||
|
// The caller should invoke Run() to start the event loop.
|
||||||
|
func New(
|
||||||
|
state LocalState,
|
||||||
|
hubURL, apiKey string,
|
||||||
|
ifaceName string,
|
||||||
|
) (*App, error) {
|
||||||
|
a4 := state.VPNIP.As4()
|
||||||
|
if err := wginterface.Create(ifaceName, a4[:], 24); err != nil {
|
||||||
|
return nil, fmt.Errorf("create WG interface: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
dev, err := wginterface.Open(ifaceName)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("open WG device: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := dev.Configure(state.PrivKey, int(state.WGPort)); err != nil {
|
||||||
|
return nil, fmt.Errorf("configure WG device: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if state.IsRelay {
|
||||||
|
if err := dev.EnableForwarding(); err != nil {
|
||||||
|
return nil, fmt.Errorf("enable forwarding: %w", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cc, err := newUDPControlConn(state.VPNIP, ControlPort)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("control conn: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
pingCh := make(chan PingEvent)
|
||||||
|
hubAddCh := make(chan HubPeer)
|
||||||
|
hubRemoveCh := make(chan wgtypes.Key)
|
||||||
|
multicastCh := make(chan MulticastEvent)
|
||||||
|
|
||||||
|
poller, err := NewHubPoller(
|
||||||
|
state.VPNIP, state.VPNNet,
|
||||||
|
hubURL, apiKey,
|
||||||
|
hubAddCh, hubRemoveCh,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("hub poller: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
go cc.run(pingCh)
|
||||||
|
go poller.Run()
|
||||||
|
go RunMCWriter(state.VPNIP, state.PrivKey.PublicKey(), state.WGPort)
|
||||||
|
if !state.IsPublic {
|
||||||
|
go RunMCReader(state.VPNNet, state.VPNIP, multicastCh)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &App{
|
||||||
|
vpnIP: state.VPNIP,
|
||||||
|
vpnNet: state.VPNNet,
|
||||||
|
privKey: state.PrivKey,
|
||||||
|
pubKey: state.PrivKey.PublicKey(),
|
||||||
|
isRelay: state.IsRelay,
|
||||||
|
isPublic: state.IsPublic,
|
||||||
|
|
||||||
|
dev: dev,
|
||||||
|
controlConn: cc,
|
||||||
|
|
||||||
|
peersByKey: make(map[wgtypes.Key]*Peer),
|
||||||
|
peersByIP: make(map[netip.Addr]*Peer),
|
||||||
|
|
||||||
|
hubAddCh: hubAddCh,
|
||||||
|
hubRemoveCh: hubRemoveCh,
|
||||||
|
pingCh: pingCh,
|
||||||
|
multicastCh: multicastCh,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user