Skip to content

Add support for spiffe.io/helper-jwt-audience and spiffe.io/helper-jwt-filename annotations#84

Open
lucasmellos wants to merge 3 commits intocofide:mainfrom
lucasmellos:fix/spiffe-helper-jwt-health-nonroot
Open

Add support for spiffe.io/helper-jwt-audience and spiffe.io/helper-jwt-filename annotations#84
lucasmellos wants to merge 3 commits intocofide:mainfrom
lucasmellos:fix/spiffe-helper-jwt-health-nonroot

Conversation

@lucasmellos
Copy link
Copy Markdown

@lucasmellos lucasmellos commented Feb 9, 2026

Summary

  • Add support for configuring spiffe-helper JWT SVID generation via Pod annotations:
    • spiffe.io/helper-jwt-audience
    • spiffe.io/helper-jwt-filename
    • spiffe.io/helper-jwt-svid-file-mode (optional; default 600)
    • spiffe.io/helper-jwt-extra-audiences (optional)
  • Generate jwt_svids as an HCL list (and avoid emitting null values) to prevent spiffe-helper config parse failures.
  • Set explicit health_checks defaults (bind port + readiness/liveness paths) so probes work reliably with spiffe-helper versions that support the health server.
  • Run the injected config-writer init container as non-root to avoid CreateContainerConfigError on workloads enforcing runAsNonRoot (e.g. cert-manager).

Example

metadata:
  annotations:
    spiffe.cofide.io/inject: helper
    spiffe.io/helper-jwt-audience: sts.amazonaws.com
    spiffe.io/helper-jwt-filename: aws_token
    # optional
    spiffe.io/helper-jwt-svid-file-mode: "600"

Test plan

  • go test ./...
  • Deploy webhook image and verify mutated Pods include:
    • spec.initContainers[name=inject-spiffe-helper-config].securityContext.runAsUser: 65532
    • health_checks uses port 8081 and /ready + /live
    • jwt_svids renders without any null
    • jwt_svid_file_mode defaults to 600 (or matches the annotation)

- Generate jwt_svids as an HCL list without emitting nulls
- Set health_checks defaults (bind port + ready/live paths)
- Run config writer init container as non-root for restricted pods

Co-authored-by: Cursor <cursoragent@cursor.com>
@gemini-code-assist
Copy link
Copy Markdown
Contributor

Summary of Changes

Hello @lucasmellos, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request significantly enhances the robustness, security, and compatibility of the spiffe-enable webhook. It addresses critical issues in spiffe-helper configuration generation, particularly for JWT SVIDs, by ensuring correct HCL list formatting and preventing null values. The changes also standardize health check configurations, improve security by running the init container as non-root, and refactor the spiffe-helper injection from an init container to a sidecar. These updates collectively lead to a more stable and secure integration with Kubernetes workloads.

Highlights

  • Improved JWT Configuration Generation: The spiffe-helper configuration now correctly generates jwt_svids as an HCL list, preventing parsing failures and avoiding null values. New annotations are introduced for JWT audience, filename, and extra audiences.
  • Explicit Health Check Defaults: Health checks for spiffe-helper are now configured with explicit defaults for the bind port (8081) and readiness/liveness paths (/ready, /live), ensuring reliable probe functionality.
  • Non-Root Init Container Execution: The injected config-writer init container now runs as a non-root user (UID/GID 65532) with restricted privileges, enhancing security and compatibility with workloads enforcing runAsNonRoot.
  • SPIFFE Helper as Sidecar: The spiffe-helper container is now injected as a sidecar rather than an init container, aligning with common patterns for long-running services.
  • Updated Image Versions: The SPIFFEHelperImage and InitHelperImage versions have been updated to 0.10.0 and 0.5.2 respectively.
  • Conditional Cert Volume Mounts: Application containers now receive a cert volume mount only when JWT SVIDs are configured, ensuring necessary access for JWT operations.

🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console.

Changelog
  • internal/const/const.go
    • Added HelperJWTAudienceAnnotation, HelperJWTFilenameAnnotation, and HelperJWTExtraAudiencesAnnotation constants for new JWT configuration annotations.
  • internal/helper/config.go
    • Updated SPIFFEHelperImage to 0.10.0 and InitHelperImage to 0.5.2.
    • Modified SPIFFEHelperJWTConfig to mark JWTExtraAudiences as optional in HCL.
    • Added JWTConfigs field to SPIFFEHelperConfigParams to pass JWT configurations.
    • In NewSPIFFEHelper, explicitly set BindPort, LivenessPath, and ReadinessPath for HealthCheck.
    • Implemented custom HCL generation logic to correctly format jwt_svids as a list of objects, avoiding null values when no extra audiences are present.
    • Added SecurityContext to the GetInitContainer function, setting RunAsUser, RunAsGroup to 65532, RunAsNonRoot to true, and dropping all capabilities.
  • internal/helper/config_test.go
    • Added a strings.Contains check to TestNewSPIFFEHelper to assert that the generated HCL config does not contain the string "null".
  • internal/helper/jwt.go
    • New file containing ParseJWTConfigFromAnnotations to extract JWT configuration from pod annotations.
    • New file containing EnsureCertVolumeMount to conditionally add a cert volume mount to a container.
  • internal/helper/jwt_test.go
    • New file containing unit tests for ParseJWTConfigFromAnnotations, covering various annotation scenarios.
  • internal/webhook/webhook.go
    • Integrated helper.ParseJWTConfigFromAnnotations to read JWT configuration from pod annotations.
    • Passed the parsed jwtConfigs to helper.SPIFFEHelperConfigParams.
    • Modified the webhook logic to inject spiffe-helper as a regular Container (sidecar) instead of an InitContainer.
    • Added logic to conditionally call helper.EnsureCertVolumeMount for application containers if JWT SVIDs are configured.
  • internal/webhook/webhook_test.go
    • Updated test assertions in TestSpiffeEnableWebhook_Handle to reflect that spiffe-helper is now a sidecar container, adjusting expected container and init container counts.
