Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Error cascade clean #93

Merged
merged 7 commits into from
Aug 13, 2024
46 changes: 34 additions & 12 deletions cmd/cli/add.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,23 +9,35 @@ import (
)

// Add prompts for the required information and creates a new peer
func Add(hostname string, privKey, pubKey bool, owner, description string, confirm bool) {
func Add(hostname string, privKey, pubKey bool, owner, description string, confirm bool) error {
config, err := LoadConfigFile()
check(err, "failed to load configuration file")
if err != nil {
return fmt.Errorf("%w - failed to load configuration file", err)
}
server := GetServer(config)

var private, public string
if privKey {
private = MustPromptString("private key", true)
if private, err = PromptString("private key", true); err != nil {
return err
}
}
if pubKey {
public = MustPromptString("public key", true)
if public, err = PromptString("public key", true); err != nil {
return err
}
}
if owner == "" {
owner = MustPromptString("owner", true)
owner, err = PromptString("owner", true)
if err != nil {
return fmt.Errorf("%w - invalid input for owner", err)
}
}
if description == "" {
description = MustPromptString("Description", true)
description, err = PromptString("Description", true)
if err != nil {
return fmt.Errorf("%w - invalid input for Description", err)
}
}

// publicKey := MustPromptString("PublicKey (optional)", false)
Expand All @@ -37,22 +49,32 @@ func Add(hostname string, privKey, pubKey bool, owner, description string, confi
fmt.Fprintln(os.Stderr)

peer, err := lib.NewPeer(server, private, public, owner, hostname, description)
check(err, "failed to get new peer")
if err != nil {
return fmt.Errorf("%w - failed to get new peer", err)
}

// TODO Some kind of recovery here would be nice, to avoid
// leaving things in a potential broken state

config.MustAddPeer(peer)
if err = config.AddPeer(peer); err != nil {
return fmt.Errorf("%w - failed to add new peer", err)
}

peerType := viper.GetString("output")

peerConfigBytes, err := lib.AsciiPeerConfig(peer, peerType, *server)
check(err, "failed to get peer configuration")
if err != nil {
return fmt.Errorf("%w - failed to get peer configuration", err)
}
os.Stdout.Write(peerConfigBytes.Bytes())

config.MustSave()
if err = config.Save(); err != nil {
return fmt.Errorf("%w - failed to save config file", err)
}

server = GetServer(config)
err = server.ConfigureDevice()
check(err, "failed to configure device")
if err = server.ConfigureDevice(); err != nil {
return fmt.Errorf("%w - failed to configure device", err)
}
return nil
}
24 changes: 0 additions & 24 deletions cmd/cli/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -106,12 +106,6 @@ func LoadConfigFile() (*DsnetConfig, error) {
return &conf, nil
}

func MustLoadConfigFile() *DsnetConfig {
config, err := LoadConfigFile()
check(err, "failed to load configuration file")
return config
}

// Save writes the configuration to disk
func (conf *DsnetConfig) Save() error {
configFile := viper.GetString("config_file")
Expand All @@ -124,12 +118,6 @@ func (conf *DsnetConfig) Save() error {
return nil
}

// MustSave is like Save except it exits on error
func (conf *DsnetConfig) MustSave() {
err := conf.Save()
check(err, "failed to save config file")
}

// AddPeer adds a provided peer to the Peers list in the conf
func (conf *DsnetConfig) AddPeer(peer lib.Peer) error {
// TODO validate all PeerConfig (keys etc)
Expand Down Expand Up @@ -169,12 +157,6 @@ func (conf *DsnetConfig) AddPeer(peer lib.Peer) error {
return nil
}

// MustAddPeer is like AddPeer, except it exist on error
func (conf *DsnetConfig) MustAddPeer(peer lib.Peer) {
err := conf.AddPeer(peer)
check(err)
}

// RemovePeer removes a peer from the peer list based on hostname
func (conf *DsnetConfig) RemovePeer(hostname string) error {
peerIndex := -1
Expand All @@ -195,12 +177,6 @@ func (conf *DsnetConfig) RemovePeer(hostname string) error {
return nil
}

// MustRemovePeer is like RemovePeer, except it exits on error
func (conf *DsnetConfig) MustRemovePeer(hostname string) {
err := conf.RemovePeer(hostname)
check(err)
}

func (conf DsnetConfig) GetWgPeerConfigs() []wgtypes.PeerConfig {
wgPeers := make([]wgtypes.PeerConfig, 0, len(conf.Peers))

Expand Down
54 changes: 38 additions & 16 deletions cmd/cli/init.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,22 +15,31 @@ import (
"github.com/spf13/viper"
)

func Init() {
func Init() error {
listenPort := viper.GetInt("listen_port")
configFile := viper.GetString("config_file")
interfaceName := viper.GetString("interface_name")

_, err := os.Stat(configFile)

if !os.IsNotExist(err) {
ExitFail("Refusing to overwrite existing %s", configFile)
return fmt.Errorf("%w - Refusing to overwrite existing %s", err, configFile)
}

privateKey, err := lib.GenerateJSONPrivateKey()
check(err, "failed to generate private key")
if err != nil {
return fmt.Errorf("%w - failed to generate private key", err)
}

externalIPV4, err := getExternalIP()
check(err)
if err != nil {
return err
}

externalIPV6, err := getExternalIP6()
if err != nil {
return err
}

conf := &DsnetConfig{
PrivateKey: privateKey,
Expand All @@ -40,7 +49,7 @@ func Init() {
Peers: []PeerConfig{},
Domain: "dsnet",
ExternalIP: externalIPV4,
ExternalIP6: getExternalIP6(),
ExternalIP6: externalIPV6,
InterfaceName: interfaceName,
Networks: []lib.JSONIPNet{},
PersistentKeepalive: 25,
Expand All @@ -49,21 +58,28 @@ func Init() {
server := GetServer(conf)

ipv4, err := server.AllocateIP()
check(err, "failed to allocate ipv4 address")
if err != nil {
return fmt.Errorf("%w - failed to allocate ipv4 address", err)
}

ipv6, err := server.AllocateIP6()
check(err, "failed to allocate ipv6 address")
if err != nil {
return fmt.Errorf("%w - failed to allocate ipv6 address", err)
}

conf.IP = ipv4
conf.IP6 = ipv6

if len(conf.ExternalIP) == 0 && len(conf.ExternalIP6) == 0 {
ExitFail("Could not determine any external IP, v4 or v6")
return fmt.Errorf("Could not determine any external IP, v4 or v6")
}

conf.MustSave()
if err := conf.Save(); err != nil {
return fmt.Errorf("%w - failed to save config file", err)
}

fmt.Printf("Config written to %s. Please check/edit.\n", configFile)
return nil
}

// get a random IPv4 /22 subnet on 10.0.0.0 (1023 hosts) (or /24?)
Expand Down Expand Up @@ -119,20 +135,24 @@ func getExternalIP() (net.IP, error) {
Timeout: 5 * time.Second,
}
resp, err := client.Get("https://ipv4.icanhazip.com/")
check(err)
if err != nil {
return nil, err
}
defer resp.Body.Close()

if resp.StatusCode == http.StatusOK {
body, err := ioutil.ReadAll(resp.Body)
check(err)
if err != nil {
return nil, err
}
IP = net.ParseIP(strings.TrimSpace(string(body)))
return IP.To4(), nil
}

return nil, errors.New("failed to determine external ip")
}

func getExternalIP6() net.IP {
func getExternalIP6() (net.IP, error) {
var IP net.IP
conn, err := net.Dial("udp", "2001:4860:4860::8888:53")
if err == nil {
Expand All @@ -143,7 +163,7 @@ func getExternalIP6() net.IP {

// check is not a ULA
if IP[0] != 0xfd && IP[0] != 0xfc {
return IP
return IP, nil
}
}

Expand All @@ -156,11 +176,13 @@ func getExternalIP6() net.IP {

if resp.StatusCode == http.StatusOK {
body, err := ioutil.ReadAll(resp.Body)
check(err)
if err != nil {
return nil, err
}
IP = net.ParseIP(strings.TrimSpace(string(body)))
return IP
return IP, nil
}
}

return net.IP{}
return net.IP{}, nil
}
34 changes: 25 additions & 9 deletions cmd/cli/regenerate.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,11 @@ import (
"github.com/spf13/viper"
)

func Regenerate(hostname string, confirm bool) {
config := MustLoadConfigFile()
func Regenerate(hostname string, confirm bool) error {
config, err := LoadConfigFile()
if err != nil {
return fmt.Errorf("%w - failure to load config file", err)
}
server := GetServer(config)

found := false
Expand All @@ -21,36 +24,49 @@ func Regenerate(hostname string, confirm bool) {
for _, peer := range server.Peers {
if peer.Hostname == hostname {
privateKey, err := lib.GenerateJSONPrivateKey()
check(err, "failed to generate private key")
if err != nil {
return fmt.Errorf("%w - failed to generate private key", err)
}

preshareKey, err := lib.GenerateJSONKey()
check(err, "failed to generate preshared key")
if err != nil {
return fmt.Errorf("%w - failed to generate preshared key", err)
}

peer.PrivateKey = privateKey
peer.PublicKey = privateKey.PublicKey()
peer.PresharedKey = preshareKey

err = config.RemovePeer(hostname)
check(err, "failed to regenerate peer")
if err != nil {
return fmt.Errorf("%w - failed to regenerate peer", err)
}

peerType := viper.GetString("output")

peerConfigBytes, err := lib.AsciiPeerConfig(peer, peerType, *server)
check(err, "failed to get peer configuration")
if err != nil {
return fmt.Errorf("%w - failed to get peer configuration", err)
}
os.Stdout.Write(peerConfigBytes.Bytes())
found = true
config.MustAddPeer(peer)
if err = config.AddPeer(peer); err != nil {
return fmt.Errorf("%w - failure to add peer", err)
}

break
}
}

if !found {
ExitFail(fmt.Sprintf("unknown hostname: %s", hostname))
return fmt.Errorf("unknown hostname: %s", hostname)
}

// Get a new server configuration so we can update the wg interface with the new peer details
server = GetServer(config)
config.MustSave()
if err = config.Save(); err != nil {
return fmt.Errorf("%w - failure saving config", err)
}
server.ConfigureDevice()
return nil
}
22 changes: 15 additions & 7 deletions cmd/cli/remove.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,27 @@ package cli

import "fmt"

func Remove(hostname string, confirm bool) {
conf := MustLoadConfigFile()
func Remove(hostname string, confirm bool) error {
conf, err := LoadConfigFile()
if err != nil {
return fmt.Errorf("%w - failed to load config", err)
}

err := conf.RemovePeer(hostname)
check(err, "failed to update config")
if err = conf.RemovePeer(hostname); err != nil {
return fmt.Errorf("%w - failed to update config", err)
}

if !confirm {
ConfirmOrAbort("Do you really want to remove %s?", hostname)
}

conf.MustSave()
if err = conf.Save(); err != nil {
return fmt.Errorf("%w - failure to save config", err)
}
server := GetServer(conf)

err = server.ConfigureDevice()
check(err, fmt.Sprintf("failed to sync server config to wg interface: %s", server.InterfaceName))
if err = server.ConfigureDevice(); err != nil {
return fmt.Errorf("%w - failed to sync server config to wg interface: %s", err, server.InterfaceName)
}
return nil
}
Loading
Loading