Skip to content

Commit

Permalink
fix(spin/certs): inject CA bunde in the filesystem
Browse files Browse the repository at this point in the history
This commit injects a default CA bundle in the root filesystem for every
Spin application pod created, so host components can execute TLS
operations.

Signed-off-by: Radu Matei <radu@fermyon.com>
  • Loading branch information
radu-matei authored and adamreese committed Apr 17, 2024
1 parent 9ffa340 commit 679c6f3
Show file tree
Hide file tree
Showing 4 changed files with 44 additions and 10 deletions.
33 changes: 32 additions & 1 deletion internal/controller/deployment.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,11 +39,37 @@ func constructRuntimeConfigSecretMount(_ctx context.Context, secretName string)
return volume, volumeMount
}

func constructCASecretMount(_ctx context.Context, secretName string) (corev1.Volume, corev1.VolumeMount) {
volume := corev1.Volume{
Name: "spin-ca",
VolumeSource: corev1.VolumeSource{
Secret: &corev1.SecretVolumeSource{
SecretName: secretName,
Optional: ptr(true),
Items: []corev1.KeyToPath{
{
Key: "ca-certificates.crt",
Path: "ca-certificates.crt",
},
},
},
},
}
volumeMount := corev1.VolumeMount{
Name: "spin-ca",
ReadOnly: true,
MountPath: "/etc/ssl/certs/ca-certificates.crt",
SubPath: "ca-certificates.crt",
}

return volume, volumeMount
}

