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 }