Use yaml instead of tgz for bootstrap file
This commit is contained in:
parent
7d95825f97
commit
3ac86e07cf
@ -17,7 +17,7 @@ in rec {
|
|||||||
builder = builtins.toFile "builder.sh" ''
|
builder = builtins.toFile "builder.sh" ''
|
||||||
source $stdenv/setup
|
source $stdenv/setup
|
||||||
mkdir -p "$out"/share
|
mkdir -p "$out"/share
|
||||||
cp "$src" "$out"/share/bootstrap.tgz
|
cp "$src" "$out"/share/bootstrap.yml
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -7,12 +7,12 @@ wishes to add.
|
|||||||
There are two ways for a user to add a host to the cryptic-net network.
|
There are two ways for a user to add a host to the cryptic-net network.
|
||||||
|
|
||||||
- If the user is savy enough to obtain their own `cryptic-net` binary, they can
|
- If the user is savy enough to obtain their own `cryptic-net` binary, they can
|
||||||
do so. The admin can then generate a `bootstrap.tgz` file for their host,
|
do so. The admin can then generate a `bootstrap.yml` file for their host,
|
||||||
give that to the user, and the user can run `cryptic-net daemon` using that
|
give that to the user, and the user can run `cryptic-net daemon` using that
|
||||||
bootstrap file.
|
bootstrap file.
|
||||||
|
|
||||||
- If the user is not so savy, the admin can generate a custom `cryptic-net`
|
- If the user is not so savy, the admin can generate a custom `cryptic-net`
|
||||||
binary with the `bootstrap.tgz` embedded into it. The user can be given this
|
binary with the `bootstrap.yml` embedded into it. The user can be given this
|
||||||
binary and run `cryptic-net daemon` without any configuration on their end.
|
binary and run `cryptic-net daemon` without any configuration on their end.
|
||||||
|
|
||||||
From the admin's perspective the only difference between these cases is one
|
From the admin's perspective the only difference between these cases is one
|
||||||
@ -35,11 +35,11 @@ The admin should choose an IP for the host. The IP you choose for the new host
|
|||||||
should be one which is not yet used by any other host, and which is in subnet
|
should be one which is not yet used by any other host, and which is in subnet
|
||||||
which was configured when creating the network.
|
which was configured when creating the network.
|
||||||
|
|
||||||
## Step 3: Create a `bootstrap.tgz` File
|
## Step 3: Create a `bootstrap.yml` File
|
||||||
|
|
||||||
Access to an `admin.yml` 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.yml` file for the new host, the admin should perform the
|
||||||
following command from their own host:
|
following command from their own host:
|
||||||
|
|
||||||
```
|
```
|
||||||
@ -47,15 +47,15 @@ cryptic-net hosts make-bootstrap \
|
|||||||
--name <name> \
|
--name <name> \
|
||||||
--ip <ip> \
|
--ip <ip> \
|
||||||
--admin-path <path to admin.yml> \
|
--admin-path <path to admin.yml> \
|
||||||
> bootstrap.tgz
|
> bootstrap.yml
|
||||||
```
|
```
|
||||||
|
|
||||||
The resulting `bootstrap.tgz` file should be treated as a secret file that is
|
The resulting `bootstrap.yml` file should be treated as a secret file that is
|
||||||
shared only with the user it was generated for. The `bootstrap.tgz` file should
|
shared only with the user it was generated for. The `bootstrap.yml` file should
|
||||||
not be re-used between hosts either.
|
not be re-used between hosts either.
|
||||||
|
|
||||||
If the user already has access to a `cryptic-net` binary then the new
|
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.yml` 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.yml`
|
### Encrypted `admin.yml`
|
||||||
@ -63,14 +63,14 @@ running their host's `cryptic-net daemon`.
|
|||||||
If `admin.yml` 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.yml` 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.yml`:
|
||||||
|
|
||||||
```
|
```
|
||||||
gpg -d <path to admin.yml.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.yml
|
||||||
```
|
```
|
||||||
|
|
||||||
Note that the value of `--admin-path` is `-`, indicating that `admin.yml` should
|
Note that the value of `--admin-path` is `-`, indicating that `admin.yml` should
|
||||||
@ -78,14 +78,14 @@ be read from stdin.
|
|||||||
|
|
||||||
## Step 4: Optionally, Build Binary
|
## Step 4: Optionally, Build Binary
|
||||||
|
|
||||||
If you wish to embed the `bootstrap.tgz` into a custom binary for the user (to
|
If you wish to embed the `bootstrap.yml` into a custom binary for the user (to
|
||||||
make installation _extremely_ easy for them) then you can run the following:
|
make installation _extremely_ easy for them) then you can run the following:
|
||||||
|
|
||||||
```
|
```
|
||||||
nix-build --arg bootstrap <path to bootstrap.tgz> -A appImage
|
nix-build --arg bootstrap <path to bootstrap.yml> -A appImage
|
||||||
```
|
```
|
||||||
|
|
||||||
The resulting binary can be found in the `result` directory which is created.
|
The resulting binary can be found in the `result` directory which is created.
|
||||||
|
|
||||||
This binary should be treated like a `bootstrap.tgz` in terms of its uniqueness
|
This binary should be treated like a `bootstrap.yml` in terms of its uniqueness
|
||||||
and sensitivity.
|
and sensitivity.
|
||||||
|
@ -15,8 +15,8 @@ state AppDir {
|
|||||||
entrypoint : * Create runtime dir at $_RUNTIME_DIR_PATH
|
entrypoint : * Create runtime dir at $_RUNTIME_DIR_PATH
|
||||||
entrypoint : * Lock runtime dir
|
entrypoint : * Lock runtime dir
|
||||||
entrypoint : * Merge given and default daemon.yml files
|
entrypoint : * Merge given and default daemon.yml files
|
||||||
entrypoint : * Copy bootstrap.tgz into $_DATA_DIR_PATH, if it's not there
|
entrypoint : * Copy bootstrap.yml into $_DATA_DIR_PATH, if it's not there
|
||||||
entrypoint : * Merge daemon.yml config into bootstrap.tgz
|
entrypoint : * Merge daemon.yml config into bootstrap.yml
|
||||||
entrypoint : * Create $_RUNTIME_DIR_PATH/dnsmasq.conf
|
entrypoint : * Create $_RUNTIME_DIR_PATH/dnsmasq.conf
|
||||||
entrypoint : * Create $_RUNTIME_DIR_PATH/nebula.yml
|
entrypoint : * Create $_RUNTIME_DIR_PATH/nebula.yml
|
||||||
entrypoint : * Create $_RUNTIME_DIR_PATH/garage-N.toml\n (one per storage allocation)
|
entrypoint : * Create $_RUNTIME_DIR_PATH/garage-N.toml\n (one per storage allocation)
|
||||||
|
File diff suppressed because one or more lines are too long
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 11 KiB |
@ -63,7 +63,7 @@ would be great. Such a mobile app could be based on the existing
|
|||||||
[mobile_nebula](https://github.com/DefinedNet/mobile_nebula). The main changes
|
[mobile_nebula](https://github.com/DefinedNet/mobile_nebula). The main changes
|
||||||
needed would be:
|
needed would be:
|
||||||
|
|
||||||
- Allow importing a `bootstrap.tgz` file, rather than requiring manual setup by
|
- Allow importing a `bootstrap.yml` file, rather than requiring manual setup by
|
||||||
users.
|
users.
|
||||||
|
|
||||||
- Set device's DNS settings. There is an [open
|
- Set device's DNS settings. There is an [open
|
||||||
|
@ -37,7 +37,7 @@ variable for `nix-daemon` (see [this github issue][tmpdir-gh].))
|
|||||||
|
|
||||||
The resulting binary can be found in the `result` directory which is created.
|
The resulting binary can be found in the `result` directory which is created.
|
||||||
|
|
||||||
In this case you will need an admin to provide you with a `bootstrap.tgz` for
|
In this case you will need an admin to provide you with a `bootstrap.yml` for
|
||||||
your host, rather than a custom binary. When running the daemon in the following
|
your host, rather than a custom binary. When running the daemon in the following
|
||||||
steps you will need to provide the `--bootstrap-path` CLI argument to the daemon
|
steps you will need to provide the `--bootstrap-path` CLI argument to the daemon
|
||||||
process.
|
process.
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
// Package bootstrap deals with the parsing and creation of bootstrap.tgz files.
|
// Package bootstrap deals with the parsing and creation of bootstrap.yml files.
|
||||||
// It also contains some helpers which rely on bootstrap data.
|
// It also contains some helpers which rely on bootstrap data.
|
||||||
package bootstrap
|
package bootstrap
|
||||||
|
|
||||||
@ -6,12 +6,9 @@ import (
|
|||||||
"cryptic-net/admin"
|
"cryptic-net/admin"
|
||||||
"cryptic-net/garage"
|
"cryptic-net/garage"
|
||||||
"cryptic-net/nebula"
|
"cryptic-net/nebula"
|
||||||
"cryptic-net/tarutil"
|
|
||||||
"cryptic-net/yamlutil"
|
|
||||||
"crypto/sha512"
|
"crypto/sha512"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"io/fs"
|
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"sort"
|
"sort"
|
||||||
@ -19,101 +16,45 @@ import (
|
|||||||
"gopkg.in/yaml.v3"
|
"gopkg.in/yaml.v3"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Paths within the bootstrap FS which for general data.
|
|
||||||
const (
|
|
||||||
adminCreationParamsPath = "admin/creation-params.yml"
|
|
||||||
hostNamePath = "hostname"
|
|
||||||
)
|
|
||||||
|
|
||||||
// DataDirPath returns the path within the user's data directory where the
|
// DataDirPath returns the path within the user's data directory where the
|
||||||
// bootstrap file is stored.
|
// bootstrap file is stored.
|
||||||
func DataDirPath(dataDirPath string) string {
|
func DataDirPath(dataDirPath string) string {
|
||||||
return filepath.Join(dataDirPath, "bootstrap.tgz")
|
return filepath.Join(dataDirPath, "bootstrap.yml")
|
||||||
}
|
}
|
||||||
|
|
||||||
// AppDirPath returns the path within the AppDir where an embedded bootstrap
|
// AppDirPath returns the path within the AppDir where an embedded bootstrap
|
||||||
// file might be found.
|
// file might be found.
|
||||||
func AppDirPath(appDirPath string) string {
|
func AppDirPath(appDirPath string) string {
|
||||||
return filepath.Join(appDirPath, "share/bootstrap.tgz")
|
return filepath.Join(appDirPath, "share/bootstrap.yml")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Bootstrap is used for accessing all information contained within a
|
// Bootstrap is used for accessing all information contained within a
|
||||||
// bootstrap.tgz file.
|
// bootstrap.yml file.
|
||||||
type Bootstrap struct {
|
type Bootstrap struct {
|
||||||
AdminCreationParams admin.CreationParams
|
AdminCreationParams admin.CreationParams `yaml:"admin_creation_params"`
|
||||||
|
|
||||||
Hosts map[string]Host
|
Hosts map[string]Host `yaml:"hosts"`
|
||||||
HostName string
|
HostName string `yaml:"hostname"`
|
||||||
|
|
||||||
NebulaHostCredentials nebula.HostCredentials
|
Nebula struct {
|
||||||
|
HostCredentials nebula.HostCredentials `yaml:"host_credentials"`
|
||||||
|
} `yaml:"nebula"`
|
||||||
|
|
||||||
GarageRPCSecret string
|
Garage struct {
|
||||||
GarageAdminToken string
|
RPCSecret string `yaml:"rpc_secret"`
|
||||||
GarageGlobalBucketS3APICredentials garage.S3APICredentials
|
AdminToken string `yaml:"admin_token"`
|
||||||
|
GlobalBucketS3APICredentials garage.S3APICredentials `yaml:"global_bucket_s3_api_credentials"`
|
||||||
|
} `yaml:"garage"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// FromFS loads a Boostrap instance from the given fs.FS, which presumably
|
// FromReader reads a bootstrap.yml file from the given io.Reader.
|
||||||
// represents the file structure of a bootstrap.tgz file.
|
|
||||||
func FromFS(bootstrapFS fs.FS) (Bootstrap, error) {
|
|
||||||
|
|
||||||
var (
|
|
||||||
b Bootstrap
|
|
||||||
err error
|
|
||||||
)
|
|
||||||
|
|
||||||
if b.Hosts, err = loadHosts(bootstrapFS); err != nil {
|
|
||||||
return Bootstrap{}, fmt.Errorf("loading hosts info from fs: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
filesToLoadAsYAML := []struct {
|
|
||||||
into interface{}
|
|
||||||
path string
|
|
||||||
}{
|
|
||||||
{&b.AdminCreationParams, adminCreationParamsPath},
|
|
||||||
{&b.GarageGlobalBucketS3APICredentials, garageGlobalBucketKeyYmlPath},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, f := range filesToLoadAsYAML {
|
|
||||||
if err := yamlutil.LoadYamlFSFile(f.into, bootstrapFS, f.path); err != nil {
|
|
||||||
return Bootstrap{}, fmt.Errorf("loading %q from fs: %w", f.path, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
filesToLoadAsString := []struct {
|
|
||||||
into *string
|
|
||||||
path string
|
|
||||||
}{
|
|
||||||
{&b.HostName, hostNamePath},
|
|
||||||
{&b.NebulaHostCredentials.CACertPEM, nebulaCertsCACertPath},
|
|
||||||
{&b.NebulaHostCredentials.HostCertPEM, nebulaCertsHostCertPath},
|
|
||||||
{&b.NebulaHostCredentials.HostKeyPEM, nebulaCertsHostKeyPath},
|
|
||||||
{&b.GarageRPCSecret, garageRPCSecretPath},
|
|
||||||
{&b.GarageAdminToken, garageAdminTokenPath},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, f := range filesToLoadAsString {
|
|
||||||
body, err := fs.ReadFile(bootstrapFS, f.path)
|
|
||||||
if err != nil {
|
|
||||||
return Bootstrap{}, fmt.Errorf("loading %q from fs: %w", f.path, err)
|
|
||||||
}
|
|
||||||
*f.into = string(body)
|
|
||||||
}
|
|
||||||
|
|
||||||
return b, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// FromReader reads a bootstrap.tgz file from the given io.Reader.
|
|
||||||
func FromReader(r io.Reader) (Bootstrap, error) {
|
func FromReader(r io.Reader) (Bootstrap, error) {
|
||||||
|
var b Bootstrap
|
||||||
fs, err := tarutil.FSFromReader(r)
|
err := yaml.NewDecoder(r).Decode(&b)
|
||||||
if err != nil {
|
return b, err
|
||||||
return Bootstrap{}, fmt.Errorf("reading bootstrap.tgz: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return FromFS(fs)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// FromFile reads a bootstrap.tgz from a file at the given path.
|
// FromFile reads a bootstrap.yml from a file at the given path.
|
||||||
func FromFile(path string) (Bootstrap, error) {
|
func FromFile(path string) (Bootstrap, error) {
|
||||||
|
|
||||||
f, err := os.Open(path)
|
f, err := os.Open(path)
|
||||||
@ -125,58 +66,9 @@ func FromFile(path string) (Bootstrap, error) {
|
|||||||
return FromReader(f)
|
return FromReader(f)
|
||||||
}
|
}
|
||||||
|
|
||||||
// WriteTo writes the Bootstrap as a new bootstrap.tgz to the given io.Writer.
|
// WriteTo writes the Bootstrap as a new bootstrap.yml to the given io.Writer.
|
||||||
func (b Bootstrap) WriteTo(into io.Writer) error {
|
func (b Bootstrap) WriteTo(into io.Writer) error {
|
||||||
|
return yaml.NewEncoder(into).Encode(b)
|
||||||
w := tarutil.NewTGZWriter(into)
|
|
||||||
|
|
||||||
for _, host := range b.Hosts {
|
|
||||||
|
|
||||||
hostB, err := yaml.Marshal(host)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("yaml encoding host %#v: %w", host, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
path := filepath.Join(hostsDirPath, host.Name+".yml")
|
|
||||||
|
|
||||||
w.WriteFileBytes(path, hostB)
|
|
||||||
}
|
|
||||||
|
|
||||||
filesToWriteAsYAML := []struct {
|
|
||||||
value interface{}
|
|
||||||
path string
|
|
||||||
}{
|
|
||||||
{b.AdminCreationParams, adminCreationParamsPath},
|
|
||||||
{b.GarageGlobalBucketS3APICredentials, garageGlobalBucketKeyYmlPath},
|
|
||||||
}
|
|
||||||
|
|
||||||
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
|
|
||||||
}{
|
|
||||||
{b.HostName, hostNamePath},
|
|
||||||
{b.NebulaHostCredentials.CACertPEM, nebulaCertsCACertPath},
|
|
||||||
{b.NebulaHostCredentials.HostCertPEM, nebulaCertsHostCertPath},
|
|
||||||
{b.NebulaHostCredentials.HostKeyPEM, nebulaCertsHostKeyPath},
|
|
||||||
{b.GarageRPCSecret, garageRPCSecretPath},
|
|
||||||
{b.GarageAdminToken, garageAdminTokenPath},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, f := range filesToWriteAsString {
|
|
||||||
w.WriteFileBytes(f.path, []byte(f.value))
|
|
||||||
}
|
|
||||||
|
|
||||||
return w.Close()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ThisHost is a shortcut for b.Hosts[b.HostName], but will panic if the
|
// ThisHost is a shortcut for b.Hosts[b.HostName], but will panic if the
|
||||||
|
@ -4,13 +4,6 @@ import (
|
|||||||
"cryptic-net/garage"
|
"cryptic-net/garage"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Paths within the bootstrap FS related to garage.
|
|
||||||
const (
|
|
||||||
garageRPCSecretPath = "garage/rpc-secret.txt"
|
|
||||||
garageAdminTokenPath = "garage/admin-token.txt"
|
|
||||||
garageGlobalBucketKeyYmlPath = "garage/cryptic-net-global-bucket-key.yml"
|
|
||||||
)
|
|
||||||
|
|
||||||
// GaragePeers returns a Peer for each known garage instance in the network.
|
// GaragePeers returns a Peer for each known garage instance in the network.
|
||||||
func (b Bootstrap) GaragePeers() []garage.RemotePeer {
|
func (b Bootstrap) GaragePeers() []garage.RemotePeer {
|
||||||
|
|
||||||
@ -77,6 +70,6 @@ func (b Bootstrap) ChooseGaragePeer() garage.RemotePeer {
|
|||||||
// the global bucket.
|
// the global bucket.
|
||||||
func (b Bootstrap) GlobalBucketS3APIClient() garage.S3APIClient {
|
func (b Bootstrap) GlobalBucketS3APIClient() garage.S3APIClient {
|
||||||
addr := b.ChooseGaragePeer().S3APIAddr()
|
addr := b.ChooseGaragePeer().S3APIAddr()
|
||||||
creds := b.GarageGlobalBucketS3APICredentials
|
creds := b.Garage.GlobalBucketS3APICredentials
|
||||||
return garage.NewS3APIClient(addr, creds)
|
return garage.NewS3APIClient(addr, creds)
|
||||||
}
|
}
|
||||||
|
@ -33,7 +33,7 @@ func (b Bootstrap) PutGarageBoostrapHost(ctx context.Context) error {
|
|||||||
|
|
||||||
buf := new(bytes.Buffer)
|
buf := new(bytes.Buffer)
|
||||||
|
|
||||||
err = nebula.SignAndWrap(buf, b.NebulaHostCredentials.HostKeyPEM, hostB)
|
err = nebula.SignAndWrap(buf, b.Nebula.HostCredentials.HostKeyPEM, hostB)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("signing encoded host data: %w", err)
|
return fmt.Errorf("signing encoded host data: %w", err)
|
||||||
}
|
}
|
||||||
@ -82,7 +82,7 @@ func (b Bootstrap) GetGarageBootstrapHosts(
|
|||||||
map[string]Host, error,
|
map[string]Host, error,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
caCertPEM := b.NebulaHostCredentials.CACertPEM
|
caCertPEM := b.Nebula.HostCredentials.CACertPEM
|
||||||
client := b.GlobalBucketS3APIClient()
|
client := b.GlobalBucketS3APIClient()
|
||||||
|
|
||||||
hosts := map[string]Host{}
|
hosts := map[string]Host{}
|
||||||
@ -124,12 +124,12 @@ func (b Bootstrap) GetGarageBootstrapHosts(
|
|||||||
hostCertPEM := host.Nebula.CertPEM
|
hostCertPEM := host.Nebula.CertPEM
|
||||||
|
|
||||||
if err := nebula.ValidateSignature(hostCertPEM, hostB, sig); err != nil {
|
if err := nebula.ValidateSignature(hostCertPEM, hostB, sig); err != nil {
|
||||||
fmt.Fprintf(os.Stderr, "invalid host data for %q: %w\n", objInfo.Key, err)
|
fmt.Fprintf(os.Stderr, "invalid host data for %q: %v\n", objInfo.Key, err)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := nebula.ValidateHostCertPEM(caCertPEM, hostCertPEM); err != nil {
|
if err := nebula.ValidateHostCertPEM(caCertPEM, hostCertPEM); err != nil {
|
||||||
fmt.Fprintf(os.Stderr, "invalid nebula cert for %q: %w\n", objInfo.Key, err)
|
fmt.Fprintf(os.Stderr, "invalid nebula cert for %q: %v\n", objInfo.Key, err)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3,16 +3,7 @@ package bootstrap
|
|||||||
import (
|
import (
|
||||||
"cryptic-net/nebula"
|
"cryptic-net/nebula"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/fs"
|
|
||||||
"net"
|
"net"
|
||||||
"path/filepath"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"gopkg.in/yaml.v3"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
hostsDirPath = "hosts"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// NebulaHost describes the nebula configuration of a Host which is relevant for
|
// NebulaHost describes the nebula configuration of a Host which is relevant for
|
||||||
@ -54,43 +45,3 @@ func (h Host) IP() net.IP {
|
|||||||
|
|
||||||
return ip
|
return ip
|
||||||
}
|
}
|
||||||
|
|
||||||
func loadHosts(bootstrapFS fs.FS) (map[string]Host, error) {
|
|
||||||
|
|
||||||
hosts := map[string]Host{}
|
|
||||||
|
|
||||||
readAsYaml := func(into interface{}, path string) error {
|
|
||||||
b, err := fs.ReadFile(bootstrapFS, path)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("reading file from fs: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return yaml.Unmarshal(b, into)
|
|
||||||
}
|
|
||||||
|
|
||||||
globPath := filepath.Join(hostsDirPath, "*.yml")
|
|
||||||
|
|
||||||
hostPaths, err := fs.Glob(bootstrapFS, globPath)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("listing host files at %q in fs: %w", globPath, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, hostPath := range hostPaths {
|
|
||||||
|
|
||||||
hostName := filepath.Base(hostPath)
|
|
||||||
hostName = strings.TrimSuffix(hostName, filepath.Ext(hostName))
|
|
||||||
|
|
||||||
var host Host
|
|
||||||
if err := readAsYaml(&host, hostPath); err != nil {
|
|
||||||
return nil, fmt.Errorf("reading %q as yaml: %w", hostPath, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
hosts[hostName] = host
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(hosts) == 0 {
|
|
||||||
return nil, fmt.Errorf("failed to load any hosts from fs")
|
|
||||||
}
|
|
||||||
|
|
||||||
return hosts, nil
|
|
||||||
}
|
|
||||||
|
@ -1,8 +0,0 @@
|
|||||||
package bootstrap
|
|
||||||
|
|
||||||
// Paths within the bootstrap FS related to nebula.
|
|
||||||
const (
|
|
||||||
nebulaCertsCACertPath = "nebula/certs/ca.crt"
|
|
||||||
nebulaCertsHostCertPath = "nebula/certs/host.crt"
|
|
||||||
nebulaCertsHostKeyPath = "nebula/certs/host.key"
|
|
||||||
)
|
|
@ -143,12 +143,13 @@ var subCmdAdminCreateNetwork = subCmd{
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
HostName: *hostName,
|
HostName: *hostName,
|
||||||
NebulaHostCredentials: nebulaHostCreds,
|
|
||||||
GarageRPCSecret: randStr(32),
|
|
||||||
GarageAdminToken: randStr(32),
|
|
||||||
GarageGlobalBucketS3APICredentials: garage.NewS3APICredentials(),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
hostBootstrap.Nebula.HostCredentials = nebulaHostCreds
|
||||||
|
hostBootstrap.Garage.RPCSecret = randStr(32)
|
||||||
|
hostBootstrap.Garage.AdminToken = randStr(32)
|
||||||
|
hostBootstrap.Garage.GlobalBucketS3APICredentials = garage.NewS3APICredentials()
|
||||||
|
|
||||||
if hostBootstrap, err = mergeDaemonConfigIntoBootstrap(hostBootstrap, daemonConfig); err != nil {
|
if hostBootstrap, err = mergeDaemonConfigIntoBootstrap(hostBootstrap, daemonConfig); err != nil {
|
||||||
return fmt.Errorf("merging daemon config into bootstrap data: %w", err)
|
return fmt.Errorf("merging daemon config into bootstrap data: %w", err)
|
||||||
}
|
}
|
||||||
@ -215,8 +216,8 @@ var subCmdAdminCreateNetwork = subCmd{
|
|||||||
CreationParams: adminCreationParams,
|
CreationParams: adminCreationParams,
|
||||||
}
|
}
|
||||||
adm.Nebula.CACredentials = nebulaCACreds
|
adm.Nebula.CACredentials = nebulaCACreds
|
||||||
adm.Garage.RPCSecret = hostBootstrap.GarageRPCSecret
|
adm.Garage.RPCSecret = hostBootstrap.Garage.RPCSecret
|
||||||
adm.Garage.GlobalBucketS3APICredentials = hostBootstrap.GarageGlobalBucketS3APICredentials
|
adm.Garage.GlobalBucketS3APICredentials = hostBootstrap.Garage.GlobalBucketS3APICredentials
|
||||||
|
|
||||||
if err := adm.WriteTo(os.Stdout); err != nil {
|
if err := adm.WriteTo(os.Stdout); err != nil {
|
||||||
return fmt.Errorf("writing admin.yml to stdout")
|
return fmt.Errorf("writing admin.yml to stdout")
|
||||||
@ -228,7 +229,7 @@ var subCmdAdminCreateNetwork = subCmd{
|
|||||||
|
|
||||||
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.yml file for a particular host and writes it to stdout",
|
||||||
checkLock: true,
|
checkLock: true,
|
||||||
do: func(subCmdCtx subCmdCtx) error {
|
do: func(subCmdCtx subCmdCtx) error {
|
||||||
|
|
||||||
@ -236,7 +237,7 @@ var subCmdAdminMakeBootstrap = subCmd{
|
|||||||
|
|
||||||
name := flags.StringP(
|
name := flags.StringP(
|
||||||
"name", "n", "",
|
"name", "n", "",
|
||||||
"Name of the host to generate bootstrap.tgz for",
|
"Name of the host to generate bootstrap.yml for",
|
||||||
)
|
)
|
||||||
|
|
||||||
ipStr := flags.StringP(
|
ipStr := flags.StringP(
|
||||||
@ -287,14 +288,13 @@ var subCmdAdminMakeBootstrap = subCmd{
|
|||||||
|
|
||||||
Hosts: hostBootstrap.Hosts,
|
Hosts: hostBootstrap.Hosts,
|
||||||
HostName: *name,
|
HostName: *name,
|
||||||
|
|
||||||
NebulaHostCredentials: nebulaHostCreds,
|
|
||||||
|
|
||||||
GarageRPCSecret: adm.Garage.RPCSecret,
|
|
||||||
GarageAdminToken: randStr(32),
|
|
||||||
GarageGlobalBucketS3APICredentials: adm.Garage.GlobalBucketS3APICredentials,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
newHostBootstrap.Nebula.HostCredentials = nebulaHostCreds
|
||||||
|
newHostBootstrap.Garage.RPCSecret = adm.Garage.RPCSecret
|
||||||
|
newHostBootstrap.Garage.AdminToken = randStr(32)
|
||||||
|
newHostBootstrap.Garage.GlobalBucketS3APICredentials = adm.Garage.GlobalBucketS3APICredentials
|
||||||
|
|
||||||
return newHostBootstrap.WriteTo(os.Stdout)
|
return newHostBootstrap.WriteTo(os.Stdout)
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -213,7 +213,7 @@ var subCmdDaemon = subCmd{
|
|||||||
|
|
||||||
bootstrapPath := flags.StringP(
|
bootstrapPath := flags.StringP(
|
||||||
"bootstrap-path", "b", "",
|
"bootstrap-path", "b", "",
|
||||||
`Path to a bootstrap.tgz file. This only needs to be provided the first time the daemon is started, after that it is ignored. If the cryptic-net binary has a bootstrap built into it then this argument is always optional.`,
|
`Path to a bootstrap.yml file. This only needs to be provided the first time the daemon is started, after that it is ignored. If the cryptic-net binary has a bootstrap built into it then this argument is always optional.`,
|
||||||
)
|
)
|
||||||
|
|
||||||
if err := flags.Parse(subCmdCtx.args); err != nil {
|
if err := flags.Parse(subCmdCtx.args); err != nil {
|
||||||
@ -249,7 +249,7 @@ var subCmdDaemon = subCmd{
|
|||||||
return false
|
return false
|
||||||
|
|
||||||
} else if err != nil {
|
} else if err != nil {
|
||||||
err = fmt.Errorf("parsing bootstrap.tgz at %q: %w", path, err)
|
err = fmt.Errorf("parsing bootstrap.yml at %q: %w", path, err)
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -262,17 +262,17 @@ var subCmdDaemon = subCmd{
|
|||||||
foundHostBootstrap = !foundHostBootstrap && tryLoadBootstrap(bootstrapAppDirPath)
|
foundHostBootstrap = !foundHostBootstrap && tryLoadBootstrap(bootstrapAppDirPath)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("attempting to load bootstrap.tgz file: %w", err)
|
return fmt.Errorf("attempting to load bootstrap.yml file: %w", err)
|
||||||
|
|
||||||
} else if !foundHostBootstrap {
|
} else if !foundHostBootstrap {
|
||||||
return errors.New("No bootstrap.tgz file could be found, and one is not provided with --bootstrap-path")
|
return errors.New("No bootstrap.yml file could be found, and one is not provided with --bootstrap-path")
|
||||||
|
|
||||||
} else if hostBootstrapPath != bootstrapDataDirPath {
|
} else if hostBootstrapPath != bootstrapDataDirPath {
|
||||||
|
|
||||||
// If the bootstrap file is not being stored in the data dir, copy
|
// If the bootstrap file is not being stored in the data dir, copy
|
||||||
// it there, so it can be loaded from there next time.
|
// it there, so it can be loaded from there next time.
|
||||||
if err := writeBootstrapToDataDir(hostBootstrap); err != nil {
|
if err := writeBootstrapToDataDir(hostBootstrap); err != nil {
|
||||||
return fmt.Errorf("writing bootstrap.tgz to data dir: %w", err)
|
return fmt.Errorf("writing bootstrap.yml to data dir: %w", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -38,11 +38,11 @@ var subCmdGarageMC = subCmd{
|
|||||||
if *keyID == "" || *keySecret == "" {
|
if *keyID == "" || *keySecret == "" {
|
||||||
|
|
||||||
if *keyID == "" {
|
if *keyID == "" {
|
||||||
*keyID = hostBootstrap.GarageGlobalBucketS3APICredentials.ID
|
*keyID = hostBootstrap.Garage.GlobalBucketS3APICredentials.ID
|
||||||
}
|
}
|
||||||
|
|
||||||
if *keySecret == "" {
|
if *keySecret == "" {
|
||||||
*keyID = hostBootstrap.GarageGlobalBucketS3APICredentials.Secret
|
*keyID = hostBootstrap.Garage.GlobalBucketS3APICredentials.Secret
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -97,7 +97,7 @@ var subCmdGarageCLI = subCmd{
|
|||||||
cliEnv = append(
|
cliEnv = append(
|
||||||
os.Environ(),
|
os.Environ(),
|
||||||
"GARAGE_RPC_HOST="+hostBootstrap.ChooseGaragePeer().RPCAddr(),
|
"GARAGE_RPC_HOST="+hostBootstrap.ChooseGaragePeer().RPCAddr(),
|
||||||
"GARAGE_RPC_SECRET="+hostBootstrap.GarageRPCSecret,
|
"GARAGE_RPC_SECRET="+hostBootstrap.Garage.RPCSecret,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -26,7 +26,7 @@ func newGarageAdminClient(
|
|||||||
thisHost.IP().String(),
|
thisHost.IP().String(),
|
||||||
strconv.Itoa(daemonConfig.Storage.Allocations[0].AdminPort),
|
strconv.Itoa(daemonConfig.Storage.Allocations[0].AdminPort),
|
||||||
),
|
),
|
||||||
hostBootstrap.GarageAdminToken,
|
hostBootstrap.Garage.AdminToken,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -53,7 +53,7 @@ func waitForGarageAndNebula(
|
|||||||
|
|
||||||
adminClient := garage.NewAdminClient(
|
adminClient := garage.NewAdminClient(
|
||||||
adminAddr,
|
adminAddr,
|
||||||
hostBootstrap.GarageAdminToken,
|
hostBootstrap.Garage.AdminToken,
|
||||||
)
|
)
|
||||||
|
|
||||||
if err := adminClient.Wait(ctx); err != nil {
|
if err := adminClient.Wait(ctx); err != nil {
|
||||||
@ -112,8 +112,8 @@ func garageWriteChildConfig(
|
|||||||
MetaPath: alloc.MetaPath,
|
MetaPath: alloc.MetaPath,
|
||||||
DataPath: alloc.DataPath,
|
DataPath: alloc.DataPath,
|
||||||
|
|
||||||
RPCSecret: hostBootstrap.GarageRPCSecret,
|
RPCSecret: hostBootstrap.Garage.RPCSecret,
|
||||||
AdminToken: hostBootstrap.GarageAdminToken,
|
AdminToken: hostBootstrap.Garage.AdminToken,
|
||||||
|
|
||||||
RPCAddr: peer.RPCAddr(),
|
RPCAddr: peer.RPCAddr(),
|
||||||
S3APIAddr: peer.S3APIAddr(),
|
S3APIAddr: peer.S3APIAddr(),
|
||||||
@ -167,7 +167,7 @@ func garageInitializeGlobalBucket(
|
|||||||
|
|
||||||
var (
|
var (
|
||||||
adminClient = newGarageAdminClient(hostBootstrap, daemonConfig)
|
adminClient = newGarageAdminClient(hostBootstrap, daemonConfig)
|
||||||
globalBucketCreds = hostBootstrap.GarageGlobalBucketS3APICredentials
|
globalBucketCreds = hostBootstrap.Garage.GlobalBucketS3APICredentials
|
||||||
)
|
)
|
||||||
|
|
||||||
// first attempt to import the key
|
// first attempt to import the key
|
||||||
|
@ -58,9 +58,9 @@ func nebulaPmuxProcConfig(
|
|||||||
|
|
||||||
config := map[string]interface{}{
|
config := map[string]interface{}{
|
||||||
"pki": map[string]string{
|
"pki": map[string]string{
|
||||||
"ca": hostBootstrap.NebulaHostCredentials.CACertPEM,
|
"ca": hostBootstrap.Nebula.HostCredentials.CACertPEM,
|
||||||
"cert": hostBootstrap.NebulaHostCredentials.HostCertPEM,
|
"cert": hostBootstrap.Nebula.HostCredentials.HostCertPEM,
|
||||||
"key": hostBootstrap.NebulaHostCredentials.HostKeyPEM,
|
"key": hostBootstrap.Nebula.HostCredentials.HostKeyPEM,
|
||||||
},
|
},
|
||||||
"static_host_map": staticHostMap,
|
"static_host_map": staticHostMap,
|
||||||
"punchy": map[string]bool{
|
"punchy": map[string]bool{
|
||||||
|
@ -1,24 +0,0 @@
|
|||||||
// Package tarutil implements utilities which are useful for interacting with
|
|
||||||
// tar and tgz files.
|
|
||||||
package tarutil
|
|
||||||
|
|
||||||
import (
|
|
||||||
"compress/gzip"
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"io/fs"
|
|
||||||
|
|
||||||
"github.com/nlepage/go-tarfs"
|
|
||||||
)
|
|
||||||
|
|
||||||
// FSFromReader returns a FS instance which will read the contents of a tgz
|
|
||||||
// file from the given Reader.
|
|
||||||
func FSFromReader(r io.Reader) (fs.FS, error) {
|
|
||||||
gf, err := gzip.NewReader(r)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("un-gziping: %w", err)
|
|
||||||
}
|
|
||||||
defer gf.Close()
|
|
||||||
|
|
||||||
return tarfs.New(gf)
|
|
||||||
}
|
|
@ -1,112 +0,0 @@
|
|||||||
package tarutil
|
|
||||||
|
|
||||||
import (
|
|
||||||
"archive/tar"
|
|
||||||
"bytes"
|
|
||||||
"compress/gzip"
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"path/filepath"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
// TGZWriter is a utility for writing tgz files. If an internal error is
|
|
||||||
// encountered by any method then all subsequent methods will be no-ops, and
|
|
||||||
// Close() will return that error (after closing out resources).
|
|
||||||
type TGZWriter struct {
|
|
||||||
gzipW *gzip.Writer
|
|
||||||
tarW *tar.Writer
|
|
||||||
err error
|
|
||||||
|
|
||||||
dirsWritten map[string]bool
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewTGZWriter initializes and returns a new instance of TGZWriter which will
|
|
||||||
// write all data to the given io.Writer.
|
|
||||||
func NewTGZWriter(w io.Writer) *TGZWriter {
|
|
||||||
gzipW := gzip.NewWriter(w)
|
|
||||||
tarW := tar.NewWriter(gzipW)
|
|
||||||
return &TGZWriter{
|
|
||||||
gzipW: gzipW,
|
|
||||||
tarW: tarW,
|
|
||||||
dirsWritten: map[string]bool{},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Close cleans up all open resources being held by TGZWriter, and returns the
|
|
||||||
// first internal error which was encountered during its operation (if any).
|
|
||||||
func (w *TGZWriter) Close() error {
|
|
||||||
w.tarW.Close()
|
|
||||||
w.gzipW.Close()
|
|
||||||
return w.err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *TGZWriter) writeDir(path string) {
|
|
||||||
|
|
||||||
if w.err != nil {
|
|
||||||
return
|
|
||||||
|
|
||||||
} else if path != "." {
|
|
||||||
w.writeDir(filepath.Dir(path))
|
|
||||||
}
|
|
||||||
|
|
||||||
if path == "." {
|
|
||||||
path = "./"
|
|
||||||
} else {
|
|
||||||
path = "./" + strings.TrimPrefix(path, "./")
|
|
||||||
path = path + "/"
|
|
||||||
}
|
|
||||||
|
|
||||||
if w.dirsWritten[path] {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
err := w.tarW.WriteHeader(&tar.Header{
|
|
||||||
Name: path,
|
|
||||||
Mode: 0700,
|
|
||||||
})
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
w.err = fmt.Errorf("writing header for directory %q: %w", path, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
w.dirsWritten[path] = true
|
|
||||||
}
|
|
||||||
|
|
||||||
// WriteFile writes a file to the tgz archive. The file will automatically be
|
|
||||||
// rooted to the "." directory, and any sub-directories the file exists in
|
|
||||||
// should have already been created.
|
|
||||||
func (w *TGZWriter) WriteFile(path string, size int64, body io.Reader) {
|
|
||||||
|
|
||||||
if w.err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
path = "./" + strings.TrimPrefix(path, "./")
|
|
||||||
|
|
||||||
w.writeDir(filepath.Dir(path))
|
|
||||||
|
|
||||||
err := w.tarW.WriteHeader(&tar.Header{
|
|
||||||
Name: path,
|
|
||||||
Size: size,
|
|
||||||
Mode: 0400,
|
|
||||||
})
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
w.err = fmt.Errorf("writing header for file %q: %w", path, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, err := io.Copy(w.tarW, body); err != nil {
|
|
||||||
w.err = fmt.Errorf("writing file body of file %q: %w", path, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// WriteFileBytes is a shortcut for calling WriteFile with the given byte slice
|
|
||||||
// being used as the file body.
|
|
||||||
func (w *TGZWriter) WriteFileBytes(path string, body []byte) {
|
|
||||||
bodyR := bytes.NewReader(body)
|
|
||||||
w.WriteFile(path, bodyR.Size(), bodyR)
|
|
||||||
}
|
|
Loading…
Reference in New Issue
Block a user