Skip to content

Commit 5f2bcf8

Browse files
Flo4604autofix-ci[bot]chronark
authored
feat: decrypt env vars in CTRL workflow before passing to Krane (#4453)
* feat: add environment variables db schema and queries * fix db query * feat: add SecretsConfig proto for encrypted env vars * [autofix.ci] apply automated fixes * feat: dashboard UI for environment variables management * fix comment and rename file * fix file export name * Remove unnecessary comments from add-env-vars * add toasts for environment variable operations * [autofix.ci] apply automated fixes * fix: add try/catch error handling to env var mutations * unfmt file * [autofix.ci] apply automated fixes * feat: decrypt env vars in CTRL workflow before passing to Krane --------- Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com> Co-authored-by: Andreas Thomas <[email protected]>
1 parent b358cbf commit 5f2bcf8

File tree

4 files changed

+55
-2
lines changed

4 files changed

+55
-2
lines changed

go/apps/ctrl/run.go

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -105,8 +105,6 @@ func Run(ctx context.Context, cfg Config) error {
105105
return fmt.Errorf("unable to create vault service: %w", err)
106106
}
107107
}
108-
// make go happy
109-
_ = vaultSvc
110108

111109
// Initialize database
112110
database, err := db.New(db.Config{
@@ -237,6 +235,7 @@ func Run(ctx context.Context, cfg Config) error {
237235
Krane: kraneDeploymentClient,
238236
BuildClient: buildService,
239237
DefaultDomain: cfg.DefaultDomain,
238+
Vault: vaultSvc,
240239
})))
241240

