1
0
Fork 0
mirror of https://github.com/pomerium/pomerium.git synced 2025-05-22 21:47:16 +02:00
pomerium/internal/zero/bootstrap/bootstrap.go
Joe Kralicky de603f87de
Add new configurable bootstrap writers () ()
* Add new configurable bootstrap writers ()

This PR adds the ability to configure different backends to use for
storing modifications to the zero bootstrap config. The two currently
implemented backends allow writing changes to a file or to a Kubernetes
secret. Backend selection is determined by the scheme in a URI passed to
the flag '--config-writeback-uri'.

In a Kubernetes environment, where the bootstrap config is mounted into
the pod from a secret, this option allows Pomerium to write changes back
to the secret, as writes to the mounted secret file on disk are not
persisted.

* Use env vars for bootstrap config filepath/writeback uri

* linter pass and code cleanup

* Add new config writer options mechanism

This moves the encryption cipher parameter out of the WriteConfig()
method in the ConfigWriter interface and into a new ConfigWriterOptions
struct. Options (e.g. cipher) can be applied to an existing ConfigWriter
to allow customizing implementation-specific behavior.

* Code cleanup/lint fixes

* Move vendored k8s code into separate package, and add license header and package comment
2024-05-31 12:26:17 -04:00

128 lines
3.5 KiB
Go

// Package bootstrap fetches the very initial configuration for Pomerium Core to start.
package bootstrap
/*
* Initial configuration for Pomerium start-up is obtained from the cloud.
* Some parameters are derived from the cluster token.
*
* The expectation is that if the user wishes to survive a cloud outage,
* it should be sufficient to set up Pomerium to use a durable database (Postgres)
* and receive cloud configuration once.
*
*/
import (
"context"
"fmt"
"time"
"golang.org/x/sync/errgroup"
"github.com/pomerium/pomerium/internal/log"
"github.com/pomerium/pomerium/internal/retry"
connect_mux "github.com/pomerium/pomerium/internal/zero/connect-mux"
)
const (
// DefaultCheckForUpdateIntervalWhenDisconnected is the default interval to check for updates
// if there is no connection to the update service
DefaultCheckForUpdateIntervalWhenDisconnected = 5 * time.Minute
// DefaultCheckForUpdateIntervalWhenConnected is the default interval to check for updates
// if there is a connection to the update service
DefaultCheckForUpdateIntervalWhenConnected = time.Hour
)
// Run initializes the bootstrap config source
func (svc *Source) Run(ctx context.Context) error {
svc.tryLoadFromFile(ctx)
eg, ctx := errgroup.WithContext(ctx)
eg.Go(func() error { return svc.watchUpdates(ctx) })
eg.Go(func() error { return svc.updateLoop(ctx) })
return eg.Wait()
}
func (svc *Source) watchUpdates(ctx context.Context) error {
return svc.api.Watch(ctx,
connect_mux.WithOnConnected(func(_ context.Context) {
svc.triggerUpdate(DefaultCheckForUpdateIntervalWhenConnected)
}),
connect_mux.WithOnDisconnected(func(_ context.Context) {
svc.updateInterval.Store(DefaultCheckForUpdateIntervalWhenDisconnected)
}),
connect_mux.WithOnBootstrapConfigUpdated(func(_ context.Context) {
svc.triggerUpdate(DefaultCheckForUpdateIntervalWhenConnected)
}),
)
}
func (svc *Source) updateLoop(ctx context.Context) error {
ticker := time.NewTicker(svc.updateInterval.Load())
defer ticker.Stop()
for {
err := retry.Retry(ctx,
"update bootstrap", svc.updateAndSave,
retry.WithWatch("bootstrap config updated", svc.checkForUpdate, nil),
)
if err != nil {
return fmt.Errorf("update bootstrap config: %w", err)
}
ticker.Reset(svc.updateInterval.Load())
select {
case <-ctx.Done():
return ctx.Err()
case <-svc.checkForUpdate:
case <-ticker.C:
}
}
}
// triggerUpdate triggers an update of the bootstrap config
// and sets the interval for the next update
func (svc *Source) triggerUpdate(newUpdateInterval time.Duration) {
svc.updateInterval.Store(newUpdateInterval)
select {
case svc.checkForUpdate <- struct{}{}:
default:
}
}
func (svc *Source) updateAndSave(ctx context.Context) error {
cfg, err := svc.api.GetClusterBootstrapConfig(ctx)
if err != nil {
return fmt.Errorf("load bootstrap config from API: %w", err)
}
svc.UpdateBootstrap(ctx, *cfg)
if svc.writer == nil {
return nil
}
err = SaveBootstrapConfig(ctx, svc.writer, cfg)
if err != nil {
log.Ctx(ctx).Error().Err(err).
Msg("failed to save bootstrap config to file, note it may prevent Pomerium from starting up in case of connectivity issues")
}
return nil
}
func (svc *Source) tryLoadFromFile(ctx context.Context) {
if svc.fileCachePath == nil {
return
}
cfg, err := LoadBootstrapConfigFromFile(*svc.fileCachePath, svc.fileCipher)
if err != nil {
log.Ctx(ctx).Error().Err(err).Msg("failed to load bootstrap config from file")
return
}
svc.UpdateBootstrap(ctx, *cfg)
}