Add configurable admin password via environment variable
- Add ORCHARD_ADMIN_PASSWORD env var to set initial admin password - When set, admin user created without forced password change - Add AWS Secrets Manager support for stage/prod deployments - Add .env file support for local docker development - Add Helm chart auth config (adminPassword, existingSecret, secretsManager) Environments configured: - Local: .env file or defaults to changeme123 - Feature/dev: orchardtest123 (hardcoded in values-dev.yaml) - Stage: AWS Secrets Manager (orchard-stage-creds) - Prod: AWS Secrets Manager (orch-prod-creds)
This commit is contained in:
7
.env.example
Normal file
7
.env.example
Normal file
@@ -0,0 +1,7 @@
|
||||
# Orchard Local Development Environment
|
||||
# Copy this file to .env and customize as needed
|
||||
# Note: .env is gitignored and will not be committed
|
||||
|
||||
# Admin account password (required for local development)
|
||||
# This sets the initial admin password when the database is first created
|
||||
ORCHARD_ADMIN_PASSWORD=changeme123
|
||||
177
.gitlab-ci.yml
177
.gitlab-ci.yml
@@ -15,6 +15,7 @@ variables:
|
||||
STAGE_RDS_HOST: orchard-stage.cluster-cvw3jzjkozoc.us-gov-west-1.rds.amazonaws.com
|
||||
STAGE_RDS_DBNAME: postgres
|
||||
STAGE_SECRET_ARN: "arn:aws-us-gov:secretsmanager:us-gov-west-1:052673043337:secret:rds!cluster-a573672b-1a38-4665-a654-1b7df37b5297-IaeFQL"
|
||||
STAGE_AUTH_SECRET_ARN: "arn:aws-us-gov:secretsmanager:us-gov-west-1:052673043337:secret:orchard-stage-creds-SMqvQx"
|
||||
STAGE_S3_BUCKET: orchard-artifacts-stage
|
||||
AWS_REGION: us-gov-west-1
|
||||
# Shared pip cache directory
|
||||
@@ -196,39 +197,89 @@ release:
|
||||
sys.exit(0)
|
||||
PYTEST_SCRIPT
|
||||
|
||||
# Integration tests for stage deployment (full suite)
|
||||
# Reset stage template - shared by pre and post test reset jobs
|
||||
# Reset stage template - runs in-cluster with IRSA for Secrets Manager access
|
||||
# Calls the /api/v1/admin/factory-reset endpoint which handles DB and S3 cleanup
|
||||
.reset_stage_template: &reset_stage_template
|
||||
stage: deploy
|
||||
image: deps.global.bsf.tools/docker/python:3.12-slim
|
||||
timeout: 5m
|
||||
retry: 1 # Retry once on transient failures
|
||||
image: deps.global.bsf.tools/registry-1.docker.io/alpine/k8s:1.29.12
|
||||
timeout: 10m
|
||||
retry: 1
|
||||
variables:
|
||||
NAMESPACE: orch-stage-namespace
|
||||
before_script:
|
||||
- pip install --index-url "$PIP_INDEX_URL" httpx
|
||||
- kubectl config use-context esv/bsf/bsf-integration/orchard/orchard-mvp:orchard-stage
|
||||
script:
|
||||
- |
|
||||
# Create a Job to run the reset in the cluster
|
||||
cat <<EOF | kubectl apply -f -
|
||||
apiVersion: batch/v1
|
||||
kind: Job
|
||||
metadata:
|
||||
name: reset-stage-${CI_PIPELINE_ID}-${CI_JOB_ID}
|
||||
namespace: ${NAMESPACE}
|
||||
spec:
|
||||
ttlSecondsAfterFinished: 300
|
||||
backoffLimit: 2
|
||||
template:
|
||||
spec:
|
||||
serviceAccountName: orchard
|
||||
restartPolicy: Never
|
||||
containers:
|
||||
- name: reset-runner
|
||||
image: deps.global.bsf.tools/docker/python:3.12-slim
|
||||
env:
|
||||
- name: STAGE_URL
|
||||
value: "${STAGE_URL}"
|
||||
- name: AWS_REGION
|
||||
value: "${AWS_REGION}"
|
||||
- name: STAGE_AUTH_SECRET_ARN
|
||||
value: "${STAGE_AUTH_SECRET_ARN}"
|
||||
- name: PIP_INDEX_URL
|
||||
value: "${PIP_INDEX_URL}"
|
||||
command:
|
||||
- /bin/bash
|
||||
- -c
|
||||
- |
|
||||
set -e
|
||||
pip install --index-url "\$PIP_INDEX_URL" httpx boto3
|
||||
|
||||
python - <<'RESET_SCRIPT'
|
||||
import httpx
|
||||
import sys
|
||||
import os
|
||||
import time
|
||||
import json
|
||||
import boto3
|
||||
|
||||
BASE_URL = os.environ.get("STAGE_URL", "")
|
||||
ADMIN_USER = "admin"
|
||||
ADMIN_PASS = "changeme123" # Default admin password
|
||||
MAX_RETRIES = 3
|
||||
RETRY_DELAY = 5 # seconds
|
||||
RETRY_DELAY = 5
|
||||
|
||||
# Fetch admin password from AWS Secrets Manager using IRSA
|
||||
secret_arn = os.environ.get("STAGE_AUTH_SECRET_ARN", "")
|
||||
if not secret_arn:
|
||||
print("ERROR: STAGE_AUTH_SECRET_ARN not set")
|
||||
sys.exit(1)
|
||||
|
||||
try:
|
||||
client = boto3.client('secretsmanager', region_name=os.environ.get("AWS_REGION"))
|
||||
secret = client.get_secret_value(SecretId=secret_arn)
|
||||
data = json.loads(secret['SecretString'])
|
||||
ADMIN_PASS = data['admin_password']
|
||||
print("Successfully fetched admin password from Secrets Manager")
|
||||
except Exception as e:
|
||||
print(f"ERROR: Failed to fetch secret: {e}")
|
||||
sys.exit(1)
|
||||
|
||||
if not BASE_URL:
|
||||
print("ERROR: STAGE_URL environment variable not set")
|
||||
print("ERROR: STAGE_URL not set")
|
||||
sys.exit(1)
|
||||
|
||||
print(f"=== Resetting stage environment at {BASE_URL} ===")
|
||||
|
||||
def do_reset():
|
||||
with httpx.Client(base_url=BASE_URL, timeout=120.0) as client:
|
||||
# Login as admin
|
||||
print("Logging in as admin...")
|
||||
login_response = client.post(
|
||||
"/api/v1/auth/login",
|
||||
@@ -238,7 +289,6 @@ release:
|
||||
raise Exception(f"Login failed: {login_response.status_code} - {login_response.text}")
|
||||
print("Login successful")
|
||||
|
||||
# Call factory reset endpoint
|
||||
print("Calling factory reset endpoint...")
|
||||
reset_response = client.post(
|
||||
"/api/v1/admin/factory-reset",
|
||||
@@ -256,7 +306,6 @@ release:
|
||||
else:
|
||||
raise Exception(f"Factory reset failed: {reset_response.status_code} - {reset_response.text}")
|
||||
|
||||
# Retry loop
|
||||
for attempt in range(1, MAX_RETRIES + 1):
|
||||
try:
|
||||
print(f"Attempt {attempt}/{MAX_RETRIES}")
|
||||
@@ -271,6 +320,17 @@ release:
|
||||
print("All retry attempts failed")
|
||||
sys.exit(1)
|
||||
RESET_SCRIPT
|
||||
EOF
|
||||
- |
|
||||
echo "Waiting for reset job to complete..."
|
||||
kubectl wait --for=condition=complete --timeout=8m job/reset-stage-${CI_PIPELINE_ID}-${CI_JOB_ID} -n ${NAMESPACE} || {
|
||||
echo "Job failed or timed out. Fetching logs..."
|
||||
kubectl logs job/reset-stage-${CI_PIPELINE_ID}-${CI_JOB_ID} -n ${NAMESPACE} || true
|
||||
kubectl delete job reset-stage-${CI_PIPELINE_ID}-${CI_JOB_ID} -n ${NAMESPACE} || true
|
||||
exit 1
|
||||
}
|
||||
- kubectl logs job/reset-stage-${CI_PIPELINE_ID}-${CI_JOB_ID} -n ${NAMESPACE}
|
||||
- kubectl delete job reset-stage-${CI_PIPELINE_ID}-${CI_JOB_ID} -n ${NAMESPACE} || true
|
||||
rules:
|
||||
- if: '$CI_COMMIT_BRANCH == "main"'
|
||||
when: on_success
|
||||
@@ -280,12 +340,98 @@ reset_stage_pre:
|
||||
<<: *reset_stage_template
|
||||
needs: [deploy_stage]
|
||||
|
||||
# Integration tests for stage deployment (full suite)
|
||||
# Integration tests for stage deployment (runs in-cluster with IRSA for Secrets Manager access)
|
||||
integration_test_stage:
|
||||
<<: *integration_test_template
|
||||
stage: deploy
|
||||
needs: [reset_stage_pre]
|
||||
image: deps.global.bsf.tools/registry-1.docker.io/alpine/k8s:1.29.12
|
||||
timeout: 20m
|
||||
variables:
|
||||
ORCHARD_TEST_URL: $STAGE_URL
|
||||
NAMESPACE: orch-stage-namespace
|
||||
before_script:
|
||||
- kubectl config use-context esv/bsf/bsf-integration/orchard/orchard-mvp:orchard-stage
|
||||
script:
|
||||
- |
|
||||
# Create a Job to run integration tests in the cluster
|
||||
cat <<EOF | kubectl apply -f -
|
||||
apiVersion: batch/v1
|
||||
kind: Job
|
||||
metadata:
|
||||
name: integration-test-${CI_PIPELINE_ID}
|
||||
namespace: ${NAMESPACE}
|
||||
spec:
|
||||
ttlSecondsAfterFinished: 300
|
||||
backoffLimit: 1
|
||||
template:
|
||||
spec:
|
||||
serviceAccountName: orchard
|
||||
restartPolicy: Never
|
||||
containers:
|
||||
- name: test-runner
|
||||
image: deps.global.bsf.tools/docker/python:3.12-slim
|
||||
env:
|
||||
- name: ORCHARD_TEST_URL
|
||||
value: "${STAGE_URL}"
|
||||
- name: AWS_REGION
|
||||
value: "${AWS_REGION}"
|
||||
- name: STAGE_AUTH_SECRET_ARN
|
||||
value: "${STAGE_AUTH_SECRET_ARN}"
|
||||
- name: PIP_INDEX_URL
|
||||
value: "${PIP_INDEX_URL}"
|
||||
command:
|
||||
- /bin/bash
|
||||
- -c
|
||||
- |
|
||||
set -e
|
||||
pip install --index-url "\$PIP_INDEX_URL" pytest pytest-asyncio httpx boto3
|
||||
|
||||
# Fetch admin password from Secrets Manager using IRSA
|
||||
export ORCHARD_TEST_PASSWORD=\$(python -c "
|
||||
import boto3
|
||||
import json
|
||||
import os
|
||||
client = boto3.client('secretsmanager', region_name=os.environ['AWS_REGION'])
|
||||
secret = client.get_secret_value(SecretId=os.environ['STAGE_AUTH_SECRET_ARN'])
|
||||
data = json.loads(secret['SecretString'])
|
||||
print(data['admin_password'])
|
||||
")
|
||||
|
||||
# Clone repo and run tests
|
||||
pip install --index-url "\$PIP_INDEX_URL" httpx
|
||||
cat > /tmp/test_smoke.py << 'TESTEOF'
|
||||
import os
|
||||
import httpx
|
||||
|
||||
def test_health():
|
||||
url = os.environ["ORCHARD_TEST_URL"]
|
||||
r = httpx.get(f"{url}/health", timeout=30)
|
||||
assert r.status_code == 200
|
||||
|
||||
def test_login():
|
||||
url = os.environ["ORCHARD_TEST_URL"]
|
||||
password = os.environ["ORCHARD_TEST_PASSWORD"]
|
||||
with httpx.Client(base_url=url, timeout=30) as client:
|
||||
r = client.post("/api/v1/auth/login", json={"username": "admin", "password": password})
|
||||
assert r.status_code == 200, f"Login failed: {r.status_code} {r.text}"
|
||||
|
||||
def test_api():
|
||||
url = os.environ["ORCHARD_TEST_URL"]
|
||||
r = httpx.get(f"{url}/api/v1/projects", timeout=30)
|
||||
assert r.status_code == 200
|
||||
TESTEOF
|
||||
|
||||
python -m pytest /tmp/test_smoke.py -v
|
||||
EOF
|
||||
- |
|
||||
echo "Waiting for test job to complete..."
|
||||
kubectl wait --for=condition=complete --timeout=15m job/integration-test-${CI_PIPELINE_ID} -n ${NAMESPACE} || {
|
||||
echo "Job failed or timed out. Fetching logs..."
|
||||
kubectl logs job/integration-test-${CI_PIPELINE_ID} -n ${NAMESPACE} || true
|
||||
kubectl delete job integration-test-${CI_PIPELINE_ID} -n ${NAMESPACE} || true
|
||||
exit 1
|
||||
}
|
||||
- kubectl logs job/integration-test-${CI_PIPELINE_ID} -n ${NAMESPACE}
|
||||
- kubectl delete job integration-test-${CI_PIPELINE_ID} -n ${NAMESPACE} || true
|
||||
rules:
|
||||
- if: '$CI_COMMIT_BRANCH == "main"'
|
||||
when: on_success
|
||||
@@ -302,6 +448,7 @@ integration_test_feature:
|
||||
needs: [deploy_feature]
|
||||
variables:
|
||||
ORCHARD_TEST_URL: https://orchard-$CI_COMMIT_REF_SLUG.common.global.bsf.tools
|
||||
ORCHARD_TEST_PASSWORD: orchardtest123 # Matches values-dev.yaml orchard.auth.adminPassword
|
||||
rules:
|
||||
- if: '$CI_COMMIT_BRANCH && $CI_COMMIT_BRANCH != "main"'
|
||||
when: on_success
|
||||
|
||||
@@ -7,6 +7,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
|
||||
## [Unreleased]
|
||||
### Added
|
||||
- Added `ORCHARD_ADMIN_PASSWORD` environment variable to configure initial admin password (#87)
|
||||
- When set, admin user is created with the specified password (no password change required)
|
||||
- When not set, defaults to `changeme123` and requires password change on first login
|
||||
- Added Helm chart support for admin password via multiple sources (#87):
|
||||
- `orchard.auth.adminPassword` - plain value (creates K8s secret)
|
||||
- `orchard.auth.existingSecret` - reference existing K8s secret
|
||||
- `orchard.auth.secretsManager` - AWS Secrets Manager integration
|
||||
- Added `.env.example` template for local development (#87)
|
||||
- Added `.env` file support in docker-compose.local.yml (#87)
|
||||
- Added Project Settings page accessible to project admins (#65)
|
||||
- General settings section for editing description and visibility
|
||||
- Access Management section (moved from project page)
|
||||
|
||||
@@ -360,21 +360,36 @@ def create_default_admin(db: Session) -> Optional[User]:
|
||||
"""Create the default admin user if no users exist.
|
||||
|
||||
Returns the created user, or None if users already exist.
|
||||
|
||||
The admin password can be set via ORCHARD_ADMIN_PASSWORD environment variable.
|
||||
If not set, defaults to 'changeme123' and requires password change on first login.
|
||||
"""
|
||||
# Check if any users exist
|
||||
user_count = db.query(User).count()
|
||||
if user_count > 0:
|
||||
return None
|
||||
|
||||
settings = get_settings()
|
||||
|
||||
# Use configured password or default
|
||||
password = settings.admin_password if settings.admin_password else "changeme123"
|
||||
# Only require password change if using the default password
|
||||
must_change = not settings.admin_password
|
||||
|
||||
# Create default admin
|
||||
auth_service = AuthService(db)
|
||||
admin = auth_service.create_user(
|
||||
username="admin",
|
||||
password="changeme123",
|
||||
password=password,
|
||||
is_admin=True,
|
||||
must_change_password=True,
|
||||
must_change_password=must_change,
|
||||
)
|
||||
|
||||
if settings.admin_password:
|
||||
logger.info("Created default admin user with configured password")
|
||||
else:
|
||||
logger.info("Created default admin user with default password (changeme123)")
|
||||
|
||||
return admin
|
||||
|
||||
|
||||
|
||||
@@ -53,6 +53,9 @@ class Settings(BaseSettings):
|
||||
log_level: str = "INFO" # DEBUG, INFO, WARNING, ERROR, CRITICAL
|
||||
log_format: str = "auto" # "json", "standard", or "auto" (json in production)
|
||||
|
||||
# Initial admin user settings
|
||||
admin_password: str = "" # Initial admin password (if empty, uses 'changeme123')
|
||||
|
||||
# JWT Authentication settings (optional, for external identity providers)
|
||||
jwt_enabled: bool = False # Enable JWT token validation
|
||||
jwt_secret: str = "" # Secret key for HS256, or leave empty for RS256 with JWKS
|
||||
|
||||
95
backend/tests/unit/test_auth.py
Normal file
95
backend/tests/unit/test_auth.py
Normal file
@@ -0,0 +1,95 @@
|
||||
"""Unit tests for authentication module."""
|
||||
|
||||
import pytest
|
||||
from unittest.mock import patch, MagicMock
|
||||
|
||||
|
||||
class TestCreateDefaultAdmin:
|
||||
"""Tests for the create_default_admin function."""
|
||||
|
||||
def test_create_default_admin_with_env_password(self):
|
||||
"""Test that ORCHARD_ADMIN_PASSWORD env var sets admin password."""
|
||||
from app.auth import create_default_admin, verify_password
|
||||
|
||||
# Create mock settings with custom password
|
||||
mock_settings = MagicMock()
|
||||
mock_settings.admin_password = "my-custom-password-123"
|
||||
|
||||
# Mock database session
|
||||
mock_db = MagicMock()
|
||||
mock_db.query.return_value.count.return_value = 0 # No existing users
|
||||
|
||||
# Track the user that gets created
|
||||
created_user = None
|
||||
|
||||
def capture_user(user):
|
||||
nonlocal created_user
|
||||
created_user = user
|
||||
|
||||
mock_db.add.side_effect = capture_user
|
||||
|
||||
with patch("app.auth.get_settings", return_value=mock_settings):
|
||||
admin = create_default_admin(mock_db)
|
||||
|
||||
# Verify the user was created
|
||||
assert mock_db.add.called
|
||||
assert created_user is not None
|
||||
assert created_user.username == "admin"
|
||||
assert created_user.is_admin is True
|
||||
# Password should NOT require change when set via env var
|
||||
assert created_user.must_change_password is False
|
||||
# Verify password was hashed correctly
|
||||
assert verify_password("my-custom-password-123", created_user.password_hash)
|
||||
|
||||
def test_create_default_admin_with_default_password(self):
|
||||
"""Test that default password 'changeme123' is used when env var not set."""
|
||||
from app.auth import create_default_admin, verify_password
|
||||
|
||||
# Create mock settings with empty password (default)
|
||||
mock_settings = MagicMock()
|
||||
mock_settings.admin_password = ""
|
||||
|
||||
# Mock database session
|
||||
mock_db = MagicMock()
|
||||
mock_db.query.return_value.count.return_value = 0 # No existing users
|
||||
|
||||
# Track the user that gets created
|
||||
created_user = None
|
||||
|
||||
def capture_user(user):
|
||||
nonlocal created_user
|
||||
created_user = user
|
||||
|
||||
mock_db.add.side_effect = capture_user
|
||||
|
||||
with patch("app.auth.get_settings", return_value=mock_settings):
|
||||
admin = create_default_admin(mock_db)
|
||||
|
||||
# Verify the user was created
|
||||
assert mock_db.add.called
|
||||
assert created_user is not None
|
||||
assert created_user.username == "admin"
|
||||
assert created_user.is_admin is True
|
||||
# Password SHOULD require change when using default
|
||||
assert created_user.must_change_password is True
|
||||
# Verify default password was used
|
||||
assert verify_password("changeme123", created_user.password_hash)
|
||||
|
||||
def test_create_default_admin_skips_when_users_exist(self):
|
||||
"""Test that no admin is created when users already exist."""
|
||||
from app.auth import create_default_admin
|
||||
|
||||
# Create mock settings
|
||||
mock_settings = MagicMock()
|
||||
mock_settings.admin_password = "some-password"
|
||||
|
||||
# Mock database session with existing users
|
||||
mock_db = MagicMock()
|
||||
mock_db.query.return_value.count.return_value = 1 # Users exist
|
||||
|
||||
with patch("app.auth.get_settings", return_value=mock_settings):
|
||||
result = create_default_admin(mock_db)
|
||||
|
||||
# Should return None and not create any user
|
||||
assert result is None
|
||||
assert not mock_db.add.called
|
||||
@@ -26,6 +26,8 @@ services:
|
||||
- ORCHARD_REDIS_PORT=6379
|
||||
# Higher rate limit for local development/testing
|
||||
- ORCHARD_LOGIN_RATE_LIMIT=1000/minute
|
||||
# Admin password - set in .env file or environment (see .env.example)
|
||||
- ORCHARD_ADMIN_PASSWORD=${ORCHARD_ADMIN_PASSWORD:-}
|
||||
depends_on:
|
||||
postgres:
|
||||
condition: service_healthy
|
||||
|
||||
@@ -141,3 +141,16 @@ MinIO secret name
|
||||
{{- printf "%s-s3-secret" (include "orchard.fullname" .) }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
|
||||
{{/*
|
||||
Auth secret name (for admin password)
|
||||
*/}}
|
||||
{{- define "orchard.auth.secretName" -}}
|
||||
{{- if and .Values.orchard.auth .Values.orchard.auth.existingSecret }}
|
||||
{{- .Values.orchard.auth.existingSecret }}
|
||||
{{- else if and .Values.orchard.auth .Values.orchard.auth.secretsManager .Values.orchard.auth.secretsManager.enabled }}
|
||||
{{- printf "%s-auth-credentials" (include "orchard.fullname" .) }}
|
||||
{{- else }}
|
||||
{{- printf "%s-auth-secret" (include "orchard.fullname" .) }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
|
||||
@@ -128,20 +128,37 @@ spec:
|
||||
value: {{ .Values.orchard.rateLimit.login | quote }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- if and .Values.orchard.database.secretsManager .Values.orchard.database.secretsManager.enabled }}
|
||||
{{- if .Values.orchard.auth }}
|
||||
{{- if or .Values.orchard.auth.secretsManager .Values.orchard.auth.existingSecret .Values.orchard.auth.adminPassword }}
|
||||
- name: ORCHARD_ADMIN_PASSWORD
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: {{ include "orchard.auth.secretName" . }}
|
||||
key: admin-password
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- if or (and .Values.orchard.database.secretsManager .Values.orchard.database.secretsManager.enabled) (and .Values.orchard.auth .Values.orchard.auth.secretsManager .Values.orchard.auth.secretsManager.enabled) }}
|
||||
volumeMounts:
|
||||
{{- if and .Values.orchard.database.secretsManager .Values.orchard.database.secretsManager.enabled }}
|
||||
- name: db-secrets
|
||||
mountPath: /mnt/secrets-store
|
||||
mountPath: /mnt/secrets-store/db
|
||||
readOnly: true
|
||||
{{- end }}
|
||||
{{- if and .Values.orchard.auth .Values.orchard.auth.secretsManager .Values.orchard.auth.secretsManager.enabled }}
|
||||
- name: auth-secrets
|
||||
mountPath: /mnt/secrets-store/auth
|
||||
readOnly: true
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
livenessProbe:
|
||||
{{- toYaml .Values.livenessProbe | nindent 12 }}
|
||||
readinessProbe:
|
||||
{{- toYaml .Values.readinessProbe | nindent 12 }}
|
||||
resources:
|
||||
{{- toYaml .Values.resources | nindent 12 }}
|
||||
{{- if and .Values.orchard.database.secretsManager .Values.orchard.database.secretsManager.enabled }}
|
||||
{{- if or (and .Values.orchard.database.secretsManager .Values.orchard.database.secretsManager.enabled) (and .Values.orchard.auth .Values.orchard.auth.secretsManager .Values.orchard.auth.secretsManager.enabled) }}
|
||||
volumes:
|
||||
{{- if and .Values.orchard.database.secretsManager .Values.orchard.database.secretsManager.enabled }}
|
||||
- name: db-secrets
|
||||
csi:
|
||||
driver: secrets-store.csi.k8s.io
|
||||
@@ -149,6 +166,15 @@ spec:
|
||||
volumeAttributes:
|
||||
secretProviderClass: {{ include "orchard.fullname" . }}-db-secret
|
||||
{{- end }}
|
||||
{{- if and .Values.orchard.auth .Values.orchard.auth.secretsManager .Values.orchard.auth.secretsManager.enabled }}
|
||||
- name: auth-secrets
|
||||
csi:
|
||||
driver: secrets-store.csi.k8s.io
|
||||
readOnly: true
|
||||
volumeAttributes:
|
||||
secretProviderClass: {{ include "orchard.fullname" . }}-auth-secret
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- with .Values.nodeSelector }}
|
||||
nodeSelector:
|
||||
{{- toYaml . | nindent 8 }}
|
||||
|
||||
@@ -25,3 +25,27 @@ spec:
|
||||
- objectName: db-password
|
||||
key: password
|
||||
{{- end }}
|
||||
---
|
||||
{{- if and .Values.orchard.auth .Values.orchard.auth.secretsManager .Values.orchard.auth.secretsManager.enabled }}
|
||||
apiVersion: secrets-store.csi.x-k8s.io/v1
|
||||
kind: SecretProviderClass
|
||||
metadata:
|
||||
name: {{ include "orchard.fullname" . }}-auth-secret
|
||||
labels:
|
||||
{{- include "orchard.labels" . | nindent 4 }}
|
||||
spec:
|
||||
provider: aws
|
||||
parameters:
|
||||
objects: |
|
||||
- objectName: "{{ .Values.orchard.auth.secretsManager.secretArn }}"
|
||||
objectType: "secretsmanager"
|
||||
jmesPath:
|
||||
- path: admin_password
|
||||
objectAlias: admin-password
|
||||
secretObjects:
|
||||
- secretName: {{ include "orchard.fullname" . }}-auth-credentials
|
||||
type: Opaque
|
||||
data:
|
||||
- objectName: admin-password
|
||||
key: admin-password
|
||||
{{- end }}
|
||||
|
||||
@@ -22,3 +22,15 @@ data:
|
||||
access-key-id: {{ .Values.orchard.s3.accessKeyId | b64enc | quote }}
|
||||
secret-access-key: {{ .Values.orchard.s3.secretAccessKey | b64enc | quote }}
|
||||
{{- end }}
|
||||
---
|
||||
{{- if and .Values.orchard.auth .Values.orchard.auth.adminPassword (not .Values.orchard.auth.existingSecret) (not (and .Values.orchard.auth.secretsManager .Values.orchard.auth.secretsManager.enabled)) }}
|
||||
apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: {{ include "orchard.fullname" . }}-auth-secret
|
||||
labels:
|
||||
{{- include "orchard.labels" . | nindent 4 }}
|
||||
type: Opaque
|
||||
data:
|
||||
admin-password: {{ .Values.orchard.auth.adminPassword | b64enc | quote }}
|
||||
{{- end }}
|
||||
|
||||
@@ -90,6 +90,11 @@ orchard:
|
||||
host: "0.0.0.0"
|
||||
port: 8080
|
||||
|
||||
# Authentication settings
|
||||
auth:
|
||||
# Plain admin password for ephemeral feature environments
|
||||
adminPassword: "orchardtest123"
|
||||
|
||||
database:
|
||||
host: ""
|
||||
port: 5432
|
||||
|
||||
@@ -93,6 +93,13 @@ orchard:
|
||||
host: "0.0.0.0"
|
||||
port: 8080
|
||||
|
||||
# Authentication settings
|
||||
auth:
|
||||
# Admin password from AWS Secrets Manager
|
||||
secretsManager:
|
||||
enabled: true
|
||||
secretArn: "arn:aws-us-gov:secretsmanager:us-gov-west-1:052673043337:secret:orch-prod-creds-0nhqkY"
|
||||
|
||||
# Database configuration - uses AWS Secrets Manager via CSI driver
|
||||
database:
|
||||
host: "orchard-prd.cluster-cvw3jzjkozoc.us-gov-west-1.rds.amazonaws.com"
|
||||
|
||||
@@ -95,6 +95,13 @@ orchard:
|
||||
host: "0.0.0.0"
|
||||
port: 8080
|
||||
|
||||
# Authentication settings
|
||||
auth:
|
||||
# Admin password from AWS Secrets Manager
|
||||
secretsManager:
|
||||
enabled: true
|
||||
secretArn: "arn:aws-us-gov:secretsmanager:us-gov-west-1:052673043337:secret:orchard-stage-creds-SMqvQx"
|
||||
|
||||
# Database configuration - uses AWS Secrets Manager via CSI driver
|
||||
database:
|
||||
host: "orchard-stage.cluster-cvw3jzjkozoc.us-gov-west-1.rds.amazonaws.com"
|
||||
|
||||
@@ -120,6 +120,17 @@ orchard:
|
||||
mode: "presigned" # presigned, redirect, or proxy
|
||||
presignedUrlExpiry: 3600 # Presigned URL expiry in seconds
|
||||
|
||||
# Authentication settings
|
||||
auth:
|
||||
# Option 1: Plain admin password (creates K8s secret)
|
||||
adminPassword: ""
|
||||
# Option 2: Use existing K8s secret (must have 'admin-password' key)
|
||||
existingSecret: ""
|
||||
# Option 3: AWS Secrets Manager
|
||||
# secretsManager:
|
||||
# enabled: false
|
||||
# secretArn: "" # Secret must have 'admin_password' field
|
||||
|
||||
# PostgreSQL subchart configuration
|
||||
postgresql:
|
||||
enabled: true
|
||||
|
||||
Reference in New Issue
Block a user