Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
90 changes: 90 additions & 0 deletions .github/workflows/e2e-tests.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -257,3 +257,93 @@ jobs:
with:
name: s3-tls-verify-artifacts
path: artifacts

e2e-tests-tls:
name: E2E TLS
runs-on: ubuntu-22.04-8core
timeout-minutes: 45
needs: dev-image
env:
CLOUDSERVER_TAG: ${{ vars.CLOUDSERVER_RING_9_5 }}
steps:
- name: Check out repository
uses: actions/checkout@v5
with:
ref: ${{ github.sha }}
fetch-depth: 0

- name: CI Setup
uses: ./.github/actions/ci-setup
with:
cluster_name: tls-e2e-cluster

- name: Set up Go
uses: actions/setup-go@v4
with:
go-version-file: "tests/e2e/go.mod"
cache: true

- name: Install Ginkgo CLI
run: go install github.com/onsi/ginkgo/v2/ginkgo@v2.25.2

- name: Pull Container Images and Download Go Dependencies in Parallel
run: |
CSI_IMAGE_REPOSITORY=ghcr.io/${{ github.repository }} \
CSI_IMAGE_TAG=${{ github.sha }} \
CLOUDSERVER_TAG=${CLOUDSERVER_TAG} \
mage e2e:pullImages

- name: Load CSI Driver into KIND
run: kind load docker-image ghcr.io/${{ github.repository }}:${{ github.sha }} --name tls-e2e-cluster

- name: Deploy S3 with TLS
run: mage e2e:deployS3TLS

- name: Get Host IP Address
id: get_ip
run: echo "host_ip=$(hostname -I | awk '{print $1}')" >> $GITHUB_OUTPUT

# DNS must be configured at two levels for TLS hostname resolution:
# - /etc/hosts: so the CI runner (Ginkgo test binary) can reach s3.scality.com for bucket operations
# - CoreDNS: so pods inside the KIND cluster (mounter pods, init containers) can reach s3.scality.com
- name: Configure hosts file for S3 FQDN
run: |
echo "${{ steps.get_ip.outputs.host_ip }} s3.scality.com" | sudo tee -a /etc/hosts

- name: Configure CoreDNS for S3 FQDN
run: S3_HOST_IP=${{ steps.get_ip.outputs.host_ip }} mage e2e:configureCIDNS

- name: Start Kubernetes Event and Log Capture
run: mage e2e:startCapture

- name: Apply CRDs
run: mage e2e:applyCRDs

- name: Run TLS E2E Tests
run: |
mkdir -p test-results
S3_ENDPOINT_URL=https://s3.scality.com:8443 \
CSI_IMAGE_TAG=${{ github.sha }} \
CSI_IMAGE_REPOSITORY=ghcr.io/${{ github.repository }} \
JUNIT_REPORT=./test-results/e2e-tls-tests-results.xml \
mage e2e:tlsAll

- name: Stop Capture and Collect Artifacts
if: always()
run: mage e2e:stopCapture

- name: Upload Test Artifacts
if: always()
uses: actions/upload-artifact@v4
with:
name: e2e-tls-test-artifacts
path: artifacts

- name: Upload test results to Codecov
if: ${{ always() }}
uses: codecov/test-results-action@v1
with:
token: ${{ secrets.CODECOV_TOKEN }}
file: ./tests/e2e/test-results/e2e-tls-tests-results.xml
flags: e2e_tls_tests
Comment on lines +322 to +348
Copy link

Copilot AI Mar 25, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The JUnit report is written to ./test-results/e2e-tls-tests-results.xml via JUNIT_REPORT, but the Codecov upload step points to ./tests/e2e/test-results/e2e-tls-tests-results.xml. This mismatch will cause the Codecov step to fail to find the report (or upload an empty/missing file). Align the paths (either write into ./tests/e2e/test-results/ or upload from ./test-results/).

