WIP
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:
parent
15c5c904a2
commit
3f3ad43cb2
@ -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",
|
||||||
|
@ -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
|
||||||
|
|
||||||
|
72
go-workspace/src/cmd/entrypoint/daemon_yml.go
Normal file
72
go-workspace/src/cmd/entrypoint/daemon_yml.go
Normal 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
|
||||||
|
}
|
@ -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,
|
||||||
|
Loading…
Reference in New Issue
Block a user