diff --git a/docs/admin/adding-a-host-to-the-network.md b/docs/admin/adding-a-host-to-the-network.md index 3ec3575..03a126e 100644 --- a/docs/admin/adding-a-host-to-the-network.md +++ b/docs/admin/adding-a-host-to-the-network.md @@ -37,7 +37,7 @@ which was configured when creating the network. ## Step 3: Create a `bootstrap.tgz` File -Access to an `admin.tgz` file is required for this step. +Access to an `admin.yml` file is required for this step. To create a `bootstrap.tgz` file for the new host, the admin should perform the following command from their own host: @@ -46,7 +46,7 @@ following command from their own host: cryptic-net hosts make-bootstrap \ --name \ --ip \ - --admin-path \ + --admin-path \ > bootstrap.tgz ``` @@ -58,22 +58,22 @@ If the user already has access to a `cryptic-net` binary then the new `bootstrap.tgz` file can be given to them as-is, and they can proceed with running their host's `cryptic-net daemon`. -### Encrypted `admin.tgz` +### Encrypted `admin.yml` -If `admin.tgz` is kept in an encrypted format on disk (it should be!) then the +If `admin.yml` is kept in an encrypted format on disk (it should be!) then the decrypted form can be piped into `make-bootstrap` over stdin. For example, if -GPG is being used to secure `admin.tgz` then the following could be used to +GPG is being used to secure `admin.yml` then the following could be used to generate a `bootstrap.tgz`: ``` -gpg -d | cryptic-net hosts make-boostrap \ +gpg -d | cryptic-net hosts make-boostrap \ --name \ --ip \ --admin-path - \ > bootstrap.tgz ``` -Note that the value of `--admin-path` is `-`, indicating that `admin.tgz` should +Note that the value of `--admin-path` is `-`, indicating that `admin.yml` should be read from stdin. ## Step 4: Optionally, Build Binary diff --git a/entrypoint/src/admin/admin.go b/entrypoint/src/admin/admin.go index b7be9c6..a4a13c8 100644 --- a/entrypoint/src/admin/admin.go +++ b/entrypoint/src/admin/admin.go @@ -1,29 +1,14 @@ -// Package admin deals with the parsing and creation of admin.tgz files. +// Package admin deals with the parsing and creation of admin.yml files. package admin import ( "cryptic-net/garage" "cryptic-net/nebula" - "cryptic-net/tarutil" - "cryptic-net/yamlutil" - "fmt" "io" - "io/fs" "gopkg.in/yaml.v3" ) -const ( - creationParamsPath = "admin/creation-params.yml" - - nebulaCertsCACertPath = "nebula/certs/ca.crt" - nebulaCertsCAKeyPath = "nebula/certs/ca.key" - - garageGlobalBucketKeyYmlPath = "garage/cryptic-net-global-bucket-key.yml" - garageAdminBucketKeyYmlPath = "garage/cryptic-net-admin-bucket-key.yml" - garageRPCSecretPath = "garage/rpc-secret.txt" -) - // CreationParams are general parameters used when creating a new network. These // are available to all hosts within the network via their bootstrap files. type CreationParams struct { @@ -31,105 +16,28 @@ type CreationParams struct { Domain string `yaml:"domain"` } -// Admin is used for accessing all information contained within an admin.tgz. +// Admin is used for accessing all information contained within an admin.yml. type Admin struct { - CreationParams CreationParams + CreationParams CreationParams `yaml:"creation_params"` - NebulaCACredentials nebula.CACredentials + Nebula struct { + CACredentials nebula.CACredentials `yaml:"ca_credentials"` + } `yaml:"nebula"` - GarageRPCSecret string - GarageGlobalBucketS3APICredentials garage.S3APICredentials - GarageAdminBucketS3APICredentials garage.S3APICredentials + Garage struct { + RPCSecret string `yaml:"rpc_secret"` + GlobalBucketS3APICredentials garage.S3APICredentials `yaml:"global_bucket_s3_api_credentials"` + } `yaml:"garage"` } -// FromFS loads an Admin instance from the given fs.FS, which presumably -// represents the file structure of an admin.tgz file. -func FromFS(adminFS fs.FS) (Admin, error) { - - var a Admin - - filesToLoadAsYAML := []struct { - into interface{} - path string - }{ - {&a.CreationParams, creationParamsPath}, - {&a.GarageGlobalBucketS3APICredentials, garageGlobalBucketKeyYmlPath}, - {&a.GarageAdminBucketS3APICredentials, garageAdminBucketKeyYmlPath}, - } - - for _, f := range filesToLoadAsYAML { - if err := yamlutil.LoadYamlFSFile(f.into, adminFS, f.path); err != nil { - return Admin{}, fmt.Errorf("loading %q from fs: %w", f.path, err) - } - } - - filesToLoadAsString := []struct { - into *string - path string - }{ - {&a.NebulaCACredentials.CACertPEM, nebulaCertsCACertPath}, - {&a.NebulaCACredentials.CAKeyPEM, nebulaCertsCAKeyPath}, - {&a.GarageRPCSecret, garageRPCSecretPath}, - } - - for _, f := range filesToLoadAsString { - body, err := fs.ReadFile(adminFS, f.path) - if err != nil { - return Admin{}, fmt.Errorf("loading %q from fs: %w", f.path, err) - } - *f.into = string(body) - } - - return a, nil -} - -// FromReader reads an admin.tgz file from the given io.Reader. +// FromReader reads an admin.yml from the given io.Reader. func FromReader(r io.Reader) (Admin, error) { - - fs, err := tarutil.FSFromReader(r) - if err != nil { - return Admin{}, fmt.Errorf("reading admin.tgz: %w", err) - } - - return FromFS(fs) + var a Admin + err := yaml.NewDecoder(r).Decode(&a) + return a, err } -// WriteTo writes the Admin as a new admin.tgz to the given io.Writer. +// WriteTo writes the Admin as an admin.yml to the given io.Writer. func (a Admin) WriteTo(into io.Writer) error { - - w := tarutil.NewTGZWriter(into) - - filesToWriteAsYAML := []struct { - value interface{} - path string - }{ - {a.CreationParams, creationParamsPath}, - {a.GarageGlobalBucketS3APICredentials, garageGlobalBucketKeyYmlPath}, - {a.GarageAdminBucketS3APICredentials, garageAdminBucketKeyYmlPath}, - } - - for _, f := range filesToWriteAsYAML { - - b, err := yaml.Marshal(f.value) - if err != nil { - return fmt.Errorf("yaml encoding data for %q: %w", f.path, err) - } - - w.WriteFileBytes(f.path, b) - } - - filesToWriteAsString := []struct { - value string - path string - }{ - {a.NebulaCACredentials.CACertPEM, nebulaCertsCACertPath}, - {a.NebulaCACredentials.CAKeyPEM, nebulaCertsCAKeyPath}, - {a.GarageRPCSecret, garageRPCSecretPath}, - } - - for _, f := range filesToWriteAsString { - w.WriteFileBytes(f.path, []byte(f.value)) - } - - return w.Close() + return yaml.NewEncoder(into).Encode(a) } diff --git a/entrypoint/src/cmd/entrypoint/admin.go b/entrypoint/src/cmd/entrypoint/admin.go index 25b4a05..fe124d2 100644 --- a/entrypoint/src/cmd/entrypoint/admin.go +++ b/entrypoint/src/cmd/entrypoint/admin.go @@ -32,7 +32,7 @@ func readAdmin(path string) (admin.Admin, error) { adm, err := admin.FromReader(os.Stdin) if err != nil { - return admin.Admin{}, fmt.Errorf("parsing admin.tgz from stdin: %w", err) + return admin.Admin{}, fmt.Errorf("parsing admin.yml from stdin: %w", err) } return adm, nil @@ -49,7 +49,7 @@ func readAdmin(path string) (admin.Admin, error) { var subCmdAdminCreateNetwork = subCmd{ name: "create-network", - descr: "Creates a new cryptic-net network, outputting the resulting admin.tgz to stdout", + descr: "Creates a new cryptic-net network, outputting the resulting admin.yml to stdout", do: func(subCmdCtx subCmdCtx) error { flags := subCmdCtx.flagSet(false) @@ -178,7 +178,7 @@ var subCmdAdminCreateNetwork = subCmd{ fmt.Fprintln(os.Stderr, "starting child processes") go func() { // NOTE both stdout and stderr are sent to stderr, so that the user - // can pipe the resulting admin.tgz to stdout. + // can pipe the resulting admin.yml to stdout. pmuxlib.Run(ctx, os.Stderr, os.Stderr, pmuxConfig) close(pmuxDoneCh) }() @@ -209,18 +209,17 @@ var subCmdAdminCreateNetwork = subCmd{ return fmt.Errorf("initializing garage shared global bucket: %w", err) } - fmt.Fprintln(os.Stderr, "cluster initialized successfully, writing admin.tgz to stdout") + fmt.Fprintln(os.Stderr, "cluster initialized successfully, writing admin.yml to stdout") - err = admin.Admin{ - CreationParams: adminCreationParams, - NebulaCACredentials: nebulaCACreds, - GarageRPCSecret: hostBootstrap.GarageRPCSecret, - GarageGlobalBucketS3APICredentials: hostBootstrap.GarageGlobalBucketS3APICredentials, - GarageAdminBucketS3APICredentials: garage.NewS3APICredentials(), - }.WriteTo(os.Stdout) + adm := admin.Admin{ + CreationParams: adminCreationParams, + } + adm.Nebula.CACredentials = nebulaCACreds + adm.Garage.RPCSecret = hostBootstrap.GarageRPCSecret + adm.Garage.GlobalBucketS3APICredentials = hostBootstrap.GarageGlobalBucketS3APICredentials - if err != nil { - return fmt.Errorf("writing admin.tgz to stdout") + if err := adm.WriteTo(os.Stdout); err != nil { + return fmt.Errorf("writing admin.yml to stdout") } return nil @@ -247,7 +246,7 @@ var subCmdAdminMakeBootstrap = subCmd{ adminPath := flags.StringP( "admin-path", "a", "", - `Path to admin.tgz file. If the given path is "-" then stdin is used.`, + `Path to admin.yml file. If the given path is "-" then stdin is used.`, ) if err := flags.Parse(subCmdCtx.args); err != nil { @@ -270,7 +269,7 @@ var subCmdAdminMakeBootstrap = subCmd{ adm, err := readAdmin(*adminPath) if err != nil { - return fmt.Errorf("reading admin.tgz with --admin-path of %q: %w", *adminPath, err) + return fmt.Errorf("reading admin.yml with --admin-path of %q: %w", *adminPath, err) } hostBootstrap, err := loadHostBootstrap() @@ -278,7 +277,7 @@ var subCmdAdminMakeBootstrap = subCmd{ return fmt.Errorf("loading host bootstrap: %w", err) } - nebulaHostCreds, err := nebula.NewHostCredentials(adm.NebulaCACredentials, *name, ip) + nebulaHostCreds, err := nebula.NewHostCredentials(adm.Nebula.CACredentials, *name, ip) if err != nil { return fmt.Errorf("creating new nebula host key/cert: %w", err) } @@ -291,9 +290,9 @@ var subCmdAdminMakeBootstrap = subCmd{ NebulaHostCredentials: nebulaHostCreds, - GarageRPCSecret: adm.GarageRPCSecret, + GarageRPCSecret: adm.Garage.RPCSecret, GarageAdminToken: randStr(32), - GarageGlobalBucketS3APICredentials: adm.GarageGlobalBucketS3APICredentials, + GarageGlobalBucketS3APICredentials: adm.Garage.GlobalBucketS3APICredentials, } return newHostBootstrap.WriteTo(os.Stdout) diff --git a/entrypoint/src/nebula/nebula.go b/entrypoint/src/nebula/nebula.go index 4fe986c..56a80c1 100644 --- a/entrypoint/src/nebula/nebula.go +++ b/entrypoint/src/nebula/nebula.go @@ -24,16 +24,16 @@ var ErrInvalidSignature = errors.New("invalid signature") // HostCredentials contains the certificate and private key files which will // need to be present on a particular host. Each file is PEM encoded. type HostCredentials struct { - CACertPEM string - HostKeyPEM string - HostCertPEM string + CACertPEM string `yaml:"ca_cert_pem"` + HostKeyPEM string `yaml:"host_key_pem"` + HostCertPEM string `yaml:"host_cert_pem"` } // CACredentials contains the certificate and private files which can be used to // create and validate HostCredentials. Each file is PEM encoded. type CACredentials struct { - CACertPEM string - CAKeyPEM string + CACertPEM string `yaml:"ca_cert_pem"` + CAKeyPEM string `yaml:"ca_key_pem"` } // NewHostCredentials generates a new key/cert for a nebula host using the CA