Copilot uses AI. Check for mistakes.
slug: scality/mountpoint-s3-csi-driver
33 changes: 33 additions & 0 deletions charts/scality-mountpoint-s3-csi-driver/templates/controller.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,11 @@ spec:
volumeMounts:
- name: socket-dir
mountPath: /csi
{{- if .Values.tls.caCertConfigMap }}
- name: custom-ca-cert
mountPath: /etc/ssl/custom-ca
readOnly: true
{{- end }}
{{- with .Values.controller.resources }}
resources:
{{- toYaml . | nindent 12 }}
Expand All @@ -84,6 +89,10 @@ spec:
secretKeyRef:
name: {{ .Values.s3CredentialSecret.name }}
key: {{ .Values.s3CredentialSecret.secretAccessKey }}
{{- if .Values.tls.caCertConfigMap }}
- name: AWS_CA_BUNDLE
value: "/etc/ssl/custom-ca/ca-bundle.crt"
{{- end }}
# Reconciler for MountpointS3PodAttachment CRDs
- name: s3-pod-reconciler
image: {{ printf "%s%s:%s" (default "" .Values.image.containerRegistry) .Values.image.repository (default (printf "v%s" .Chart.AppVersion) (toString .Values.image.tag)) }}
Expand Down Expand Up @@ -115,6 +124,20 @@ spec:
value: {{ printf "%s:%s" .Values.mountpointPod.headroomImage.repository .Values.mountpointPod.headroomImage.tag | quote }}
- name: MOUNTPOINT_IMAGE_PULL_POLICY
value: {{ .Values.image.pullPolicy | quote }}
{{- if .Values.tls.caCertConfigMap }}
- name: TLS_CA_CERT_CONFIGMAP
value: {{ .Values.tls.caCertConfigMap | quote }}
- name: TLS_INIT_IMAGE
value: {{ printf "%s:%s" .Values.tls.initImage.repository .Values.tls.initImage.tag | quote }}
- name: TLS_INIT_IMAGE_PULL_POLICY
value: {{ .Values.tls.initImage.pullPolicy | quote }}
- name: TLS_INIT_RESOURCES_REQUESTS_CPU
value: {{ .Values.tls.initResources.requests.cpu | quote }}
- name: TLS_INIT_RESOURCES_REQUESTS_MEMORY
value: {{ .Values.tls.initResources.requests.memory | quote }}
- name: TLS_INIT_RESOURCES_LIMITS_MEMORY
value: {{ .Values.tls.initResources.limits.memory | quote }}
{{- end }}
- name: csi-provisioner
image: {{ .Values.sidecars.csiProvisioner.image.repository }}:{{ .Values.sidecars.csiProvisioner.image.tag }}
imagePullPolicy: {{ .Values.sidecars.csiProvisioner.image.pullPolicy }}
Expand All @@ -127,3 +150,13 @@ spec:
volumes:
- name: socket-dir
emptyDir: {}
{{- if .Values.tls.caCertConfigMap }}
# ConfigMap volume is NOT optional — if the ConfigMap doesn't exist, the pod stays in
# ContainerCreating with a clear event, matching the behavior of the credentials Secret above.
- name: custom-ca-cert
configMap:
name: {{ .Values.tls.caCertConfigMap }}
items:
- key: ca-bundle.crt
path: ca-bundle.crt
{{- end }}
20 changes: 20 additions & 0 deletions charts/scality-mountpoint-s3-csi-driver/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,26 @@ mountpointPod:
tag: "3.10"
pullPolicy: IfNotPresent

