Skip to content
Open
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
2 changes: 1 addition & 1 deletion charts/langsmith-auth-proxy/Chart.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,5 @@ maintainers:
email: brian@langchain.dev
description: Helm chart to deploy the langsmith auth-proxy application.
type: application
version: 0.0.9
version: 0.0.10
appVersion: "1.37.0"
81 changes: 80 additions & 1 deletion charts/langsmith-auth-proxy/README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# langsmith-auth-proxy

![Version: 0.0.9](https://img.shields.io/badge/Version-0.0.9-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 1.37.0](https://img.shields.io/badge/AppVersion-1.37.0-informational?style=flat-square)
![Version: 0.0.10](https://img.shields.io/badge/Version-0.0.10-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 1.37.0](https://img.shields.io/badge/AppVersion-1.37.0-informational?style=flat-square)

Helm chart to deploy the langsmith auth-proxy application.

Expand Down Expand Up @@ -65,6 +65,81 @@ Control which phases are sent to the transformer via `processingMode`:

`BUFFERED` mode buffers the entire body before sending — simplest for transformations but uses more memory for large payloads. `STREAMED` sends chunks incrementally (complex to implement). Use `NONE` to skip body processing entirely.

## Custom CA support

Use `customCa.secretName` and `customCa.secretKey` to mount a CA bundle that Envoy should trust for outbound HTTPS connections.

This bundle is applied to every HTTPS peer Envoy validates in this chart:
- The main upstream cluster defined by `authProxy.upstream`

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.

Is upstream always https?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

updated readme to say it's only needed if upstream is using a private/internal CA

- The remote JWKS cluster when `authProxy.jwksUri` uses `https://`

### Secret example

```yaml
apiVersion: v1
kind: Secret
metadata:
name: corporate-ca-bundle
type: Opaque
stringData:
ca.crt: |
-----BEGIN CERTIFICATE-----
...
-----END CERTIFICATE-----
```

```yaml
customCa:
secretName: corporate-ca-bundle
secretKey: ca.crt
```

### Important notes

- Provide the full CA bundle Envoy should trust, not just a single private root. If your upstream or JWKS endpoint chains to public roots as well, include those certificates in the bundle.
- `customCa.secretName` and `customCa.secretKey` must either both be set or both be left empty.
- Envoy reads the CA bundle from a mounted Secret volume. To make trust changes deterministic, roll the pod when the bundle changes.

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.

Isn't pod rolling automatic?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

yeah I think this was probably overdone, removed the rolloutToken field and checksums to simplify

- Required when using `mtls` — see [mTLS support](#mtls-support) below.

### Rotation workflow

- Preferred: publish a new Secret name such as `corporate-ca-bundle-v2` and update `customCa.secretName`.
- Alternate: keep the same Secret name and bump `customCa.rolloutToken` to force Helm to update the pod template and restart Envoy.

## mTLS support

Use `mtls.secretName`, `mtls.certKey`, and `mtls.keyKey` to present a client certificate when connecting to the upstream. This is required when the upstream enforces mutual TLS.

`customCa` **must** also be configured — without a trusted CA bundle Envoy sends client certificates but does not verify the upstream server's identity, which is not mutual authentication. The chart will fail validation if `mtls` is set without `customCa`.

### Secret example

```yaml
apiVersion: v1
kind: Secret
metadata:
name: upstream-client-cert
type: kubernetes.io/tls
data:
tls.crt: <base64-encoded-client-certificate>
tls.key: <base64-encoded-client-private-key>
```

```yaml
customCa:
secretName: corporate-ca-bundle
secretKey: ca.crt
mtls:
secretName: upstream-client-cert
certKey: tls.crt
keyKey: tls.key
```

### Rotation workflow

- Preferred: publish a new Secret name such as `upstream-client-cert-v2` and update `mtls.secretName`.
- Alternate: keep the same Secret name and bump `mtls.rolloutToken` to force a pod restart.

## Values

| Key | Type | Default | Description |
Expand Down Expand Up @@ -173,6 +248,10 @@ Control which phases are sent to the transformer via `processingMode`:
| commonLabels | object | `{}` | Labels that will be applied to all resources created by the chart |
| commonPodAnnotations | object | `{}` | Annotations that will be applied to all pods created by the chart |
| commonPodSecurityContext | object | `{}` | Common pod security context applied to all pods. Component-specific podSecurityContext values will be merged on top of this (component values take precedence). |
| customCa | object | `{"rolloutToken":"","secretKey":"","secretName":""}` | Custom CA certificate for upstream TLS verification. Envoy uses BoringSSL and does NOT trust the system CA store. Provide a Kubernetes Secret with your CA bundle to verify upstream HTTPS connections signed by private/internal CAs. |
| customCa.rolloutToken | string | `""` | Optional manual rollout trigger. Bump this when the Secret contents change without changing secretName, so Helm updates the pod template and restarts Envoy. |
| customCa.secretKey | string | `""` | Key within the Secret that holds the CA certificate PEM data |
| customCa.secretName | string | `""` | Name of the Kubernetes Secret containing the CA certificate |
| fullnameOverride | string | `""` | String to fully override `"langsmith.fullname"` |
| gateway | object | `{"annotations":{},"enabled":false,"hostnames":[],"labels":{},"name":"","namespace":"","sectionName":""}` | Gateway API HTTPRoute configuration |
| gateway.hostnames | list | `[]` | Hostnames to match on |
Expand Down
75 changes: 75 additions & 0 deletions charts/langsmith-auth-proxy/README.md.gotmpl
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,81 @@ Control which phases are sent to the transformer via `processingMode`:

`BUFFERED` mode buffers the entire body before sending — simplest for transformations but uses more memory for large payloads. `STREAMED` sends chunks incrementally (complex to implement). Use `NONE` to skip body processing entirely.

## Custom CA support

Use `customCa.secretName` and `customCa.secretKey` to mount a CA bundle that Envoy should trust for outbound HTTPS connections.

This bundle is applied to every HTTPS peer Envoy validates in this chart:
- The main upstream cluster defined by `authProxy.upstream`
- The remote JWKS cluster when `authProxy.jwksUri` uses `https://`

### Secret example

```yaml
apiVersion: v1
kind: Secret
metadata:
name: corporate-ca-bundle
type: Opaque
stringData:
ca.crt: |
-----BEGIN CERTIFICATE-----
...
-----END CERTIFICATE-----
```

```yaml
customCa:
secretName: corporate-ca-bundle
secretKey: ca.crt
```

### Important notes

- Provide the full CA bundle Envoy should trust, not just a single private root. If your upstream or JWKS endpoint chains to public roots as well, include those certificates in the bundle.
- `customCa.secretName` and `customCa.secretKey` must either both be set or both be left empty.
- Envoy reads the CA bundle from a mounted Secret volume. To make trust changes deterministic, roll the pod when the bundle changes.
- Required when using `mtls` — see [mTLS support](#mtls-support) below.

### Rotation workflow

- Preferred: publish a new Secret name such as `corporate-ca-bundle-v2` and update `customCa.secretName`.
- Alternate: keep the same Secret name and bump `customCa.rolloutToken` to force Helm to update the pod template and restart Envoy.

## mTLS support

Use `mtls.secretName`, `mtls.certKey`, and `mtls.keyKey` to present a client certificate when connecting to the upstream. This is required when the upstream enforces mutual TLS.

`customCa` **must** also be configured — without a trusted CA bundle Envoy sends client certificates but does not verify the upstream server's identity, which is not mutual authentication. The chart will fail validation if `mtls` is set without `customCa`.

### Secret example

```yaml
apiVersion: v1
kind: Secret
metadata:
name: upstream-client-cert
type: kubernetes.io/tls
data:
tls.crt: <base64-encoded-client-certificate>
tls.key: <base64-encoded-client-private-key>
```

```yaml
customCa:
secretName: corporate-ca-bundle
secretKey: ca.crt
mtls:
secretName: upstream-client-cert
certKey: tls.crt
keyKey: tls.key
```

### Rotation workflow

- Preferred: publish a new Secret name such as `upstream-client-cert-v2` and update `mtls.secretName`.
- Alternate: keep the same Secret name and bump `mtls.rolloutToken` to force a pod restart.

## Values

| Key | Type | Default | Description |
Expand Down
125 changes: 125 additions & 0 deletions charts/langsmith-auth-proxy/templates/_helpers.tpl
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,131 @@ Usage: include "authProxy.shouldUseProxy" (dict "hostname" $hostname "noProxy" .
{{- if $result.bypass -}}false{{- else -}}true{{- end -}}
{{- end -}}

{{/*
Normalized custom CA values and paths.
*/}}
{{- define "authProxy.customCaSecretName" -}}
{{- .Values.customCa.secretName | default "" | trim -}}
{{- end -}}

{{- define "authProxy.customCaSecretKey" -}}
{{- .Values.customCa.secretKey | default "" | trim -}}
{{- end -}}

{{- define "authProxy.customCaRolloutToken" -}}
{{- .Values.customCa.rolloutToken | default "" | trim -}}
{{- end -}}

{{- define "authProxy.customCaEnabled" -}}
{{- $secretName := include "authProxy.customCaSecretName" . -}}
{{- $secretKey := include "authProxy.customCaSecretKey" . -}}
{{- if and $secretName $secretKey -}}true{{- end -}}
{{- end -}}

{{- define "authProxy.customCaMountDir" -}}
/etc/langsmith/custom-ca
{{- end -}}

{{- define "authProxy.customCaFileName" -}}
ca.crt
{{- end -}}

{{- define "authProxy.customCaFilePath" -}}
{{- printf "%s/%s" (include "authProxy.customCaMountDir" .) (include "authProxy.customCaFileName" .) -}}
{{- end -}}

{{- define "authProxy.customCaRolloutHash" -}}
{{- $name := include "authProxy.customCaSecretName" . -}}
{{- $key := include "authProxy.customCaSecretKey" . -}}
{{- if and $name $key -}}
{{- dict "secretName" $name "secretKey" $key "rolloutToken" (include "authProxy.customCaRolloutToken" .) | toYaml | sha256sum -}}
{{- end -}}
{{- end -}}

{{/*
Client certificate helpers for mTLS with upstream services.
*/}}
{{- define "authProxy.mtlsSecretName" -}}
{{- .Values.mtls.secretName | default "" | trim -}}
{{- end -}}

{{- define "authProxy.mtlsCertKey" -}}
{{- .Values.mtls.certKey | default "" | trim -}}
{{- end -}}

{{- define "authProxy.mtlsKeyKey" -}}
{{- .Values.mtls.keyKey | default "" | trim -}}
{{- end -}}

{{- define "authProxy.mtlsRolloutToken" -}}
{{- .Values.mtls.rolloutToken | default "" | trim -}}
{{- end -}}

{{- define "authProxy.mtlsEnabled" -}}
{{- $secretName := include "authProxy.mtlsSecretName" . -}}
{{- $certKey := include "authProxy.mtlsCertKey" . -}}
{{- $keyKey := include "authProxy.mtlsKeyKey" . -}}
{{- if and $secretName $certKey $keyKey -}}true{{- end -}}
{{- end -}}

{{- define "authProxy.mtlsMountDir" -}}
/etc/langsmith/client-cert
{{- end -}}

{{- define "authProxy.mtlsCertFileName" -}}
tls.crt
{{- end -}}

{{- define "authProxy.mtlsKeyFileName" -}}
tls.key
{{- end -}}

{{- define "authProxy.mtlsFilePath" -}}
{{- printf "%s/%s" (include "authProxy.mtlsMountDir" .) (include "authProxy.mtlsCertFileName" .) -}}
{{- end -}}

{{- define "authProxy.mtlsKeyFilePath" -}}
{{- printf "%s/%s" (include "authProxy.mtlsMountDir" .) (include "authProxy.mtlsKeyFileName" .) -}}
{{- end -}}

{{- define "authProxy.mtlsRolloutHash" -}}
{{- $name := include "authProxy.mtlsSecretName" . -}}
{{- $certKey := include "authProxy.mtlsCertKey" . -}}
{{- $keyKey := include "authProxy.mtlsKeyKey" . -}}
{{- if and $name $certKey $keyKey -}}
{{- dict "secretName" $name "certKey" $certKey "keyKey" $keyKey "rolloutToken" (include "authProxy.mtlsRolloutToken" .) | toYaml | sha256sum -}}
{{- end -}}
{{- end -}}

{{/*
Renders the Envoy UpstreamTlsContext extras for custom CA and/or client certificate.
Combines validation_context and tls_certificates under a single common_tls_context
block to avoid duplicate YAML keys.
Usage: include "authProxy.envoyUpstreamTlsContextExtras" . | nindent N
*/}}
{{- define "authProxy.envoyUpstreamTlsContextExtras" -}}
{{- $customCa := include "authProxy.customCaEnabled" . -}}
{{- $mtls := include "authProxy.mtlsEnabled" . -}}
{{- if $customCa -}}
auto_sni_san_validation: true
{{- end -}}
{{- if or $customCa $mtls }}
common_tls_context:
{{- if $customCa }}
validation_context:
trusted_ca:
filename: {{ include "authProxy.customCaFilePath" . }}
{{- end }}
{{- if $mtls }}
tls_certificates:
- certificate_chain:
filename: {{ include "authProxy.mtlsFilePath" . }}
private_key:
filename: {{ include "authProxy.mtlsKeyFilePath" . }}
{{- end }}
{{- end -}}
{{- end -}}

{{- define "authProxy.serviceAccountName" -}}
{{- if .Values.authProxy.serviceAccount.create -}}
{{ default (printf "%s-%s" (include "authProxy.fullname" .) .Values.authProxy.name) .Values.authProxy.serviceAccount.name | trunc 63 | trimSuffix "-" }}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -236,6 +236,7 @@ data:
typed_config:
"@type": type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext
sni: {{ $upstreamHostname }}
{{- include "authProxy.envoyUpstreamTlsContextExtras" . | nindent 14 }}
{{- end }}
{{- else }}
- name: upstream_provider
Expand All @@ -257,6 +258,7 @@ data:
typed_config:
"@type": type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext
sni: {{ $upstreamHostname }}
{{- include "authProxy.envoyUpstreamTlsContextExtras" . | nindent 14 }}
{{- end }}
{{- end }}
{{- if $needsProxyCluster }}
Expand Down Expand Up @@ -293,6 +295,7 @@ data:
typed_config:
"@type": type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext
sni: {{ $jwksHostname }}
{{- include "authProxy.envoyUpstreamTlsContextExtras" . | nindent 14 }}
{{- end }}
{{- else if $jwksUri }}
- name: jwks_service
Expand All @@ -314,6 +317,7 @@ data:
typed_config:
"@type": type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext
sni: {{ $jwksHostname }}
{{- include "authProxy.envoyUpstreamTlsContextExtras" . | nindent 14 }}
{{- end }}
{{- end }}
{{- if .Values.authProxy.extAuthz.enabled }}
Expand Down
Loading
Loading