Merge pull request 'feat: render runtime secret GitOps' (#48) from feat/app-secret-gitops into main
Reviewed-on: #48
This commit is contained in:
commit
d4f6b74bef
|
|
@ -52,7 +52,10 @@ func OnboardApp(cfg config.Config, sourceDir string) error {
|
|||
return fmt.Errorf("protect Forgejo production branch: %w", err)
|
||||
}
|
||||
changed, err := manager.PublishDeliveryBranch(sourceDir, sourceBranch, resolved.Delivery.AppRepoURL, deliveryBranch, func(dir string) error {
|
||||
return GenerateAppDelivery(dir, resolved)
|
||||
if err := GenerateAppDelivery(dir, resolved); err != nil {
|
||||
return err
|
||||
}
|
||||
return GenerateAppSecretAccess(dir, resolved)
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
|
|
@ -205,7 +208,7 @@ func renderAppRegistration(cfg config.Config) ([]byte, error) {
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return []byte(fmt.Sprintf(`apiVersion: source.toolkit.fluxcd.io/v1
|
||||
content := fmt.Sprintf(`apiVersion: source.toolkit.fluxcd.io/v1
|
||||
kind: GitRepository
|
||||
metadata:
|
||||
name: %s
|
||||
|
|
@ -234,7 +237,104 @@ spec:
|
|||
sourceRef:
|
||||
kind: GitRepository
|
||||
name: %s
|
||||
`, cfg.Delivery.AppName, cfg.Delivery.AppRepoURL, branch, cfg.Delivery.AppName, cfg.Delivery.AppName)), nil
|
||||
`, cfg.Delivery.AppName, cfg.Delivery.AppRepoURL, branch, cfg.Delivery.AppName, cfg.Delivery.AppName)
|
||||
if hasRuntimeSecretGrant(cfg) {
|
||||
content += fmt.Sprintf(`---
|
||||
apiVersion: kustomize.toolkit.fluxcd.io/v1
|
||||
kind: Kustomization
|
||||
metadata:
|
||||
name: %s-secrets
|
||||
namespace: flux-system
|
||||
spec:
|
||||
interval: 5m
|
||||
path: ./.maidn
|
||||
prune: true
|
||||
wait: true
|
||||
dependsOn:
|
||||
- name: external-secrets-config
|
||||
sourceRef:
|
||||
kind: GitRepository
|
||||
name: %s
|
||||
`, cfg.Delivery.AppName, cfg.Delivery.AppName)
|
||||
}
|
||||
return []byte(content), nil
|
||||
}
|
||||
|
||||
func hasRuntimeSecretGrant(cfg config.Config) bool {
|
||||
for _, grant := range cfg.SecretGrants {
|
||||
if grant.Application == cfg.Delivery.AppName && grant.Consumer == "runtime" {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// GenerateAppSecretAccess renders only OpenBao references, never secret values.
|
||||
func GenerateAppSecretAccess(dir string, cfg config.Config) error {
|
||||
if err := config.ValidateSecretGrants(cfg.SecretGrants); err != nil {
|
||||
return err
|
||||
}
|
||||
var manifests string
|
||||
for _, grant := range cfg.SecretGrants {
|
||||
if grant.Application != cfg.Delivery.AppName || grant.Consumer != "runtime" {
|
||||
continue
|
||||
}
|
||||
name := cfg.Delivery.AppName + "-runtime-" + grant.Environment
|
||||
manifests += fmt.Sprintf(`apiVersion: v1
|
||||
kind: ServiceAccount
|
||||
metadata:
|
||||
name: maidn-%s
|
||||
namespace: %s
|
||||
---
|
||||
apiVersion: external-secrets.io/v1
|
||||
kind: SecretStore
|
||||
metadata:
|
||||
name: openbao-%s
|
||||
namespace: %s
|
||||
spec:
|
||||
provider:
|
||||
vault:
|
||||
server: http://openbao.openbao.svc:8200
|
||||
path: secret
|
||||
version: v2
|
||||
auth:
|
||||
kubernetes:
|
||||
mountPath: kubernetes
|
||||
role: maidn-%s
|
||||
serviceAccountRef:
|
||||
name: maidn-%s
|
||||
---
|
||||
apiVersion: external-secrets.io/v1
|
||||
kind: ExternalSecret
|
||||
metadata:
|
||||
name: %s
|
||||
namespace: %s
|
||||
spec:
|
||||
refreshInterval: 1h
|
||||
secretStoreRef:
|
||||
kind: SecretStore
|
||||
name: openbao-%s
|
||||
target:
|
||||
name: %s
|
||||
creationPolicy: Owner
|
||||
data:
|
||||
`, name, grant.Environment, name, grant.Environment, name, name, name, grant.Environment, name, name)
|
||||
for _, secret := range grant.Secrets {
|
||||
manifests += fmt.Sprintf(" - secretKey: %s\n remoteRef:\n key: apps/%s/%s\n property: value\n", secret, grant.Application, secret)
|
||||
}
|
||||
manifests += "---\n"
|
||||
}
|
||||
if manifests == "" {
|
||||
return nil
|
||||
}
|
||||
maidnDir := filepath.Join(dir, ".maidn")
|
||||
if err := os.MkdirAll(maidnDir, 0755); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := os.WriteFile(filepath.Join(maidnDir, "kustomization.yaml"), []byte("apiVersion: kustomize.config.k8s.io/v1beta1\nkind: Kustomization\nresources:\n - secret-access.yaml\n"), 0644); err != nil {
|
||||
return err
|
||||
}
|
||||
return os.WriteFile(filepath.Join(maidnDir, "secret-access.yaml"), []byte(manifests), 0644)
|
||||
}
|
||||
|
||||
func readRegularFile(path string) ([]byte, error) {
|
||||
|
|
|
|||
|
|
@ -111,6 +111,23 @@ func TestRegisterAppInClusterRendersManagedFluxSource(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestGenerateAppSecretAccessRendersOnlyDeclaredRuntimeSecrets(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
cfg := onboardingConfig()
|
||||
cfg.SecretGrants = []config.SecretGrant{{Application: "web-ui", Consumer: "runtime", Environment: "staging", Secrets: []string{"api-key"}, Shared: []string{"payments"}}}
|
||||
if err := GenerateAppSecretAccess(dir, cfg); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
access, err := os.ReadFile(filepath.Join(dir, ".maidn", "secret-access.yaml"))
|
||||
if err != nil || !strings.Contains(string(access), "namespace: staging") || !strings.Contains(string(access), "key: apps/web-ui/api-key") || strings.Contains(string(access), "shared/payments") {
|
||||
t.Fatalf("secret access = %q, %v", access, err)
|
||||
}
|
||||
registration, err := renderAppRegistration(cfg)
|
||||
if err != nil || !strings.Contains(string(registration), "name: web-ui-secrets") || !strings.Contains(string(registration), "path: ./.maidn") {
|
||||
t.Fatalf("secret registration = %q, %v", registration, err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestPublishInitialAppBranchesCreatesAndPreservesProduction(t *testing.T) {
|
||||
source := filepath.Join(t.TempDir(), "source")
|
||||
target := filepath.Join(t.TempDir(), "target.git")
|
||||
|
|
|
|||
Loading…
Reference in a new issue