Activity
  • The pull request introduces several functional and security enhancements. No specific reviewer comments or interactions are recorded in the provided context.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@lucasmellos lucasmellos changed the title Fix spiffe-helper JWT config generation, health checks and init non-root Add support for spiffe.io/helper-jwt-audience and spiffe.io/helper-jwt-filename annotations Feb 9, 2026
Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

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

Code Review

The pull request significantly enhances the spiffe-helper integration by addressing key configuration generation issues. It correctly generates jwt_svids as an HCL list, preventing null values that could cause parse failures. Explicit health check defaults are now set, improving probe reliability. Crucially, the config-writer init container now runs as non-root, resolving CreateContainerConfigError issues in environments enforcing runAsNonRoot. The changes also refactor the spiffe-helper into a sidecar container and introduce new annotations for JWT configuration, along with a dedicated jwt.go file for parsing these annotations. The test suite has been updated to reflect these changes and ensure correctness.

Comment thread internal/helper/config.go
Comment on lines +117 to +137
jwtObjects := make([]cty.Value, len(params.JWTConfigs))
for i, jwtConfig := range params.JWTConfigs {
objMap := map[string]cty.Value{
"jwt_audience": cty.StringVal(jwtConfig.JWTAudience),
"jwt_svid_file_name": cty.StringVal(jwtConfig.JWTSVIDFilename),
}

// Only add jwt_extra_audiences if it has values
if len(jwtConfig.JWTExtraAudiences) > 0 {
extraAuds := make([]cty.Value, len(jwtConfig.JWTExtraAudiences))
for j, aud := range jwtConfig.JWTExtraAudiences {
extraAuds[j] = cty.StringVal(aud)
}
objMap["jwt_extra_audiences"] = cty.ListVal(extraAuds)
}

jwtObjects[i] = cty.ObjectVal(objMap)
}

// Set jwt_svids as a list attribute
body.SetAttributeValue("jwt_svids", cty.ListVal(jwtObjects))
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 manual construction of HCL objects using cty.Value and cty.ObjectVal for jwt_svids is effective but verbose and tightly coupled to the SPIFFEHelperJWTConfig struct's fields. If the SPIFFEHelperJWTConfig struct changes (e.g., field names, types), this manual construction logic would need to be updated carefully, increasing maintenance burden. While necessary for the specific HCL output format and avoiding null values, it's a pattern that can become brittle.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Good callout. We intentionally build jwt_svids via cty because spiffe-helper expects jwt_svids as a list attribute (not blocks) and gohcl was producing either block syntax or null values in some cases.

To reduce brittleness, I refactored the mapping into a single helper (jwtSVIDConfigToCtyValue) so future changes only need updating in one place. We also keep coverage that asserts the generated config contains no null.

Comment thread internal/helper/config.go
Comment on lines +241 to +246
AllowPrivilegeEscalation: ptr.To(false),
RunAsUser: ptr.To(int64(65532)),
RunAsGroup: ptr.To(int64(65532)),
RunAsNonRoot: ptr.To(true),
Privileged: ptr.To(false),
Capabilities: &corev1.Capabilities{Drop: []corev1.Capability{"all"}},
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 SecurityContext for the init container sets AllowPrivilegeEscalation to ptr.To(false) and Privileged to ptr.To(false). It also drops all capabilities. This is excellent for security, ensuring the init container runs with least privilege. Setting RunAsUser and RunAsGroup to 65532 is a good practice for non-root execution, commonly used for unprivileged users in container images.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Thanks — the intent here is to keep the config-writer init container as locked down as possible while still being compatible with pods enforcing runAsNonRoot (e.g. cert-manager).

Comment thread internal/helper/jwt.go Outdated
Comment on lines +30 to +31
// Important: keep this non-nil so HCL encoding emits `[]` not `null`.
JWTExtraAudiences: []string{},
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 comment // Important: keep this non-nil so HCL encoding emits [] not null. indicates a potential quirk or limitation in the HCL encoding library where an empty slice might be encoded as null instead of an empty list []. While the explicit initialization JWTExtraAudiences: []string{} correctly addresses this, it's a defensive measure against an unexpected behavior of the HCL encoder. The optional tag in the struct (hcl:"jwt_extra_audiences,optional") should ideally handle this gracefully by omitting the field entirely if empty, or encoding it as an empty list.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Agreed this is defensive. In the final config generation path we omit jwt_extra_audiences entirely when empty (so we never emit null). We still initialize JWTExtraAudiences to an empty slice in the annotation parser to keep downstream behavior consistent and avoid reintroducing null if we later add additional encoding paths that rely on struct encoding.

lucasmellos and others added 2 commits February 24, 2026 11:05
- Support `spiffe.io/helper-jwt-svid-file-mode` with default 600
- Propagate mode into generated spiffe-helper config
- Fix go.mod go directive format

Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant