From b6d794af9d6ecf7e4f93dfec35538a7e795817f4 Mon Sep 17 00:00:00 2001 From: serversidehannes Date: Thu, 22 Jan 2026 13:16:48 +0100 Subject: [PATCH 1/3] docs: remove ingress and external redis examples from README --- README.md | 33 --------------------------------- 1 file changed, 33 deletions(-) diff --git a/README.md b/README.md index 159ebce..04de707 100644 --- a/README.md +++ b/README.md @@ -254,39 +254,6 @@ aws s3 --endpoint-url https://s3proxy.example.com cp file.txt s3://bucket/ > **Recommended for internal access:** Enable both `gateway.enabled=true` and `ingress.enabled=true`. This routes traffic through the ingress controller for load balancing across pods, while providing a convenient internal DNS name (`s3-gateway.`) without external DNS configuration. -#### Example: External Access with Ingress - -```yaml -# values-prod.yaml -gateway: - enabled: true -ingress: - enabled: true - className: nginx - hosts: - - s3proxy.example.com - tls: - - secretName: s3proxy-tls - hosts: - - s3proxy.example.com -``` - -```bash -helm install s3proxy ./manifests -f values-prod.yaml \ - --set secrets.existingSecrets.enabled=true \ - --set secrets.existingSecrets.name=s3proxy-secrets -``` - -#### Example: Using External Redis (ElastiCache, etc.) - -```bash -helm install s3proxy ./manifests \ - --set redis-ha.enabled=false \ - --set externalRedis.url="redis://my-elasticache.xxx.cache.amazonaws.com:6379/0" \ - --set secrets.existingSecrets.enabled=true \ - --set secrets.existingSecrets.name=s3proxy-secrets -``` - ### Health Checks The proxy exposes health endpoints for Kubernetes probes: From 2995381ff6d8f704341e350812f395522c4ae992 Mon Sep 17 00:00:00 2001 From: serversidehannes Date: Thu, 22 Jan 2026 15:25:32 +0100 Subject: [PATCH 2/3] fix: wip --- .github/workflows/cluster-test.yml | 17 +++++++++++++++- .github/workflows/helm-install-test.yml | 17 +++++++++++++++- e2e/docker-compose.cluster.yml | 12 +++++------ manifests/templates/deployment.yaml | 27 ++++++++++++++++++++++++- manifests/values.yaml | 16 ++++++++++++--- s3proxy/config.py | 1 + s3proxy/main.py | 2 +- s3proxy/multipart.py | 9 +++++++-- 8 files changed, 85 insertions(+), 16 deletions(-) diff --git a/.github/workflows/cluster-test.yml b/.github/workflows/cluster-test.yml index 493f93a..3b15a4e 100644 --- a/.github/workflows/cluster-test.yml +++ b/.github/workflows/cluster-test.yml @@ -80,8 +80,13 @@ jobs: EOF kubectl wait --for=condition=ready pod -l app=minio -n s3proxy --timeout=120s - - name: Deploy Redis + - name: Deploy Redis with password run: | + # Create Redis password secret + kubectl create secret generic redis-secret \ + --from-literal=redis-password=testredispassword123 \ + -n s3proxy + cat </dev/null 2>&1; then - echo "Image already exists, skipping build" - else - docker build -t s3proxy:latest /app - fi + docker build -t s3proxy:latest /app echo "=== Loading image into kind ===" kind load docker-image s3proxy:latest --name s3proxy-test @@ -164,6 +159,7 @@ services: helm upgrade --install s3proxy /app/manifests \ -n s3proxy --wait --timeout 1800s \ --set image.repository=s3proxy \ + --debug \ --set image.pullPolicy=IfNotPresent \ --set s3.host="http://minio:9000" \ --set secrets.encryptKey="$$ENCRYPT_KEY" \ @@ -177,7 +173,9 @@ services: --set redis-ha.hardAntiAffinity=false \ --set redis-ha.affinity=null \ --set redis-ha.haproxy.hardAntiAffinity=false \ - --set redis-ha.haproxy.affinity=null + --set redis-ha.haproxy.affinity=null \ + --set redis-ha.auth=true \ + --set redis-ha.redisPassword=testredispassword123 echo "=== Deployment Status ===" kubectl get all -n s3proxy diff --git a/manifests/templates/deployment.yaml b/manifests/templates/deployment.yaml index 072ff16..79da06a 100644 --- a/manifests/templates/deployment.yaml +++ b/manifests/templates/deployment.yaml @@ -29,8 +29,13 @@ spec: {{- if not .Values.secrets.existingSecrets.enabled }} - secretRef: name: {{ printf "%s-secrets" .Chart.Name }} - {{- else }} + {{- end }} + {{- /* Determine if we need env section */ -}} + {{- $needsEnv := or .Values.secrets.existingSecrets.enabled (and (index .Values "redis-ha" "enabled") (index .Values "redis-ha" "auth")) (and (not (index .Values "redis-ha" "enabled")) .Values.externalRedis.existingSecret) }} + {{- if $needsEnv }} env: + {{- /* App secrets from existing secret */ -}} + {{- if .Values.secrets.existingSecrets.enabled }} - name: S3PROXY_ENCRYPT_KEY valueFrom: secretKeyRef: @@ -46,6 +51,26 @@ spec: secretKeyRef: name: {{ .Values.secrets.existingSecrets.name }} key: {{ .Values.secrets.existingSecrets.keys.awsSecretAccessKey }} + {{- end }} + {{- /* Redis password from redis-ha secret */ -}} + {{- if and (index .Values "redis-ha" "enabled") (index .Values "redis-ha" "auth") }} + - name: S3PROXY_REDIS_PASSWORD + valueFrom: + secretKeyRef: + {{- if index .Values "redis-ha" "existingSecret" }} + name: {{ index .Values "redis-ha" "existingSecret" }} + {{- else }} + name: {{ .Release.Name }}-redis-ha + {{- end }} + key: {{ index .Values "redis-ha" "authKey" | default "auth" }} + {{- /* Redis password from external Redis secret */ -}} + {{- else if and (not (index .Values "redis-ha" "enabled")) .Values.externalRedis.existingSecret }} + - name: S3PROXY_REDIS_PASSWORD + valueFrom: + secretKeyRef: + name: {{ .Values.externalRedis.existingSecret }} + key: {{ .Values.externalRedis.passwordKey | default "redis-password" }} + {{- end }} {{- end }} resources: {{- toYaml .Values.resources | nindent 12 }} diff --git a/manifests/values.yaml b/manifests/values.yaml index 00968be..749c32f 100644 --- a/manifests/values.yaml +++ b/manifests/values.yaml @@ -22,14 +22,26 @@ performance: # External Redis (for managed services) externalRedis: - url: "" + url: "" # e.g., "redis://host:6379/0" or "redis://:password@host:6379/0" uploadTtlHours: 24 + # Password can be provided in one of two ways: + # 1. Embedded in URL above: redis://:password@host:6379/0 + # 2. Via existingSecret (recommended - keeps password out of configmap) + existingSecret: "" + passwordKey: "redis-password" # Redis HA (embedded) redis-ha: enabled: true replicas: 3 + # Redis authentication - when auth: true, you MUST provide one of: + # existingSecret: name of a pre-existing secret (recommended for production) + # redisPassword: password value (chart will create a secret) + # The secret key name is configured via authKey (default: "auth") + auth: false + redisPassword: "" existingSecret: "" + authKey: "auth" persistentVolume: enabled: true @@ -62,8 +74,6 @@ redis-ha: min-replicas-to-write: 1 min-replicas-max-lag: 5 - auth: false - authKey: "" hardAntiAffinity: true resources: diff --git a/s3proxy/config.py b/s3proxy/config.py index d83f366..5ee9958 100644 --- a/s3proxy/config.py +++ b/s3proxy/config.py @@ -35,6 +35,7 @@ class Settings(BaseSettings): # Redis settings (for distributed state in HA deployments) redis_url: str = Field(default="", description="Redis URL for HA mode (empty = in-memory single-instance)") + redis_password: str = Field(default="", description="Redis password (optional, can also be in URL)") redis_upload_ttl_hours: int = Field(default=24, description="TTL for upload state in Redis (hours)") # Logging diff --git a/s3proxy/main.py b/s3proxy/main.py index fabc9a6..13fa7d3 100644 --- a/s3proxy/main.py +++ b/s3proxy/main.py @@ -112,7 +112,7 @@ def create_lifespan(settings: Settings) -> "AsyncIterator[None]": async def lifespan(_app: FastAPI) -> "AsyncIterator[None]": logger.info("Starting", endpoint=settings.s3_endpoint, port=settings.port) # Initialize Redis if configured (for HA), otherwise use in-memory storage - await init_redis(settings.redis_url or None) + await init_redis(settings.redis_url or None, settings.redis_password or None) yield # Close Redis connection if active await close_redis() diff --git a/s3proxy/multipart.py b/s3proxy/multipart.py index 0b3936b..10db67a 100644 --- a/s3proxy/multipart.py +++ b/s3proxy/multipart.py @@ -56,11 +56,12 @@ def json_loads(data: bytes) -> dict: _use_redis: bool = False -async def init_redis(redis_url: str | None) -> "Redis | None": +async def init_redis(redis_url: str | None, redis_password: str | None = None) -> "Redis | None": """Initialize Redis connection pool if URL is provided. Args: redis_url: Redis URL or None/empty to use in-memory storage + redis_password: Optional password (overrides any password in URL) Returns: Redis client if connected, None if using in-memory storage @@ -72,7 +73,11 @@ async def init_redis(redis_url: str | None) -> "Redis | None": _use_redis = False return None - _redis_client = redis.from_url(redis_url, decode_responses=False) + # Pass password separately if provided (overrides URL password) + if redis_password: + _redis_client = redis.from_url(redis_url, password=redis_password, decode_responses=False) + else: + _redis_client = redis.from_url(redis_url, decode_responses=False) # Test connection await _redis_client.ping() _use_redis = True From 3a8d1d534ce87249de8642102ca3adb0c7e32e8b Mon Sep 17 00:00:00 2001 From: serversidehannes Date: Thu, 22 Jan 2026 15:29:41 +0100 Subject: [PATCH 3/3] feat: use redis-ha with auth in cluster test Switch from external Redis to redis-ha subchart with authentication enabled in the GitHub Actions cluster test workflow. --- .github/workflows/cluster-test.yml | 61 +++--------------------------- 1 file changed, 5 insertions(+), 56 deletions(-) diff --git a/.github/workflows/cluster-test.yml b/.github/workflows/cluster-test.yml index 3b15a4e..aacc507 100644 --- a/.github/workflows/cluster-test.yml +++ b/.github/workflows/cluster-test.yml @@ -80,58 +80,6 @@ jobs: EOF kubectl wait --for=condition=ready pod -l app=minio -n s3proxy --timeout=120s - - name: Deploy Redis with password - run: | - # Create Redis password secret - kubectl create secret generic redis-secret \ - --from-literal=redis-password=testredispassword123 \ - -n s3proxy - - cat <