mirror of
https://github.com/versity/versitygw.git
synced 2026-09-19 14:34:19 +00:00
This adds a new iamServer Deployment, split public and private Services, a PersistentVolumeClaim, and cert-manager Certificate resources so the standalone versitygw iam API server can be deployed directly from this chart, independently scalable from the S3 gateway and backed by either internal file storage or HashiCorp Vault, with Vault auth credentials and Vault TLS material kept in separate Kubernetes secrets. The gateway side gains iam.type=standalone client wiring that reaches the IAM service over its private mTLS endpoint, with certificates supplied either through an existing secret or auto-provisioned via cert-manager using a shared CA-type issuer so both peers can verify each other from their own certificate's ca.crt, and the chart auto-targets the in-chart service when no external endpoint is configured. gateway.logLevel and iamServer.logLevel replace the old boolean debug flag with the silent, debug, and unsafe levels the binary now supports, docker-entrypoint.sh gained iam as a recognized VGW_BACKEND value so the new deployment can start through the existing entrypoint, NetworkPolicy resources were corrected to use proper peer lists and to default to deny instead of allow when no ingress rules are configured, pod and Deployment selector labels were separated between the gateway and the IAM server to prevent them from matching each other's Services, and a battery of template time validation guards was added to fail fast on invalid combinations such as multiple replicas against the internal file store or a missing certificate for a TCP private endpoint, together with expanded helm lint coverage in CI for these new configurations.
383 lines
18 KiB
YAML
383 lines
18 KiB
YAML
{{- $gatewayLogLevel := default "silent" .Values.gateway.logLevel -}}
|
|
{{- $iamServer := .Values.iamServer | default dict -}}
|
|
{{- $iamServerEnabled := $iamServer.enabled | default false -}}
|
|
{{- $iamServerPrivate := $iamServer.private | default dict -}}
|
|
{{- $iamServerPrivateEnabled := $iamServerPrivate.enabled | default false -}}
|
|
{{- $iamServerAuth := $iamServer.auth | default dict -}}
|
|
{{- $iamStandalone := .Values.iam.standalone | default dict -}}
|
|
{{- $iamStandaloneEndpoint := $iamStandalone.endpoint | default "" -}}
|
|
{{- $iamStandaloneCredentials := $iamStandalone.credentials | default dict -}}
|
|
{{- /* Safety check: multiple replicas with local state must have persistence enabled */}}
|
|
{{- if and (gt (int .Values.replicaCount) 1) .Values.iam.enabled (eq .Values.iam.type "internal") }}
|
|
{{- fail "replicaCount > 1 cannot use iam.type=internal because the file store does not coordinate concurrent writers; use standalone or another external IAM backend" }}
|
|
{{- end }}
|
|
{{- if and (gt (int .Values.replicaCount) 1) (not .Values.persistence.enabled) }}
|
|
{{- if eq .Values.gateway.backend.type "posix" }}
|
|
{{- fail "Multiple replicas with a POSIX backend require persistence.enabled=true. If using a stateless backend (e.g. S3 or Azure) and external IAM, persistence can be disabled." }}
|
|
{{- end }}
|
|
{{- end }}
|
|
{{- if and .Values.persistence.enabled (not .Values.persistence.create) (not .Values.persistence.claimName) (not .Values.persistence.hostPath) }}
|
|
{{- fail "persistence.claimName or persistence.hostPath is required when persistence.create is false" }}
|
|
{{- end }}
|
|
{{- if and .Values.persistence.claimName .Values.persistence.hostPath }}
|
|
{{- fail "only persistence.claimName or persistence.hostPath can be set" }}
|
|
{{- end }}
|
|
{{- if and .Values.persistence.create .Values.persistence.hostPath }}
|
|
{{- fail "persistence.create must be false for persistence.hostPath can be set" }}
|
|
{{- end }}
|
|
{{- if and .Values.gateway.backend.sidecarDir (ne .Values.gateway.backend.type "posix") }}
|
|
{{- fail "gateway.backend.sidecarDir is only supported with the posix backend" }}
|
|
{{- end }}
|
|
{{- if and .Values.gateway.backend.versioningDir (not (or (eq .Values.gateway.backend.type "posix") (eq .Values.gateway.backend.type "scoutfs"))) }}
|
|
{{- fail "gateway.backend.versioningDir is only supported with the posix and scoutfs backends" }}
|
|
{{- end }}
|
|
{{- if not (or (eq $gatewayLogLevel "silent") (eq $gatewayLogLevel "debug") (eq $gatewayLogLevel "unsafe")) }}
|
|
{{- fail "gateway.logLevel must be one of silent, debug, or unsafe" }}
|
|
{{- end }}
|
|
{{- if and .Values.iam.enabled (eq .Values.iam.type "standalone") (not .Values.iam.standalone.endpoint) (not $iamServerEnabled) }}
|
|
{{- fail "iam.type=standalone requires either iam.standalone.endpoint or iamServer.enabled=true" }}
|
|
{{- end }}
|
|
{{- if and .Values.iam.enabled (eq .Values.iam.type "standalone") (not .Values.iam.standalone.endpoint) $iamServerEnabled (not $iamServerPrivateEnabled) }}
|
|
{{- fail "in-chart standalone IAM requires iamServer.private.enabled=true; otherwise set iam.standalone.endpoint explicitly" }}
|
|
{{- end }}
|
|
{{- if and .Values.iam.enabled (eq .Values.iam.type "standalone") $iamStandaloneEndpoint (not (regexMatch "^(\\[[0-9A-Fa-f:]+\\]|[^/:[:space:]]+):[0-9]+$" $iamStandaloneEndpoint)) }}
|
|
{{- fail "iam.standalone.endpoint must be a host:port TCP address; the chart does not provide a shared volume for Unix sockets" }}
|
|
{{- end }}
|
|
{{- if and .Values.iam.enabled (eq .Values.iam.type "standalone") (not (or .Values.iam.standalone.certificate.create .Values.iam.standalone.certificate.existingSecret)) }}
|
|
{{- fail "a standalone IAM TCP endpoint requires iam.standalone.certificate.create=true or iam.standalone.certificate.existingSecret" }}
|
|
{{- end }}
|
|
{{- if and .Values.iam.enabled (eq .Values.iam.type "standalone") .Values.iam.standalone.certificate.create (not .Values.iam.standalone.certificate.existingSecret) (not .Values.iam.standalone.certificate.issuerRef.name) }}
|
|
{{- fail "iam.standalone.certificate.issuerRef.name is required when creating the IAM client certificate" }}
|
|
{{- end }}
|
|
{{- if and .Values.iam.enabled (eq .Values.iam.type "standalone") (or (and .Values.iam.standalone.access (not .Values.iam.standalone.secret)) (and .Values.iam.standalone.secret (not .Values.iam.standalone.access))) }}
|
|
{{- fail "iam.standalone.access and iam.standalone.secret must be set together, or both left empty to use the root credentials" }}
|
|
{{- end }}
|
|
{{- if and .Values.iam.enabled (eq .Values.iam.type "standalone") $iamStandaloneCredentials.existingSecret (or .Values.iam.standalone.access .Values.iam.standalone.secret) }}
|
|
{{- fail "iam.standalone.credentials.existingSecret cannot be combined with literal iam.standalone.access/secret values" }}
|
|
{{- end }}
|
|
{{- if and .Values.iam.enabled (eq .Values.iam.type "standalone") $iamStandaloneCredentials.existingSecret (or (not $iamStandaloneCredentials.accessKeyKey) (not $iamStandaloneCredentials.secretKeyKey)) }}
|
|
{{- fail "iam.standalone.credentials.accessKeyKey and secretKeyKey are required when credentials.existingSecret is set" }}
|
|
{{- end }}
|
|
{{- if and .Values.iam.enabled (eq .Values.iam.type "standalone") (not .Values.iam.standalone.endpoint) $iamServerAuth.existingSecret (not $iamStandaloneCredentials.existingSecret) (not .Values.iam.standalone.access) }}
|
|
{{- fail "in-chart IAM with iamServer.auth.existingSecret requires iam.standalone.credentials.existingSecret (normally the same Secret)" }}
|
|
{{- end }}
|
|
apiVersion: apps/v1
|
|
kind: Deployment
|
|
metadata:
|
|
name: {{ include "versitygw.fullname" . }}
|
|
labels:
|
|
{{- include "versitygw.labels" . | nindent 4 }}
|
|
{{- with .Values.deploymentAnnotations }}
|
|
annotations:
|
|
{{- toYaml . | nindent 4 }}
|
|
{{- end }}
|
|
spec:
|
|
replicas: {{ .Values.replicaCount }}
|
|
selector:
|
|
matchLabels:
|
|
{{- include "versitygw.selectorLabels" . | nindent 6 }}
|
|
{{- with .Values.strategy }}
|
|
strategy:
|
|
{{- toYaml . | nindent 4 }}
|
|
{{- end }}
|
|
template:
|
|
metadata:
|
|
annotations:
|
|
{{- if not .Values.auth.existingSecret }}
|
|
checksum/credentials: {{ cat .Release.Name .Values.auth.accessKey .Values.auth.secretKey | sha256sum | trunc 12 }}
|
|
{{- end }}
|
|
{{- with .Values.podAnnotations }}
|
|
{{- toYaml . | nindent 8 }}
|
|
{{- end }}
|
|
labels:
|
|
{{- include "versitygw.selectorLabels" . | nindent 8 }}
|
|
{{- with .Values.podLabels }}
|
|
{{- toYaml . | nindent 8 }}
|
|
{{- end }}
|
|
spec:
|
|
{{- with .Values.image.pullSecrets }}
|
|
imagePullSecrets:
|
|
{{- toYaml . | nindent 8 }}
|
|
{{- end }}
|
|
serviceAccountName: {{ include "versitygw.serviceAccountName" . }}
|
|
automountServiceAccountToken: false
|
|
securityContext:
|
|
{{- toYaml .Values.podSecurityContext | nindent 8 }}
|
|
containers:
|
|
- name: {{ .Chart.Name }}
|
|
image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
|
|
imagePullPolicy: {{ .Values.image.pullPolicy }}
|
|
env:
|
|
# Backend selection — consumed by docker-entrypoint.sh
|
|
- name: VGW_BACKEND
|
|
value: {{ .Values.gateway.backend.type | quote }}
|
|
- name: VGW_BACKEND_ARGS
|
|
value: {{ .Values.gateway.backend.args | quote }}
|
|
{{- if .Values.gateway.backend.sidecarDir }}
|
|
- name: VGW_META_SIDECAR
|
|
value: {{ .Values.gateway.backend.sidecarDir | quote }}
|
|
{{- end }}
|
|
{{- if .Values.gateway.backend.versioningDir }}
|
|
- name: VGW_VERSIONING_DIR
|
|
value: {{ .Values.gateway.backend.versioningDir | quote }}
|
|
{{- end }}
|
|
# Root credentials — sourced from a Kubernetes Secret
|
|
- name: ROOT_ACCESS_KEY_ID
|
|
valueFrom:
|
|
secretKeyRef:
|
|
name: {{ include "versitygw.credentialsSecretName" . }}
|
|
key: rootAccessKeyId
|
|
- name: ROOT_SECRET_ACCESS_KEY
|
|
valueFrom:
|
|
secretKeyRef:
|
|
name: {{ include "versitygw.credentialsSecretName" . }}
|
|
key: rootSecretAccessKey
|
|
# Gateway server settings
|
|
- name: VGW_PORT
|
|
value: ":{{ .Values.gateway.port }}"
|
|
- name: VGW_REGION
|
|
value: {{ .Values.gateway.region | quote }}
|
|
- name: VGW_HEALTH
|
|
value: "/_/health"
|
|
{{- if .Values.gateway.virtualDomain }}
|
|
- name: VGW_VIRTUAL_DOMAIN
|
|
value: {{ .Values.gateway.virtualDomain | quote }}
|
|
{{- end }}
|
|
{{- if .Values.gateway.readOnly }}
|
|
- name: VGW_READ_ONLY
|
|
value: "true"
|
|
{{- end }}
|
|
{{- if .Values.gateway.quiet }}
|
|
- name: VGW_QUIET
|
|
value: "true"
|
|
{{- end }}
|
|
{{- if ne $gatewayLogLevel "silent" }}
|
|
- name: VGW_LOG_LEVEL
|
|
value: {{ $gatewayLogLevel | quote }}
|
|
{{- else if .Values.gateway.debug }}
|
|
- name: VGW_LOG_LEVEL
|
|
value: "debug"
|
|
{{- end }}
|
|
{{- if .Values.gateway.accessLog }}
|
|
- name: VGW_ACCESS_LOG
|
|
value: {{ .Values.gateway.accessLog | quote }}
|
|
{{- end }}
|
|
{{- if .Values.gateway.corsAllowOrigin }}
|
|
- name: VGW_CORS_ALLOW_ORIGIN
|
|
value: {{ .Values.gateway.corsAllowOrigin | quote }}
|
|
{{- end }}
|
|
{{- if .Values.gateway.disableStrictBucketNames }}
|
|
- name: VGW_DISABLE_STRICT_BUCKET_NAMES
|
|
value: "true"
|
|
{{- end }}
|
|
# Admin API server
|
|
{{- if .Values.admin.enabled }}
|
|
- name: VGW_ADMIN_PORT
|
|
value: ":{{ .Values.admin.port }}"
|
|
- name: VGW_ADMIN_MAX_CONNECTIONS
|
|
value: {{ .Values.admin.maxConnections | quote }}
|
|
- name: VGW_ADMIN_MAX_REQUESTS
|
|
value: {{ .Values.admin.maxRequests | quote }}
|
|
{{- end }}
|
|
# WebUI
|
|
{{- if .Values.webui.enabled }}
|
|
- name: VGW_WEBUI_PORT
|
|
value: ":{{ .Values.webui.port }}"
|
|
{{- if .Values.webui.noTls }}
|
|
- name: VGW_WEBUI_NO_TLS
|
|
value: "true"
|
|
{{- end }}
|
|
{{- if .Values.webui.apiGateways }}
|
|
- name: VGW_WEBUI_GATEWAYS
|
|
value: {{ .Values.webui.apiGateways | join "," | quote }}
|
|
{{- end }}
|
|
{{- if .Values.webui.adminGateways }}
|
|
- name: VGW_WEBUI_ADMIN_GATEWAYS
|
|
value: {{ .Values.webui.adminGateways | join "," | quote }}
|
|
{{- end }}
|
|
{{- end }}
|
|
# Website Hosting
|
|
{{- if .Values.website.enabled }}
|
|
- name: VGW_WEBSITE_PORT
|
|
value: ":{{ .Values.website.port }}"
|
|
- name: VGW_WEBSITE_DOMAIN
|
|
value: {{ .Values.website.domain | quote }}
|
|
{{- if .Values.website.noTls }}
|
|
- name: VGW_WEBSITE_NO_TLS
|
|
value: "true"
|
|
{{- end }}
|
|
{{- end }}
|
|
{{- if .Values.iam.enabled }}
|
|
# IAM settings
|
|
{{- if eq .Values.iam.type "internal" }}
|
|
- name: VGW_IAM_DIR
|
|
value: "/mnt/iam"
|
|
{{- else if eq .Values.iam.type "standalone" }}
|
|
- name: VGW_IAM_STANDALONE_ENDPOINT
|
|
value: {{ include "versitygw.standaloneIAMEndpoint" . | quote }}
|
|
{{- if $iamStandaloneCredentials.existingSecret }}
|
|
- name: VGW_IAM_STANDALONE_ACCESS
|
|
valueFrom:
|
|
secretKeyRef:
|
|
name: {{ $iamStandaloneCredentials.existingSecret }}
|
|
key: {{ $iamStandaloneCredentials.accessKeyKey }}
|
|
- name: VGW_IAM_STANDALONE_SECRET
|
|
valueFrom:
|
|
secretKeyRef:
|
|
name: {{ $iamStandaloneCredentials.existingSecret }}
|
|
key: {{ $iamStandaloneCredentials.secretKeyKey }}
|
|
{{- else }}
|
|
{{- if .Values.iam.standalone.access }}
|
|
- name: VGW_IAM_STANDALONE_ACCESS
|
|
value: {{ .Values.iam.standalone.access | quote }}
|
|
{{- end }}
|
|
{{- if .Values.iam.standalone.secret }}
|
|
- name: VGW_IAM_STANDALONE_SECRET
|
|
value: {{ .Values.iam.standalone.secret | quote }}
|
|
{{- end }}
|
|
{{- end }}
|
|
{{- if or .Values.iam.standalone.certificate.create .Values.iam.standalone.certificate.existingSecret }}
|
|
- name: VGW_IAM_STANDALONE_CLIENT_CERT
|
|
value: /iam-client-certs/tls.crt
|
|
- name: VGW_IAM_STANDALONE_CLIENT_CERT_KEY
|
|
value: /iam-client-certs/tls.key
|
|
- name: VGW_IAM_STANDALONE_SERVER_CA
|
|
value: /iam-client-certs/ca.crt
|
|
{{- end }}
|
|
- name: VGW_IAM_STANDALONE_DEFAULT_UID
|
|
value: {{ .Values.iam.standalone.defaultUid | quote }}
|
|
- name: VGW_IAM_STANDALONE_DEFAULT_GID
|
|
value: {{ .Values.iam.standalone.defaultGid | quote }}
|
|
- name: VGW_IAM_STANDALONE_DEFAULT_PROJECT_ID
|
|
value: {{ .Values.iam.standalone.defaultProjectId | quote }}
|
|
{{- end }}
|
|
{{- if .Values.iam.cacheDisable }}
|
|
- name: VGW_IAM_CACHE_DISABLE
|
|
value: "true"
|
|
{{- end }}
|
|
- name: VGW_IAM_CACHE_TTL
|
|
value: {{ .Values.iam.cacheTtl | quote }}
|
|
- name: VGW_IAM_CACHE_PRUNE
|
|
value: {{ .Values.iam.cachePrune | quote }}
|
|
{{- if .Values.iam.debug }}
|
|
- name: VGW_IAM_DEBUG
|
|
value: "true"
|
|
{{- end }}
|
|
{{- end }}
|
|
# TLS
|
|
{{- if .Values.tls.enabled }}
|
|
- name: VGW_CERT
|
|
value: /certs/tls.crt
|
|
- name: VGW_KEY
|
|
value: /certs/tls.key
|
|
{{- end }}
|
|
# Extra user-provided environment variables
|
|
{{- with .Values.extraEnv }}
|
|
{{- toYaml . | nindent 12 }}
|
|
{{- end }}
|
|
ports:
|
|
- name: s3-api
|
|
containerPort: {{ .Values.gateway.port }}
|
|
protocol: TCP
|
|
{{- if .Values.admin.enabled }}
|
|
- name: admin
|
|
containerPort: {{ .Values.admin.port }}
|
|
protocol: TCP
|
|
{{- end }}
|
|
{{- if .Values.webui.enabled }}
|
|
- name: webui
|
|
containerPort: {{ .Values.webui.port }}
|
|
protocol: TCP
|
|
{{- end }}
|
|
{{- if .Values.website.enabled }}
|
|
- name: website
|
|
containerPort: {{ .Values.website.port }}
|
|
protocol: TCP
|
|
{{- end }}
|
|
readinessProbe:
|
|
httpGet:
|
|
path: "/_/health"
|
|
port: s3-api
|
|
initialDelaySeconds: 5
|
|
periodSeconds: 15
|
|
securityContext:
|
|
{{- toYaml .Values.securityContext | nindent 12 }}
|
|
resources:
|
|
{{- toYaml .Values.resources | nindent 12 }}
|
|
terminationMessagePolicy: FallbackToLogsOnError
|
|
volumeMounts:
|
|
- name: data
|
|
mountPath: /mnt/data
|
|
subPath: data
|
|
readOnly: false
|
|
{{- if .Values.gateway.backend.sidecarDir }}
|
|
- name: data
|
|
mountPath: {{ .Values.gateway.backend.sidecarDir }}
|
|
subPath: metadata
|
|
readOnly: false
|
|
{{- end }}
|
|
{{- if .Values.gateway.backend.versioningDir }}
|
|
- name: data
|
|
mountPath: {{ .Values.gateway.backend.versioningDir }}
|
|
subPath: versioning
|
|
readOnly: false
|
|
{{- end }}
|
|
{{- if or (not .Values.iam.enabled) (eq .Values.iam.type "internal") }}
|
|
- name: data
|
|
mountPath: /mnt/iam
|
|
subPath: iam
|
|
readOnly: false
|
|
{{- end }}
|
|
{{- if and .Values.iam.enabled (eq .Values.iam.type "standalone") (or .Values.iam.standalone.certificate.create .Values.iam.standalone.certificate.existingSecret) }}
|
|
- name: iam-client-certs
|
|
mountPath: /iam-client-certs
|
|
readOnly: true
|
|
{{- end }}
|
|
{{- if .Values.tls.enabled }}
|
|
- name: certificates
|
|
mountPath: /certs
|
|
readOnly: true
|
|
{{- end }}
|
|
volumes:
|
|
- name: data
|
|
{{- if .Values.persistence.enabled }}
|
|
{{- if .Values.persistence.hostPath }}
|
|
hostPath:
|
|
path: {{ .Values.persistence.hostPath }}
|
|
type: DirectoryOrCreate
|
|
{{- else }}
|
|
persistentVolumeClaim:
|
|
claimName: {{ include "versitygw.pvcName" . }}
|
|
{{- end }}
|
|
{{- else }}
|
|
emptyDir: {}
|
|
{{- end }}
|
|
{{- if .Values.tls.enabled }}
|
|
- name: certificates
|
|
secret:
|
|
secretName: {{ include "versitygw.certificateSecretName" . }}
|
|
{{- end }}
|
|
{{- if and .Values.iam.enabled (eq .Values.iam.type "standalone") (or .Values.iam.standalone.certificate.create .Values.iam.standalone.certificate.existingSecret) }}
|
|
- name: iam-client-certs
|
|
secret:
|
|
secretName: {{ include "versitygw.iamClientCertSecretName" . }}
|
|
defaultMode: 0440
|
|
{{- end }}
|
|
|
|
{{- with .Values.nodeSelector }}
|
|
nodeSelector:
|
|
{{- toYaml . | nindent 8 }}
|
|
{{- end }}
|
|
{{- with .Values.affinity }}
|
|
affinity:
|
|
{{- toYaml . | nindent 8 }}
|
|
{{- end }}
|
|
{{- with .Values.topologySpreadConstraints }}
|
|
topologySpreadConstraints:
|
|
{{- toYaml . | nindent 8 }}
|
|
{{- end }}
|
|
{{- with .Values.tolerations }}
|
|
tolerations:
|
|
{{- toYaml . | nindent 8 }}
|
|
{{- end }}
|