@@ -4,14 +4,13 @@ import (
4
4
"encoding/base64"
5
5
"encoding/json"
6
6
"fmt"
7
+ log "github.com/sirupsen/logrus"
8
+ flag "github.com/spf13/pflag"
7
9
"io/ioutil"
8
10
"os"
9
11
"os/exec"
10
12
"path/filepath"
11
13
"time"
12
-
13
- log "github.com/sirupsen/logrus"
14
- flag "github.com/spf13/pflag"
15
14
)
16
15
17
16
var (
22
21
ControlPlaneWGConfigPath string
23
22
DataPlanePrivateKeyPath string
24
23
DataPlaneWGConfigPath string
24
+ EnrollmentTokenPath string
25
25
)
26
26
27
27
func init () {
@@ -41,16 +41,23 @@ func init() {
41
41
ControlPlaneWGConfigPath = filepath .Join (cfg .ConfigDir , "wgctrl.conf" )
42
42
DataPlanePrivateKeyPath = filepath .Join (cfg .ConfigDir , "wgdata-private.key" )
43
43
DataPlaneWGConfigPath = filepath .Join (cfg .ConfigDir , "wgdata.conf" )
44
+ EnrollmentTokenPath = filepath .Join (cfg .ConfigDir , "enrollment.token" )
44
45
}
45
46
46
47
// client-agent is responsible for enabling the end-user to connect to it's permitted gateways.
47
48
// To be able to connect, a series of prerequisites must be in place. These will be helped/ensured by client-agent.
48
49
//
50
+
49
51
// 1. A information exchange between end-user and naisdevice administrator/slackbot:
52
+ // If neither EnrollmentTokenPath nor EnrollmentToken is present, user will be prompted to enroll using public key, and the agent will exit.
53
+ // If EnrollmentToken command line option is provided, the agent will store it as EnrollmentTokenPath.
54
+ // If EnrollmentTokenPath is present the agent will generate `wgctrl.conf` and continue.
55
+
50
56
// - The end-user will provide it's generated public key ($(wg pubkey < `ControlPlanePrivateKeyPath`))
51
- // - The end-user will receive the control plane tunnel endpoint and public key.
52
- // The received information will be persisted as `ControlPlaneInfoFile`.
53
- // When client-agent detects `ControlPlaneInfoFile` is present,
57
+ // - The end-user will receive the control plane tunnel endpoint, public key, apiserver tunnel ip, and it's own tunnel
58
+ // ip encoded as a base64 string.
59
+ // The received information will be persisted as `EnrollmentTokenPath`.
60
+ // When client-agent detects `EnrollmentTokenPath` is present,
54
61
// it will generate a WireGuard config file called wgctrl.conf placed in `cfg.ConfigDir`
55
62
//
56
63
// 2. (When) A valid control plane WireGuard config is present, ensure control plane tunnel is configured and connected:
@@ -73,7 +80,7 @@ func init() {
73
80
//TODO: (authenticate user, not part of MVP)
74
81
//TODO: get config from apiserver
75
82
//TODO: establish data plane (continously monitored, will trigger ^ if it goes down and user wants to connect)
76
- // $$$$$$
83
+ // $$$$$$$$$
77
84
func main () {
78
85
log .Infof ("starting client-agent with config:\n %+v" , cfg )
79
86
@@ -85,71 +92,98 @@ func main() {
85
92
log .Fatalf ("ensuring directory exists: %w" , err )
86
93
}
87
94
88
- if err := filesExist (ControlPlaneWGConfigPath ); err != nil {
89
- // no wgctrl.conf
95
+ if err := ensureKey (ControlPlanePrivateKeyPath ); err != nil {
96
+ log .Fatalf ("ensuring private key for control plane exists: %v" , err )
97
+ }
98
+
99
+ if len (cfg .EnrollmentToken ) == 0 {
100
+ if err := filesExist (EnrollmentTokenPath ); err != nil {
101
+ pubkey , err := generatePublicKey (ControlPlanePrivateKeyPath )
102
+ if err != nil {
103
+ log .Fatalf ("generate public key during enroll: %v" , err )
104
+ }
105
+
106
+ fmt .Printf ("no enrollment token present. Send 'Nais Device' this message on slack: 'enroll %v'" , string (pubkey ))
107
+ os .Exit (0 )
108
+ }
90
109
91
- if err := enroll (); err != nil {
92
- log .Fatalf ("enrolling device: %v" , err )
110
+ enrollmentToken , err := ioutil .ReadFile (EnrollmentTokenPath )
111
+ if err != nil {
112
+ log .Fatalf ("reading enrollment token: %v" , err )
93
113
}
94
114
115
+ cfg .EnrollmentToken = string (enrollmentToken )
116
+ } else {
117
+ if err := ioutil .WriteFile (EnrollmentTokenPath , []byte (cfg .EnrollmentToken ), 0600 ); err != nil {
118
+ log .Fatalf ("writing enrollment token to disk: %v" , err )
119
+ }
120
+ }
121
+
122
+ if err := setupControlPlane (cfg .EnrollmentToken ); err != nil {
123
+ log .Fatalf ("setting up control plane: %v" , err )
95
124
}
96
125
97
126
for range time .NewTicker (10 * time .Second ).C {
98
127
log .Info ("titei" )
99
128
}
100
129
}
101
130
102
- type EnrollmentConfig struct {
103
- ClientIP string `json:"clientIP"`
104
- PublicKey string `json:"publicKey"`
105
- Endpoint string `json:"endpoint"`
106
- APIServerIP string `json:"apiServerIP"`
107
- }
108
-
109
- func enroll () error {
110
- if err := ensureKey (ControlPlanePrivateKeyPath ); err != nil {
111
- return fmt .Errorf ("ensuring private key for control plane exists: %v" , err )
112
- }
113
-
114
- if len (cfg .EnrollmentToken ) == 0 {
115
- pubkey , err := generatePublicKey (ControlPlanePrivateKeyPath )
116
- if err != nil {
117
- return fmt .Errorf ("generate public key during enroll: %v" , err )
118
- }
119
-
120
- return fmt .Errorf ("no enrollment token present. Send 'Nais Device' this message on slack: 'enroll %v'" , string (pubkey ))
131
+ func setupControlPlane (enrollmentToken string ) error {
132
+ enrollmentConfig , err := ParseEnrollmentToken (enrollmentToken )
133
+ if err != nil {
134
+ return fmt .Errorf ("parsing enrollment config key: %w" , err )
121
135
}
122
136
123
137
privateKey , err := ioutil .ReadFile (ControlPlanePrivateKeyPath )
124
138
if err != nil {
125
139
return fmt .Errorf ("reading private key: %w" , err )
126
140
}
127
141
128
- enrollmentConfig , err := ParseEnrollmentToken (cfg .EnrollmentToken )
129
- if err != nil {
130
- return fmt .Errorf ("parsing enrollment token: %w" , err )
131
- }
132
-
133
142
wgConfigContent := GenerateWGConfig (enrollmentConfig , privateKey )
134
- fmt .Println (wgConfigContent )
143
+ fmt .Println (string ( wgConfigContent ) )
135
144
136
145
if err := ioutil .WriteFile (ControlPlaneWGConfigPath , wgConfigContent , 0600 ); err != nil {
137
146
return fmt .Errorf ("writing control plane wireguard config to disk: %w" , err )
138
147
}
139
148
140
- /*
141
- cmd := exec.Command("/usr/bin/wg", "syncconf", "wgdata", "/etc/wireguard/wgdata.conf")
142
- if stdout, err := cmd.Output(); err != nil {
143
- return fmt.Errorf("executing %w: %v", err, string(stdout))
144
- }
149
+ if err := setupInterface (cfg .ControlPlaneInterface , enrollmentConfig .ClientIP , ControlPlaneWGConfigPath ); err != nil {
150
+ return fmt .Errorf ("setting up control plane interface: %w" , err )
151
+ }
145
152
146
- return nil
147
- }
148
- */
153
+ return nil
154
+ }
149
155
150
- // create config file
156
+ func setupInterface (interfaceName string , ip string , configPath string ) error {
157
+ run := func (commands [][]string ) error {
158
+ for _ , s := range commands {
159
+ cmd := exec .Command (s [0 ], s [1 :]... )
151
160
152
- return nil
161
+ if out , err := cmd .Output (); err != nil {
162
+ return fmt .Errorf ("running %v: %w" , cmd , err )
163
+ } else {
164
+ fmt .Printf ("%v: %v\n " , cmd , string (out ))
165
+ }
166
+ }
167
+ return nil
168
+ }
169
+
170
+ commands := [][]string {
171
+ {WireguardGoBinary , interfaceName },
172
+ {"ifconfig" , interfaceName , "inet" , ip + "/21" , ip , "add" },
173
+ {"ifconfig" , interfaceName , "mtu" , "1380" },
174
+ {"ifconfig" , interfaceName , "up" },
175
+ {"route" , "-q" , "-n" , "add" , "-inet" , ip + "/21" , "-interface" , interfaceName },
176
+ {WGBinary , "syncconf" , interfaceName , configPath },
177
+ }
178
+
179
+ return run (commands )
180
+ }
181
+
182
+ type EnrollmentConfig struct {
183
+ ClientIP string `json:"clientIP"`
184
+ PublicKey string `json:"publicKey"`
185
+ Endpoint string `json:"endpoint"`
186
+ APIServerIP string `json:"apiServerIP"`
153
187
}
154
188
155
189
func ParseEnrollmentToken (enrollmentToken string ) (enrollmentConfig * EnrollmentConfig , err error ) {
0 commit comments