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)
|
return fmt.Errorf("protect Forgejo production branch: %w", err)
|
||||||
}
|
}
|
||||||
changed, err := manager.PublishDeliveryBranch(sourceDir, sourceBranch, resolved.Delivery.AppRepoURL, deliveryBranch, func(dir string) error {
|
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 {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|
@ -205,7 +208,7 @@ func renderAppRegistration(cfg config.Config) ([]byte, error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return []byte(fmt.Sprintf(`apiVersion: source.toolkit.fluxcd.io/v1
|
content := fmt.Sprintf(`apiVersion: source.toolkit.fluxcd.io/v1
|
||||||
kind: GitRepository
|
kind: GitRepository
|
||||||
metadata:
|
metadata:
|
||||||
name: %s
|
name: %s
|
||||||
|
|
@ -231,10 +234,107 @@ spec:
|
||||||
targetNamespace: tekton-pipelines
|
targetNamespace: tekton-pipelines
|
||||||
dependsOn:
|
dependsOn:
|
||||||
- name: tekton-catalog
|
- name: tekton-catalog
|
||||||
|
sourceRef:
|
||||||
|
kind: GitRepository
|
||||||
|
name: %s
|
||||||
|
`, 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:
|
sourceRef:
|
||||||
kind: GitRepository
|
kind: GitRepository
|
||||||
name: %s
|
name: %s
|
||||||
`, cfg.Delivery.AppName, cfg.Delivery.AppRepoURL, branch, cfg.Delivery.AppName, cfg.Delivery.AppName)), nil
|
`, 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) {
|
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) {
|
func TestPublishInitialAppBranchesCreatesAndPreservesProduction(t *testing.T) {
|
||||||
source := filepath.Join(t.TempDir(), "source")
|
source := filepath.Join(t.TempDir(), "source")
|
||||||
target := filepath.Join(t.TempDir(), "target.git")
|
target := filepath.Join(t.TempDir(), "target.git")
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue