Skip to content

Commit 71fe507

Browse files
committed
setup control plane
1 parent 694c09f commit 71fe507

File tree

1 file changed

+80
-46
lines changed

1 file changed

+80
-46
lines changed

cmd/client-agent/main.go

+80-46
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,13 @@ import (
44
"encoding/base64"
55
"encoding/json"
66
"fmt"
7+
log "github.com/sirupsen/logrus"
8+
flag "github.com/spf13/pflag"
79
"io/ioutil"
810
"os"
911
"os/exec"
1012
"path/filepath"
1113
"time"
12-
13-
log "github.com/sirupsen/logrus"
14-
flag "github.com/spf13/pflag"
1514
)
1615

1716
var (
@@ -22,6 +21,7 @@ var (
2221
ControlPlaneWGConfigPath string
2322
DataPlanePrivateKeyPath string
2423
DataPlaneWGConfigPath string
24+
EnrollmentTokenPath string
2525
)
2626

2727
func init() {
@@ -41,16 +41,23 @@ func init() {
4141
ControlPlaneWGConfigPath = filepath.Join(cfg.ConfigDir, "wgctrl.conf")
4242
DataPlanePrivateKeyPath = filepath.Join(cfg.ConfigDir, "wgdata-private.key")
4343
DataPlaneWGConfigPath = filepath.Join(cfg.ConfigDir, "wgdata.conf")
44+
EnrollmentTokenPath = filepath.Join(cfg.ConfigDir, "enrollment.token")
4445
}
4546

4647
// client-agent is responsible for enabling the end-user to connect to it's permitted gateways.
4748
// To be able to connect, a series of prerequisites must be in place. These will be helped/ensured by client-agent.
4849
//
50+
4951
// 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+
5056
// - 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,
5461
// it will generate a WireGuard config file called wgctrl.conf placed in `cfg.ConfigDir`
5562
//
5663
// 2. (When) A valid control plane WireGuard config is present, ensure control plane tunnel is configured and connected:
@@ -73,7 +80,7 @@ func init() {
7380
//TODO: (authenticate user, not part of MVP)
7481
//TODO: get config from apiserver
7582
//TODO: establish data plane (continously monitored, will trigger ^ if it goes down and user wants to connect)
76-
// $$$$$$
83+
// $$$$$$$$$
7784
func main() {
7885
log.Infof("starting client-agent with config:\n%+v", cfg)
7986

@@ -85,71 +92,98 @@ func main() {
8592
log.Fatalf("ensuring directory exists: %w", err)
8693
}
8794

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+
}
90109

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)
93113
}
94114

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)
95124
}
96125

97126
for range time.NewTicker(10 * time.Second).C {
98127
log.Info("titei")
99128
}
100129
}
101130

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)
121135
}
122136

123137
privateKey, err := ioutil.ReadFile(ControlPlanePrivateKeyPath)
124138
if err != nil {
125139
return fmt.Errorf("reading private key: %w", err)
126140
}
127141

128-
enrollmentConfig, err := ParseEnrollmentToken(cfg.EnrollmentToken)
129-
if err != nil {
130-
return fmt.Errorf("parsing enrollment token: %w", err)
131-
}
132-
133142
wgConfigContent := GenerateWGConfig(enrollmentConfig, privateKey)
134-
fmt.Println(wgConfigContent)
143+
fmt.Println(string(wgConfigContent))
135144

136145
if err := ioutil.WriteFile(ControlPlaneWGConfigPath, wgConfigContent, 0600); err != nil {
137146
return fmt.Errorf("writing control plane wireguard config to disk: %w", err)
138147
}
139148

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+
}
145152

146-
return nil
147-
}
148-
*/
153+
return nil
154+
}
149155

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:]...)
151160

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"`
153187
}
154188

155189
func ParseEnrollmentToken(enrollmentToken string) (enrollmentConfig *EnrollmentConfig, err error) {

0 commit comments

Comments
 (0)