# TLS configuration for custom CA certificates
tls:
# Name of the ConfigMap containing the CA certificate bundle (key: ca-bundle.crt).
# Must exist in TWO namespaces because the controller and mounter pods run in separate namespaces:
# 1. Controller namespace (e.g., kube-system) — mounted by the s3-csi-controller for AWS SDK S3 calls
# 2. mountpointPod.namespace (e.g., mount-s3) — mounted by mounter pod init containers for mount-s3
# If missing from either namespace, the respective pod stays in ContainerCreating.
# Leave empty to disable TLS CA certificate injection
caCertConfigMap: ""
initImage:
repository: alpine
tag: "3.21"
pullPolicy: IfNotPresent
initResources:
requests:
cpu: 10m
memory: 16Mi
limits:
memory: 64Mi

# CRD Cleanup Configuration
# Enable automatic cleanup of MountpointS3PodAttachment CRDs and Mountpoint Pods during helm uninstall
# Warning: This will forcefully terminate all active S3 mounts
Expand Down
67 changes: 67 additions & 0 deletions cmd/scality-csi-controller/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@ import (
"flag"
"os"

"github.com/go-logr/logr"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/resource"
"k8s.io/apimachinery/pkg/runtime"
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
clientgoscheme "k8s.io/client-go/kubernetes/scheme"
Expand All @@ -34,6 +36,12 @@ var (
headroomImage = flag.String("headroom-image", os.Getenv("MOUNTPOINT_HEADROOM_IMAGE"), "Image of a pause container to use in spawned Headroom Pods.")
mountpointImagePullPolicy = flag.String("mountpoint-image-pull-policy", os.Getenv("MOUNTPOINT_IMAGE_PULL_POLICY"), "Pull policy of Mountpoint images.")
mountpointContainerCommand = flag.String("mountpoint-container-command", "/bin/scality-s3-csi-mounter", "Entrypoint command of the Mountpoint Pods.")
tlsCACertConfigMap = flag.String("tls-ca-cert-configmap", os.Getenv("TLS_CA_CERT_CONFIGMAP"), "Name of ConfigMap containing custom CA certificate(s).")
tlsInitImage = flag.String("tls-init-image", os.Getenv("TLS_INIT_IMAGE"), "Image for CA certificate installation initContainer.")
tlsInitImagePullPolicy = flag.String("tls-init-image-pull-policy", os.Getenv("TLS_INIT_IMAGE_PULL_POLICY"), "Pull policy for TLS init image.")
tlsInitResourcesReqCPU = flag.String("tls-init-resources-req-cpu", os.Getenv("TLS_INIT_RESOURCES_REQUESTS_CPU"), "CPU request for TLS init container.")
tlsInitResourcesReqMemory = flag.String("tls-init-resources-req-memory", os.Getenv("TLS_INIT_RESOURCES_REQUESTS_MEMORY"), "Memory request for TLS init container.")
tlsInitResourcesLimMemory = flag.String("tls-init-resources-lim-memory", os.Getenv("TLS_INIT_RESOURCES_LIMITS_MEMORY"), "Memory limit for TLS init container.")
)

var scheme = runtime.NewScheme()
Expand Down Expand Up @@ -79,6 +87,7 @@ func main() {
},
CSIDriverVersion: version.GetVersion().DriverVersion,
ClusterVariant: cluster.DetectVariant(conf, log),
TLS: buildTLSConfig(log),
}

// Setup the pod reconciler that will create MountpointS3PodAttachments
Expand All @@ -105,3 +114,61 @@ func main() {
os.Exit(1)
}
}

