isle/go/daemon/child_nebula.go

185 lines
4.2 KiB
Go

package daemon
import (
"context"
"fmt"
"isle/bootstrap"
"isle/yamlutil"
"net"
"path/filepath"
"code.betamike.com/micropelago/pmux/pmuxlib"
"dev.mediocregopher.com/mediocre-go-lib.git/mctx"
"dev.mediocregopher.com/mediocre-go-lib.git/mlog"
"github.com/slackhq/nebula/cert"
)
// waitForNebula waits for the nebula interface to have been started up. It does
// this by attempting to create a UDP connection which has the nebula IP set as
// its source. If this succeeds we can assume that at the very least the nebula
// interface has been initialized.
func waitForNebula(
ctx context.Context, logger *mlog.Logger, hostBootstrap bootstrap.Bootstrap,
) error {
var (
ip = net.IP(hostBootstrap.ThisHost().IP().AsSlice())
lUDPAddr = &net.UDPAddr{IP: ip, Port: 0}
rUDPAddr = &net.UDPAddr{IP: ip, Port: 45535}
)
ctx = mctx.Annotate(ctx, "lUDPAddr", lUDPAddr, "rUDPAddr", rUDPAddr)
until(
ctx,
logger,
"Creating UDP socket from nebula addr",
func(context.Context) error {
conn, err := net.DialUDP("udp", lUDPAddr, rUDPAddr)
if err != nil {
return err
}
conn.Close()
return nil
},
)
return ctx.Err()
}
func nebulaConfig(
daemonConfig Config,
hostBootstrap bootstrap.Bootstrap,
) (
map[string]any, error,
) {
var (
lighthouseHostIPs []string
staticHostMap = map[string][]string{}
)
for _, host := range hostBootstrap.Hosts {
if host.Nebula.PublicAddr == "" {
continue
}
ip := host.IP().String()
lighthouseHostIPs = append(lighthouseHostIPs, ip)
staticHostMap[ip] = []string{host.Nebula.PublicAddr}
}
caCertPEM, err := hostBootstrap.CAPublicCredentials.Cert.Unwrap().MarshalToPEM()
if err != nil {
return nil, fmt.Errorf("marshaling CA cert to PEM: :%w", err)
}
hostCertPEM, err := hostBootstrap.PublicCredentials.Cert.Unwrap().MarshalToPEM()
if err != nil {
return nil, fmt.Errorf("marshaling host cert to PEM: :%w", err)
}
hostKeyPEM := cert.MarshalX25519PrivateKey(
hostBootstrap.PrivateCredentials.EncryptingPrivateKey.Bytes(),
)
config := map[string]any{
"pki": map[string]string{
"ca": string(caCertPEM),
"cert": string(hostCertPEM),
"key": string(hostKeyPEM),
},
"static_host_map": staticHostMap,
"punchy": map[string]bool{
"punch": true,
"respond": true,
},
"tun": map[string]any{
"dev": daemonConfig.VPN.Tun.Device,
},
"firewall": daemonConfig.VPN.Firewall,
}
if publicAddr := daemonConfig.VPN.PublicAddr; publicAddr == "" {
config["listen"] = map[string]string{
"host": "0.0.0.0",
"port": "0",
}
config["lighthouse"] = map[string]any{
"hosts": lighthouseHostIPs,
}
} else {
_, port, err := net.SplitHostPort(publicAddr)
if err != nil {
return nil, fmt.Errorf(
"parsing public address %q: %w", publicAddr, err,
)
}
config["listen"] = map[string]string{
"host": "0.0.0.0",
"port": port,
}
config["lighthouse"] = map[string]any{
"hosts": []string{},
"am_lighthouse": true,
}
}
return config, nil
}
func nebulaWriteConfig(
runtimeDirPath string,
daemonConfig Config,
hostBootstrap bootstrap.Bootstrap,
) (
string, error,
) {
config, err := nebulaConfig(daemonConfig, hostBootstrap)
if err != nil {
return "", fmt.Errorf("creating nebula config: %w", err)
}
nebulaYmlPath := filepath.Join(runtimeDirPath, "nebula.yml")
if err := yamlutil.WriteYamlFile(config, nebulaYmlPath, 0600); err != nil {
return "", fmt.Errorf("writing nebula.yml to %q: %w", nebulaYmlPath, err)
}
return nebulaYmlPath, nil
}
func nebulaPmuxProcConfig(
runtimeDirPath, binDirPath string,
daemonConfig Config,
hostBootstrap bootstrap.Bootstrap,
) (
pmuxlib.ProcessConfig, error,
) {
config, err := nebulaConfig(daemonConfig, hostBootstrap)
if err != nil {
return pmuxlib.ProcessConfig{}, fmt.Errorf(
"creating nebula config: %w", err,
)
}
nebulaYmlPath := filepath.Join(runtimeDirPath, "nebula.yml")
if err := yamlutil.WriteYamlFile(config, nebulaYmlPath, 0600); err != nil {
return pmuxlib.ProcessConfig{}, fmt.Errorf(
"writing nebula.yml to %q: %w", nebulaYmlPath, err,
)
}
return pmuxlib.ProcessConfig{
Cmd: filepath.Join(binDirPath, "nebula"),
Args: []string{"-config", nebulaYmlPath},
Group: -1, // Make sure nebula is shut down last.
}, nil
}