Use secrets store for global garage bucket creds

This commit is contained in:
Brian Picciano 2024-07-14 12:19:39 +02:00
parent 86abdb6ae1
commit 9d5c8ea4db
8 changed files with 69 additions and 81 deletions

View File

@ -4,7 +4,6 @@ package admin
import ( import (
"encoding/json" "encoding/json"
"io" "io"
"isle/garage"
"isle/nebula" "isle/nebula"
) )
@ -23,10 +22,6 @@ type Admin struct {
Nebula struct { Nebula struct {
CACredentials nebula.CACredentials CACredentials nebula.CACredentials
} }
Garage struct {
GlobalBucketS3APICredentials garage.S3APICredentials
}
} }
// FromReader reads an admin.json from the given io.Reader. // FromReader reads an admin.json from the given io.Reader.

View File

@ -7,7 +7,6 @@ import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"isle/admin" "isle/admin"
"isle/garage"
"isle/nebula" "isle/nebula"
"net/netip" "net/netip"
"path/filepath" "path/filepath"
@ -26,20 +25,11 @@ func AppDirPath(appDirPath string) string {
return filepath.Join(appDirPath, "share/bootstrap.json") return filepath.Join(appDirPath, "share/bootstrap.json")
} }
// Garage contains parameters needed to connect to and use the garage cluster.
type Garage struct {
AdminToken string
// TODO this should be part of admin.CreationParams
GlobalBucketS3APICredentials garage.S3APICredentials
}
// Bootstrap contains all information which is needed by a host daemon to join a // Bootstrap contains all information which is needed by a host daemon to join a
// network on boot. // network on boot.
type Bootstrap struct { type Bootstrap struct {
AdminCreationParams admin.CreationParams AdminCreationParams admin.CreationParams
CAPublicCredentials nebula.CAPublicCredentials CAPublicCredentials nebula.CAPublicCredentials
Garage Garage
PrivateCredentials nebula.HostPrivateCredentials PrivateCredentials nebula.HostPrivateCredentials
HostAssigned `json:"-"` HostAssigned `json:"-"`
@ -53,7 +43,6 @@ type Bootstrap struct {
func New( func New(
caCreds nebula.CACredentials, caCreds nebula.CACredentials,
adminCreationParams admin.CreationParams, adminCreationParams admin.CreationParams,
garage Garage,
name nebula.HostName, name nebula.HostName,
ip netip.Addr, ip netip.Addr,
) ( ) (
@ -79,7 +68,6 @@ func New(
return Bootstrap{ return Bootstrap{
AdminCreationParams: adminCreationParams, AdminCreationParams: adminCreationParams,
CAPublicCredentials: caCreds.Public, CAPublicCredentials: caCreds.Public,
Garage: garage,
PrivateCredentials: hostPrivCreds, PrivateCredentials: hostPrivCreds,
HostAssigned: assigned, HostAssigned: assigned,
SignedHostAssigned: signedAssigned, SignedHostAssigned: signedAssigned,

View File

@ -19,11 +19,12 @@ func garageAdminClientLogger(logger *mlog.Logger) *mlog.Logger {
return logger.WithNamespace("garageAdminClient") return logger.WithNamespace("garageAdminClient")
} }
// NewGarageAdminClient will return an AdminClient for a local garage instance, // newGarageAdminClient will return an AdminClient for a local garage instance,
// or it will _panic_ if there is no local instance configured. // or it will _panic_ if there is no local instance configured.
func NewGarageAdminClient( func newGarageAdminClient(
logger *mlog.Logger, logger *mlog.Logger,
daemonConfig Config, daemonConfig Config,
adminToken string,
hostBootstrap bootstrap.Bootstrap, hostBootstrap bootstrap.Bootstrap,
) *garage.AdminClient { ) *garage.AdminClient {
@ -35,7 +36,7 @@ func NewGarageAdminClient(
thisHost.IP().String(), thisHost.IP().String(),
strconv.Itoa(daemonConfig.Storage.Allocations[0].AdminPort), strconv.Itoa(daemonConfig.Storage.Allocations[0].AdminPort),
), ),
hostBootstrap.Garage.AdminToken, adminToken,
) )
} }
@ -43,6 +44,7 @@ func waitForGarage(
ctx context.Context, ctx context.Context,
logger *mlog.Logger, logger *mlog.Logger,
daemonConfig Config, daemonConfig Config,
adminToken string,
hostBootstrap bootstrap.Bootstrap, hostBootstrap bootstrap.Bootstrap,
) error { ) error {
@ -64,9 +66,7 @@ func waitForGarage(
) )
adminClient := garage.NewAdminClient( adminClient := garage.NewAdminClient(
adminClientLogger, adminClientLogger, adminAddr, adminToken,
adminAddr,
hostBootstrap.Garage.AdminToken,
) )
ctx := mctx.Annotate(ctx, "garageAdminAddr", adminAddr) ctx := mctx.Annotate(ctx, "garageAdminAddr", adminAddr)
@ -101,7 +101,7 @@ func bootstrapGarageHostForAlloc(
} }
func garageWriteChildConfig( func garageWriteChildConfig(
rpcSecret, runtimeDirPath string, rpcSecret, runtimeDirPath, adminToken string,
hostBootstrap bootstrap.Bootstrap, hostBootstrap bootstrap.Bootstrap,
alloc ConfigStorageAllocation, alloc ConfigStorageAllocation,
) ( ) (
@ -130,7 +130,7 @@ func garageWriteChildConfig(
DataPath: alloc.DataPath, DataPath: alloc.DataPath,
RPCSecret: rpcSecret, RPCSecret: rpcSecret,
AdminToken: hostBootstrap.Garage.AdminToken, AdminToken: adminToken,
LocalPeer: peer, LocalPeer: peer,
BootstrapPeers: hostBootstrap.GaragePeers(), BootstrapPeers: hostBootstrap.GaragePeers(),
@ -148,6 +148,7 @@ func garagePmuxProcConfigs(
logger *mlog.Logger, logger *mlog.Logger,
rpcSecret, runtimeDirPath, binDirPath string, rpcSecret, runtimeDirPath, binDirPath string,
daemonConfig Config, daemonConfig Config,
adminToken string,
hostBootstrap bootstrap.Bootstrap, hostBootstrap bootstrap.Bootstrap,
) ( ) (
[]pmuxlib.ProcessConfig, error, []pmuxlib.ProcessConfig, error,
@ -165,7 +166,7 @@ func garagePmuxProcConfigs(
for _, alloc := range allocs { for _, alloc := range allocs {
childConfigPath, err := garageWriteChildConfig( childConfigPath, err := garageWriteChildConfig(
rpcSecret, runtimeDirPath, hostBootstrap, alloc, rpcSecret, runtimeDirPath, adminToken, hostBootstrap, alloc,
) )
if err != nil { if err != nil {
return nil, fmt.Errorf("writing child config file for alloc %+v: %w", alloc, err) return nil, fmt.Errorf("writing child config file for alloc %+v: %w", alloc, err)
@ -188,15 +189,18 @@ func garageApplyLayout(
ctx context.Context, ctx context.Context,
logger *mlog.Logger, logger *mlog.Logger,
daemonConfig Config, daemonConfig Config,
adminToken string,
hostBootstrap bootstrap.Bootstrap, hostBootstrap bootstrap.Bootstrap,
) error { ) error {
var ( var (
adminClient = NewGarageAdminClient(logger, daemonConfig, hostBootstrap) adminClient = newGarageAdminClient(
thisHost = hostBootstrap.ThisHost() logger, daemonConfig, adminToken, hostBootstrap,
hostName = thisHost.Name )
allocs = daemonConfig.Storage.Allocations thisHost = hostBootstrap.ThisHost()
peers = make([]garage.PeerLayout, len(allocs)) hostName = thisHost.Name
allocs = daemonConfig.Storage.Allocations
peers = make([]garage.PeerLayout, len(allocs))
) )
for i, alloc := range allocs { for i, alloc := range allocs {

View File

@ -12,6 +12,7 @@ func (c *Children) newPmuxConfig(
ctx context.Context, ctx context.Context,
garageRPCSecret, binDirPath string, garageRPCSecret, binDirPath string,
daemonConfig Config, daemonConfig Config,
garageAdminToken string,
hostBootstrap bootstrap.Bootstrap, hostBootstrap bootstrap.Bootstrap,
) ( ) (
pmuxlib.Config, error, pmuxlib.Config, error,
@ -45,6 +46,7 @@ func (c *Children) newPmuxConfig(
c.opts.EnvVars.RuntimeDirPath, c.opts.EnvVars.RuntimeDirPath,
binDirPath, binDirPath,
daemonConfig, daemonConfig,
garageAdminToken,
hostBootstrap, hostBootstrap,
) )
if err != nil { if err != nil {
@ -67,6 +69,7 @@ func (c *Children) newPmuxConfig(
func (c *Children) postPmuxInit( func (c *Children) postPmuxInit(
ctx context.Context, ctx context.Context,
daemonConfig Config, daemonConfig Config,
garageAdminToken string,
hostBootstrap bootstrap.Bootstrap, hostBootstrap bootstrap.Bootstrap,
) error { ) error {
c.logger.Info(ctx, "Waiting for nebula VPN to come online") c.logger.Info(ctx, "Waiting for nebula VPN to come online")
@ -75,7 +78,9 @@ func (c *Children) postPmuxInit(
} }
c.logger.Info(ctx, "Waiting for garage instances to come online") c.logger.Info(ctx, "Waiting for garage instances to come online")
err := waitForGarage(ctx, c.logger, daemonConfig, hostBootstrap) err := waitForGarage(
ctx, c.logger, daemonConfig, garageAdminToken, hostBootstrap,
)
if err != nil { if err != nil {
return fmt.Errorf("waiting for garage to start: %w", err) return fmt.Errorf("waiting for garage to start: %w", err)
} }

View File

@ -32,6 +32,7 @@ func NewChildren(
binDirPath string, binDirPath string,
secretsStore secrets.Store, secretsStore secrets.Store,
daemonConfig Config, daemonConfig Config,
garageAdminToken string,
hostBootstrap bootstrap.Bootstrap, hostBootstrap bootstrap.Bootstrap,
opts *Opts, opts *Opts,
) ( ) (
@ -55,7 +56,12 @@ func NewChildren(
} }
pmuxConfig, err := c.newPmuxConfig( pmuxConfig, err := c.newPmuxConfig(
ctx, garageRPCSecret, binDirPath, daemonConfig, hostBootstrap, ctx,
garageRPCSecret,
binDirPath,
daemonConfig,
garageAdminToken,
hostBootstrap,
) )
if err != nil { if err != nil {
return nil, fmt.Errorf("generating pmux config: %w", err) return nil, fmt.Errorf("generating pmux config: %w", err)
@ -67,7 +73,9 @@ func NewChildren(
c.logger.Debug(pmuxCtx, "pmux stopped") c.logger.Debug(pmuxCtx, "pmux stopped")
}() }()
initErr := c.postPmuxInit(ctx, daemonConfig, hostBootstrap) initErr := c.postPmuxInit(
ctx, daemonConfig, garageAdminToken, hostBootstrap,
)
if initErr != nil { if initErr != nil {
logger.Warn(ctx, "failed to initialize Children, shutting down child processes", err) logger.Warn(ctx, "failed to initialize Children, shutting down child processes", err)
if err := c.Shutdown(); err != nil { if err := c.Shutdown(); err != nil {

View File

@ -11,7 +11,6 @@ import (
"io/fs" "io/fs"
"isle/admin" "isle/admin"
"isle/bootstrap" "isle/bootstrap"
"isle/garage"
"isle/jsonutil" "isle/jsonutil"
"isle/nebula" "isle/nebula"
"isle/secrets" "isle/secrets"
@ -141,7 +140,8 @@ type daemon struct {
envBinDirPath string envBinDirPath string
opts *Opts opts *Opts
secretsStore secrets.Store secretsStore secrets.Store
garageAdminToken string
l sync.RWMutex l sync.RWMutex
state int state int
@ -177,11 +177,12 @@ func NewDaemon(
) { ) {
var ( var (
d = &daemon{ d = &daemon{
logger: logger, logger: logger,
daemonConfig: daemonConfig, daemonConfig: daemonConfig,
envBinDirPath: envBinDirPath, envBinDirPath: envBinDirPath,
opts: opts.withDefaults(), opts: opts.withDefaults(),
shutdownCh: make(chan struct{}), garageAdminToken: randStr(32),
shutdownCh: make(chan struct{}),
} }
bootstrapFilePath = bootstrap.StateDirPath(d.opts.EnvVars.StateDirPath) bootstrapFilePath = bootstrap.StateDirPath(d.opts.EnvVars.StateDirPath)
) )
@ -370,7 +371,11 @@ func (d *daemon) postInit(ctx context.Context) bool {
"Applying garage layout", "Applying garage layout",
func(ctx context.Context) error { func(ctx context.Context) error {
return garageApplyLayout( return garageApplyLayout(
ctx, d.logger, d.daemonConfig, d.currBootstrap, ctx,
d.logger,
d.daemonConfig,
d.garageAdminToken,
d.currBootstrap,
) )
}, },
) { ) {
@ -383,28 +388,29 @@ func (d *daemon) postInit(ctx context.Context) bool {
// //
// TODO this is pretty hacky, but there doesn't seem to be a better way to // TODO this is pretty hacky, but there doesn't seem to be a better way to
// manage it at the moment. // manage it at the moment.
if d.currBootstrap.Garage.GlobalBucketS3APICredentials == (garage.S3APICredentials{}) { _, err := getGarageS3APIGlobalBucketCredentials(ctx, d.secretsStore)
currBootstrap := d.currBootstrap if errors.Is(err, secrets.ErrNotFound) {
if !until( if !until(
ctx, ctx,
d.logger, d.logger,
"Initializing garage shared global bucket", "Initializing garage shared global bucket",
func(ctx context.Context) error { func(ctx context.Context) error {
garageGlobalBucketCreds, err := garageInitializeGlobalBucket( garageGlobalBucketCreds, err := garageInitializeGlobalBucket(
ctx, d.logger, d.daemonConfig, d.currBootstrap, ctx,
d.logger,
d.daemonConfig,
d.garageAdminToken,
d.currBootstrap,
) )
if err != nil { if err != nil {
return fmt.Errorf("initializing global bucket: %w", err) return fmt.Errorf("initializing global bucket: %w", err)
} }
currBootstrap.Garage.GlobalBucketS3APICredentials = garageGlobalBucketCreds err = setGarageS3APIGlobalBucketCredentials(
ctx, d.secretsStore, garageGlobalBucketCreds,
d.logger.Info(ctx, "Writing bootstrap to state directory")
err = writeBootstrapToStateDir(
d.opts.EnvVars.StateDirPath, currBootstrap,
) )
if err != nil { if err != nil {
return fmt.Errorf("writing bootstrap to state dir: %w", err) return fmt.Errorf("storing global bucket creds: %w", err)
} }
return nil return nil
@ -412,10 +418,6 @@ func (d *daemon) postInit(ctx context.Context) bool {
) { ) {
return false return false
} }
d.l.Lock()
d.currBootstrap = currBootstrap
d.l.Unlock()
} }
if !until( if !until(
@ -461,6 +463,7 @@ func (d *daemon) restartLoop(ctx context.Context, readyCh chan<- struct{}) {
d.envBinDirPath, d.envBinDirPath,
d.secretsStore, d.secretsStore,
d.daemonConfig, d.daemonConfig,
d.garageAdminToken,
d.currBootstrap, d.currBootstrap,
d.opts, d.opts,
) )
@ -533,13 +536,9 @@ func (d *daemon) CreateNetwork(
}, },
} }
garageBootstrap = bootstrap.Garage{ garageRPCSecret = randStr(32)
AdminToken: randStr(32),
}
) )
garageRPCSecret := randStr(32)
err = setGarageRPCSecret(ctx, d.secretsStore, garageRPCSecret) err = setGarageRPCSecret(ctx, d.secretsStore, garageRPCSecret)
if err != nil { if err != nil {
return admin.Admin{}, fmt.Errorf("storing garage RPC secret: %w", err) return admin.Admin{}, fmt.Errorf("storing garage RPC secret: %w", err)
@ -548,7 +547,6 @@ func (d *daemon) CreateNetwork(
hostBootstrap, err := bootstrap.New( hostBootstrap, err := bootstrap.New(
nebulaCACreds, nebulaCACreds,
adm.CreationParams, adm.CreationParams,
garageBootstrap,
hostName, hostName,
ipNet.FirstAddr(), ipNet.FirstAddr(),
) )
@ -584,16 +582,7 @@ func (d *daemon) CreateNetwork(
return adm, ctx.Err() return adm, ctx.Err()
} }
// As part of postInit, which is called prior to ready(), the restartLoop
// will check if the global bucket creds have been created yet or not, and
// create them if so. So once ready() is called we can get the created creds
// from the currBootstrap
d.l.RLock()
garageGlobalBucketCreds := d.currBootstrap.Garage.GlobalBucketS3APICredentials
d.l.RUnlock()
adm.Nebula.CACredentials = nebulaCACreds adm.Nebula.CACredentials = nebulaCACreds
adm.Garage.GlobalBucketS3APICredentials = garageGlobalBucketCreds
return adm, nil return adm, nil
} }
@ -697,13 +686,6 @@ func (d *daemon) CreateHost(
JoiningBootstrap, error, JoiningBootstrap, error,
) { ) {
var ( var (
garageGlobalBucketS3APICreds = currBootstrap.Garage.GlobalBucketS3APICredentials
garageBootstrap = bootstrap.Garage{
AdminToken: randStr(32),
GlobalBucketS3APICredentials: garageGlobalBucketS3APICreds,
}
joiningBootstrap JoiningBootstrap joiningBootstrap JoiningBootstrap
err error err error
) )
@ -711,7 +693,6 @@ func (d *daemon) CreateHost(
joiningBootstrap.Bootstrap, err = bootstrap.New( joiningBootstrap.Bootstrap, err = bootstrap.New(
makeCACreds(currBootstrap, caSigningPrivateKey), makeCACreds(currBootstrap, caSigningPrivateKey),
currBootstrap.AdminCreationParams, currBootstrap.AdminCreationParams,
garageBootstrap,
hostName, hostName,
ip, ip,
) )
@ -726,6 +707,7 @@ func (d *daemon) CreateHost(
if joiningBootstrap.Secrets, err = secrets.Export( if joiningBootstrap.Secrets, err = secrets.Export(
ctx, d.secretsStore, []secrets.ID{ ctx, d.secretsStore, []secrets.ID{
garageRPCSecretSecretID, garageRPCSecretSecretID,
garageS3APIGlobalBucketCredentialsSecretID,
}, },
); err != nil { ); err != nil {
return JoiningBootstrap{}, fmt.Errorf("exporting secrets: %w", err) return JoiningBootstrap{}, fmt.Errorf("exporting secrets: %w", err)

View File

@ -24,6 +24,11 @@ func (d *daemon) getGarageClientParams(
) ( ) (
GarageClientParams, error, GarageClientParams, error,
) { ) {
creds, err := getGarageS3APIGlobalBucketCredentials(ctx, d.secretsStore)
if err != nil {
return GarageClientParams{}, fmt.Errorf("getting garage global bucket creds: %w", err)
}
rpcSecret, err := getGarageRPCSecret(ctx, d.secretsStore) rpcSecret, err := getGarageRPCSecret(ctx, d.secretsStore)
if err != nil && !errors.Is(err, secrets.ErrNotFound) { if err != nil && !errors.Is(err, secrets.ErrNotFound) {
return GarageClientParams{}, fmt.Errorf("getting garage rpc secret: %w", err) return GarageClientParams{}, fmt.Errorf("getting garage rpc secret: %w", err)
@ -31,7 +36,7 @@ func (d *daemon) getGarageClientParams(
return GarageClientParams{ return GarageClientParams{
Peer: currBootstrap.ChooseGaragePeer(), Peer: currBootstrap.ChooseGaragePeer(),
GlobalBucketS3APICredentials: currBootstrap.Garage.GlobalBucketS3APICredentials, GlobalBucketS3APICredentials: creds,
RPCSecret: rpcSecret, RPCSecret: rpcSecret,
}, nil }, nil
} }

View File

@ -24,12 +24,13 @@ func garageInitializeGlobalBucket(
ctx context.Context, ctx context.Context,
logger *mlog.Logger, logger *mlog.Logger,
daemonConfig Config, daemonConfig Config,
adminToken string,
hostBootstrap bootstrap.Bootstrap, hostBootstrap bootstrap.Bootstrap,
) ( ) (
garage.S3APICredentials, error, garage.S3APICredentials, error,
) { ) {
adminClient := NewGarageAdminClient( adminClient := newGarageAdminClient(
logger, daemonConfig, hostBootstrap, logger, daemonConfig, adminToken, hostBootstrap,
) )
creds, err := adminClient.CreateS3APICredentials( creds, err := adminClient.CreateS3APICredentials(