Skip to content

Commit 4f9299e

Browse files
committed
fix: address remaining review issues from PR #477
- Remove custom wireguard.PrivateKey type; use wgtypes.Key everywhere - Fix ReadOrCreatePrivateKey to write/read base64 format (not raw bytes) - Fix double base64 encoding in enrollgateway.go - Replace fragile manual base64+copy with wgtypes.ParseKey in EnsurePrivateKey - Add strings.TrimSpace when reading key files to handle trailing newlines - Fix error wrapping: %v -> %w for proper errors.Is/As support - Make Configure retry loop context-aware (select on ctx.Done vs time.Sleep) - Clean up Linux interface on partial SetupInterface failure - Make IPv6 address configuration conditional on all platforms - Change GatewayRequest.WireGuardPublicKey from []byte to string
1 parent b01561f commit 4f9299e

11 files changed

Lines changed: 64 additions & 62 deletions

File tree

cmd/apiserver/main.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -158,7 +158,7 @@ func run(log *logrus.Entry, cfg config.Config) error {
158158
}
159159
cfg.WireGuardPrivateKey = key
160160

161-
netConf, err := wg.NewConfigurer(log.WithField("component", "network-configurer"), cfg.WireGuardConfigPath, cfg.WireGuardIPv4Prefix, cfg.WireGuardIPv6Prefix, string(cfg.WireGuardPrivateKey.Private()), "wg0", 51820, nil, nil, nil)
161+
netConf, err := wg.NewConfigurer(log.WithField("component", "network-configurer"), cfg.WireGuardConfigPath, cfg.WireGuardIPv4Prefix, cfg.WireGuardIPv6Prefix, cfg.WireGuardPrivateKey.String(), "wg0", 51820, nil, nil, nil)
162162
if err != nil {
163163
return fmt.Errorf("create WireGuard configurer: %w", err)
164164
}

