Skip to content

Commit 29b7d2f

Browse files
DharmitDVaniHaripriyaHumairAK
authored
chore: Add ability to mount self-signed certs to kfp (#10849)
* Add ability to mount self-signed certs to kfp This update allows CA bundles to be mounted to the launcher/executor pods since those make external connections to object store, which can be behind self signed certs. Detailed Changes: - Added `REQUESTS_CA_BUNDLE` to the environment variables. This is necessary because many Python-based libraries (e.g., requests) utilize this environment variable for SSL/TLS certificate verification. Notably, even though Boto3 is documented to use `AWS_CA_BUNDLE`, tests have shown that it only respects `REQUESTS_CA_BUNDLE`. Reference: https://requests.readthedocs.io/en/latest/user/advanced/#ssl-cert-verification and aws/aws-cli#3425. - Configured `AWS_CA_BUNDLE` for AWS CLI and related utilities to ensure AWS services utilize our custom CA bundle for SSL/TLS. - Set up `SSL_CERT_FILE` environment variable for OpenSSL's default certificate file. This setting is important as the `SSL_CERT_DIR` path adjustments had inconsistent results across different environments, as discussed in OpenSSL documentation: https://www.openssl.org/docs/man1.1.1/man3/SSL_CTX_set_default_verify_paths.html Signed-off-by: ddalvi <ddalvi@redhat.com> Co-authored-by: Vani Haripriya <vmudadla@redhat.com> Co-authored-by: Humair Khan <HumairAK@users.noreply.github.com> Signed-off-by: ddalvi <ddalvi@redhat.com> * Add unit test to check the certificate mounting Signed-off-by: ddalvi <ddalvi@redhat.com> --------- Signed-off-by: ddalvi <ddalvi@redhat.com> Co-authored-by: Vani Haripriya <vmudadla@redhat.com> Co-authored-by: Humair Khan <HumairAK@users.noreply.github.com>
1 parent bdc3bb1 commit 29b7d2f

File tree

2 files changed

+130
-0
lines changed

2 files changed

+130
-0
lines changed

backend/src/v2/compiler/argocompiler/container.go

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,9 @@
1515
package argocompiler
1616

1717
import (
18+
"fmt"
1819
"os"
20+
"strings"
1921

2022
wfapi "github.com/argoproj/argo-workflows/v3/pkg/apis/workflow/v1alpha1"
2123
"github.com/golang/protobuf/jsonpb"
@@ -27,6 +29,7 @@ import (
2729

2830
const (
2931
volumeNameKFPLauncher = "kfp-launcher"
32+
volumeNameCABundle = "ca-bundle"
3033
DefaultLauncherImage = "gcr.io/ml-pipeline/kfp-launcher@sha256:8fe5e6e4718f20b021736022ad3741ddf2abd82aa58c86ae13e89736fdc3f08f"
3134
LauncherImageEnvVar = "V2_LAUNCHER_IMAGE"
3235
DefaultDriverImage = "gcr.io/ml-pipeline/kfp-driver@sha256:3c0665cd36aa87e4359a4c8b6271dcba5bdd817815cd0496ed12eb5dde5fd2ec"
@@ -361,6 +364,59 @@ func (c *workflowCompiler) addContainerExecutorTemplate(refName string) string {
361364
extendPodMetadata(&executor.Metadata, k8sExecCfg)
362365
}
363366
}
367+
caBundleCfgMapName := os.Getenv("EXECUTOR_CABUNDLE_CONFIGMAP_NAME")
368+
caBundleCfgMapKey := os.Getenv("EXECUTOR_CABUNDLE_CONFIGMAP_KEY")
369+
caBundleMountPath := os.Getenv("EXECUTOR_CABUNDLE_MOUNTPATH")
370+
if caBundleCfgMapName != "" && caBundleCfgMapKey != "" {
371+
caFile := fmt.Sprintf("%s/%s", caBundleMountPath, caBundleCfgMapKey)
372+
var certDirectories = []string{
373+
caBundleMountPath,
374+
"/etc/ssl/certs",
375+
"/etc/pki/tls/certs",
376+
}
377+
// Add to REQUESTS_CA_BUNDLE for python request library.
378+
executor.Container.Env = append(executor.Container.Env, k8score.EnvVar{
379+
Name: "REQUESTS_CA_BUNDLE",
380+
Value: caFile,
381+
})
382+
// For AWS utilities like cli, and packages.
383+
executor.Container.Env = append(executor.Container.Env, k8score.EnvVar{
384+
Name: "AWS_CA_BUNDLE",
385+
Value: caFile,
386+
})
387+
// OpenSSL default cert file env variable.
388+
// https://www.openssl.org/docs/man1.1.1/man3/SSL_CTX_set_default_verify_paths.html
389+
executor.Container.Env = append(executor.Container.Env, k8score.EnvVar{
390+
Name: "SSL_CERT_FILE",
391+
Value: caFile,
392+
})
393+
sslCertDir := strings.Join(certDirectories, ":")
394+
executor.Container.Env = append(executor.Container.Env, k8score.EnvVar{
395+
Name: "SSL_CERT_DIR",
396+
Value: sslCertDir,
397+
})
398+
volume := k8score.Volume{
399+
Name: volumeNameCABundle,
400+
VolumeSource: k8score.VolumeSource{
401+
ConfigMap: &k8score.ConfigMapVolumeSource{
402+
LocalObjectReference: k8score.LocalObjectReference{
403+
Name: caBundleCfgMapName,
404+
},
405+
},
406+
},
407+
}
408+
409+
executor.Volumes = append(executor.Volumes, volume)
410+
411+
volumeMount := k8score.VolumeMount{
412+
Name: volumeNameCABundle,
413+
MountPath: caFile,
414+
SubPath: caBundleCfgMapKey,
415+
}
416+
417+
executor.Container.VolumeMounts = append(executor.Container.VolumeMounts, volumeMount)
418+
419+
}
364420
c.templates[nameContainerImpl] = executor
365421
c.wf.Spec.Templates = append(c.wf.Spec.Templates, *container, *executor)
366422
return nameContainerExecutor

backend/src/v2/compiler/argocompiler/container_test.go

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,87 @@
1515
package argocompiler
1616

1717
import (
18+
"os"
1819
"testing"
1920

2021
wfapi "github.com/argoproj/argo-workflows/v3/pkg/apis/workflow/v1alpha1"
2122
"github.com/kubeflow/pipelines/kubernetes_platform/go/kubernetesplatform"
2223
"github.com/stretchr/testify/assert"
2324
)
2425

26+
func TestAddContainerExecutorTemplate(t *testing.T) {
27+
tests := []struct {
28+
name string
29+
configMapName string
30+
configMapKey string
31+
mountPath string
32+
expectedVolumeName string
33+
expectedConfigMapName string
34+
expectedMountPath string
35+
}{
36+
{
37+
name: "Test with valid settings",
38+
configMapName: "kube-root-ca.crt",
39+
configMapKey: "ca.crt",
40+
mountPath: "/etc/ssl/custom",
41+
expectedVolumeName: "ca-bundle",
42+
expectedConfigMapName: "kube-root-ca.crt",
43+
expectedMountPath: "/etc/ssl/custom/ca.crt",
44+
},
45+
}
46+
47+
for _, tt := range tests {
48+
t.Run(tt.name, func(t *testing.T) {
49+
os.Setenv("EXECUTOR_CABUNDLE_CONFIGMAP_NAME", tt.configMapName)
50+
os.Setenv("EXECUTOR_CABUNDLE_CONFIGMAP_KEY", tt.configMapKey)
51+
os.Setenv("EXECUTOR_CABUNDLE_MOUNTPATH", tt.mountPath)
52+
53+
c := &workflowCompiler{
54+
templates: make(map[string]*wfapi.Template),
55+
wf: &wfapi.Workflow{
56+
Spec: wfapi.WorkflowSpec{
57+
Templates: []wfapi.Template{},
58+
},
59+
},
60+
}
61+
62+
c.addContainerExecutorTemplate("test-ref")
63+
assert.NotEmpty(t, "system-container-impl", "Template name should not be empty")
64+
65+
executorTemplate, exists := c.templates["system-container-impl"]
66+
assert.True(t, exists, "Template should exist with the returned name")
67+
assert.NotNil(t, executorTemplate, "Executor template should not be nil")
68+
69+
foundVolume := false
70+
for _, volume := range executorTemplate.Volumes {
71+
if volume.Name == tt.expectedVolumeName {
72+
foundVolume = true
73+
assert.Equal(t, tt.expectedConfigMapName, volume.VolumeSource.ConfigMap.Name, "ConfigMap name should match")
74+
break
75+
}
76+
}
77+
assert.True(t, foundVolume, "CA bundle volume should be included in the template")
78+
79+
foundVolumeMount := false
80+
if executorTemplate.Container != nil {
81+
for _, mount := range executorTemplate.Container.VolumeMounts {
82+
if mount.Name == tt.expectedVolumeName && mount.MountPath == tt.expectedMountPath {
83+
foundVolumeMount = true
84+
break
85+
}
86+
}
87+
}
88+
assert.True(t, foundVolumeMount, "CA bundle volume mount should be included in the container")
89+
})
90+
}
91+
defer func() {
92+
os.Unsetenv("EXECUTOR_CABUNDLE_CONFIGMAP_NAME")
93+
os.Unsetenv("EXECUTOR_CABUNDLE_CONFIGMAP_KEY")
94+
os.Unsetenv("EXECUTOR_CABUNDLE_MOUNTPATH")
95+
}()
96+
97+
}
98+
2599
func Test_extendPodMetadata(t *testing.T) {
26100
tests := []struct {
27101
name string

0 commit comments

Comments
 (0)