Skip to content
Draft
Show file tree
Hide file tree
Changes from 1 commit
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
51 changes: 33 additions & 18 deletions pkg/webhook/mutatingwebhook.go
Original file line number Diff line number Diff line change
Expand Up @@ -149,12 +149,22 @@ func (si *SidecarInjector) Handle(ctx context.Context, req admission.Request) ad
if err != nil {
return admission.Errored(http.StatusBadRequest, err)
}
mountPath := filepath.Dir(credentialConfig.CredentialSource.File)

credentialVolumeMounts := []corev1.VolumeMount{
{Name: SidecarContainerWICredentialConfigMapVolumeName, MountPath: SidecarContainerWICredentialConfigMapVolumeMountPath},
}
var envVars []corev1.EnvVar
if credentialConfig.CredentialSource.Executable == nil {
credentialVolumeMounts = append(credentialVolumeMounts, corev1.VolumeMount{Name: SidecarContainerWITokenVolumeName, MountPath: mountPath})
} else {
envVars = append(envVars, corev1.EnvVar{Name: "GOOGLE_EXTERNAL_ACCOUNT_ALLOW_EXECUTABLES", Value: "1"})
}
Comment on lines +158 to +168

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

The mountPath variable is only used when Executable is nil. Moving its calculation inside the if block improves code clarity and avoids unnecessary processing when an executable-sourced credential is used. Additionally, consider using a constant for the environment variable name GOOGLE_EXTERNAL_ACCOUNT_ALLOW_EXECUTABLES to avoid magic strings.

		credentialVolumeMounts := []corev1.VolumeMount{
			{Name: SidecarContainerWICredentialConfigMapVolumeName, MountPath: SidecarContainerWICredentialConfigMapVolumeMountPath},
		}
		var envVars []corev1.EnvVar
		if credentialConfig.CredentialSource.Executable == nil {
			mountPath := filepath.Dir(credentialConfig.CredentialSource.File)
			credentialVolumeMounts = append(credentialVolumeMounts, corev1.VolumeMount{Name: SidecarContainerWITokenVolumeName, MountPath: mountPath})
		} else {
			envVars = append(envVars, corev1.EnvVar{Name: "GOOGLE_EXTERNAL_ACCOUNT_ALLOW_EXECUTABLES", Value: "1"})
		}


sidecarCredentialConfig = &SidecarContainerCredentialConfiguration{
GacEnv: &corev1.EnvVar{Name: "GOOGLE_APPLICATION_CREDENTIALS", Value: fmt.Sprintf("%s/%s", SidecarContainerWICredentialConfigMapVolumeMountPath, filename)},
CredentialVolumeMounts: []corev1.VolumeMount{
{Name: SidecarContainerWITokenVolumeName, MountPath: filepath.Dir(credentialConfig.CredentialSource.File)},
{Name: SidecarContainerWICredentialConfigMapVolumeName, MountPath: SidecarContainerWICredentialConfigMapVolumeMountPath},
},
CredentialVolumeMounts: credentialVolumeMounts,
EnvVars: envVars,
}
klog.Infof("Injected GCP workload identity credential configuration configMap %s in namespace %s", configMapName, pod.Namespace)
}
Expand Down Expand Up @@ -417,23 +427,25 @@ func appendWorkloadCredentialConfigurationVolumes(client kubernetes.Interface, p
}
klog.Infof("Parsed the workload identity credential configuration configMap %s in namespace %s %+v", configMapName, pod.Namespace, credConfig)