cmd/gateway-agent/main.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ func run(log *logrus.Entry, cfg config.Config) error {
7979

8080
ecfg, err := enroll.NewGatewayClient(
8181
ctx,
82-
privateKey.Public(),
82+
privateKey.PublicKey().String(),
8383
hashedPassword,
8484
wireguardListenPort,
8585
log.WithField("component", "bootstrap"),
@@ -96,7 +96,7 @@ func run(log *logrus.Entry, cfg config.Config) error {
9696
}
9797

9898
cfg.Name = ecfg.Name
99-
cfg.PrivateKey = string(privateKey.Private())
99+
cfg.PrivateKey = privateKey.String()
100100
cfg.APIServerURL = enrollResp.APIServerGRPCAddress
101101
cfg.DeviceIPv4 = enrollResp.WireGuardIPv4
102102

internal/apiserver/config/config.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import (
1111
"github.com/nais/device/internal/wireguard"
1212
"github.com/nais/device/pkg/pb"
1313
"github.com/sirupsen/logrus"
14+
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
1415
)
1516

1617
type Config struct {
@@ -44,7 +45,7 @@ type Config struct {
4445
WireGuardIP string // for passing in raw string
4546
WireGuardIPv6 string // for passing in raw string
4647
WireGuardConfigPath string
47-
WireGuardPrivateKey wireguard.PrivateKey
48+
WireGuardPrivateKey wgtypes.Key
4849
WireGuardPrivateKeyPath string
4950
WireGuardNetworkAddress string
5051
WireGuardIPv4Prefix *netip.Prefix `ignored:"true"`
@@ -141,7 +142,7 @@ func (cfg *Config) APIServerPeer() *pb.Gateway {
141142

142143
return &pb.Gateway{
143144
Name: "apiserver",
144-
PublicKey: string(cfg.WireGuardPrivateKey.Public()),
145+
PublicKey: cfg.WireGuardPrivateKey.PublicKey().String(),
145146
Endpoint: cfg.Endpoint,
146147
Ipv4: cfg.WireGuardIPv4Prefix.Addr().String(),
147148
Ipv6: ipv6,

internal/controlplane-cli/enrollgateway.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,9 @@ import (
88
"os"
99

1010
"github.com/nais/device/internal/passwordhash"
11-
"github.com/nais/device/internal/wireguard"
1211
"github.com/nais/device/pkg/pb"
1312
"github.com/urfave/cli/v2"
13+
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
1414
"google.golang.org/grpc"
1515
"google.golang.org/grpc/credentials/insecure"
1616
)
@@ -133,18 +133,18 @@ func EnrollGateway(c *cli.Context) error {
133133
key := passwordhash.HashPassword([]byte(password), salt)
134134
passhash := passwordhash.FormatHash(key, salt)
135135

136-
privateKey, err := wireguard.GenKey()
136+
privateKey, err := wgtypes.GeneratePrivateKey()
137137
if err != nil {
138138
return fmt.Errorf("generating private key: %w", err)
139139
}
140-
publicKey := privateKey.Public()
140+
publicKey := privateKey.PublicKey().String()
141141

142142
req := &pb.ModifyGatewayRequest{
143143
Username: AdminUsername,
144144
Password: c.String(FlagAdminPassword),
145145
Gateway: &pb.Gateway{
146146
Name: c.String(FlagName),
147-
PublicKey: string(publicKey),
147+
PublicKey: publicKey,
148148
Endpoint: c.String(FlagEndpoint),
149149
PasswordHash: string(passhash),
150150
},
@@ -177,7 +177,7 @@ func EnrollGateway(c *cli.Context) error {
177177

178178
fmt.Printf("GATEWAY_AGENT_NAME=\"%s\"\n", response.GetGateway().GetName())
179179
fmt.Printf("GATEWAY_AGENT_APISERVERPASSWORD=\"%s\"\n", password)
180-
fmt.Printf("GATEWAY_AGENT_PRIVATEKEY=\"%s\"\n", base64.StdEncoding.EncodeToString(privateKey))
180+
fmt.Printf("GATEWAY_AGENT_PRIVATEKEY=\"%s\"\n", privateKey.String())
181181
fmt.Printf("GATEWAY_AGENT_DEVICEIP=\"%s/21\"\n", response.GetGateway().GetIpv4())
182182

183183
return err

internal/deviceagent/wireguard/wireguard.go

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
package wireguard
22

33
import (
4-
"encoding/base64"
54
"fmt"
65
"os"
6+
"strings"
77

88
"github.com/nais/device/internal/deviceagent/filesystem"
99
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
@@ -25,15 +25,13 @@ func EnsurePrivateKey(keyPath string) (wgtypes.Key, error) {
2525

2626
privateKeyEncoded, err := os.ReadFile(keyPath)
2727
if err != nil {
28-
return wgtypes.Key{}, fmt.Errorf("reading private key: %v", err)
28+
return wgtypes.Key{}, fmt.Errorf("reading private key: %w", err)
2929
}
3030

31-
var key wgtypes.Key
32-
b, err := base64.StdEncoding.DecodeString(string(privateKeyEncoded))
31+
key, err := wgtypes.ParseKey(strings.TrimSpace(string(privateKeyEncoded)))
3332
if err != nil {
34-
return wgtypes.Key{}, fmt.Errorf("decoding private key: %v", err)
33+
return wgtypes.Key{}, fmt.Errorf("parsing private key: %w", err)
3534
}
36-
copy(key[:], b)
3735

3836
return key, nil
3937
}

internal/enroll/enroll.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ type DeviceRequest struct {
1010
}
1111

1212
type GatewayRequest struct {
13-
WireGuardPublicKey []byte `json:"wireguard_public_key"`
13+
WireGuardPublicKey string `json:"wireguard_public_key"`
1414
Name string `json:"name"`
1515
Endpoint string `json:"endpoint"`
1616
HashedPassword string `json:"hashed_password"`

internal/enroll/gateway.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ import (
1616
)
1717

1818
type GatewayClient struct {
19-
wireGuardPublicKey []byte
19+
wireGuardPublicKey string
2020
port int
2121
hashedPassword string
2222
log logrus.FieldLogger
@@ -28,7 +28,7 @@ type GatewayClient struct {
2828
ExternalIP string `json:"external_ip"`
2929
}
3030

31-
func NewGatewayClient(ctx context.Context, publicKey []byte, hashedPassword string, wireguardListenPort int, log logrus.FieldLogger) (*GatewayClient, error) {
31+
func NewGatewayClient(ctx context.Context, publicKey string, hashedPassword string, wireguardListenPort int, log logrus.FieldLogger) (*GatewayClient, error) {
3232
b, err := GetGoogleMetadata(ctx, "instance/attributes/enroll-config", log)
3333
if err != nil {
3434
return nil, err

internal/helper/helper.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,11 @@ func (dhs *DeviceHelperServer) Configure(
7676
backoff := time.Duration(attempt) * time.Second
7777
dhs.log.WithError(loopErr).Error("synchronize WireGuard configuration")
7878
dhs.log.WithField("attempt", attempt+1).WithField("backoff", backoff).Info("configuring failed, sleeping before retrying")
79-
time.Sleep(backoff)
79+
select {
80+
case <-ctx.Done():
81+
return nil, status.Errorf(codes.Canceled, "context canceled during WireGuard sync retry: %s", loopErr)
82+
case <-time.After(backoff):
83+
}
8084
continue
8185
}
8286
break

internal/helper/helper_darwin.go

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -138,9 +138,11 @@ func (c *DarwinConfigurator) SetupInterface(ctx context.Context, cfg *pb.Configu
138138
// tool used by the macOS networking stack itself.
139139
commands := [][]string{
140140
{"ifconfig", ifaceName, "inet", cfg.GetDeviceIPv4() + "/21", cfg.GetDeviceIPv4(), "alias"},
141-
{"ifconfig", ifaceName, "inet6", cfg.GetDeviceIPv6() + "/64", "alias"},
142-
{"ifconfig", ifaceName, "up"},
143141
}
142+
if cfg.GetDeviceIPv6() != "" {
143+
commands = append(commands, []string{"ifconfig", ifaceName, "inet6", cfg.GetDeviceIPv6() + "/64", "alias"})
144+
}
145+
commands = append(commands, []string{"ifconfig", ifaceName, "up"})
144146

145147
for _, s := range commands {
146148
cmd := exec.CommandContext(ctx, s[0], s[1:]...)

internal/helper/helper_linux.go

Lines changed: 19 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -92,24 +92,34 @@ func (c *LinuxConfigurator) SetupInterface(ctx context.Context, cfg *pb.Configur
9292
return fmt.Errorf("lookup interface after creation: %w", err)
9393
}
9494

95+
// cleanup deletes the interface if any subsequent configuration step fails.
96+
cleanup := func(cause error) error {
97+
if delErr := netlink.LinkDel(link); delErr != nil {
98+
return fmt.Errorf("%w (additionally, failed to delete interface: %v)", cause, delErr)
99+
}
100+
return cause
101+
}
102+
95103
ipv4Addr, err := netlink.ParseAddr(cfg.DeviceIPv4 + "/21")
96104
if err != nil {
97-
return fmt.Errorf("parse IPv4 address: %w", err)
105+
return cleanup(fmt.Errorf("parse IPv4 address: %w", err))
98106
}
99107
if err := netlink.AddrAdd(link, ipv4Addr); err != nil {
100-
return fmt.Errorf("add IPv4 address: %w", err)
108+
return cleanup(fmt.Errorf("add IPv4 address: %w", err))
101109
}
102110

103-
ipv6Addr, err := netlink.ParseAddr(cfg.DeviceIPv6 + "/64")
104-
if err != nil {
105-
return fmt.Errorf("parse IPv6 address: %w", err)
106-
}
107-
if err := netlink.AddrAdd(link, ipv6Addr); err != nil {
108-
return fmt.Errorf("add IPv6 address: %w", err)
111+
if cfg.DeviceIPv6 != "" {
112+
ipv6Addr, err := netlink.ParseAddr(cfg.DeviceIPv6 + "/64")
113+
if err != nil {
114+
return cleanup(fmt.Errorf("parse IPv6 address: %w", err))
115+
}
116+
if err := netlink.AddrAdd(link, ipv6Addr); err != nil {
117+
return cleanup(fmt.Errorf("add IPv6 address: %w", err))
118+
}
109119
}
110120

111121
if err := netlink.LinkSetUp(link); err != nil {
112-
return fmt.Errorf("bring interface up: %w", err)
122+
return cleanup(fmt.Errorf("bring interface up: %w", err))
113123
}
114124

115125
return nil

0 commit comments

Comments
 (0)