isle/entrypoint/src/bootstrap/garage_global_bucket.go
Brian Picciano 629a8ec9b2 Improve logging, introduce log levels
I switched to using mlog for logging, as opposed to writing directly to
Stderr. This gives us control over log levels, as well as coordination
so that we don't have multiple go-routines writing to stderr at the same
time.
2022-11-13 16:45:42 +01:00

172 lines
4.1 KiB
Go

package bootstrap
import (
"bytes"
"context"
"cryptic-net/garage"
"cryptic-net/nebula"
"fmt"
"path/filepath"
"strings"
"github.com/mediocregopher/mediocre-go-lib/v2/mctx"
"github.com/mediocregopher/mediocre-go-lib/v2/mlog"
"github.com/minio/minio-go/v7"
"gopkg.in/yaml.v3"
)
// Paths within garage's global bucket
const (
garageGlobalBucketBootstrapHostsDirPath = "bootstrap/hosts"
)
// PutGarageBoostrapHost places the <hostname>.yml.signed file for this host
// into garage so that other hosts are able to see relevant configuration for
// it.
func (b Bootstrap) PutGarageBoostrapHost(ctx context.Context) error {
host := b.ThisHost()
client := b.GlobalBucketS3APIClient()
// the base Bootstrap has the public credentials signed by the CA, but we
// need this to be presented in the data stored into garage, so other hosts
// can verify that the stored host object is signed by the host public key,
// and that the host public key is signed by the CA.
host.Nebula.SignedPublicCredentials = b.Nebula.SignedPublicCredentials
hostB, err := yaml.Marshal(host)
if err != nil {
return fmt.Errorf("yaml encoding host data: %w", err)
}
buf := new(bytes.Buffer)
err = nebula.SignAndWrap(buf, b.Nebula.HostCredentials.SigningPrivateKeyPEM, hostB)
if err != nil {
return fmt.Errorf("signing encoded host data: %w", err)
}
filePath := filepath.Join(
garageGlobalBucketBootstrapHostsDirPath,
host.Name+".yml.signed",
)
_, err = client.PutObject(
ctx, garage.GlobalBucket, filePath, buf, int64(buf.Len()),
minio.PutObjectOptions{},
)
if err != nil {
return fmt.Errorf("writing to %q in global bucket: %w", filePath, err)
}
return nil
}
// RemoveGarageBootstrapHost removes the <hostname>.yml.signed for the given
// host from garage.
//
// The given client should be for the global bucket.
func RemoveGarageBootstrapHost(
ctx context.Context, client garage.S3APIClient, hostName string,
) error {
filePath := filepath.Join(
garageGlobalBucketBootstrapHostsDirPath,
hostName+".yml.signed",
)
return client.RemoveObject(
ctx, garage.GlobalBucket, filePath,
minio.RemoveObjectOptions{},
)
}
// GetGarageBootstrapHosts loads the <hostname>.yml.signed file for all hosts
// stored in garage.
func (b Bootstrap) GetGarageBootstrapHosts(
ctx context.Context,
logger *mlog.Logger,
) (
map[string]Host, error,
) {
client := b.GlobalBucketS3APIClient()
hosts := map[string]Host{}
objInfoCh := client.ListObjects(
ctx, garage.GlobalBucket,
minio.ListObjectsOptions{
Prefix: garageGlobalBucketBootstrapHostsDirPath,
Recursive: true,
},
)
for objInfo := range objInfoCh {
ctx := mctx.Annotate(ctx, "object-key", objInfo.Key)
if objInfo.Err != nil {
return nil, fmt.Errorf("listing objects: %w", objInfo.Err)
}
obj, err := client.GetObject(
ctx, garage.GlobalBucket, objInfo.Key, minio.GetObjectOptions{},
)
if err != nil {
return nil, fmt.Errorf("retrieving object %q: %w", objInfo.Key, err)
}
hostB, hostSig, err := nebula.Unwrap(obj)
obj.Close()
if err != nil {
return nil, fmt.Errorf("unwrapping signature from %q: %w", objInfo.Key, err)
}
var host Host
if err = yaml.Unmarshal(hostB, &host); err != nil {
return nil, fmt.Errorf("yaml decoding object %q: %w", objInfo.Key, err)
}
hostPublicCredsB, hostPublicCredsSig, err := nebula.Unwrap(
strings.NewReader(host.Nebula.SignedPublicCredentials),
)
if err != nil {
logger.Warn(ctx, "unwrapping signed public creds", err)
continue
}
err = nebula.ValidateSignature(
b.Nebula.CAPublicCredentials.SigningKeyPEM,
hostPublicCredsB,
hostPublicCredsSig,
)
if err != nil {
logger.Warn(ctx, "invalid signed public creds", err)
continue
}
var hostPublicCreds nebula.HostPublicCredentials
if err := yaml.Unmarshal(hostPublicCredsB, &hostPublicCreds); err != nil {
logger.Warn(ctx, "yaml unmarshaling signed public creds", err)
continue
}
err = nebula.ValidateSignature(hostPublicCreds.SigningKeyPEM, hostB, hostSig)
if err != nil {
logger.Warn(ctx, "invalid host data", err)
continue
}
hosts[host.Name] = host
}
return hosts, nil
}