// ConstructVolumeMountsForApp introspects the application and generates
// any required volume mounts. A generated runtime secret is mutually
// exclusive with a user-provided secret - this is to require _either_ a
// manual runtime-config or a generated one from the crd.
func ConstructVolumeMountsForApp(ctx context.Context, app *spinv1alpha1.SpinApp, generatedRuntimeSecret string) ([]corev1.Volume, []corev1.VolumeMount, error) {
func ConstructVolumeMountsForApp(ctx context.Context, app *spinv1alpha1.SpinApp, generatedRuntimeSecret string, caSecretName string) ([]corev1.Volume, []corev1.VolumeMount, error) {
volumes := []corev1.Volume{}
volumeMounts := []corev1.VolumeMount{}

Expand All @@ -67,6 +93,11 @@ func ConstructVolumeMountsForApp(ctx context.Context, app *spinv1alpha1.SpinApp,
volumes = append(volumes, app.Spec.Volumes...)
volumeMounts = append(volumeMounts, app.Spec.VolumeMounts...)

// TODO: eventually add runtime configuration for specifying the CA bundle to use.
caVolume, caVolumeMount := constructCASecretMount(ctx, caSecretName)
volumes = append(volumes, caVolume)
volumeMounts = append(volumeMounts, caVolumeMount)

return volumes, volumeMounts, nil
}

Expand Down
10 changes: 5 additions & 5 deletions internal/controller/deployment_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,22 +52,22 @@ func TestConstructVolumeMountsForApp_Contract(t *testing.T) {
// places.
app := minimalSpinApp()
app.Spec.RuntimeConfig.LoadFromSecret = "a-secret"
_, _, err := ConstructVolumeMountsForApp(context.Background(), app, "a-generated-secret")
_, _, err := ConstructVolumeMountsForApp(context.Background(), app, "a-generated-secret", "a-ca-secret")
require.Error(t, err)
require.ErrorContains(t, err, "cannot specify both a user-provided runtime secret and a generated one")

// No runtime secret at all is ok
app = minimalSpinApp()
app.Spec.RuntimeConfig.LoadFromSecret = ""
volumes, mounts, err := ConstructVolumeMountsForApp(context.Background(), app, "")
volumes, mounts, err := ConstructVolumeMountsForApp(context.Background(), app, "", "")
require.NoError(t, err)
require.Empty(t, volumes)
require.Empty(t, mounts)

// User provided runtime secret is ok
app = minimalSpinApp()
app.Spec.RuntimeConfig.LoadFromSecret = "foo-secret-v1"
volumes, mounts, err = ConstructVolumeMountsForApp(context.Background(), app, "")
volumes, mounts, err = ConstructVolumeMountsForApp(context.Background(), app, "", "")
require.NoError(t, err)
require.Len(t, volumes, 1)
require.Len(t, mounts, 1)
Expand All @@ -76,7 +76,7 @@ func TestConstructVolumeMountsForApp_Contract(t *testing.T) {
// Generated runtime secret is ok
app = minimalSpinApp()
app.Spec.RuntimeConfig.LoadFromSecret = ""
volumes, mounts, err = ConstructVolumeMountsForApp(context.Background(), app, "gen-secret")
volumes, mounts, err = ConstructVolumeMountsForApp(context.Background(), app, "gen-secret", "spin-ca")
require.NoError(t, err)
require.Len(t, volumes, 1)
require.Len(t, mounts, 1)
Expand Down Expand Up @@ -227,7 +227,7 @@ func TestSpinHealthCheckToCoreProbe(t *testing.T) {
func TestDeploymentLabel(t *testing.T) {
scheme := registerAndGetScheme()
app := minimalSpinApp()
deployment, err := constructDeployment(context.Background(), app, &spinv1alpha1.ExecutorDeploymentConfig{}, "", scheme)
deployment, err := constructDeployment(context.Background(), app, &spinv1alpha1.ExecutorDeploymentConfig{}, "", "", scheme)

require.Nil(t, err)
require.NotNil(t, deployment.ObjectMeta.Labels)
Expand Down
9 changes: 6 additions & 3 deletions internal/controller/spinapp_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -279,7 +279,10 @@ func (r *SpinAppReconciler) reconcileDeployment(ctx context.Context, app *spinv1
}
}

desiredDeployment, err := constructDeployment(ctx, app, config, generatedRuntimeConfigSecretName, r.Scheme)
// TODO: make this configurable
caSecretName := "spin-ca"

desiredDeployment, err := constructDeployment(ctx, app, config, generatedRuntimeConfigSecretName, caSecretName, r.Scheme)
if err != nil {
return fmt.Errorf("failed to construct Deployment: %w", err)
}
Expand Down Expand Up @@ -346,7 +349,7 @@ func (r *SpinAppReconciler) deleteDeployment(ctx context.Context, app *spinv1alp

// constructDeployment builds an appsv1.Deployment based on the configuration of a SpinApp.
func constructDeployment(ctx context.Context, app *spinv1alpha1.SpinApp, config *spinv1alpha1.ExecutorDeploymentConfig,
generatedRuntimeConfigSecretName string, scheme *runtime.Scheme) (*appsv1.Deployment, error) {
generatedRuntimeConfigSecretName string, caSecretName string, scheme *runtime.Scheme) (*appsv1.Deployment, error) {
// TODO: Once we land admission webhooks write some validation to make
// replicas and enableAutoscaling mutually exclusive.
var replicas *int32
Expand All @@ -356,7 +359,7 @@ func constructDeployment(ctx context.Context, app *spinv1alpha1.SpinApp, config
replicas = ptr(app.Spec.Replicas)
}

volumes, volumeMounts, err := ConstructVolumeMountsForApp(ctx, app, generatedRuntimeConfigSecretName)
volumes, volumeMounts, err := ConstructVolumeMountsForApp(ctx, app, generatedRuntimeConfigSecretName, caSecretName)
if err != nil {
return nil, err
}
Expand Down
2 changes: 1 addition & 1 deletion internal/controller/spinapp_controller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -402,7 +402,7 @@ func TestConstructDeployment_MinimalApp(t *testing.T) {
cfg := &spinv1alpha1.ExecutorDeploymentConfig{
RuntimeClassName: "bananarama",
}
deployment, err := constructDeployment(context.Background(), app, cfg, "", nil)
deployment, err := constructDeployment(context.Background(), app, cfg, "", "", nil)
require.NoError(t, err)
require.NotNil(t, deployment)

Expand Down

0 comments on commit 679c6f3

Please sign in to comment.