Cleanup
This commit is contained in:
parent
5d97cccb98
commit
f0076939d5
39
README.md
39
README.md
@ -1,41 +1,8 @@
|
|||||||
# vppn: Virtual Pretty Private Network
|
# vppn: Virtual Pretty Private Network
|
||||||
|
|
||||||
## Roadmap
|
## TODO
|
||||||
|
|
||||||
* Use probe and relayed-probe packets vs ping/pong.
|
* Add `-force-init` argument to `node` main?
|
||||||
* Rename Mediator -> Relay
|
|
||||||
* Use default port 456
|
|
||||||
* Remove signing key from hub
|
|
||||||
* Peer: UDP hole-punching
|
|
||||||
* Peer: local peer discovery - part of RoutingProcessor
|
|
||||||
* Peer: update hub w/ latest port on startup
|
|
||||||
|
|
||||||
## Learnings
|
|
||||||
|
|
||||||
* Encryption / decryption is 20x faster than signing/opening.
|
|
||||||
* Allowing out-of order packets is massively important for throughput with TCP
|
|
||||||
|
|
||||||
## Principles
|
|
||||||
|
|
||||||
* Creates an IPv4/24 network with a maximum of 254 peers. (1-254)
|
|
||||||
* Simple setup: via setup link from the hub.
|
|
||||||
* Each peer has full network state replicated from the hub.
|
|
||||||
|
|
||||||
## Routing
|
|
||||||
|
|
||||||
* Routing is different for public vs non-public peers
|
|
||||||
* Public: routes are initialized via incoming ping requests
|
|
||||||
* NonPub: routes are initialized via incoming ping responses
|
|
||||||
|
|
||||||
A non-public peer needs to maintain connections with every public peer.
|
|
||||||
|
|
||||||
* Sending:
|
|
||||||
* Public: send to address
|
|
||||||
* Non-public: send to a mediator
|
|
||||||
|
|
||||||
* Pings:
|
|
||||||
* Servers don't need to ping
|
|
||||||
* Clients need to ping all public and local peers to keep connections open
|
|
||||||
|
|
||||||
## Hub Server Configuration
|
## Hub Server Configuration
|
||||||
|
|
||||||
@ -106,7 +73,7 @@ AmbientCapabilities=CAP_NET_BIND_SERVICE CAP_NET_ADMIN
|
|||||||
Type=simple
|
Type=simple
|
||||||
User=user
|
User=user
|
||||||
WorkingDirectory=/home/user/
|
WorkingDirectory=/home/user/
|
||||||
ExecStart=/home/user/vppn -name vppn
|
ExecStart=/home/user/vppn -name vppn -hub-address https://my.hub -api-key 1234567890
|
||||||
Restart=always
|
Restart=always
|
||||||
RestartSec=8
|
RestartSec=8
|
||||||
TimeoutStopSec=24
|
TimeoutStopSec=24
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
package api
|
package api
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/rand"
|
|
||||||
"database/sql"
|
"database/sql"
|
||||||
"embed"
|
"embed"
|
||||||
"errors"
|
"errors"
|
||||||
@ -14,17 +13,14 @@ import (
|
|||||||
"git.crumpington.com/lib/go/idgen"
|
"git.crumpington.com/lib/go/idgen"
|
||||||
"git.crumpington.com/lib/go/sqliteutil"
|
"git.crumpington.com/lib/go/sqliteutil"
|
||||||
"golang.org/x/crypto/bcrypt"
|
"golang.org/x/crypto/bcrypt"
|
||||||
"golang.org/x/crypto/nacl/box"
|
|
||||||
"golang.org/x/crypto/nacl/sign"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
//go:embed migrations
|
//go:embed migrations
|
||||||
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
|
||||||
initIntents map[string]byte // Map from intent key to peer IP
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func New(dbPath string) (*API, error) {
|
func New(dbPath string) (*API, error) {
|
||||||
@ -38,8 +34,7 @@ func New(dbPath string) (*API, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
a := &API{
|
a := &API{
|
||||||
db: sqlDB,
|
db: sqlDB,
|
||||||
initIntents: map[string]byte{},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return a, a.ensurePassword()
|
return a, a.ensurePassword()
|
||||||
@ -151,55 +146,13 @@ func (a *API) Peer_CreateNew(p *Peer) error {
|
|||||||
return db.Peer_Insert(a.db, p)
|
return db.Peer_Insert(a.db, p)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create the intention to initialize a peer. The returned code is used to
|
func (a *API) Peer_Init(peer *Peer, args m.PeerInitArgs) (*m.PeerConfig, error) {
|
||||||
// complete the peer initialization. The code is valid for 5 minutes.
|
|
||||||
func (a *API) Peer_CreateInitIntent(peerIP byte) string {
|
|
||||||
a.lock.Lock()
|
a.lock.Lock()
|
||||||
defer a.lock.Unlock()
|
defer a.lock.Unlock()
|
||||||
|
|
||||||
code := idgen.NewToken()
|
|
||||||
a.initIntents[code] = peerIP
|
|
||||||
|
|
||||||
go func() {
|
|
||||||
time.Sleep(5 * time.Minute)
|
|
||||||
a.lock.Lock()
|
|
||||||
defer a.lock.Unlock()
|
|
||||||
delete(a.initIntents, code)
|
|
||||||
}()
|
|
||||||
|
|
||||||
return code
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *API) Peer_Init(initCode string) (*m.PeerConfig, error) {
|
|
||||||
a.lock.Lock()
|
|
||||||
defer a.lock.Unlock()
|
|
||||||
|
|
||||||
ip, ok := a.initIntents[initCode]
|
|
||||||
if !ok {
|
|
||||||
return nil, ErrNotAuthorized
|
|
||||||
}
|
|
||||||
|
|
||||||
peer, err := a.Peer_Get(ip)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
delete(a.initIntents, initCode)
|
|
||||||
|
|
||||||
encPubKey, encPrivKey, err := box.GenerateKey(rand.Reader)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
signPubKey, signPrivKey, err := sign.GenerateKey(rand.Reader)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
peer.Version = idgen.NextID(0)
|
peer.Version = idgen.NextID(0)
|
||||||
peer.APIKey = idgen.NewToken()
|
peer.PubKey = args.EncPubKey
|
||||||
peer.PubKey = encPubKey[:]
|
peer.PubSignKey = args.PubSignKey
|
||||||
peer.PubSignKey = signPubKey[:]
|
|
||||||
|
|
||||||
if err := db.Peer_UpdateFull(a.db, peer); err != nil {
|
if err := db.Peer_UpdateFull(a.db, peer); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -208,17 +161,11 @@ func (a *API) Peer_Init(initCode string) (*m.PeerConfig, error) {
|
|||||||
conf := a.Config_Get()
|
conf := a.Config_Get()
|
||||||
|
|
||||||
return &m.PeerConfig{
|
return &m.PeerConfig{
|
||||||
PeerIP: peer.PeerIP,
|
PeerIP: peer.PeerIP,
|
||||||
HubAddress: conf.HubAddress,
|
Network: conf.VPNNetwork,
|
||||||
APIKey: peer.APIKey,
|
PublicIP: peer.PublicIP,
|
||||||
Network: conf.VPNNetwork,
|
Port: peer.Port,
|
||||||
PublicIP: peer.PublicIP,
|
Relay: peer.Relay,
|
||||||
Port: peer.Port,
|
|
||||||
Relay: peer.Relay,
|
|
||||||
PubKey: encPubKey[:],
|
|
||||||
PrivKey: encPrivKey[:],
|
|
||||||
PubSignKey: signPubKey[:],
|
|
||||||
PrivSignKey: signPrivKey[:],
|
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -65,13 +65,26 @@ func (app *App) handleSignedIn(pattern string, fn handlerFunc) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
type peerHandlerFunc func(w http.ResponseWriter, r *http.Request) error
|
type peerHandlerFunc func(p *api.Peer, w http.ResponseWriter, r *http.Request) error
|
||||||
|
|
||||||
func (app *App) handlePeer(pattern string, fn peerHandlerFunc) {
|
func (app *App) handlePeer(pattern string, fn peerHandlerFunc) {
|
||||||
wrapped := func(w http.ResponseWriter, r *http.Request) {
|
wrapped := func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
_, apiKey, ok := r.BasicAuth()
|
||||||
|
if !ok {
|
||||||
|
http.Error(w, "Not authorized", http.StatusUnauthorized)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
peer, err := app.api.Peer_GetByAPIKey(apiKey)
|
||||||
|
if err != nil {
|
||||||
|
http.Error(w, "Not authorized", http.StatusUnauthorized)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
r.ParseForm()
|
r.ParseForm()
|
||||||
if err := fn(w, r); err != nil {
|
if err := fn(peer, w, r); err != nil {
|
||||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package hub
|
package hub
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
@ -201,22 +202,6 @@ func (a *App) _adminPeerCreateSubmit(s *api.Session, w http.ResponseWriter, r *h
|
|||||||
return a.redirect(w, r, "/admin/peer/view/?PeerIP=%d", p.PeerIP)
|
return a.redirect(w, r, "/admin/peer/view/?PeerIP=%d", p.PeerIP)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *App) _adminPeerInit(s *api.Session, w http.ResponseWriter, r *http.Request) error {
|
|
||||||
var peerIP byte
|
|
||||||
err := webutil.NewFormScanner(r.Form).Scan("PeerIP", &peerIP).Error()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
code := a.api.Peer_CreateInitIntent(peerIP)
|
|
||||||
log.Printf("Got code: %v / %v", peerIP, code)
|
|
||||||
|
|
||||||
return a.render("/admin-peer-init.html", w, struct {
|
|
||||||
Session *api.Session
|
|
||||||
HubAddress string
|
|
||||||
Code string
|
|
||||||
}{s, a.api.Config_Get().HubAddress, code})
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *App) _adminPeerView(s *api.Session, w http.ResponseWriter, r *http.Request) error {
|
func (a *App) _adminPeerView(s *api.Session, w http.ResponseWriter, r *http.Request) error {
|
||||||
var peerIP byte
|
var peerIP byte
|
||||||
err := webutil.NewFormScanner(r.Form).Scan("PeerIP", &peerIP).Error()
|
err := webutil.NewFormScanner(r.Form).Scan("PeerIP", &peerIP).Error()
|
||||||
@ -321,9 +306,13 @@ func (a *App) _adminPeerDeleteSubmit(s *api.Session, w http.ResponseWriter, r *h
|
|||||||
return a.redirect(w, r, "/admin/peer/list/")
|
return a.redirect(w, r, "/admin/peer/list/")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *App) _peerInit(w http.ResponseWriter, r *http.Request) error {
|
func (a *App) _peerInit(peer *api.Peer, w http.ResponseWriter, r *http.Request) error {
|
||||||
code := r.FormValue("Code")
|
args := m.PeerInitArgs{}
|
||||||
conf, err := a.api.Peer_Init(code)
|
if err := json.NewDecoder(r.Body).Decode(&args); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
conf, err := a.api.Peer_Init(peer, args)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -331,31 +320,13 @@ func (a *App) _peerInit(w http.ResponseWriter, r *http.Request) error {
|
|||||||
return a.sendJSON(w, conf)
|
return a.sendJSON(w, conf)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *App) _peerFetchState(w http.ResponseWriter, r *http.Request) error {
|
func (a *App) _peerFetchState(peer *api.Peer, w http.ResponseWriter, r *http.Request) error {
|
||||||
_, apiKey, ok := r.BasicAuth()
|
|
||||||
if !ok {
|
|
||||||
return api.ErrNotAuthorized
|
|
||||||
}
|
|
||||||
|
|
||||||
peer, err := a.api.Peer_GetByAPIKey(apiKey)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
peers, err := a.api.Peer_List()
|
peers, err := a.api.Peer_List()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
conf := a.api.Config_Get()
|
state := m.NetworkState{}
|
||||||
|
|
||||||
state := m.NetworkState{
|
|
||||||
HubAddress: conf.HubAddress,
|
|
||||||
Network: conf.VPNNetwork,
|
|
||||||
PeerIP: peer.PeerIP,
|
|
||||||
PublicIP: peer.PublicIP,
|
|
||||||
Port: peer.Port,
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, p := range peers {
|
for _, p := range peers {
|
||||||
if len(p.PubKey) != 0 {
|
if len(p.PubKey) != 0 {
|
||||||
|
@ -19,13 +19,12 @@ func (a *App) registerRoutes() {
|
|||||||
a.handleSignedIn("GET /admin/peer/hosts/", a._adminHosts)
|
a.handleSignedIn("GET /admin/peer/hosts/", a._adminHosts)
|
||||||
a.handleSignedIn("GET /admin/peer/create/", a._adminPeerCreate)
|
a.handleSignedIn("GET /admin/peer/create/", a._adminPeerCreate)
|
||||||
a.handleSignedIn("POST /admin/peer/create/", a._adminPeerCreateSubmit)
|
a.handleSignedIn("POST /admin/peer/create/", a._adminPeerCreateSubmit)
|
||||||
a.handleSignedIn("GET /admin/peer/init/", a._adminPeerInit)
|
|
||||||
a.handleSignedIn("GET /admin/peer/view/", a._adminPeerView)
|
a.handleSignedIn("GET /admin/peer/view/", a._adminPeerView)
|
||||||
a.handleSignedIn("GET /admin/peer/edit/", a._adminPeerEdit)
|
a.handleSignedIn("GET /admin/peer/edit/", a._adminPeerEdit)
|
||||||
a.handleSignedIn("POST /admin/peer/edit/", a._adminPeerEditSubmit)
|
a.handleSignedIn("POST /admin/peer/edit/", a._adminPeerEditSubmit)
|
||||||
a.handleSignedIn("GET /admin/peer/delete/", a._adminPeerDelete)
|
a.handleSignedIn("GET /admin/peer/delete/", a._adminPeerDelete)
|
||||||
a.handleSignedIn("POST /admin/peer/delete/", a._adminPeerDeleteSubmit)
|
a.handleSignedIn("POST /admin/peer/delete/", a._adminPeerDeleteSubmit)
|
||||||
|
|
||||||
a.handlePeer("GET /peer/init/", a._peerInit)
|
a.handlePeer("POST /peer/init/", a._peerInit)
|
||||||
a.handlePeer("GET /peer/fetch-state/", a._peerFetchState)
|
a.handlePeer("GET /peer/fetch-state/", a._peerFetchState)
|
||||||
}
|
}
|
||||||
|
@ -10,6 +10,7 @@
|
|||||||
<h1>VPPN</h1>
|
<h1>VPPN</h1>
|
||||||
<nav>
|
<nav>
|
||||||
{{if .Session.SignedIn -}}
|
{{if .Session.SignedIn -}}
|
||||||
|
<a href="/admin/config/">Home</a> /
|
||||||
<a href="/admin/sign-out/">Sign out</a>
|
<a href="/admin/sign-out/">Sign out</a>
|
||||||
{{- end}}
|
{{- end}}
|
||||||
</nav>
|
</nav>
|
||||||
|
30
m/models.go
30
m/models.go
@ -1,18 +1,17 @@
|
|||||||
// The package `m` contains models shared between the hub and peer programs.
|
// The package `m` contains models shared between the hub and peer programs.
|
||||||
package m
|
package m
|
||||||
|
|
||||||
|
type PeerInitArgs struct {
|
||||||
|
EncPubKey []byte
|
||||||
|
PubSignKey []byte
|
||||||
|
}
|
||||||
|
|
||||||
type PeerConfig struct {
|
type PeerConfig struct {
|
||||||
PeerIP byte
|
PeerIP byte
|
||||||
HubAddress string
|
Network []byte
|
||||||
Network []byte
|
PublicIP []byte
|
||||||
APIKey string
|
Port uint16
|
||||||
PublicIP []byte
|
Relay bool
|
||||||
Port uint16
|
|
||||||
Relay bool
|
|
||||||
PubKey []byte
|
|
||||||
PrivKey []byte
|
|
||||||
PubSignKey []byte
|
|
||||||
PrivSignKey []byte
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type Peer struct {
|
type Peer struct {
|
||||||
@ -27,14 +26,5 @@ type Peer struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type NetworkState struct {
|
type NetworkState struct {
|
||||||
HubAddress string
|
|
||||||
|
|
||||||
// The requester's data:
|
|
||||||
Network []byte
|
|
||||||
PeerIP byte
|
|
||||||
PublicIP []byte
|
|
||||||
Port uint16
|
|
||||||
|
|
||||||
// All peer data.
|
|
||||||
Peers [256]*Peer
|
Peers [256]*Peer
|
||||||
}
|
}
|
||||||
|
@ -56,7 +56,7 @@ func storeJson(x any, outPath string) error {
|
|||||||
return os.Rename(tmpPath, outPath)
|
return os.Rename(tmpPath, outPath)
|
||||||
}
|
}
|
||||||
|
|
||||||
func storePeerConfig(netName string, pc m.PeerConfig) error {
|
func storePeerConfig(netName string, pc localConfig) error {
|
||||||
return storeJson(pc, peerConfigPath(netName))
|
return storeJson(pc, peerConfigPath(netName))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -73,7 +73,7 @@ func loadJson(dataPath string, ptr any) error {
|
|||||||
return json.Unmarshal(data, ptr)
|
return json.Unmarshal(data, ptr)
|
||||||
}
|
}
|
||||||
|
|
||||||
func loadPeerConfig(netName string) (pc m.PeerConfig, err error) {
|
func loadPeerConfig(netName string) (pc localConfig, err error) {
|
||||||
return pc, loadJson(peerConfigPath(netName), &pc)
|
return pc, loadJson(peerConfigPath(netName), &pc)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3,6 +3,7 @@ package node
|
|||||||
import (
|
import (
|
||||||
"net"
|
"net"
|
||||||
"net/netip"
|
"net/netip"
|
||||||
|
"net/url"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
@ -33,6 +34,9 @@ type peerRoute struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
hubURL *url.URL
|
||||||
|
apiKey string
|
||||||
|
|
||||||
// Configuration for this peer.
|
// Configuration for this peer.
|
||||||
netName string
|
netName string
|
||||||
localIP byte
|
localIP byte
|
||||||
|
@ -5,7 +5,6 @@ import (
|
|||||||
"io"
|
"io"
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
|
||||||
"time"
|
"time"
|
||||||
"vppn/m"
|
"vppn/m"
|
||||||
)
|
)
|
||||||
@ -16,21 +15,18 @@ type hubPoller struct {
|
|||||||
versions [256]int64
|
versions [256]int64
|
||||||
}
|
}
|
||||||
|
|
||||||
func newHubPoller(conf m.PeerConfig) *hubPoller {
|
func newHubPoller() *hubPoller {
|
||||||
u, err := url.Parse(conf.HubAddress)
|
u := *hubURL
|
||||||
if err != nil {
|
|
||||||
log.Fatalf("Failed to parse hub address %s: %v", conf.HubAddress, err)
|
|
||||||
}
|
|
||||||
u.Path = "/peer/fetch-state/"
|
u.Path = "/peer/fetch-state/"
|
||||||
|
|
||||||
client := &http.Client{Timeout: 8 * time.Second}
|
client := &http.Client{Timeout: 8 * time.Second}
|
||||||
|
|
||||||
req := &http.Request{
|
req := &http.Request{
|
||||||
Method: http.MethodGet,
|
Method: http.MethodGet,
|
||||||
URL: u,
|
URL: &u,
|
||||||
Header: http.Header{},
|
Header: http.Header{},
|
||||||
}
|
}
|
||||||
req.SetBasicAuth("", conf.APIKey)
|
req.SetBasicAuth("", apiKey)
|
||||||
|
|
||||||
return &hubPoller{
|
return &hubPoller{
|
||||||
client: client,
|
client: client,
|
||||||
@ -71,7 +67,7 @@ func (hp *hubPoller) pollHub() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if err := json.Unmarshal(body, &state); err != nil {
|
if err := json.Unmarshal(body, &state); err != nil {
|
||||||
log.Printf("Failed to unmarshal response from hub: %v", err)
|
log.Printf("Failed to unmarshal response from hub: %v\n%s", err, body)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
90
node/main.go
90
node/main.go
@ -1,6 +1,8 @@
|
|||||||
package node
|
package node
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
|
"crypto/rand"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
@ -9,10 +11,14 @@ import (
|
|||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/netip"
|
"net/netip"
|
||||||
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
"runtime/debug"
|
"runtime/debug"
|
||||||
"time"
|
"time"
|
||||||
"vppn/m"
|
"vppn/m"
|
||||||
|
|
||||||
|
"golang.org/x/crypto/nacl/box"
|
||||||
|
"golang.org/x/crypto/nacl/sign"
|
||||||
)
|
)
|
||||||
|
|
||||||
func panicHandler() {
|
func panicHandler() {
|
||||||
@ -24,33 +30,61 @@ func panicHandler() {
|
|||||||
func Main() {
|
func Main() {
|
||||||
defer panicHandler()
|
defer panicHandler()
|
||||||
|
|
||||||
var (
|
var hubAddress string
|
||||||
initURL string
|
|
||||||
listenIP string
|
|
||||||
)
|
|
||||||
|
|
||||||
flag.StringVar(&netName, "name", "", "[REQUIRED] The network name.")
|
flag.StringVar(&netName, "name", "", "[REQUIRED] The network name.")
|
||||||
flag.StringVar(&initURL, "init-url", "", "Initializes peer from the hub URL.")
|
flag.StringVar(&hubAddress, "hub-address", "", "[REQUIRED] The hub address.")
|
||||||
flag.StringVar(&listenIP, "listen-ip", "", "IP address to listen on.")
|
flag.StringVar(&apiKey, "api-key", "", "[REQUIRED] The node's API key.")
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
|
|
||||||
if netName == "" {
|
if netName == "" || hubAddress == "" || apiKey == "" {
|
||||||
flag.Usage()
|
flag.Usage()
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
if initURL != "" {
|
var err error
|
||||||
mainInit(initURL)
|
|
||||||
return
|
hubURL, err = url.Parse(hubAddress)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("Failed to parse hub address: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
main(listenIP)
|
main()
|
||||||
}
|
}
|
||||||
|
|
||||||
func mainInit(initURL string) {
|
func initPeerWithHub() {
|
||||||
resp, err := http.Get(initURL)
|
encPubKey, encPrivKey, err := box.GenerateKey(rand.Reader)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("Failed to fetch data from hub: %v", err)
|
log.Fatalf("Failed to generate encryption keys: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
signPubKey, signPrivKey, err := sign.GenerateKey(rand.Reader)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("Failed to generate signing keys: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
initURL := *hubURL
|
||||||
|
initURL.Path = "/peer/init/"
|
||||||
|
|
||||||
|
args := m.PeerInitArgs{
|
||||||
|
EncPubKey: encPubKey[:],
|
||||||
|
PubSignKey: signPubKey[:],
|
||||||
|
}
|
||||||
|
|
||||||
|
buf := &bytes.Buffer{}
|
||||||
|
if err := json.NewEncoder(buf).Encode(args); err != nil {
|
||||||
|
log.Fatalf("Failed to encode init args: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
req, err := http.NewRequest(http.MethodPost, initURL.String(), buf)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("Failed to construct request: %v", err)
|
||||||
|
}
|
||||||
|
req.SetBasicAuth("", apiKey)
|
||||||
|
|
||||||
|
resp, err := http.DefaultClient.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("Failed to init with hub: %v", err)
|
||||||
}
|
}
|
||||||
defer resp.Body.Close()
|
defer resp.Body.Close()
|
||||||
|
|
||||||
@ -59,11 +93,16 @@ func mainInit(initURL string) {
|
|||||||
log.Fatalf("Failed to read response body: %v", err)
|
log.Fatalf("Failed to read response body: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
peerConfig := m.PeerConfig{}
|
peerConfig := localConfig{}
|
||||||
if err := json.Unmarshal(data, &peerConfig); err != nil {
|
if err := json.Unmarshal(data, &peerConfig.PeerConfig); err != nil {
|
||||||
log.Fatalf("Failed to parse configuration: %v", err)
|
log.Fatalf("Failed to parse configuration: %v\n%s", err, data)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
peerConfig.PubKey = encPubKey[:]
|
||||||
|
peerConfig.PrivKey = encPrivKey[:]
|
||||||
|
peerConfig.PubSignKey = signPubKey[:]
|
||||||
|
peerConfig.PrivSignKey = signPrivKey[:]
|
||||||
|
|
||||||
if err := storePeerConfig(netName, peerConfig); err != nil {
|
if err := storePeerConfig(netName, peerConfig); err != nil {
|
||||||
log.Fatalf("Failed to store configuration: %v", err)
|
log.Fatalf("Failed to store configuration: %v", err)
|
||||||
}
|
}
|
||||||
@ -73,10 +112,17 @@ func mainInit(initURL string) {
|
|||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
|
|
||||||
func main(listenIP string) {
|
func main() {
|
||||||
config, err := loadPeerConfig(netName)
|
config, err := loadPeerConfig(netName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("Failed to load configuration: %v", err)
|
log.Printf("Failed to load configuration: %v", err)
|
||||||
|
log.Printf("Initializing...")
|
||||||
|
initPeerWithHub()
|
||||||
|
|
||||||
|
config, err = loadPeerConfig(netName)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("Failed to load configuration: %v", err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
iface, err := openInterface(config.Network, config.PeerIP, netName)
|
iface, err := openInterface(config.Network, config.PeerIP, netName)
|
||||||
@ -84,7 +130,7 @@ func main(listenIP string) {
|
|||||||
log.Fatalf("Failed to open interface: %v", err)
|
log.Fatalf("Failed to open interface: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
myAddr, err := net.ResolveUDPAddr("udp", fmt.Sprintf("%s:%d", listenIP, config.Port))
|
myAddr, err := net.ResolveUDPAddr("udp", fmt.Sprintf(":%d", config.Port))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("Failed to resolve UDP address: %v", err)
|
log.Fatalf("Failed to resolve UDP address: %v", err)
|
||||||
}
|
}
|
||||||
@ -137,7 +183,7 @@ func main(listenIP string) {
|
|||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
go newHubPoller(config).Run()
|
go newHubPoller().Run()
|
||||||
go readFromConn(conn)
|
go readFromConn(conn)
|
||||||
readFromIFace(iface)
|
readFromIFace(iface)
|
||||||
}
|
}
|
||||||
@ -258,7 +304,7 @@ func handleDataPacket(h header, data []byte, decBuf []byte) {
|
|||||||
|
|
||||||
destRoute := routingTable[h.DestIP].Load()
|
destRoute := routingTable[h.DestIP].Load()
|
||||||
if !destRoute.Up {
|
if !destRoute.Up {
|
||||||
log.Printf("Not connected (relay): %v", destRoute)
|
log.Printf("Not connected (relay): %d", destRoute.IP)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -214,7 +214,7 @@ func (s *peerSupervisor) server() stateFunc {
|
|||||||
s.sendControlPacketTo(probePacket{TraceID: msg.Packet.TraceID}, msg.SrcAddr)
|
s.sendControlPacketTo(probePacket{TraceID: msg.Packet.TraceID}, msg.SrcAddr)
|
||||||
|
|
||||||
case pingTimerMsg:
|
case pingTimerMsg:
|
||||||
if time.Since(lastSeen) > timeoutInterval {
|
if time.Since(lastSeen) > timeoutInterval && s.staged.Up {
|
||||||
logf("Connection timeout")
|
logf("Connection timeout")
|
||||||
s.staged.Up = false
|
s.staged.Up = false
|
||||||
s.publish()
|
s.publish()
|
||||||
|
Loading…
x
Reference in New Issue
Block a user