Skip to content

Commit

Permalink
feat: integrate asm and db into controller (#1697)
Browse files Browse the repository at this point in the history
Fixes #1692

This PR integrates AWS secrets manager (ASM) and DB project
resolver/config provider into the controller.

Please note that `ftl dev` does not support these yet.

Manual test with localstack:

```bash
ftl🐚 ➜  ftl git:(gak/asm-in-controller) ✗ export AWS_REGION=us-west-2 export AWS_ACCESS_KEY_ID=test AWS_SECRET_ACCESS_KEY=test AWS_ENDPOINT_URL=http://localhost:4566
ftl🐚 ➜  ftl git:(gak/asm-in-controller) ✗ ftl-controller
info: Web console available at: http://localhost:8892
info: HTTP ingress server listening on: http://localhost:8891
warn:heartbeatController: failed to heartbeat controller: duplicate key value violates unique constraint "controller_endpoint_not_dead_idx": conflict

etc...
```

Another terminal using AWS secrets:
```bash
ftl🐚 ➜  ftl git:(gak/asm-in-controller) echo -n '{"user": "sup!", "pass": "<0001f9be>"}' | LOG_LEVEL=trace ftl secret set my.module --asm
trace: Loading config from /Users/gak/src/ftl/ftl-project.toml
trace: /xyz.block.ftl.v1.AdminService/SecretSet (unary)
ftl🐚 ➜  ftl git:(gak/asm-in-controller) ✗ ftl secret list
my.module
ftl🐚 ➜  ftl git:(gak/asm-in-controller) ✗ ftl secret get my.module
{"user": "sup!", "pass": "🦾"}
```

And config via db:
```bash
ftl🐚 ➜  ftl git:(gak/asm-in-controller) ftl config set myconfig hithere --db
ftl🐚 ➜  ftl git:(gak/asm-in-controller) ftl config list
myconfig
ftl🐚 ➜  ftl git:(gak/asm-in-controller) ftl config get myconfig
hithere
```
  • Loading branch information
gak authored Jun 7, 2024
1 parent 90c41b9 commit 9d8901f
Show file tree
Hide file tree
Showing 7 changed files with 251 additions and 206 deletions.
4 changes: 4 additions & 0 deletions backend/controller/admin/admin.go
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,8 @@ func configProviderKey(p *ftlv1.ConfigProvider) string {
return "inline"
case ftlv1.ConfigProvider_CONFIG_ENVAR:
return "envar"
case ftlv1.ConfigProvider_CONFIG_DB:
return "db"
}
return ""
}
Expand Down Expand Up @@ -174,6 +176,8 @@ func secretProviderKey(p *ftlv1.SecretProvider) string {
return "keychain"
case ftlv1.SecretProvider_SECRET_OP:
return "op"
case ftlv1.SecretProvider_SECRET_ASM:
return "asm"
}
return ""
}
Expand Down
392 changes: 201 additions & 191 deletions backend/protos/xyz/block/ftl/v1/ftl.pb.go

Large diffs are not rendered by default.

6 changes: 6 additions & 0 deletions backend/protos/xyz/block/ftl/v1/ftl.proto
Original file line number Diff line number Diff line change
Expand Up @@ -406,6 +406,9 @@ enum ConfigProvider {

// Print configuration as environment variables.
CONFIG_ENVAR = 1;

// Use the database as a configuration store.
CONFIG_DB = 2;
}