// buildTLSConfig constructs a TLSConfig from flags/env vars. Returns nil if no ConfigMap name is set.
func buildTLSConfig(log logr.Logger) *mppod.TLSConfig {
if *tlsCACertConfigMap == "" {
return nil
}

initImage := *tlsInitImage
if initImage == "" {
initImage = "alpine:3.21"
}

pullPolicy := corev1.PullPolicy(*tlsInitImagePullPolicy)
if pullPolicy == "" {
pullPolicy = corev1.PullIfNotPresent
}

reqCPU := resource.MustParse("10m")
if *tlsInitResourcesReqCPU != "" {
parsed, err := resource.ParseQuantity(*tlsInitResourcesReqCPU)
if err != nil {
log.Error(err, "invalid TLS init CPU request", "value", *tlsInitResourcesReqCPU)
os.Exit(1)
}
reqCPU = parsed
}

reqMemory := resource.MustParse("16Mi")
if *tlsInitResourcesReqMemory != "" {
parsed, err := resource.ParseQuantity(*tlsInitResourcesReqMemory)
if err != nil {
log.Error(err, "invalid TLS init memory request", "value", *tlsInitResourcesReqMemory)
os.Exit(1)
}
reqMemory = parsed
}

limMemory := resource.MustParse("64Mi")
if *tlsInitResourcesLimMemory != "" {
parsed, err := resource.ParseQuantity(*tlsInitResourcesLimMemory)
if err != nil {
log.Error(err, "invalid TLS init memory limit", "value", *tlsInitResourcesLimMemory)
os.Exit(1)
}
limMemory = parsed
}

log.Info("TLS configuration enabled", "configmap", *tlsCACertConfigMap, "initImage", initImage)

return &mppod.TLSConfig{
CACertConfigMapName: *tlsCACertConfigMap,
InitImage: initImage,
InitImagePullPolicy: pullPolicy,
InitResourcesReqCPU: reqCPU,
InitResourcesReqMemory: reqMemory,
InitResourcesLimMemory: limMemory,
}
}
19 changes: 19 additions & 0 deletions docs/concepts-and-reference/helm-chart-configuration-reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,25 @@ value: {{ coalesce .Values.node.s3EndpointUrl .Values.s3.endpointUrl | quote }}
| `mountpointPod.headroomImage.tag` | Image tag for headroom pods. | `3.10` | No |
| `mountpointPod.headroomImage.pullPolicy` | Image pull policy for headroom pods. | `IfNotPresent` | No |

## TLS Configuration

<!-- markdownlint-disable MD046 -->
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Might as well deactivate MD046 as a whole in the .markdownlint.yaml I find it very hard to support on mkdocs.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in a seperate commit

only 1 file is not in the above commit, which was modified with respective code changes for helm and config maps. helm-chart-configuration-reference.md

!!! info "Custom CA Certificates"
When your S3 endpoint uses TLS with a private or internal CA, configure the `tls.*` parameters to inject the CA certificate.
The ConfigMap must exist in **two** namespaces (controller and mounter pod) because they run in separate namespaces.
See the [TLS Configuration Guide](../driver-deployment/tls-configuration.md) for ordering constraints and setup instructions.
<!-- markdownlint-enable MD046 -->

| Parameter | Description | Default | Required |
|------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------|--------------------------------------------------------|-----------------------------|
| `tls.caCertConfigMap` | Name of the ConfigMap containing the CA certificate bundle (key: `ca-bundle.crt`). Must exist in both the controller namespace and `mountpointPod.namespace`. Create in the controller namespace **before** Helm install, and in `mountpointPod.namespace` **after** (since Helm creates that namespace). If missing from either namespace, the respective pod stays in `ContainerCreating`. Leave empty to disable. | `""` | No |
| `tls.initImage.repository` | Image repository for the CA certificate installation initContainer in mounter pods. | `alpine` | No |
| `tls.initImage.tag` | Image tag for the CA certificate installation initContainer. | `3.21` | No |
| `tls.initImage.pullPolicy` | Pull policy for the CA certificate init image. | `IfNotPresent` | No |
| `tls.initResources.requests.cpu` | CPU request for the CA certificate init container. | `10m` | No |
| `tls.initResources.requests.memory` | Memory request for the CA certificate init container. | `16Mi` | No |
| `tls.initResources.limits.memory` | Memory limit for the CA certificate init container. | `64Mi` | No |

## CRD Cleanup Configuration (v2.0)

| Parameter | Description | Default | Required |
Expand Down
Loading
Loading