Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
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"
70 changes: 69 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,71 @@ 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 is needed when HTTPS peers present certificates signed by a private or internal CA that Envoy does not trust by default. The bundle is applied to:
- The main upstream cluster defined by `authProxy.upstream` (when the scheme is `https://`)
- 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.
- To rotate the CA bundle, publish a new Secret (e.g. `corporate-ca-bundle-v2`) and update `customCa.secretName`. Helm will update the pod template and trigger a rolling restart. If you update a Secret in-place without changing its name, restart the pod manually — Envoy does not watch for Secret changes at runtime.
- Required when using `mtls` — see [mTLS support](#mtls-support) below.

## 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
```

## Values

| Key | Type | Default | Description |
Expand Down Expand Up @@ -173,6 +238,9 @@ 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 | `{"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.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
65 changes: 65 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,71 @@ 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 is needed when HTTPS peers present certificates signed by a private or internal CA that Envoy does not trust by default. The bundle is applied to:
- The main upstream cluster defined by `authProxy.upstream` (when the scheme is `https://`)
- 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.
- To rotate the CA bundle, publish a new Secret (e.g. `corporate-ca-bundle-v2`) and update `customCa.secretName`. Helm will update the pod template and trigger a rolling restart. If you update a Secret in-place without changing its name, restart the pod manually — Envoy does not watch for Secret changes at runtime.
- Required when using `mtls` — see [mTLS support](#mtls-support) below.

## 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
```

## Values

| Key | Type | Default | Description |
Expand Down
100 changes: 100 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,106 @@ 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.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 -}}

{{/*
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.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 -}}

{{/*
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
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
{{- if .Values.authProxy.enabled }}
{{- $volumes := .Values.authProxy.deployment.volumes -}}
{{- $volumeMounts := .Values.authProxy.deployment.volumeMounts -}}
{{- $customCaEnabled := include "authProxy.customCaEnabled" . -}}
{{- $mtlsEnabled := include "authProxy.mtlsEnabled" . -}}
{{- if .Values.authProxy.rollout.enabled }}
apiVersion: argoproj.io/v1alpha1
kind: Rollout
Expand Down Expand Up @@ -98,6 +100,16 @@ spec:
{{- with $volumeMounts }}
{{- toYaml . | nindent 12 }}
{{- end }}
{{- if $customCaEnabled }}
- mountPath: {{ include "authProxy.customCaMountDir" . }}
name: custom-ca-cert
readOnly: true
{{- end }}
{{- if $mtlsEnabled }}
- mountPath: {{ include "authProxy.mtlsMountDir" . }}
name: client-cert
readOnly: true
{{- end }}
- mountPath: /etc/envoy/envoy.yaml
name: envoy-conf
subPath: envoy.yaml
Expand Down Expand Up @@ -128,6 +140,24 @@ spec:
{{- with $volumes }}
{{- toYaml . | nindent 8 }}
{{- end }}
{{- if $customCaEnabled }}
- name: custom-ca-cert
secret:
secretName: {{ include "authProxy.customCaSecretName" . }}
items:
- key: {{ include "authProxy.customCaSecretKey" . }}
path: {{ include "authProxy.customCaFileName" . }}
{{- end }}
{{- if $mtlsEnabled }}
- name: client-cert
secret:
secretName: {{ include "authProxy.mtlsSecretName" . }}
items:
- key: {{ include "authProxy.mtlsCertKey" . }}
path: {{ include "authProxy.mtlsCertFileName" . }}
- key: {{ include "authProxy.mtlsKeyKey" . }}
path: {{ include "authProxy.mtlsKeyFileName" . }}
{{- end }}
- name: envoy-conf
configMap:
name: {{ include "authProxy.fullname" . }}-{{ .Values.authProxy.name }}
Expand Down
37 changes: 37 additions & 0 deletions charts/langsmith-auth-proxy/templates/validate.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
{{- /*
Validate custom CA configuration before rendering manifests.
This check runs regardless of authProxy.enabled so that partial
customCa config is caught early, not on first enable.
*/ -}}
{{- $secretName := include "authProxy.customCaSecretName" . -}}
{{- $secretKey := include "authProxy.customCaSecretKey" . -}}
{{- $nameSet := ne $secretName "" -}}
{{- $keySet := ne $secretKey "" -}}
{{- if ne $nameSet $keySet -}}
{{- fail "customCa.secretName and customCa.secretKey must either both be set or both be empty." -}}
{{- end -}}

{{- /*
Validate client certificate configuration.
All three of secretName, certKey, and keyKey must be set together or all empty.
*/ -}}
{{- $mtlsSecretName := include "authProxy.mtlsSecretName" . -}}
{{- $mtlsCertKey := include "authProxy.mtlsCertKey" . -}}
{{- $mtlsKeyKey := include "authProxy.mtlsKeyKey" . -}}
{{- $mtlsNameSet := ne $mtlsSecretName "" -}}
{{- $mtlsCertSet := ne $mtlsCertKey "" -}}
{{- $mtlsKeySet := ne $mtlsKeyKey "" -}}
{{- if not (or (and $mtlsNameSet $mtlsCertSet $mtlsKeySet) (and (not $mtlsNameSet) (not $mtlsCertSet) (not $mtlsKeySet))) -}}
{{- fail "mtls.secretName, mtls.certKey, and mtls.keyKey must all be set together or all be empty." -}}
{{- end -}}

{{- /*
Require customCa when mTLS is enabled. Without a trusted CA bundle Envoy
sends client certificates but does NOT verify the upstream server's identity,
so the connection is not genuinely mutually authenticated.
*/ -}}
{{- $mtlsEnabled := and $mtlsNameSet $mtlsCertSet $mtlsKeySet -}}
{{- $customCaEnabled := and $nameSet $keySet -}}
{{- if and $mtlsEnabled (not $customCaEnabled) -}}
{{- fail "mtls requires customCa. Without customCa Envoy sends client certificates but does not verify the upstream server's identity, which is not mutual TLS. Set customCa.secretName and customCa.secretKey to provide a trusted CA bundle." -}}
{{- end -}}
Loading
Loading