You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

289 lines
5.8 KiB

package cluster
import (
"bytes"
"errors"
"fmt"
"os"
"darvaza.org/core"
"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
out wireguard.KeyPair
)
data, err = m.ReadFile("wg%v.key", ring)
if err != nil {
// failed to read
return out, err
}
out.PrivateKey, err = wireguard.PrivateKeyFromBase64(string(data))
if err != nil {
// bad key
err = core.Wrap(err, "wg%v.key", ring)
return out, 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 out, err
default:
// good read
out.PublicKey, err = wireguard.PublicKeyFromBase64(string(data))
if err != nil {
// bad key
err = core.Wrap(err, "wg%v.pub", ring)
return out, err
}
}
err = out.Validate()
return out, err
}
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)
}
}
// RemoveWireguardKeys deletes wgN.key and wgN.pub from
// the machine's config directory
func (m *Machine) RemoveWireguardKeys(ring int) error {
var err error
err = m.RemoveFile("wg%v.pub", ring)
switch {
case os.IsNotExist(err):
// ignore
case err != nil:
return err
}
err = m.RemoveFile("wg%v.key", ring)
if os.IsNotExist(err) {
// ignore
err = nil
}
return err
}
// GetWireguardConfig reads a wgN.conf file
func (m *Machine) GetWireguardConfig(ring int) (*wireguard.Config, error) {
data, err := m.ReadFile("wg%v.conf", ring)
if err != nil {
return nil, err
}
r := bytes.NewReader(data)
return wireguard.NewConfigFromReader(r)
}
func (m *Machine) tryApplyWireguardConfig(ring int) error {
wg, err := m.GetWireguardConfig(ring)
switch {
case os.IsNotExist(err):
return nil
case err != nil:
return err
default:
return m.applyWireguardConfig(ring, wg)
}
}
func (m *Machine) applyWireguardConfigNode(ring int, wg *wireguard.Config) error {
addr := wg.GetAddress()
if !core.IsZero(addr) {
zoneID, nodeID, ok := Rings[ring].Decode(addr)
if !ok {
return fmt.Errorf("%s: invalid address", addr)
}
if err := m.applyZoneNodeID(zoneID, nodeID); err != nil {
return core.Wrap(err, "%s: invalid address", addr)
}
}
if err := m.applyWireguardInterfaceConfig(ring, wg.Interface); err != nil {
return core.Wrap(err, "interface")
}
return nil
}
func (m *Machine) applyWireguardConfig(ring int, wg *wireguard.Config) error {
if err := m.applyWireguardConfigNode(ring, wg); err != nil {
return err
}
for _, peer := range wg.Peer {
err := m.applyWireguardPeerConfig(ring, peer)
switch {
case errors.Is(err, ErrUnknownNode):
// ignore unknown peers
m.warn(nil).
WithField("subsystem", "wireguard").
WithField("node", m.Name).
WithField("peer", peer.Endpoint.Host).
WithField("ring", ring).
Print("ignoring unknown endpoint")
case err != nil:
return core.Wrap(err, "peer")
}
}
return nil
}
func (m *Machine) getRingInfo(ring int) (*RingInfo, bool) {
for _, ri := range m.Rings {
if ri.Ring == ring {
return ri, ri.Enabled
}
}
return nil, false
}
func (m *Machine) applyRingInfo(ring int, new *RingInfo) error {
cur, _ := m.getRingInfo(ring)
if cur == nil {
// first, append
m.debug().
WithField("node", m.Name).
WithField("ring", ring).
Print("found")
m.Rings = append(m.Rings, new)
return nil
}
// extra, merge
return cur.Merge(new)
}
func (m *Machine) applyWireguardInterfaceConfig(ring int, data wireguard.InterfaceConfig) error {
ri := &RingInfo{
Ring: ring,
Enabled: true,
Keys: wireguard.KeyPair{
PrivateKey: data.PrivateKey,
},
}
return m.applyRingInfo(ring, ri)
}
func (m *Machine) applyWireguardPeerConfig(ring int, pc wireguard.PeerConfig) error {
peer, found := m.getPeerByName(pc.Endpoint.Name())
switch {
case !found:
// unknown
return core.Wrap(ErrUnknownNode, pc.Endpoint.Host)
case ring == 1 && m.zone != peer.zone:
// invalid zone
return core.Wrap(ErrInvalidNode, peer.Name)
default:
// apply RingInfo
ri := &RingInfo{
Ring: ring,
Enabled: true,
Keys: wireguard.KeyPair{
PublicKey: pc.PublicKey,
},
}
return peer.applyRingInfo(ring, ri)
}
}
func (m *Machine) applyZoneNodeID(zoneID, nodeID int) error {
switch {
case zoneID == 0:
return fmt.Errorf("invalid %s", "zoneID")
case nodeID == 0:
return fmt.Errorf("invalid %s", "nodeID")
case m.ID != nodeID:
return fmt.Errorf("invalid %s: %v ≠ %v", "zoneID", m.ID, nodeID)
case m.zone.ID != 0 && m.zone.ID != zoneID:
return fmt.Errorf("invalid %s: %v ≠ %v", "zoneID", m.zone.ID, zoneID)
case m.zone.ID == 0:
m.zone.ID = zoneID
}
return nil
}
func (m *Machine) setRingDefaults(ri *RingInfo) error {
if ri.Keys.PrivateKey.IsZero() {
m.info().
WithField("subsystem", "wireguard").
WithField("node", m.Name).
WithField("ring", ri.Ring).
Print("generating key pair")
kp, err := wireguard.NewKeyPair()
if err != nil {
return err
}
ri.Keys = kp
}
return nil
}
// RemoveWireguardConfig deletes wgN.conf from the machine's
// config directory.
func (m *Machine) RemoveWireguardConfig(ring int) error {
err := m.RemoveFile("wg%v.conf", ring)
if os.IsNotExist(err) {
err = nil
}
return err
}
func (m *Machine) createRingInfo(ring int, enabled bool) (*RingInfo, error) {
keys, err := wireguard.NewKeyPair()
if err != nil {
return nil, err
}
ri := &RingInfo{
Ring: ring,
Enabled: enabled,
Keys: keys,
}
err = m.applyRingInfo(ring, ri)
if err != nil {
return nil, err
}
return ri, nil
}