Compare commits

...

10 Commits

Author SHA1 Message Date
amery 256c58cddc zones: Machine.CreateFile() and Machine.CreateTruncFile()
Signed-off-by: Alejandro Mery <amery@jpi.io>
2023-08-24 14:38:10 +00:00
amery 903e677071 zones: introduce Machine.OpenFile()
Signed-off-by: Alejandro Mery <amery@jpi.io>
2023-08-24 14:38:10 +00:00
amery d0171fd971 zones: introduce Machine.RemoveFile()
Signed-off-by: Alejandro Mery <amery@jpi.io>
2023-08-24 14:38:10 +00:00
amery 999faeb062 zones: switch to using hackpadfs/os.FS as the standard os.FS is incomplete
Signed-off-by: Alejandro Mery <amery@jpi.io>
2023-08-24 14:38:10 +00:00
amery 1abb406465 zones: move Machine.ReadFile to a dedicated machine_file.go
Signed-off-by: Alejandro Mery <amery@jpi.io>
2023-08-24 14:38:10 +00:00
amery 61374d4cc5 zones: load wireguard key pairs on Machine.init()
Signed-off-by: Alejandro Mery <amery@jpi.io>
2023-08-24 14:35:40 +00:00
amery 975e166da7 zones: allow RingInfo.Merge() to enable, but not disable
Signed-off-by: Alejandro Mery <amery@jpi.io>
2023-08-24 14:34:00 +00:00
amery b16c648f2c zones: introduce Machine.GetWireguardKeys()
Signed-off-by: Alejandro Mery <amery@jpi.io>
2023-08-23 21:03:03 +00:00
amery 47d79f7576 wireguard: introduce KeyPair.Validate()
it will also set the PublicKey field is empty

