7
7
"net/http"
8
8
"os/exec"
9
9
"path"
10
- "path/filepath"
11
- "strings"
12
10
"time"
13
11
14
12
log "github.com/sirupsen/logrus"
@@ -21,23 +19,23 @@ var (
21
19
22
20
func init () {
23
21
log .SetFormatter (& log.JSONFormatter {})
24
- flag .StringVar (& cfg .Apiserver , "apiserver" , cfg .Apiserver , "hostname to apiserver" )
25
22
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 " )
30
27
31
28
flag .Parse ()
32
29
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" )
34
32
}
35
33
36
34
// Gateway agent ensures desired configuration as defined by the apiserver
37
35
// is synchronized and enforced by the local wireguard process on the gateway.
38
36
//
39
37
// 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 `
41
39
//
42
40
// Prereqs for MVP (at least):
43
41
//
@@ -60,28 +58,34 @@ type Device struct {
60
58
func main () {
61
59
log .Info ("starting gateway-agent" )
62
60
log .Infof ("with config:\n %+v" , cfg )
61
+ if err := setupInterface (cfg .TunnelIP ); err != nil {
62
+ log .Fatalf ("setting up interface: %v" , err )
63
+ }
63
64
64
65
privateKey , err := readPrivateKey (cfg .PrivateKeyPath )
65
-
66
66
if err != nil {
67
67
log .Fatalf ("reading private key: %s" , err )
68
68
}
69
69
70
+ baseConfig := GenerateBaseConfig (cfg , privateKey )
71
+ if err := actuateWireGuardConfig (baseConfig , cfg .WireGuardConfigPath ); err != nil {
72
+ log .Fatalf ("actuating base config: %v" , err )
73
+ }
74
+
70
75
for range time .NewTicker (10 * time .Second ).C {
71
76
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 )
74
78
if err != nil {
75
79
log .Error (err )
76
80
// inc metric
77
81
continue
78
82
}
79
83
80
- fmt . Printf ("%+v\n " , devices )
84
+ log . Debugf ("%+v\n " , devices )
81
85
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 )
85
89
}
86
90
}
87
91
}
@@ -91,78 +95,113 @@ func readPrivateKey(privateKeyPath string) (string, error) {
91
95
return string (b ), err
92
96
}
93
97
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 )
100
103
}
101
104
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 )
105
112
}
106
113
107
- return nil
114
+ return devices , nil
108
115
}
109
116
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
+ }
114
128
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
+ }
116
136
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
+ }
118
141
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 :]... )
123
145
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" },
126
160
}
127
161
128
- return [] byte ( wgConfig )
162
+ return run ( commands )
129
163
}
130
164
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
136
169
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
+ }
138
177
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
141
185
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 )
144
188
}
145
189
146
- return devices , nil
190
+ return peers
147
191
}
148
192
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
+ }
159
198
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 )
167
202
}
203
+
204
+ log .Debugf ("Actuated WireGuard config: %v" , wireGuardConfigPath )
205
+
206
+ return nil
168
207
}
0 commit comments