Use yaml to encode admin file, not tgz
This commit is contained in:
parent
745fe31324
commit
7d95825f97
@ -37,7 +37,7 @@ which was configured when creating the network.
|
|||||||
|
|
||||||
## Step 3: Create a `bootstrap.tgz` File
|
## 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
|
To create a `bootstrap.tgz` file for the new host, the admin should perform the
|
||||||
following command from their own host:
|
following command from their own host:
|
||||||
@ -46,7 +46,7 @@ following command from their own host:
|
|||||||
cryptic-net hosts make-bootstrap \
|
cryptic-net hosts make-bootstrap \
|
||||||
--name <name> \
|
--name <name> \
|
||||||
--ip <ip> \
|
--ip <ip> \
|
||||||
--admin-path <path to admin.tgz> \
|
--admin-path <path to admin.yml> \
|
||||||
> bootstrap.tgz
|
> 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
|
`bootstrap.tgz` file can be given to them as-is, and they can proceed with
|
||||||
running their host's `cryptic-net daemon`.
|
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
|
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`:
|
generate a `bootstrap.tgz`:
|
||||||
|
|
||||||
```
|
```
|
||||||
gpg -d <path to admin.tgz.gpg> | cryptic-net hosts make-boostrap \
|
gpg -d <path to admin.yml.gpg> | cryptic-net hosts make-boostrap \
|
||||||
--name <name> \
|
--name <name> \
|
||||||
--ip <ip> \
|
--ip <ip> \
|
||||||
--admin-path - \
|
--admin-path - \
|
||||||
> bootstrap.tgz
|
> 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.
|
be read from stdin.
|
||||||
|
|
||||||
## Step 4: Optionally, Build Binary
|
## Step 4: Optionally, Build Binary
|
||||||
|
@ -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
|
package admin
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"cryptic-net/garage"
|
"cryptic-net/garage"
|
||||||
"cryptic-net/nebula"
|
"cryptic-net/nebula"
|
||||||
"cryptic-net/tarutil"
|
|
||||||
"cryptic-net/yamlutil"
|
|
||||||
"fmt"
|
|
||||||
"io"
|
"io"
|
||||||
"io/fs"
|
|
||||||
|
|
||||||
"gopkg.in/yaml.v3"
|
"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
|
// CreationParams are general parameters used when creating a new network. These
|
||||||
// are available to all hosts within the network via their bootstrap files.
|
// are available to all hosts within the network via their bootstrap files.
|
||||||
type CreationParams struct {
|
type CreationParams struct {
|
||||||
@ -31,105 +16,28 @@ type CreationParams struct {
|
|||||||
Domain string `yaml:"domain"`
|
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 {
|
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
|
Garage struct {
|
||||||
GarageGlobalBucketS3APICredentials garage.S3APICredentials
|
RPCSecret string `yaml:"rpc_secret"`
|
||||||
GarageAdminBucketS3APICredentials garage.S3APICredentials
|
GlobalBucketS3APICredentials garage.S3APICredentials `yaml:"global_bucket_s3_api_credentials"`
|
||||||
|
} `yaml:"garage"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// FromFS loads an Admin instance from the given fs.FS, which presumably
|
// FromReader reads an admin.yml from the given io.Reader.
|
||||||
// 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.
|
|
||||||
func FromReader(r io.Reader) (Admin, error) {
|
func FromReader(r io.Reader) (Admin, error) {
|
||||||
|
var a Admin
|
||||||
fs, err := tarutil.FSFromReader(r)
|
err := yaml.NewDecoder(r).Decode(&a)
|
||||||
if err != nil {
|
return a, err
|
||||||
return Admin{}, fmt.Errorf("reading admin.tgz: %w", err)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return FromFS(fs)
|
// WriteTo writes the Admin as an admin.yml to the given io.Writer.
|
||||||
}
|
|
||||||
|
|
||||||
// WriteTo writes the Admin as a new admin.tgz to the given io.Writer.
|
|
||||||
func (a Admin) WriteTo(into io.Writer) error {
|
func (a Admin) WriteTo(into io.Writer) error {
|
||||||
|
return yaml.NewEncoder(into).Encode(a)
|
||||||
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()
|
|
||||||
}
|
}
|
||||||
|
@ -32,7 +32,7 @@ func readAdmin(path string) (admin.Admin, error) {
|
|||||||
|
|
||||||
adm, err := admin.FromReader(os.Stdin)
|
adm, err := admin.FromReader(os.Stdin)
|
||||||
if err != nil {
|
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
|
return adm, nil
|
||||||
@ -49,7 +49,7 @@ func readAdmin(path string) (admin.Admin, error) {
|
|||||||
|
|
||||||
var subCmdAdminCreateNetwork = subCmd{
|
var subCmdAdminCreateNetwork = subCmd{
|
||||||
name: "create-network",
|
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 {
|
do: func(subCmdCtx subCmdCtx) error {
|
||||||
|
|
||||||
flags := subCmdCtx.flagSet(false)
|
flags := subCmdCtx.flagSet(false)
|
||||||
@ -178,7 +178,7 @@ var subCmdAdminCreateNetwork = subCmd{
|
|||||||
fmt.Fprintln(os.Stderr, "starting child processes")
|
fmt.Fprintln(os.Stderr, "starting child processes")
|
||||||
go func() {
|
go func() {
|
||||||
// NOTE both stdout and stderr are sent to stderr, so that the user
|
// 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)
|
pmuxlib.Run(ctx, os.Stderr, os.Stderr, pmuxConfig)
|
||||||
close(pmuxDoneCh)
|
close(pmuxDoneCh)
|
||||||
}()
|
}()
|
||||||
@ -209,18 +209,17 @@ var subCmdAdminCreateNetwork = subCmd{
|
|||||||
return fmt.Errorf("initializing garage shared global bucket: %w", err)
|
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{
|
adm := admin.Admin{
|
||||||
CreationParams: adminCreationParams,
|
CreationParams: adminCreationParams,
|
||||||
NebulaCACredentials: nebulaCACreds,
|
}
|
||||||
GarageRPCSecret: hostBootstrap.GarageRPCSecret,
|
adm.Nebula.CACredentials = nebulaCACreds
|
||||||
GarageGlobalBucketS3APICredentials: hostBootstrap.GarageGlobalBucketS3APICredentials,
|
adm.Garage.RPCSecret = hostBootstrap.GarageRPCSecret
|
||||||
GarageAdminBucketS3APICredentials: garage.NewS3APICredentials(),
|
adm.Garage.GlobalBucketS3APICredentials = hostBootstrap.GarageGlobalBucketS3APICredentials
|
||||||
}.WriteTo(os.Stdout)
|
|
||||||
|
|
||||||
if err != nil {
|
if err := adm.WriteTo(os.Stdout); err != nil {
|
||||||
return fmt.Errorf("writing admin.tgz to stdout")
|
return fmt.Errorf("writing admin.yml to stdout")
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
@ -247,7 +246,7 @@ var subCmdAdminMakeBootstrap = subCmd{
|
|||||||
|
|
||||||
adminPath := flags.StringP(
|
adminPath := flags.StringP(
|
||||||
"admin-path", "a", "",
|
"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 {
|
if err := flags.Parse(subCmdCtx.args); err != nil {
|
||||||
@ -270,7 +269,7 @@ var subCmdAdminMakeBootstrap = subCmd{
|
|||||||
|
|
||||||
adm, err := readAdmin(*adminPath)
|
adm, err := readAdmin(*adminPath)
|
||||||
if err != nil {
|
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()
|
hostBootstrap, err := loadHostBootstrap()
|
||||||
@ -278,7 +277,7 @@ var subCmdAdminMakeBootstrap = subCmd{
|
|||||||
return fmt.Errorf("loading host bootstrap: %w", err)
|
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 {
|
if err != nil {
|
||||||
return fmt.Errorf("creating new nebula host key/cert: %w", err)
|
return fmt.Errorf("creating new nebula host key/cert: %w", err)
|
||||||
}
|
}
|
||||||
@ -291,9 +290,9 @@ var subCmdAdminMakeBootstrap = subCmd{
|
|||||||
|
|
||||||
NebulaHostCredentials: nebulaHostCreds,
|
NebulaHostCredentials: nebulaHostCreds,
|
||||||
|
|
||||||
GarageRPCSecret: adm.GarageRPCSecret,
|
GarageRPCSecret: adm.Garage.RPCSecret,
|
||||||
GarageAdminToken: randStr(32),
|
GarageAdminToken: randStr(32),
|
||||||
GarageGlobalBucketS3APICredentials: adm.GarageGlobalBucketS3APICredentials,
|
GarageGlobalBucketS3APICredentials: adm.Garage.GlobalBucketS3APICredentials,
|
||||||
}
|
}
|
||||||
|
|
||||||
return newHostBootstrap.WriteTo(os.Stdout)
|
return newHostBootstrap.WriteTo(os.Stdout)
|
||||||
|
@ -24,16 +24,16 @@ var ErrInvalidSignature = errors.New("invalid signature")
|
|||||||
// HostCredentials contains the certificate and private key files which will
|
// HostCredentials contains the certificate and private key files which will
|
||||||
// need to be present on a particular host. Each file is PEM encoded.
|
// need to be present on a particular host. Each file is PEM encoded.
|
||||||
type HostCredentials struct {
|
type HostCredentials struct {
|
||||||
CACertPEM string
|
CACertPEM string `yaml:"ca_cert_pem"`
|
||||||
HostKeyPEM string
|
HostKeyPEM string `yaml:"host_key_pem"`
|
||||||
HostCertPEM string
|
HostCertPEM string `yaml:"host_cert_pem"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// CACredentials contains the certificate and private files which can be used to
|
// CACredentials contains the certificate and private files which can be used to
|
||||||
// create and validate HostCredentials. Each file is PEM encoded.
|
// create and validate HostCredentials. Each file is PEM encoded.
|
||||||
type CACredentials struct {
|
type CACredentials struct {
|
||||||
CACertPEM string
|
CACertPEM string `yaml:"ca_cert_pem"`
|
||||||
CAKeyPEM string
|
CAKeyPEM string `yaml:"ca_key_pem"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewHostCredentials generates a new key/cert for a nebula host using the CA
|
// NewHostCredentials generates a new key/cert for a nebula host using the CA
|
||||||
|
Loading…
Reference in New Issue
Block a user