pod.Spec.Volumes = append(pod.Spec.Volumes, corev1.Volume{
Name: SidecarContainerWITokenVolumeName,
VolumeSource: corev1.VolumeSource{
Projected: &corev1.ProjectedVolumeSource{
Sources: []corev1.VolumeProjection{
{
ServiceAccountToken: &corev1.ServiceAccountTokenProjection{
Audience: fmt.Sprintf("https:%s", credConfig.Audience), // Add the "https:" prefix to the audience.
ExpirationSeconds: &tokenExpirationSeconds,
Path: filepath.Base(credConfig.CredentialSource.File),
if credConfig.CredentialSource.Executable == nil {
pod.Spec.Volumes = append(pod.Spec.Volumes, corev1.Volume{
Comment on lines +462 to +463

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

high

Since File is now marked as omitempty in the CredentialConfig struct, it can be an empty string. If both Executable and File are missing or empty, the code will proceed to use an empty path in filepath.Base (line 440), which results in . and may lead to an invalid or unintended volume projection path. Adding validation ensures the configuration is correct before usage.

	if credConfig.CredentialSource.Executable == nil {
		if credConfig.CredentialSource.File == "" {
			return "", nil, fmt.Errorf("workload identity credential configuration must have either 'file' or 'executable' set")
		}
		pod.Spec.Volumes = append(pod.Spec.Volumes, corev1.Volume{

Name: SidecarContainerWITokenVolumeName,
VolumeSource: corev1.VolumeSource{
Projected: &corev1.ProjectedVolumeSource{
Sources: []corev1.VolumeProjection{
{
ServiceAccountToken: &corev1.ServiceAccountTokenProjection{
Audience: fmt.Sprintf("https:%s", credConfig.Audience), // Add the "https:" prefix to the audience.
ExpirationSeconds: &tokenExpirationSeconds,
Path: filepath.Base(credConfig.CredentialSource.File),
},
},
},
DefaultMode: &defaultMode,
},
DefaultMode: &defaultMode,
},
},
})
})
}

// Secondly try to add workload identity credential configuration configMap as volume.
if !checkConfigMapVolumeExists(pod) {
Expand Down Expand Up @@ -467,7 +479,10 @@ func checkConfigMapVolumeExists(pod *corev1.Pod) bool {
type CredentialConfig struct {
Audience string `json:"audience"`
CredentialSource struct {
File string `json:"file"`
File string `json:"file,omitempty"`
Executable *struct {
Command string `json:"command"`
} `json:"executable,omitempty"`
} `json:"credential_source"`
}

Expand Down
54 changes: 48 additions & 6 deletions pkg/webhook/oidc_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,10 @@ func TestParseCredentialConfigurationConfigMap(t *testing.T) {
expectedCredConfig: &CredentialConfig{
Audience: "//iam.googleapis.com/projects/123/locations/global/workloadIdentityPools/test-pool/providers/test-provider",
CredentialSource: struct {
File string `json:"file"`
File string `json:"file,omitempty"`
Executable *struct {
Command string `json:"command"`
} `json:"executable,omitempty"`
}{
File: "/var/run/service-account/token",
},
Expand Down Expand Up @@ -485,11 +488,12 @@ func TestSidecarContainerCredentialConfiguration(t *testing.T) {
t.Parallel()

testCases := []struct {
name string
credConfig *SidecarContainerCredentialConfiguration
config *Config
expectedEnvVar *corev1.EnvVar
expectedMounts []corev1.VolumeMount
name string
credConfig *SidecarContainerCredentialConfiguration
config *Config
expectedEnvVar *corev1.EnvVar
expectedEnvVars []corev1.EnvVar
expectedMounts []corev1.VolumeMount
}{
{
name: "valid credential configuration",
Expand Down Expand Up @@ -620,6 +624,33 @@ func TestSidecarContainerCredentialConfiguration(t *testing.T) {
},
},
},
{
name: "credential configuration with generic env vars",
credConfig: &SidecarContainerCredentialConfiguration{
GacEnv: &corev1.EnvVar{
Name: "GOOGLE_APPLICATION_CREDENTIALS",
Value: "/etc/workload-identity/credential-configuration.json",
},
EnvVars: []corev1.EnvVar{
{
Name: "GOOGLE_EXTERNAL_ACCOUNT_ALLOW_EXECUTABLES",
Value: "1",
},
},
},
config: FakeConfig(),
expectedEnvVar: &corev1.EnvVar{
Name: "GOOGLE_APPLICATION_CREDENTIALS",
Value: "/etc/workload-identity/credential-configuration.json",
},
expectedEnvVars: []corev1.EnvVar{
{
Name: "GOOGLE_EXTERNAL_ACCOUNT_ALLOW_EXECUTABLES",
Value: "1",
},
},
expectedMounts: nil,
},
}

for _, tc := range testCases {
Expand Down Expand Up @@ -649,6 +680,17 @@ func TestSidecarContainerCredentialConfiguration(t *testing.T) {
}
}

// Check generic environment variables
var foundEnvVars []corev1.EnvVar
for _, env := range container.Env {
if env.Name == "GOOGLE_EXTERNAL_ACCOUNT_ALLOW_EXECUTABLES" {
foundEnvVars = append(foundEnvVars, env)
}
}
if diff := cmp.Diff(tc.expectedEnvVars, foundEnvVars); diff != "" {
t.Errorf("generic env vars mismatch (-want +got):\n%s", diff)
}

// Check volume mounts - find credential-related mounts
var credentialMounts []corev1.VolumeMount
for _, mount := range container.VolumeMounts {
Expand Down
5 changes: 5 additions & 0 deletions pkg/webhook/sidecar_spec.go
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,7 @@ var (
type SidecarContainerCredentialConfiguration struct {
GacEnv *corev1.EnvVar // This is the environment variable for GOOGLE_APPLICATION_CREDENTIALS that will be injected into the sidecar container.
CredentialVolumeMounts []corev1.VolumeMount // These are the volume mounts for the credential files that will be injected into the sidecar container.
EnvVars []corev1.EnvVar // These are generic environment variables that will be injected into the sidecar container.
}

func GetNativeSidecarContainerSpec(c *Config, credentialConfig *SidecarContainerCredentialConfiguration) corev1.Container {
Expand Down Expand Up @@ -159,6 +160,10 @@ func GetSidecarContainerSpec(c *Config, credentialConfig *SidecarContainerCreden
container.Env = append(container.Env, *credentialConfig.GacEnv)
}

// Inject generic environment variables.
if len(credentialConfig.EnvVars) > 0 {
container.Env = append(container.Env, credentialConfig.EnvVars...)
}
// Inject the volume mounts for the credential files.
if len(credentialConfig.CredentialVolumeMounts) > 0 {
container.VolumeMounts = append(container.VolumeMounts, credentialConfig.CredentialVolumeMounts...)
Expand Down
Loading