Currently a bit stuck on getting the IPMask to `nebula.NewHostCert`.
Probably the best way would be to limit number of CIDRs to just 1 for
now, and include that in the `nebula.CACert`. Or maybe we can just
provide a way to parse it out of the `ca.crt`. Either way CIDRs should
get removed from `admin.CreationParams`.
This commit is contained in:
Brian Picciano 2022-10-16 17:51:51 +02:00
parent 15c5c904a2
commit 3f3ad43cb2
4 changed files with 179 additions and 73 deletions

View File

@ -6,7 +6,9 @@ import (
"cryptic-net/nebula" "cryptic-net/nebula"
"errors" "errors"
"fmt" "fmt"
"net"
"os" "os"
"strings"
) )
func readAdmin(path string) (admin.Admin, error) { func readAdmin(path string) (admin.Admin, error) {
@ -30,6 +32,98 @@ func readAdmin(path string) (admin.Admin, error) {
return admin.FromReader(f) return admin.FromReader(f)
} }
var subCmdAdminCreateNetwork = subCmd{
name: "create-network",
descr: "Creates a new cryptic-net network, outputting the resulting admin.tgz to stdout",
do: func(subCmdCtx subCmdCtx) error {
flags := subCmdCtx.flagSet(false)
daemonYmlPath := flags.StringP(
"config-path", "c", "",
"Optional path to a daemon.yml file to load configuration from.",
)
dumpConfig := flags.Bool(
"dump-config", false,
"Write the default configuration file to stdout and exit.",
)
domain := flags.StringP(
"domain", "d", "",
"Domain name that should be used as the root domain in the network.",
)
ipCIDRStrs := flags.StringP(
"ip-cidrs", "i", "",
"Comma-separated list of CIDRs which denote what IPs hosts on the network can be assigned.",
)
if err := flags.Parse(subCmdCtx.args); err != nil {
return fmt.Errorf("parsing flags: %w", err)
}
env := subCmdCtx.env
if *dumpConfig {
return writeBuiltinDaemonYml(env, os.Stdout)
}
if *domain == "" {
return errors.New("--domain is required")
}
*domain = strings.TrimRight(strings.TrimLeft(*domain, "."), ".")
var (
ipCIDRs []*net.IPNet
thisIP net.IP
)
for _, ipCIDRStr := range strings.Split(*ipCIDRStrs, ",") {
ipCIDRStr = strings.TrimSpace(ipCIDRStr)
if ipCIDRStr == "" {
continue
}
ip, ipCIDR, err := net.ParseCIDR(ipCIDRStr)
if err != nil {
return fmt.Errorf("could not parse CIDR %q: %w", ipCIDRStr, err)
}
thisIP = ip // we just need one IP from a CIDR, it doesn't matter which.
ipCIDRs = append(ipCIDRs, ipCIDR)
}
runtimeDirPath := env.RuntimeDirPath
fmt.Fprintf(os.Stderr, "will use runtime directory %q for temporary state\n", runtimeDirPath)
if err := os.MkdirAll(runtimeDirPath, 0700); err != nil {
return fmt.Errorf("creating directory %q: %w", runtimeDirPath, err)
}
defer func() {
fmt.Fprintf(os.Stderr, "cleaning up runtime directory %q\n", runtimeDirPath)
if err := os.RemoveAll(runtimeDirPath); err != nil {
fmt.Fprintf(os.Stderr, "error removing temporary directory %q: %v", runtimeDirPath, err)
}
}()
if err := writeMergedDaemonYml(env, *daemonYmlPath); err != nil {
return fmt.Errorf("merging and writing daemon.yml file: %w", err)
}
daemon := env.ThisDaemon()
if len(daemon.Storage.Allocations) < 3 {
return fmt.Errorf("daemon.yml with at least 3 allocations was not provided")
}
},
}
var subCmdAdminMakeBootstrap = subCmd{ var subCmdAdminMakeBootstrap = subCmd{
name: "make-bootstrap", name: "make-bootstrap",
descr: "Creates a new bootstrap.tgz file for a particular host and writes it to stdout", descr: "Creates a new bootstrap.tgz file for a particular host and writes it to stdout",

View File

@ -6,7 +6,6 @@ import (
"errors" "errors"
"fmt" "fmt"
"io" "io"
"io/ioutil"
"net" "net"
"os" "os"
"path/filepath" "path/filepath"
@ -17,11 +16,8 @@ import (
crypticnet "cryptic-net" crypticnet "cryptic-net"
"cryptic-net/bootstrap" "cryptic-net/bootstrap"
"cryptic-net/garage" "cryptic-net/garage"
"cryptic-net/yamlutil"
"github.com/cryptic-io/pmux/pmuxlib" "github.com/cryptic-io/pmux/pmuxlib"
"github.com/imdario/mergo"
"gopkg.in/yaml.v3"
) )
// The daemon sub-command deals with starting an actual cryptic-net daemon // The daemon sub-command deals with starting an actual cryptic-net daemon
@ -46,42 +42,6 @@ import (
// //
// * (On exit) cleans up the runtime directory. // * (On exit) cleans up the runtime directory.
func writeDaemonYml(userDaemonYmlPath, builtinDaemonYmlPath, runtimeDirPath string) error {
var fullDaemonYml map[string]interface{}
if err := yamlutil.LoadYamlFile(&fullDaemonYml, builtinDaemonYmlPath); err != nil {
return fmt.Errorf("parsing builtin daemon.yml file: %w", err)
}
if userDaemonYmlPath != "" {
var daemonYml map[string]interface{}
if err := yamlutil.LoadYamlFile(&daemonYml, userDaemonYmlPath); err != nil {
return fmt.Errorf("parsing %q: %w", userDaemonYmlPath, err)
}
err := mergo.Merge(&fullDaemonYml, daemonYml, mergo.WithOverride)
if err != nil {
return fmt.Errorf("merging contents of file %q: %w", userDaemonYmlPath, err)
}
}
fullDaemonYmlB, err := yaml.Marshal(fullDaemonYml)
if err != nil {
return fmt.Errorf("yaml marshaling daemon config: %w", err)
}
daemonYmlPath := filepath.Join(runtimeDirPath, "daemon.yml")
if err := ioutil.WriteFile(daemonYmlPath, fullDaemonYmlB, 0400); err != nil {
return fmt.Errorf("writing daemon.yml file to %q: %w", daemonYmlPath, err)
}
return nil
}
func copyBootstrapToDataDir(env *crypticnet.Env, r io.Reader) error { func copyBootstrapToDataDir(env *crypticnet.Env, r io.Reader) error {
path := env.DataDirBootstrapPath() path := env.DataDirBootstrapPath()
@ -284,28 +244,8 @@ var subCmdDaemon = subCmd{
env := subCmdCtx.env env := subCmdCtx.env
s3Client, err := env.Bootstrap.GlobalBucketS3APIClient()
if err != nil {
return fmt.Errorf("creating client for global bucket: %w", err)
}
appDirPath := env.AppDirPath
builtinDaemonYmlPath := filepath.Join(appDirPath, "etc", "daemon.yml")
if *dumpConfig { if *dumpConfig {
return writeBuiltinDaemonYml(env, os.Stdout)
builtinDaemonYml, err := os.ReadFile(builtinDaemonYmlPath)
if err != nil {
return fmt.Errorf("reading default daemon.yml at %q: %w", builtinDaemonYmlPath, err)
}
if _, err := os.Stdout.Write(builtinDaemonYml); err != nil {
return fmt.Errorf("writing default daemon.yml to stdout: %w", err)
}
return nil
} }
runtimeDirPath := env.RuntimeDirPath runtimeDirPath := env.RuntimeDirPath
@ -360,8 +300,8 @@ var subCmdDaemon = subCmd{
} }
} }
if err := writeDaemonYml(*daemonYmlPath, builtinDaemonYmlPath, runtimeDirPath); err != nil { if err := writeMergedDaemonYml(env, *daemonYmlPath); err != nil {
return fmt.Errorf("generating daemon.yml file: %w", err) return fmt.Errorf("merging and writing daemon.yml file: %w", err)
} }
{ {
@ -412,6 +352,14 @@ var subCmdDaemon = subCmd{
for { for {
// create s3Client anew on every loop, in case the topology has
// changed and we should be connecting to a different garage
// endpoint.
s3Client, err := env.Bootstrap.GlobalBucketS3APIClient()
if err != nil {
return fmt.Errorf("creating client for global bucket: %w", err)
}
if err := runDaemonPmuxOnce(env, s3Client); errors.Is(err, context.Canceled) { if err := runDaemonPmuxOnce(env, s3Client); errors.Is(err, context.Canceled) {
return nil return nil

View File

@ -0,0 +1,72 @@
package entrypoint
import (
crypticnet "cryptic-net"
"cryptic-net/yamlutil"
"fmt"
"io"
"io/ioutil"
"os"
"path/filepath"
"github.com/imdario/mergo"
"gopkg.in/yaml.v3"
)
func builtinDaemonYmlPath(env *crypticnet.Env) string {
return filepath.Join(env.AppDirPath, "etc", "daemon.yml")
}
func writeBuiltinDaemonYml(env *crypticnet.Env, w io.Writer) error {
builtinDaemonYmlPath := builtinDaemonYmlPath(env)
builtinDaemonYml, err := os.ReadFile(builtinDaemonYmlPath)
if err != nil {
return fmt.Errorf("reading default daemon.yml at %q: %w", builtinDaemonYmlPath, err)
}
if _, err := w.Write(builtinDaemonYml); err != nil {
return fmt.Errorf("writing default daemon.yml: %w", err)
}
return nil
}
func writeMergedDaemonYml(env *crypticnet.Env, userDaemonYmlPath string) error {
builtinDaemonYmlPath := builtinDaemonYmlPath(env)
var fullDaemonYml map[string]interface{}
if err := yamlutil.LoadYamlFile(&fullDaemonYml, builtinDaemonYmlPath); err != nil {
return fmt.Errorf("parsing builtin daemon.yml file: %w", err)
}
if userDaemonYmlPath != "" {
var daemonYml map[string]interface{}
if err := yamlutil.LoadYamlFile(&daemonYml, userDaemonYmlPath); err != nil {
return fmt.Errorf("parsing %q: %w", userDaemonYmlPath, err)
}
err := mergo.Merge(&fullDaemonYml, daemonYml, mergo.WithOverride)
if err != nil {
return fmt.Errorf("merging contents of file %q: %w", userDaemonYmlPath, err)
}
}
fullDaemonYmlB, err := yaml.Marshal(fullDaemonYml)
if err != nil {
return fmt.Errorf("yaml marshaling daemon config: %w", err)
}
daemonYmlPath := filepath.Join(env.RuntimeDirPath, "daemon.yml")
if err := ioutil.WriteFile(daemonYmlPath, fullDaemonYmlB, 0400); err != nil {
return fmt.Errorf("writing daemon.yml file to %q: %w", daemonYmlPath, err)
}
return nil
}

View File

@ -14,15 +14,6 @@ import (
"golang.org/x/crypto/curve25519" "golang.org/x/crypto/curve25519"
) )
// TODO this should one day not be hardcoded
var ipCIDRMask = func() net.IPMask {
_, ipNet, err := net.ParseCIDR("10.10.0.0/16")
if err != nil {
panic(err)
}
return ipNet.Mask
}()
// HostCert contains the certificate and private key files which will need to // HostCert contains the certificate and private key files which will need to
// be present on a particular host. Each file is PEM encoded. // be present on a particular host. Each file is PEM encoded.
type HostCert struct { type HostCert struct {
@ -122,7 +113,7 @@ func NewHostCert(
// NewCACert generates a CACert. The domain should be the network's root domain, // NewCACert generates a CACert. The domain should be the network's root domain,
// and is included in the signing certificate's Name field. // and is included in the signing certificate's Name field.
func NewCACert(domain string) (CACert, error) { func NewCACert(domain string, ipCIDRS []*net.IPNet) (CACert, error) {
pubKey, privKey, err := ed25519.GenerateKey(rand.Reader) pubKey, privKey, err := ed25519.GenerateKey(rand.Reader)
if err != nil { if err != nil {
@ -135,6 +126,7 @@ func NewCACert(domain string) (CACert, error) {
caCrt := cert.NebulaCertificate{ caCrt := cert.NebulaCertificate{
Details: cert.NebulaCertificateDetails{ Details: cert.NebulaCertificateDetails{
Name: fmt.Sprintf("%s cryptic-net root cert", domain), Name: fmt.Sprintf("%s cryptic-net root cert", domain),
Ips: ipCIDRS,
NotBefore: now, NotBefore: now,
NotAfter: expireAt, NotAfter: expireAt,
PublicKey: pubKey, PublicKey: pubKey,