# services/harbor/helmrelease.yaml apiVersion: helm.toolkit.fluxcd.io/v2 kind: HelmRelease metadata: name: harbor namespace: harbor spec: interval: 10m install: timeout: 20m remediation: retries: 3 upgrade: timeout: 20m remediation: retries: 3 remediateLastFailure: true cleanupOnFail: true rollback: timeout: 20m chart: spec: chart: harbor version: 1.18.1 sourceRef: kind: HelmRepository name: harbor namespace: flux-system values: externalURL: https://registry.bstein.dev imagePullPolicy: IfNotPresent imagePullSecrets: - name: harbor-regcred expose: type: ingress tls: enabled: true certSource: secret secret: secretName: registry-bstein-dev-tls ingress: className: traefik annotations: cert-manager.io/cluster-issuer: letsencrypt traefik.ingress.kubernetes.io/router.entrypoints: websecure traefik.ingress.kubernetes.io/router.tls: "true" hosts: core: registry.bstein.dev persistence: enabled: true resourcePolicy: keep persistentVolumeClaim: registry: existingClaim: harbor-registry accessMode: ReadWriteOnce size: 50Gi jobservice: jobLog: existingClaim: harbor-jobservice-logs accessMode: ReadWriteOnce size: 5Gi imageChartStorage: type: filesystem filesystem: rootdirectory: /storage database: type: external external: host: postgres-service.postgres.svc.cluster.local port: "5432" username: harbor coreDatabase: harbor existingSecret: harbor-db sslmode: disable redis: type: internal internal: image: repository: registry.bstein.dev/infra/harbor-redis tag: v2.14.1-arm64 # {"$imagepolicy": "harbor:harbor-redis:tag"} nodeSelector: kubernetes.io/hostname: titan-05 affinity: nodeAffinity: requiredDuringSchedulingIgnoredDuringExecution: nodeSelectorTerms: - matchExpressions: - key: kubernetes.io/arch operator: In values: ["arm64"] preferredDuringSchedulingIgnoredDuringExecution: - weight: 90 preference: matchExpressions: - key: hardware operator: In values: ["rpi5"] - weight: 50 preference: matchExpressions: - key: hardware operator: In values: ["rpi4"] trivy: enabled: false metrics: enabled: false cache: enabled: false existingSecretAdminPassword: harbor-core existingSecretAdminPasswordKey: harbor_admin_password existingSecretSecretKey: harbor-core core: image: repository: registry.bstein.dev/infra/harbor-core tag: v2.14.1-arm64 # {"$imagepolicy": "harbor:harbor-core:tag"} nodeSelector: kubernetes.io/hostname: titan-05 serviceAccountName: harbor-vault-sync automountServiceAccountToken: true existingSecret: harbor-core existingXsrfSecret: harbor-core existingXsrfSecretKey: CSRF_KEY secretName: harbor-core podAnnotations: vault.hashicorp.com/agent-inject: "true" vault.hashicorp.com/role: "harbor" vault.hashicorp.com/agent-inject-secret-harbor-core-env.sh: "kv/data/atlas/harbor/harbor-core" vault.hashicorp.com/agent-inject-template-harbor-core-env.sh: | {{ with secret "kv/data/atlas/harbor/harbor-core" }} export CORE_SECRET="{{ .Data.data.secret }}" export CSRF_KEY="{{ .Data.data.CSRF_KEY }}" export HARBOR_ADMIN_PASSWORD="{{ .Data.data.harbor_admin_password }}" export REGISTRY_CREDENTIAL_PASSWORD="{{ .Data.data.REGISTRY_CREDENTIAL_PASSWORD }}" {{ end }} {{ with secret "kv/data/atlas/harbor/harbor-jobservice" }} export JOBSERVICE_SECRET="{{ .Data.data.JOBSERVICE_SECRET }}" {{ end }} {{ with secret "kv/data/atlas/harbor/harbor-db" }} export POSTGRESQL_PASSWORD="{{ .Data.data.password }}" {{ end }} {{ with secret "kv/data/atlas/harbor/harbor-oidc" }} export CONFIG_OVERWRITE_JSON='{{ .Data.data.CONFIG_OVERWRITE_JSON }}' {{ end }} vault.hashicorp.com/agent-inject-secret-harbor-core-secretKey: "kv/data/atlas/harbor/harbor-core" vault.hashicorp.com/agent-inject-template-harbor-core-secretKey: | {{- with secret "kv/data/atlas/harbor/harbor-core" -}}{{ .Data.data.secretKey }}{{- end -}} vault.hashicorp.com/agent-inject-secret-harbor-core-tls-key: "kv/data/atlas/harbor/harbor-core" vault.hashicorp.com/agent-inject-template-harbor-core-tls-key: | {{- with secret "kv/data/atlas/harbor/harbor-core" -}} {{ index .Data.data "tls.key" }} {{- end }} affinity: nodeAffinity: requiredDuringSchedulingIgnoredDuringExecution: nodeSelectorTerms: - matchExpressions: - key: kubernetes.io/arch operator: In values: ["arm64"] preferredDuringSchedulingIgnoredDuringExecution: - weight: 90 preference: matchExpressions: - key: hardware operator: In values: ["rpi5"] - weight: 50 preference: matchExpressions: - key: hardware operator: In values: ["rpi4"] jobservice: image: repository: registry.bstein.dev/infra/harbor-jobservice tag: v2.14.1-arm64 # {"$imagepolicy": "harbor:harbor-jobservice:tag"} nodeSelector: kubernetes.io/hostname: titan-05 serviceAccountName: harbor-vault-sync automountServiceAccountToken: true existingSecret: harbor-jobservice podAnnotations: vault.hashicorp.com/agent-inject: "true" vault.hashicorp.com/role: "harbor" vault.hashicorp.com/agent-inject-secret-harbor-jobservice-env.sh: "kv/data/atlas/harbor/harbor-jobservice" vault.hashicorp.com/agent-inject-template-harbor-jobservice-env.sh: | {{ with secret "kv/data/atlas/harbor/harbor-core" }} export CORE_SECRET="{{ .Data.data.secret }}" {{ end }} {{ with secret "kv/data/atlas/harbor/harbor-jobservice" }} export JOBSERVICE_SECRET="{{ .Data.data.JOBSERVICE_SECRET }}" export REGISTRY_CREDENTIAL_PASSWORD="{{ .Data.data.REGISTRY_CREDENTIAL_PASSWORD }}" {{ end }} affinity: nodeAffinity: requiredDuringSchedulingIgnoredDuringExecution: nodeSelectorTerms: - matchExpressions: - key: kubernetes.io/arch operator: In values: ["arm64"] preferredDuringSchedulingIgnoredDuringExecution: - weight: 90 preference: matchExpressions: - key: hardware operator: In values: ["rpi5"] - weight: 50 preference: matchExpressions: - key: hardware operator: In values: ["rpi4"] portal: image: repository: registry.bstein.dev/infra/harbor-portal tag: v2.14.1-arm64 # {"$imagepolicy": "harbor:harbor-portal:tag"} nodeSelector: kubernetes.io/hostname: titan-05 affinity: nodeAffinity: requiredDuringSchedulingIgnoredDuringExecution: nodeSelectorTerms: - matchExpressions: - key: kubernetes.io/arch operator: In values: ["arm64"] preferredDuringSchedulingIgnoredDuringExecution: - weight: 90 preference: matchExpressions: - key: hardware operator: In values: ["rpi5"] - weight: 50 preference: matchExpressions: - key: hardware operator: In values: ["rpi4"] registry: registry: image: repository: registry.bstein.dev/infra/harbor-registry tag: v2.14.1-arm64 # {"$imagepolicy": "harbor:harbor-registry:tag"} controller: image: repository: registry.bstein.dev/infra/harbor-registryctl tag: v2.14.1-arm64 # {"$imagepolicy": "harbor:harbor-registryctl:tag"} serviceAccountName: harbor-vault-sync automountServiceAccountToken: true existingSecret: harbor-registry credentials: existingSecret: harbor-registry podAnnotations: vault.hashicorp.com/agent-inject: "true" vault.hashicorp.com/role: "harbor" vault.hashicorp.com/agent-inject-secret-harbor-registry-env.sh: "kv/data/atlas/harbor/harbor-registry" vault.hashicorp.com/agent-inject-template-harbor-registry-env.sh: | {{ with secret "kv/data/atlas/harbor/harbor-registry" }} export REGISTRY_HTTP_SECRET="{{ .Data.data.REGISTRY_HTTP_SECRET }}" export REGISTRY_REDIS_PASSWORD="{{ .Data.data.REGISTRY_REDIS_PASSWORD }}" {{ end }} vault.hashicorp.com/agent-inject-secret-harbor-registryctl-env.sh: "kv/data/atlas/harbor/harbor-registry" vault.hashicorp.com/agent-inject-template-harbor-registryctl-env.sh: | {{ with secret "kv/data/atlas/harbor/harbor-core" }} export CORE_SECRET="{{ .Data.data.secret }}" {{ end }} {{ with secret "kv/data/atlas/harbor/harbor-jobservice" }} export JOBSERVICE_SECRET="{{ .Data.data.JOBSERVICE_SECRET }}" {{ end }} {{ with secret "kv/data/atlas/harbor/harbor-registry" }} export REGISTRY_HTTP_SECRET="{{ .Data.data.REGISTRY_HTTP_SECRET }}" export REGISTRY_REDIS_PASSWORD="{{ .Data.data.REGISTRY_REDIS_PASSWORD }}" {{ end }} vault.hashicorp.com/agent-inject-secret-harbor-registry-htpasswd: "kv/data/atlas/harbor/harbor-registry-htpasswd" vault.hashicorp.com/agent-inject-template-harbor-registry-htpasswd: | {{- with secret "kv/data/atlas/harbor/harbor-registry-htpasswd" -}} {{ .Data.data.REGISTRY_HTPASSWD }} {{- end }} nodeSelector: kubernetes.io/hostname: titan-05 affinity: nodeAffinity: requiredDuringSchedulingIgnoredDuringExecution: nodeSelectorTerms: - matchExpressions: - key: kubernetes.io/arch operator: In values: ["arm64"] preferredDuringSchedulingIgnoredDuringExecution: - weight: 90 preference: matchExpressions: - key: hardware operator: In values: ["rpi5"] - weight: 50 preference: matchExpressions: - key: hardware operator: In values: ["rpi4"] nginx: image: repository: registry.bstein.dev/infra/harbor-nginx tag: v2.14.1-arm64 # {"$imagepolicy": "harbor:harbor-nginx:tag"} nodeSelector: kubernetes.io/hostname: titan-05 affinity: nodeAffinity: requiredDuringSchedulingIgnoredDuringExecution: nodeSelectorTerms: - matchExpressions: - key: kubernetes.io/arch operator: In values: ["arm64"] preferredDuringSchedulingIgnoredDuringExecution: - weight: 90 preference: matchExpressions: - key: hardware operator: In values: ["rpi5"] - weight: 50 preference: matchExpressions: - key: hardware operator: In values: ["rpi4"] prepare: image: repository: registry.bstein.dev/infra/harbor-prepare tag: v2.14.1-arm64 # {"$imagepolicy": "harbor:harbor-prepare:tag"} updateStrategy: type: Recreate postRenderers: - kustomize: patches: - target: kind: Deployment name: harbor-core patch: |- apiVersion: apps/v1 kind: Deployment metadata: name: harbor-core spec: template: spec: containers: - name: core command: - /entrypoint.sh args: - /harbor/entrypoint.sh env: - $patch: replace - name: VAULT_ENV_FILE value: /vault/secrets/harbor-core-env.sh - name: VAULT_COPY_FILES value: /vault/secrets/harbor-core-secretkey:/etc/core/key,/vault/secrets/harbor-core-tls-key:/etc/core/private_key.pem envFrom: - $patch: replace - configMapRef: name: harbor-core volumeMounts: - $patch: replace - name: harbor-vault-entrypoint mountPath: /entrypoint.sh subPath: vault-entrypoint.sh - name: core-writable mountPath: /etc/core - name: config mountPath: /etc/core/app.conf subPath: app.conf - name: ca-download mountPath: /etc/core/ca - name: psc mountPath: /etc/core/token volumes: - name: harbor-vault-entrypoint configMap: name: harbor-vault-entrypoint defaultMode: 493 - name: secret-key $patch: delete - name: token-service-private-key $patch: delete - name: core-writable emptyDir: {} - target: kind: Ingress name: harbor-ingress patch: |- - op: replace path: /spec/rules/0/http/paths/2/backend/service/name value: harbor-registry - op: replace path: /spec/rules/0/http/paths/2/backend/service/port/number value: 5000 - target: kind: Deployment name: harbor-jobservice patch: |- apiVersion: apps/v1 kind: Deployment metadata: name: harbor-jobservice spec: template: spec: containers: - name: jobservice command: - /entrypoint.sh args: - /harbor/entrypoint.sh env: - $patch: replace - name: VAULT_ENV_FILE value: /vault/secrets/harbor-jobservice-env.sh envFrom: - $patch: replace - configMapRef: name: harbor-jobservice-env volumeMounts: - $patch: replace - name: harbor-vault-entrypoint mountPath: /entrypoint.sh subPath: vault-entrypoint.sh - name: jobservice-config mountPath: /etc/jobservice/config.yml subPath: config.yml - name: job-logs mountPath: /var/log/jobs volumes: - name: harbor-vault-entrypoint configMap: name: harbor-vault-entrypoint defaultMode: 493 - target: kind: Deployment name: harbor-registry patch: |- apiVersion: apps/v1 kind: Deployment metadata: name: harbor-registry spec: template: spec: containers: - name: registry command: - /entrypoint.sh args: - /home/harbor/entrypoint.sh env: - $patch: replace - name: VAULT_ENV_FILE value: /vault/secrets/harbor-registry-env.sh - name: VAULT_COPY_FILES value: /vault/secrets/harbor-registry-htpasswd:/etc/registry/passwd envFrom: - $patch: replace volumeMounts: - $patch: replace - name: harbor-vault-entrypoint mountPath: /entrypoint.sh subPath: vault-entrypoint.sh - name: registry-writable mountPath: /etc/registry - name: registry-config mountPath: /etc/registry/config.yml subPath: config.yml - name: registry-data mountPath: /storage - name: registryctl command: - /entrypoint.sh args: - /home/harbor/start.sh env: - $patch: replace - name: VAULT_ENV_FILE value: /vault/secrets/harbor-registryctl-env.sh envFrom: - $patch: replace - configMapRef: name: harbor-registryctl volumeMounts: - $patch: replace - name: harbor-vault-entrypoint mountPath: /entrypoint.sh subPath: vault-entrypoint.sh - name: registry-config mountPath: /etc/registry/config.yml subPath: config.yml - name: registry-config mountPath: /etc/registryctl/config.yml subPath: ctl-config.yml - name: registry-data mountPath: /storage volumes: - name: harbor-vault-entrypoint configMap: name: harbor-vault-entrypoint defaultMode: 493 - name: registry-htpasswd $patch: delete - name: registry-writable emptyDir: {} - target: kind: Job name: migration-job patch: |- apiVersion: batch/v1 kind: Job metadata: name: migration-job spec: template: metadata: annotations: vault.hashicorp.com/agent-inject: "true" vault.hashicorp.com/role: "harbor" vault.hashicorp.com/agent-inject-secret-harbor-core-env.sh: "kv/data/atlas/harbor/harbor-core" vault.hashicorp.com/agent-inject-template-harbor-core-env.sh: | {{ with secret "kv/data/atlas/harbor/harbor-core" }} export CORE_SECRET="{{ .Data.data.secret }}" export CSRF_KEY="{{ .Data.data.CSRF_KEY }}" export HARBOR_ADMIN_PASSWORD="{{ .Data.data.harbor_admin_password }}" export REGISTRY_CREDENTIAL_PASSWORD="{{ .Data.data.REGISTRY_CREDENTIAL_PASSWORD }}" {{ end }} {{ with secret "kv/data/atlas/harbor/harbor-jobservice" }} export JOBSERVICE_SECRET="{{ .Data.data.JOBSERVICE_SECRET }}" {{ end }} {{ with secret "kv/data/atlas/harbor/harbor-db" }} export POSTGRESQL_PASSWORD="{{ .Data.data.password }}" {{ end }} {{ with secret "kv/data/atlas/harbor/harbor-oidc" }} export CONFIG_OVERWRITE_JSON='{{ .Data.data.CONFIG_OVERWRITE_JSON }}' {{ end }} vault.hashicorp.com/agent-inject-secret-harbor-core-secretKey: "kv/data/atlas/harbor/harbor-core" vault.hashicorp.com/agent-inject-template-harbor-core-secretKey: | {{- with secret "kv/data/atlas/harbor/harbor-core" -}}{{ .Data.data.secretKey }}{{- end -}} vault.hashicorp.com/agent-inject-secret-harbor-core-tls-key: "kv/data/atlas/harbor/harbor-core" vault.hashicorp.com/agent-inject-template-harbor-core-tls-key: | {{- with secret "kv/data/atlas/harbor/harbor-core" -}} {{ index .Data.data "tls.key" }} {{- end }} spec: automountServiceAccountToken: true containers: - name: core-job command: - /entrypoint.sh args: - /harbor/harbor_core - -mode=migrate env: - $patch: replace - name: VAULT_ENV_FILE value: /vault/secrets/harbor-core-env.sh envFrom: - $patch: replace - configMapRef: name: harbor-core volumeMounts: - $patch: replace - name: harbor-vault-entrypoint mountPath: /entrypoint.sh subPath: vault-entrypoint.sh - name: config mountPath: /etc/core/app.conf subPath: app.conf volumes: - name: harbor-vault-entrypoint configMap: name: harbor-vault-entrypoint defaultMode: 493 - target: kind: Secret name: harbor-core patch: |- apiVersion: v1 kind: Secret metadata: name: harbor-core $patch: delete - target: kind: Secret name: harbor-jobservice patch: |- apiVersion: v1 kind: Secret metadata: name: harbor-jobservice $patch: delete - target: kind: Secret name: harbor-registry patch: |- apiVersion: v1 kind: Secret metadata: name: harbor-registry $patch: delete - target: kind: Secret name: harbor-registry-htpasswd patch: |- apiVersion: v1 kind: Secret metadata: name: harbor-registry-htpasswd $patch: delete - target: kind: Secret name: harbor-registryctl patch: |- apiVersion: v1 kind: Secret metadata: name: harbor-registryctl $patch: delete