message ListConfigRequest {
Expand Down Expand Up @@ -452,6 +455,9 @@ enum SecretProvider {

// Store a secret in the 1Password vault.
SECRET_OP = 3;

// Store a secret in the AWS Secrets Manager.
SECRET_ASM = 4;
}

message ListSecretsRequest {
Expand Down
33 changes: 18 additions & 15 deletions cmd/ftl-controller/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,13 @@ import (
"time"

"github.com/alecthomas/kong"
"github.com/aws/aws-sdk-go-v2/config"
"github.com/aws/aws-sdk-go-v2/service/secretsmanager"
"github.com/jackc/pgx/v5/pgxpool"

"github.com/TBD54566975/ftl"
"github.com/TBD54566975/ftl/backend/controller"
"github.com/TBD54566975/ftl/backend/controller/dal"
"github.com/TBD54566975/ftl/backend/controller/scaling"
cf "github.com/TBD54566975/ftl/common/configuration"
_ "github.com/TBD54566975/ftl/internal/automaxprocs" // Set GOMAXPROCS to match Linux container CPU quota.
Expand All @@ -24,9 +28,6 @@ var cli struct {
LogConfig log.Config `embed:"" prefix:"log-"`
ControllerConfig controller.Config `embed:""`
ConfigFlag []string `name:"config" short:"C" help:"Paths to FTL project configuration files." env:"FTL_CONFIG" placeholder:"FILE[,FILE,...]"`

// Specify the 1Password vault to access secrets from.
Vault string `name:"opvault" help:"1Password vault to be used for secrets. The name of the 1Password item will be the <ref> and the secret will be stored in the password field." placeholder:"VAULT"`
}

func main() {
Expand All @@ -43,23 +44,25 @@ func main() {
err = observability.Init(ctx, "ftl-controller", ftl.Version, cli.ObservabilityConfig)
kctx.FatalIfErrorf(err, "failed to initialize observability")

// This is duplicating the logic in the `ftl/main.go` to resolve the current panic
// However, this should be updated to only allow providers that are supported on the current environment
// See https://github.com/TBD54566975/ftl/issues/1473 for more information
sr := cf.ProjectConfigResolver[cf.Secrets]{Config: cli.ConfigFlag}
cr := cf.ProjectConfigResolver[cf.Configuration]{Config: cli.ConfigFlag}
kctx.BindTo(sr, (*cf.Resolver[cf.Secrets])(nil))
kctx.BindTo(cr, (*cf.Resolver[cf.Configuration])(nil))

// Add config manager to context.
cm, err := cf.NewConfigurationManager(ctx, cr)
// The FTL controller currently only supports DB as a configuration provider/resolver.
conn, err := pgxpool.New(ctx, cli.ControllerConfig.DSN)
kctx.FatalIfErrorf(err)
dal, err := dal.New(ctx, conn)
kctx.FatalIfErrorf(err)
configProviders := []cf.Provider[cf.Configuration]{cf.NewDBConfigProvider(dal)}
configResolver := cf.NewDBConfigResolver(dal)
cm, err := cf.New[cf.Configuration](ctx, configResolver, configProviders)
if err != nil {
kctx.Fatalf(err.Error())
}
ctx = cf.ContextWithConfig(ctx, cm)

// Add secrets manager to context.
sm, err := cf.NewSecretsManager(ctx, sr, cli.Vault)
// The FTL controller currently only supports AWS Secrets Manager as a secrets provider.
awsConfig, err := config.LoadDefaultConfig(ctx)
asmClient := secretsmanager.NewFromConfig(awsConfig)
secretsResolver := cf.ASM{Client: *asmClient}
secretsProviders := []cf.Provider[cf.Secrets]{cf.ASM{Client: *asmClient}}
sm, err := cf.New[cf.Secrets](ctx, secretsResolver, secretsProviders)
if err != nil {
kctx.Fatalf(err.Error())
}
Expand Down
3 changes: 3 additions & 0 deletions cmd/ftl/cmd_config.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ type configCmd struct {

Envar bool `help:"Print configuration as environment variables." group:"Provider:" xor:"configwriter"`
Inline bool `help:"Write values inline in the configuration file." group:"Provider:" xor:"configwriter"`
DB bool `help:"Write values to the database." group:"Provider:" xor:"configwriter"`
}

func (s *configCmd) Help() string {
Expand All @@ -45,6 +46,8 @@ func (s *configCmd) provider() optional.Option[ftlv1.ConfigProvider] {
return optional.Some(ftlv1.ConfigProvider_CONFIG_ENVAR)
} else if s.Inline {
return optional.Some(ftlv1.ConfigProvider_CONFIG_INLINE)
} else if s.DB {
return optional.Some(ftlv1.ConfigProvider_CONFIG_DB)
}
return optional.None[ftlv1.ConfigProvider]()
}
Expand Down
3 changes: 3 additions & 0 deletions cmd/ftl/cmd_secret.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ type secretCmd struct {
Inline bool `help:"Write values inline in the configuration file." group:"Provider:" xor:"secretwriter"`
Keychain bool `help:"Write to the system keychain." group:"Provider:" xor:"secretwriter"`
Op bool `help:"Write to the controller's 1Password vault. Requires that a vault be specified to the controller. The name of the item will be the <ref> and the secret will be stored in the password field." group:"Provider:" xor:"secretwriter"`
ASM bool `help:"Write to AWS secrets manager." group:"Provider:" xor:"secretwriter"`
}

func (s *secretCmd) Help() string {
Expand All @@ -49,6 +50,8 @@ func (s *secretCmd) provider() optional.Option[ftlv1.SecretProvider] {
return optional.Some(ftlv1.SecretProvider_SECRET_KEYCHAIN)
} else if s.Op {
return optional.Some(ftlv1.SecretProvider_SECRET_OP)
} else if s.ASM {
return optional.Some(ftlv1.SecretProvider_SECRET_ASM)
}
return optional.None[ftlv1.SecretProvider]()
}
Expand Down
16 changes: 16 additions & 0 deletions frontend/src/protos/xyz/block/ftl/v1/ftl_pb.ts

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit 9d8901f

Please sign in to comment.