Signed-off-by: Alejandro Mery <amery@jpi.io>
2023-08-23 00:29:15 +00:00
amery e2f831fd6a wireguard: introduce NewKeyPair, NewPrivateKey, and PrivateKey.Public()
Signed-off-by: Alejandro Mery <amery@jpi.io>
2023-08-23 00:18:31 +00:00
10 changed files with 249 additions and 30 deletions
+2
View File
@@ -8,8 +8,10 @@ require (
darvaza.org/sidecar v0.0.0-20230721122716-b9c54b8adbaf
darvaza.org/slog v0.5.2
github.com/burntSushi/toml v0.3.1
github.com/hack-pad/hackpadfs v0.2.1
github.com/mgechev/revive v1.3.2
github.com/spf13/cobra v1.7.0
golang.org/x/crypto v0.12.0
gopkg.in/gcfg.v1 v1.2.3
)
+4
View File
@@ -26,6 +26,8 @@ github.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBD
github.com/fatih/structtag v1.2.0 h1:/OdNE99OxoI/PqaW/SuSK9uxxT3f/tcSZgon/ssNSx4=
github.com/fatih/structtag v1.2.0/go.mod h1:mBJUNpUnHmRKrKlQQlmCrh5PuhftFbNv8Ys4/aAZl94=
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/hack-pad/hackpadfs v0.2.1 h1:FelFhIhv26gyjujoA/yeFO+6YGlqzmc9la/6iKMIxMw=
github.com/hack-pad/hackpadfs v0.2.1/go.mod h1:khQBuCEwGXWakkmq8ZiFUvUZz84ZkJ2KNwKvChs4OrU=
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4=
@@ -70,6 +72,8 @@ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
golang.org/x/crypto v0.12.0 h1:tFM/ta59kqch6LlvYnPa0yx5a83cL2nHflFhYKvv9Yk=
golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw=
golang.org/x/exp v0.0.0-20230713183714-613f0c0eb8a1 h1:MGwJjxBy0HJshjDNfLsYO8xppfqWlA5ZT9OhtUUhTNw=
golang.org/x/mod v0.12.0 h1:rmsUpXtvNzj340zd98LZ4KntptpfRHwpFOHG188oHXc=
golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
+76
View File
@@ -2,8 +2,11 @@ package wireguard
import (
"bytes"
"crypto/rand"
"encoding/base64"
"errors"
"golang.org/x/crypto/curve25519"
)
const (
@@ -16,6 +19,10 @@ const (
var (
// ErrInvalidKeySize indicates the key size is wrong
ErrInvalidKeySize = errors.New("invalid key size")
// ErrInvalidPrivateKey indicates the private key is invalid
ErrInvalidPrivateKey = errors.New("invalid private key")
// ErrInvalidPublicKey indicates the public key is invalid
ErrInvalidPublicKey = errors.New("invalid public key")
)
type (
@@ -89,8 +96,77 @@ func decodeKey(data string, size int) ([]byte, error) {
}
}
// NewPrivateKey creates a new PrivateKey
func NewPrivateKey() (PrivateKey, error) {
var s [PrivateKeySize]byte
_, err := rand.Read(s[:])
if err != nil {
return []byte{}, err
}
// apply same clamping as wireguard-go/device/noise-helpers.go
s[0] &= 0xf8
s[31] = (s[31] & 0x7f) | 0x40
return s[:], nil
}
// Public generates the corresponding PublicKey
func (key PrivateKey) Public() PublicKey {
if len(key) != PrivateKeySize {
return []byte{}
}
out := [PublicKeySize]byte{}
in := (*[PrivateKeySize]byte)(key)
curve25519.ScalarBaseMult(&out, in)
return out[:]
}
// KeyPair holds a Key pair
type KeyPair struct {
PrivateKey PrivateKey
PublicKey PublicKey
}
// Validate checks the PublicKey matches the PrivateKey,
// and sets the PublicKey if missing
func (kp *KeyPair) Validate() error {
keyLen := len(kp.PrivateKey)
pubLen := len(kp.PublicKey)
switch {
case keyLen != PrivateKeySize:
// bad private key
return ErrInvalidPrivateKey
case pubLen == 0:
// no public key, set it
kp.PublicKey = kp.PrivateKey.Public()
return nil
case pubLen != PublicKeySize:
// bad public key
return ErrInvalidPublicKey
case !kp.PrivateKey.Public().Equal(kp.PublicKey):
// wrong public key
return ErrInvalidPublicKey
default:
// correct public key
return nil
}
}
// NewKeyPair creates a new KeyPair for Wireguard
func NewKeyPair() (*KeyPair, error) {
key, err := NewPrivateKey()
if err != nil {
return nil, err
}
out := &KeyPair{
PrivateKey: key,
PublicKey: key.Public(),
}
return out, nil
}
-25
View File
@@ -1,10 +1,7 @@
package zones
import (
"fmt"
"io/fs"
"net/netip"
"path/filepath"
"strconv"
"strings"
"sync"
@@ -61,28 +58,6 @@ func (m *Machine) FullName() string {
return m.Name
}
// ReadFile reads a file from the machine's config directory
func (m *Machine) ReadFile(name string, args ...any) ([]byte, error) {
base := m.zone.zones.dir
fullName := m.getFilename(name, args...)
return fs.ReadFile(base, fullName)
}
func (m *Machine) getFilename(name string, args ...any) string {
if len(args) > 0 {
name = fmt.Sprintf(name, args...)
}
s := []string{
m.zone.Name,
m.Name,
name,
}
return filepath.Join(s...)
}
func (m *Machine) getPeerByName(name string) (*Machine, bool) {
return m.zone.zones.GetMachineByName(name)
}
+63
View File
@@ -0,0 +1,63 @@
package zones
import (
"fmt"
"os"
"path/filepath"
fs "github.com/hack-pad/hackpadfs"
)
// OpenFile opens a file on the machine's config directory with the specified flags
func (m *Machine) OpenFile(name string, flags int, args ...any) (fs.File, error) {
base := m.zone.zones.dir
fullName := m.getFilename(name, args...)
return fs.OpenFile(base, fullName, flags, 0644)
}
// CreateTruncFile creates or truncates a file on the machine's config directory
func (m *Machine) CreateTruncFile(name string, args ...any) (fs.File, error) {
return m.OpenFile(name, os.O_RDWR|os.O_CREATE|os.O_TRUNC, args...)
}
// CreateFile creates a file on the machine's config directory
func (m *Machine) CreateFile(name string, args ...any) (fs.File, error) {
return m.OpenFile(name, os.O_RDWR|os.O_CREATE, args...)
}
// RemoveFile deletes a file from the machine's config directory
func (m *Machine) RemoveFile(name string, args ...any) error {
base := m.zone.zones.dir
fullName := m.getFilename(name, args...)
err := fs.Remove(base, fullName)
switch {
case os.IsNotExist(err):
return nil
default:
return err
}
}
// ReadFile reads a file from the machine's config directory
func (m *Machine) ReadFile(name string, args ...any) ([]byte, error) {
base := m.zone.zones.dir
fullName := m.getFilename(name, args...)
return fs.ReadFile(base, fullName)
}
func (m *Machine) getFilename(name string, args ...any) string {
if len(args) > 0 {
name = fmt.Sprintf(name, args...)
}
s := []string{
m.zone.Name,
m.Name,
name,
}
return filepath.Join(s...)
}
+71
View File
@@ -10,6 +10,77 @@ import (
"git.jpi.io/amery/jpictl/pkg/wireguard"
)
// GetWireguardKeys reads a wgN.key/wgN.pub files
func (m *Machine) GetWireguardKeys(ring int) (*wireguard.KeyPair, error) {
var (
data []byte
err error
key wireguard.PrivateKey
pub wireguard.PublicKey
)
data, err = m.ReadFile("wg%v.key", ring)
if err != nil {
// failed to read
return nil, err
}
key, err = wireguard.PrivateKeyFromBase64(string(data))
if err != nil {
// bad key
err = core.Wrapf(err, "wg%v.key", ring)
return nil, err
}
data, err = m.ReadFile("wg%v.pub", ring)
switch {
case os.IsNotExist(err):
// no wgN.pub is fine
case err != nil:
// failed to read
return nil, err
default:
// good read
pub, err = wireguard.PublicKeyFromBase64(string(data))
if err != nil {
// bad key
err = core.Wrapf(err, "wg%v.pub", ring)
return nil, err
}
}
kp := &wireguard.KeyPair{
PrivateKey: key,
PublicKey: pub,
}
if err = kp.Validate(); err != nil {
return nil, err
}
return kp, nil
}
func (m *Machine) tryReadWireguardKeys(ring int) error {
kp, err := m.GetWireguardKeys(ring)
switch {
case os.IsNotExist(err):
// ignore
return nil
case err != nil:
// something went wrong
return err
default:
// import keys
ri := &RingInfo{
Ring: ring,
Keys: kp,
}
return m.applyRingInfo(ring, ri)
}
}
// GetWireguardConfig reads a wgN.conf file
func (m *Machine) GetWireguardConfig(ring int) (*wireguard.Config, error) {
data, err := m.ReadFile("wg%v.conf", ring)
+9
View File
@@ -25,6 +25,15 @@ func (m *Machine) updatePublicAddresses() error {
return nil
}
func (m *Machine) init() error {
for i := 0; i < RingsCount; i++ {
if err := m.tryReadWireguardKeys(i); err != nil {
return err
}
}
return nil
}
func (m *Machine) scan() error {
for i := 0; i < RingsCount; i++ {
if err := m.tryApplyWireguardConfig(i); err != nil {
+12 -3
View File
@@ -31,9 +31,9 @@ func (ri *RingInfo) Merge(alter *RingInfo) error {
case ri.Ring != alter.Ring:
// different ring
return fmt.Errorf("invalid %s: %v ≠ %v", "ring", ri.Ring, alter.Ring)
case ri.Enabled != alter.Enabled:
// different state
return fmt.Errorf("invalid %s: %v %v", "enabled", ri.Enabled, alter.Enabled)
case ri.Enabled && !alter.Enabled:
// can't disable via Merge
return fmt.Errorf("invalid %s: %v %v", "enabled", ri.Enabled, alter.Enabled)
case !canMergeAddress(ri.Address, alter.Address):
// different address
return fmt.Errorf("invalid %s: %v ≠ %v", "address", ri.Address, alter.Address)
@@ -42,6 +42,15 @@ func (ri *RingInfo) Merge(alter *RingInfo) error {
return fmt.Errorf("invalid %s: %s ≠ %s", "keys", ri.Keys, alter.Keys)
}
return ri.unsafeMerge(alter)
}
func (ri *RingInfo) unsafeMerge(alter *RingInfo) error {
// enable via Merge
if alter.Enabled {
ri.Enabled = true
}
switch {
case ri.Keys == nil:
// assign keypair
+4
View File
@@ -129,6 +129,10 @@ func (z *Zone) scan() error {
Name: e.Name(),
}
if err := m.init(); err != nil {
return err
}
z.Machines = append(z.Machines, m)
}
}
+8 -2
View File
@@ -3,7 +3,8 @@ package zones
import (
"io/fs"
"os"
"github.com/hack-pad/hackpadfs/os"
"darvaza.org/resolver"
)
@@ -104,5 +105,10 @@ func NewFS(dir fs.FS, domain string) (*Zones, error) {
// New builds a [Zones] tree using the given directory
func New(dir, domain string) (*Zones, error) {
return NewFS(os.DirFS(dir), domain)
base, err := os.NewFS().Sub(dir)
if err != nil {
return nil, err
}
return NewFS(base, domain)
}