242241
restateSrv.Bind(hydrav1.NewRoutingServiceServer(routing.New(routing.Config{

go/apps/ctrl/services/deployment/create_deployment.go

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import (
1313
hydrav1 "github.com/unkeyed/unkey/go/gen/proto/hydra/v1"
1414
"github.com/unkeyed/unkey/go/pkg/db"
1515
"github.com/unkeyed/unkey/go/pkg/uid"
16+
"google.golang.org/protobuf/encoding/protojson"
1617
"google.golang.org/protobuf/proto"
1718
)
1819

@@ -52,6 +53,26 @@ func (s *Service) CreateDeployment(
5253
fmt.Errorf("failed to lookup environment: %w", err))
5354
}
5455

56+
// Fetch environment variables and build secrets blob
57+
envVars, err := db.Query.FindEnvironmentVariablesByEnvironmentId(ctx, s.db.RO(), env.ID)
58+
if err != nil {
59+
return nil, connect.NewError(connect.CodeInternal,
60+
fmt.Errorf("failed to fetch environment variables: %w", err))
61+
}
62+
63+
secretsConfig := &ctrlv1.SecretsConfig{
64+
Secrets: make(map[string]string, len(envVars)),
65+
}
66+
for _, ev := range envVars {
67+
secretsConfig.Secrets[ev.Key] = ev.Value
68+
}
69+
70+
secretsBlob, err := protojson.Marshal(secretsConfig)
71+
if err != nil {
72+
return nil, connect.NewError(connect.CodeInternal,
73+
fmt.Errorf("failed to marshal secrets config: %w", err))
74+
}
75+
5576
// Get git branch name for the deployment
5677
gitBranch := req.Msg.GetBranch()
5778
if gitBranch == "" {
@@ -143,6 +164,7 @@ func (s *Service) CreateDeployment(
143164
}`),
144165
OpenapiSpec: sql.NullString{String: "", Valid: false},
145166
GatewayConfig: env.GatewayConfig,
167+
SecretsConfig: secretsBlob,
146168
Status: db.DeploymentsStatusPending,
147169
CreatedAt: now,
148170
UpdatedAt: sql.NullInt64{Int64: now, Valid: true},

go/apps/ctrl/workflows/deploy/deploy_handler.go

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,10 @@ import (
1414
ctrlv1 "github.com/unkeyed/unkey/go/gen/proto/ctrl/v1"
1515
hydrav1 "github.com/unkeyed/unkey/go/gen/proto/hydra/v1"
1616
kranev1 "github.com/unkeyed/unkey/go/gen/proto/krane/v1"
17+
vaultv1 "github.com/unkeyed/unkey/go/gen/proto/vault/v1"
1718
"github.com/unkeyed/unkey/go/pkg/db"
1819
"github.com/unkeyed/unkey/go/pkg/uid"
20+
"google.golang.org/protobuf/encoding/protojson"
1921
"google.golang.org/protobuf/proto"
2022
)
2123

@@ -112,6 +114,29 @@ func (w *Workflow) Deploy(ctx restate.ObjectContext, req *hydrav1.DeployRequest)
112114
return nil, err
113115
}
114116

117+
// Unmarshal secrets config to get environment variables
118+
var secretsConfig ctrlv1.SecretsConfig
119+
if len(deployment.SecretsConfig) > 0 {
120+
if err = protojson.Unmarshal(deployment.SecretsConfig, &secretsConfig); err != nil {
121+
return nil, fmt.Errorf("failed to unmarshal secrets config: %w", err)
122+
}
123+
}
124+
125+
// Decrypt environment variables using the workspace's keyring
126+
decryptedEnvVars := make(map[string]string, len(secretsConfig.GetSecrets()))
127+
if w.vault != nil {
128+
for key, encryptedValue := range secretsConfig.GetSecrets() {
129+
decrypted, decryptErr := w.vault.Decrypt(ctx, &vaultv1.DecryptRequest{
130+
Keyring: deployment.WorkspaceID,
131+
Encrypted: encryptedValue,
132+
})
133+
if decryptErr != nil {
134+
return nil, fmt.Errorf("failed to decrypt env var %s: %w", key, decryptErr)
135+
}
136+
decryptedEnvVars[key] = decrypted.GetPlaintext()
137+
}
138+
}
139+
115140
_, err = restate.Run(ctx, func(stepCtx restate.RunContext) (restate.Void, error) {
116141
// Create deployment request
117142

@@ -123,6 +148,7 @@ func (w *Workflow) Deploy(ctx restate.ObjectContext, req *hydrav1.DeployRequest)
123148
Replicas: 1,
124149
CpuMillicores: 512,
125150
MemorySizeMib: 512,
151+
EnvVars: decryptedEnvVars,
126152
},
127153
}))
128154
if err != nil {

go/apps/ctrl/workflows/deploy/service.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import (
66
"github.com/unkeyed/unkey/go/gen/proto/krane/v1/kranev1connect"
77
"github.com/unkeyed/unkey/go/pkg/db"
88
"github.com/unkeyed/unkey/go/pkg/otel/logging"
9+
"github.com/unkeyed/unkey/go/pkg/vault"
910
)
1011

1112
const hardcodedNamespace = "unkey"
@@ -27,6 +28,7 @@ type Workflow struct {
2728
krane kranev1connect.DeploymentServiceClient
2829
buildClient ctrlv1connect.BuildServiceClient
2930
defaultDomain string
31+
vault *vault.Service
3032
}
3133

3234
var _ hydrav1.DeploymentServiceServer = (*Workflow)(nil)
@@ -47,6 +49,9 @@ type Config struct {
4749

4850
// DefaultDomain is the apex domain for generated deployment URLs (e.g., "unkey.app").
4951
DefaultDomain string
52+
53+
// Vault provides encryption/decryption services for secrets.
54+
Vault *vault.Service
5055
}
5156

5257
// New creates a new deployment workflow instance.
@@ -58,5 +63,6 @@ func New(cfg Config) *Workflow {
5863
krane: cfg.Krane,
5964
buildClient: cfg.BuildClient,
6065
defaultDomain: cfg.DefaultDomain,
66+
vault: cfg.Vault,
6167
}
6268
}

0 commit comments

Comments
 (0)