Skip to content

Commit 29a43f2

Browse files
committed
fix gateway-agent (WireGuard)config
1 parent e2b4d88 commit 29a43f2

File tree

2 files changed

+108
-86
lines changed

2 files changed

+108
-86
lines changed

cmd/gateway-agent/main.go

+106-67
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,6 @@ import (
77
"net/http"
88
"os/exec"
99
"path"
10-
"path/filepath"
11-
"strings"
1210
"time"
1311

1412
log "github.com/sirupsen/logrus"
@@ -21,23 +19,23 @@ var (
2119

2220
func init() {
2321
log.SetFormatter(&log.JSONFormatter{})
24-
flag.StringVar(&cfg.Apiserver, "apiserver", cfg.Apiserver, "hostname to apiserver")
2522
flag.StringVar(&cfg.Name, "name", cfg.Name, "gateway name")
26-
flag.StringVar(&cfg.PublicKey, "public-key", cfg.PublicKey, "path to wireguard public key")
27-
flag.StringVar(&cfg.PrivateKeyPath, "private-key", cfg.PrivateKeyPath, "path to wireguard private key")
28-
flag.StringVar(&cfg.Interface, "interface", cfg.Interface, "name of tunnel interface")
29-
flag.StringVar(&cfg.TunnelConfigDir, "tunnel-config-dir", cfg.TunnelConfigDir, "path to tunnel interface config directory")
23+
flag.StringVar(&cfg.TunnelIP, "tunnel-ip", cfg.TunnelIP, "gateway tunnel ip")
24+
flag.StringVar(&cfg.APIServerURL, "api-server-url", cfg.APIServerURL, "api server URL")
25+
flag.StringVar(&cfg.APIServerPublicKey, "api-server-public-key", cfg.APIServerPublicKey, "api server public key")
26+
flag.StringVar(&cfg.APIServerWireGuardEndpoint, "api-server-wireguard-endpoint", cfg.APIServerWireGuardEndpoint, "api server WireGuard endpoint")
3027

3128
flag.Parse()
3229

33-
cfg.WireGuardConfigPath = path.Join("/etc/wireguard/", fmt.Sprintf("%s.connf", cfg.Interface))
30+
cfg.WireGuardConfigPath = path.Join(cfg.ConfigDir, "wg0.conf")
31+
cfg.PrivateKeyPath = path.Join(cfg.ConfigDir, "private.key")
3432
}
3533

3634
// Gateway agent ensures desired configuration as defined by the apiserver
3735
// is synchronized and enforced by the local wireguard process on the gateway.
3836
//
3937
// Prerequisites:
40-
// - controlplane tunnel is set up/apiserver is reachable at `Config.APIServer`
38+
// - controlplane tunnel is set up/apiserver is reachable at `Config.APIServerURL`
4139
//
4240
// Prereqs for MVP (at least):
4341
//
@@ -60,28 +58,34 @@ type Device struct {
6058
func main() {
6159
log.Info("starting gateway-agent")
6260
log.Infof("with config:\n%+v", cfg)
61+
if err := setupInterface(cfg.TunnelIP); err != nil {
62+
log.Fatalf("setting up interface: %v", err)
63+
}
6364

6465
privateKey, err := readPrivateKey(cfg.PrivateKeyPath)
65-
6666
if err != nil {
6767
log.Fatalf("reading private key: %s", err)
6868
}
6969

70+
baseConfig := GenerateBaseConfig(cfg, privateKey)
71+
if err := actuateWireGuardConfig(baseConfig, cfg.WireGuardConfigPath); err != nil {
72+
log.Fatalf("actuating base config: %v", err)
73+
}
74+
7075
for range time.NewTicker(10 * time.Second).C {
7176
log.Infof("getting config")
72-
apiserver := fmt.Sprintf("%s/gateways/%s", cfg.Apiserver, cfg.Name)
73-
devices, err := getDevices(apiserver)
77+
devices, err := getDevices(cfg.APIServerURL, cfg.Name)
7478
if err != nil {
7579
log.Error(err)
7680
// inc metric
7781
continue
7882
}
7983

80-
fmt.Printf("%+v\n", devices)
84+
log.Debugf("%+v\n", devices)
8185

82-
if err := configureWireguard(devices, cfg, privateKey); err != nil {
83-
log.Error(err)
84-
// inc metric
86+
peerConfig := GenerateWireGuardPeers(devices)
87+
if err := actuateWireGuardConfig(baseConfig+peerConfig, cfg.WireGuardConfigPath); err != nil {
88+
log.Errorf("actuating WireGuard config: %v", err)
8589
}
8690
}
8791
}
@@ -91,78 +95,113 @@ func readPrivateKey(privateKeyPath string) (string, error) {
9195
return string(b), err
9296
}
9397

94-
func configureWireguard(devices []Device, cfg Config, privateKey string) error {
95-
wgConfigContent := generateWGConfig(devices, privateKey)
96-
fmt.Println(string(wgConfigContent))
97-
wgConfigFilePath := filepath.Join(cfg.TunnelConfigDir, cfg.Interface+".conf")
98-
if err := ioutil.WriteFile(wgConfigFilePath, wgConfigContent, 0600); err != nil {
99-
return fmt.Errorf("writing wireguard config to disk: %w", err)
98+
func getDevices(apiServerURL, name string) ([]Device, error) {
99+
gatewayConfigURL := fmt.Sprintf("%s/gateways/%s", apiServerURL, name)
100+
resp, err := http.Get(gatewayConfigURL)
101+
if err != nil {
102+
return nil, fmt.Errorf("getting peer config from apiserver: %w", err)
100103
}
101104

102-
cmd := exec.Command("/usr/bin/wg", "syncconf", cfg.Interface, cfg.WireGuardConfigPath)
103-
if stdout, err := cmd.Output(); err != nil {
104-
return fmt.Errorf("executing %w: %v", err, string(stdout))
105+
defer resp.Body.Close()
106+
107+
var devices []Device
108+
err = json.NewDecoder(resp.Body).Decode(&devices)
109+
110+
if err != nil {
111+
return nil, fmt.Errorf("unmarshal json from apiserver: %w", err)
105112
}
106113

107-
return nil
114+
return devices, nil
108115
}
109116

110-
func generateWGConfig(devices []Device, privateKey string) []byte {
111-
interfaceTemplate := `[Interface]
112-
PrivateKey = %s
113-
ListenPort = 51820
117+
type Config struct {
118+
APIServerURL string
119+
Name string
120+
TunnelIP string
121+
ConfigDir string
122+
WireGuardConfigPath string
123+
APIServerPublicKey string
124+
APIServerWireGuardEndpoint string
125+
PrivateKeyPath string
126+
APIServerTunnelIP string
127+
}
114128

115-
`
129+
func DefaultConfig() Config {
130+
return Config{
131+
APIServerURL: "http://apiserver.device.nais.io",
132+
APIServerTunnelIP: "10.255.240.1",
133+
ConfigDir: "/usr/local/etc/nais-device",
134+
}
135+
}
116136

117-
wgConfig := fmt.Sprintf(interfaceTemplate, strings.TrimSuffix(privateKey, "\n"))
137+
func setupInterface(tunnelIP string) error {
138+
if err := exec.Command("ip", "link", "del", "wg0").Run(); err != nil {
139+
log.Infof("pre-deleting WireGuard interface (ok if this fails): %v", err)
140+
}
118141

119-
peerTemplate := `[Peer]
120-
AllowedIPs = %s/32
121-
PublicKey = %s
122-
`
142+
run := func(commands [][]string) error {
143+
for _, s := range commands {
144+
cmd := exec.Command(s[0], s[1:]...)
123145

124-
for _, device := range devices {
125-
wgConfig += fmt.Sprintf(peerTemplate, device.IP, device.PublicKey)
146+
if out, err := cmd.Output(); err != nil {
147+
return fmt.Errorf("running %v: %w", cmd, err)
148+
} else {
149+
fmt.Printf("%v: %v\n", cmd, string(out))
150+
}
151+
}
152+
return nil
153+
}
154+
155+
commands := [][]string{
156+
{"ip", "link", "add", "dev", "wg0", "type", "wireguard"},
157+
{"ip", "link", "set", "wg0", "mtu", "1380"},
158+
{"ip", "address", "add", "dev", "wg0", tunnelIP},
159+
{"ip", "link", "set", "wg0", "up"},
126160
}
127161

128-
return []byte(wgConfig)
162+
return run(commands)
129163
}
130164

131-
func getDevices(apiServerURL string) ([]Device, error) {
132-
resp, err := http.Get(apiServerURL)
133-
if err != nil {
134-
return nil, fmt.Errorf("getting peer config from apiserver: %w", err)
135-
}
165+
func GenerateBaseConfig(cfg Config, privateKey string) string {
166+
template := `
167+
[Interface]
168+
PrivateKey = %s
136169
137-
defer resp.Body.Close()
170+
[Peer]
171+
PublicKey = %s
172+
AllowedIPs = %s/32
173+
Endpoint = %s
174+
`
175+
return fmt.Sprintf(template, privateKey, cfg.APIServerPublicKey, cfg.APIServerTunnelIP, cfg.WireGuardConfigPath)
176+
}
138177

139-
var devices []Device
140-
err = json.NewDecoder(resp.Body).Decode(&devices)
178+
func GenerateWireGuardPeers(devices []Device) string {
179+
peerTemplate := `
180+
[Peer]
181+
PublicKey = %s
182+
AllowedIPs = %s
183+
`
184+
var peers string
141185

142-
if err != nil {
143-
return nil, fmt.Errorf("unmarshal json from apiserver: %w", err)
186+
for _, device := range devices {
187+
peers += fmt.Sprintf(peerTemplate, device.PublicKey, device.IP)
144188
}
145189

146-
return devices, nil
190+
return peers
147191
}
148192

149-
type Config struct {
150-
Apiserver string
151-
Name string
152-
PublicKey string
153-
PrivateKeyPath string
154-
TunnelIP string
155-
TunnelConfigDir string
156-
Interface string
157-
WireGuardConfigPath string
158-
}
193+
// actuateWireGuardConfig runs syncconfig with the provided WireGuard config
194+
func actuateWireGuardConfig(wireGuardConfig, wireGuardConfigPath string) error {
195+
if err := ioutil.WriteFile(wireGuardConfigPath, []byte(wireGuardConfig), 0600); err != nil {
196+
return fmt.Errorf("writing WireGuard config to disk: %w", err)
197+
}
159198

160-
func DefaultConfig() Config {
161-
return Config{
162-
Apiserver: "http://apiserver.device.nais.io",
163-
PublicKey: "/etc/wireguard/public.key",
164-
PrivateKeyPath: "/etc/wireguard/private.key",
165-
Interface: "wg0",
166-
TunnelConfigDir: "/etc/wireguard/",
199+
cmd := exec.Command("wg", "syncconf", "wg0", wireGuardConfigPath)
200+
if err := cmd.Run(); err != nil {
201+
return fmt.Errorf("running syncconf: %w", err)
167202
}
203+
204+
log.Debugf("Actuated WireGuard config: %v", wireGuardConfigPath)
205+
206+
return nil
168207
}

terraform/gateways/vm.tf

+2-19
Original file line numberDiff line numberDiff line change
@@ -42,25 +42,8 @@ add-apt-repository --yes ppa:wireguard/wireguard
4242
apt-get update --yes
4343
apt-get install --yes wireguard
4444
45-
# Setup wg0
46-
wg0_private_key=$(wg genkey)
47-
48-
mkdir -p /etc/wireguard
49-
cat << EOF > /etc/wireguard/wg0.conf
50-
[Interface]
51-
PrivateKey = $wg0_private_key
52-
53-
[Peer]
54-
Endpoint = ${var.apiserver_endpoint}
55-
PublicKey = ${var.apiserver_public_key}
56-
AllowedIPs = ${var.apiserver_tunnel_ip}
57-
EOF
58-
59-
ip link add dev wg0 type wireguard
60-
ip link set wg0 mtu 1380
61-
ip address add dev wg0 ${var.gateways[count.index].ctrl_tunnel_ip}/21
62-
wg setconf wg0 /etc/wireguard/wg0.conf
63-
ip link set wg0 up
45+
mkdir -p /usr/local/etc/nais-device
46+
wg genkey > /usr/local/etc/nais-device/private.key
6447
6548
# Enable ip forward
6649
sed -i -e 's/#net.ipv4.ip_forward=1/net.ipv4.ip_forward=1/' /etc/sysctl.conf

0 commit comments

Comments
 (0)