Skip to content
Merged
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
40 changes: 26 additions & 14 deletions charts/currents/templates/_common.tpl
Original file line number Diff line number Diff line change
Expand Up @@ -192,39 +192,51 @@ ClickHouse data is loaded from an external export (scripts/org-import).
value: {{ include "currents.url" (dict "context" . "input" .Values.currents.domains.recordApiHost) }}
{{- end -}}

{{- define "currents.emailSMTPEnv" -}}
{{- define "currents.emailEnv" -}}
{{- $email := .Values.currents.email -}}
{{- $transporter := $email.transporter | default "smtp" -}}
{{- if not (has $transporter (list "smtp" "ses")) -}}
{{- fail (printf "currents.email.transporter must be either \"smtp\" or \"ses\", got %q" $transporter) -}}
{{- end -}}
- name: EMAIL_TRANSPORTER
value: smtp
{{- if .Values.currents.email.smtp.host }}
value: {{ $transporter | quote }}
{{- if eq $transporter "ses" }}
{{- /* Credentials are resolved by the AWS SDK from the pod itself (IRSA on EKS),
so there is no secret to wire up here — only the region. */}}
- name: SES_REGION
value: {{ required "currents.email.ses.region is required when currents.email.transporter is \"ses\"" $email.ses.region | quote }}
{{- else }}
{{- if $email.smtp.host }}
- name: SMTP_HOST
value: {{ .Values.currents.email.smtp.host }}
value: {{ $email.smtp.host }}
{{- end }}
{{- if .Values.currents.email.smtp.port }}
{{- if $email.smtp.port }}
- name: SMTP_PORT
value: {{ .Values.currents.email.smtp.port | toString | quote }}
value: {{ $email.smtp.port | toString | quote }}
{{- end }}
{{- if .Values.currents.email.smtp.tls }}
{{- if $email.smtp.tls }}
- name: SMTP_SECURE
value: "true"
{{- end }}
{{- if .Values.currents.email.smtp.secretName }}
{{- if $email.smtp.secretName }}
- name: SMTP_USER
valueFrom:
secretKeyRef:
name: {{ .Values.currents.email.smtp.secretName }}
key: {{ .Values.currents.email.smtp.secretUserKey }}
name: {{ $email.smtp.secretName }}
key: {{ $email.smtp.secretUserKey }}
{{- end }}
{{- if .Values.currents.email.smtp.secretName }}
{{- if $email.smtp.secretName }}
- name: SMTP_PASS
valueFrom:
secretKeyRef:
name: {{ .Values.currents.email.smtp.secretName }}
key: {{ .Values.currents.email.smtp.secretPasswordKey }}
name: {{ $email.smtp.secretName }}
key: {{ $email.smtp.secretPasswordKey }}
{{- end }}
Comment thread
twk3 marked this conversation as resolved.
{{- end }}
- name: AUTOMATED_REPORTS_CURRENTS_DASHBOARD_HOSTNAME
value: {{ include "currents.url" (dict "context" . "input" .Values.currents.domains.appHost) }}
- name: AUTOMATED_REPORTS_EMAIL_FROM
value: {{ tpl .Values.currents.email.smtp.from . }}
value: {{ tpl (default $email.smtp.from $email.from) . }}
{{- if .Values.currents.email.inviteFrom }}
- name: INVITE_EMAIL_FROM
value: {{ tpl .Values.currents.email.inviteFrom . }}
Expand Down
2 changes: 1 addition & 1 deletion charts/currents/templates/server/deployment.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ spec:
{{- end }}
{{- include "currents.connectionConfigEnv" . | nindent 12 }}
{{- include "currents.URLConfigEnv" . | nindent 12 }}
{{- include "currents.emailSMTPEnv" . | nindent 12 }}
{{- include "currents.emailEnv" . | nindent 12 }}
{{- include "currents.samlSSOEnv" . | nindent 12 }}
{{- if .Values.currents.betterAuth.secretName }}
- name: BETTER_AUTH_SECRET
Expand Down
2 changes: 1 addition & 1 deletion charts/currents/templates/writer/deployment.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ spec:
value: "onprem"
{{- include "currents.connectionConfigEnv" . | nindent 12 }}
{{- include "currents.URLConfigEnv" . | nindent 12 }}
{{- include "currents.emailSMTPEnv" . | nindent 12 }}
{{- include "currents.emailEnv" . | nindent 12 }}
{{- with (concat .Values.global.env .Values.writer.env) }}
{{- toYaml . | nindent 12 }}
{{- end }}
Expand Down
18 changes: 16 additions & 2 deletions charts/currents/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,19 @@ currents:
# -- The image tag to use for the Currents images
imageTag: 2026-07-26-001
email:
# -- Which transport to send outgoing email through: `smtp` or `ses`.
# With `ses` the SMTP settings are ignored and no SMTP credentials are
# needed — the AWS SDK resolves credentials from the pod itself, so grant
# the Currents service account permission to send. See
# [Using IAM Roles for Sending Email with SES](./eks/iam.md#using-iam-roles-for-sending-email-with-ses).
transporter: smtp
# -- (tpl/string) The email address to send from. Defaults to
# `currents.email.smtp.from` when unset, which is retained for compatibility.
from: ""
ses:
# -- The AWS region to send through. Required when `transporter` is `ses`,
# and the `from` address must be a verified identity in that region.
region: ""
smtp:
# -- The SMTP server port to use
port: 587
Expand All @@ -40,10 +53,11 @@ currents:
secretPasswordKey: password
# -- (tpl/string) The email address to send from
from: "Currents Report <report@{{ .Values.currents.domains.appHost }}>"
# -- the SMTP server to use
# -- the SMTP server to use. Required unless `transporter` is `ses`.
# @section -- Required
host: ""
# -- K8s secret to use for the SMTP username/password
# -- K8s secret to use for the SMTP username/password. Required unless
# `transporter` is `ses`.
# @section -- Required
secretName: ""
# -- Whether the SMTP server uses TLS
Expand Down
7 changes: 5 additions & 2 deletions docs/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@ The following table lists the configurable parameters of the `currents` chart an
| currents.domains.appHost | string | `"currents-app.localhost"` | The host for the app |
| currents.domains.recordApiHost | string | `"currents-record.localhost"` | The host for the recording endpoint that the test reporters communicate with |
| currents.rootUser.password.secretName | string | `""` | The K8s secret containing the root user password. The password is used during initial setup only. |
| currents.email.smtp.host | string | `""` | the SMTP server to use |
| currents.email.smtp.secretName | string | `""` | K8s secret to use for the SMTP username/password |
| currents.email.smtp.host | string | `""` | the SMTP server to use. Required unless `transporter` is `ses`. |
| currents.email.smtp.secretName | string | `""` | K8s secret to use for the SMTP username/password. Required unless `transporter` is `ses`. |
| currents.betterAuth.secretName | string | `""` | The K8s secret containing the Better Auth secret |
| currents.apiInternalToken.secretName | string | `""` | The K8s secret to use for the internal API token |
| currents.clickhouse.user.secretName | string | `""` | The k8s secret to use for the ClickHouse password |
Expand Down Expand Up @@ -89,6 +89,9 @@ The following table lists the configurable parameters of the `currents` chart an
|-----|------|---------|-------------|
| currents.rootUser.email | string | `"admin@{{ .Values.currents.domains.appHost }}"` | The email address of the root user |
| currents.imageTag | string | `"2026-07-26-001"` | The image tag to use for the Currents images |
| currents.email.transporter | string | `"smtp"` | Which transport to send outgoing email through: `smtp` or `ses`. With `ses` the SMTP settings are ignored and no SMTP credentials are needed — the AWS SDK resolves credentials from the pod itself, so grant the Currents service account permission to send. See [Using IAM Roles for Sending Email with SES](./eks/iam.md#using-iam-roles-for-sending-email-with-ses). |
| currents.email.from | tpl/string | `""` | The email address to send from. Defaults to `currents.email.smtp.from` when unset, which is retained for compatibility. |
| currents.email.ses.region | string | `""` | The AWS region to send through. Required when `transporter` is `ses`, and the `from` address must be a verified identity in that region. |
| currents.email.smtp.port | int | `587` | The SMTP server port to use |
| currents.email.smtp.from | tpl/string | `"Currents Report <report@{{ .Values.currents.domains.appHost }}>"` | The email address to send from |
| currents.email.smtp.tls | bool | `false` | Whether the SMTP server uses TLS |
Expand Down
113 changes: 113 additions & 0 deletions docs/eks/iam.md
Original file line number Diff line number Diff line change
Expand Up @@ -161,3 +161,116 @@ After the pods restart, describe one of the Currents pods to confirm the `AWS_WE
```shell
kubectl describe pod <pod-name>
```

## Using IAM Roles for Sending Email with SES

By default Currents sends outgoing email (invitations, automated reports) over SMTP,
which requires storing SMTP credentials in a Kubernetes secret. If you'd rather not
hold those credentials in the cluster, Currents can send through
[Amazon SES](https://docs.aws.amazon.com/ses/latest/dg/Welcome.html) instead and
authenticate with an IAM role — no email secret required.

Prerequisites:

- An EKS cluster with an [IAM OIDC Provider enabled](https://docs.aws.amazon.com/eks/latest/userguide/enable-iam-roles-for-service-accounts.html) (same requirement as IRSA for object storage, above).
- A **verified identity** in SES — the domain or address you send from — in the region you plan to use.
- If your SES account is still in the [sandbox](https://docs.aws.amazon.com/ses/latest/dg/request-production-access.html), every recipient must also be verified. Request production access before relying on this for real reports.

**1. Create the IAM Policy**

Replace `us-east-1`, `111122223333`, and the identity name with your own. Scoping to
the identity is optional but recommended; `"Resource": "*"` also works.

```json
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"ses:SendEmail",
"ses:SendRawEmail"
],
"Resource": "arn:aws:ses:us-east-1:111122223333:identity/example.com"
}
]
}
```

**2. Attach the Policy to the Currents Service Account**

Currents uses a single service account for all of its pods, so if you already set up
IRSA for object storage you can attach this policy to that same role rather than
creating a second one:

```shell
aws iam attach-role-policy --role-name currents-irsa --policy-arn <your-policy-arn>
```

If you have not set up IRSA yet, follow steps 2–4 of
[Using IAM Roles for Accessing Object Storage](#using-iam-roles-for-accessing-object-storage)
to create and associate the role, attaching this policy instead of (or in addition
to) the S3 one.

**3. Switch the Chart to the SES Transport**

In your `currents-helm-config.yaml`:

```yaml
currents:
email:
transporter: ses
# Must be a verified SES identity in the region below
from: "Currents Report <report@example.com>"
ses:
region: us-east-1
```

With `transporter: ses` the SMTP settings are ignored — `currents.email.smtp.host`
and `currents.email.smtp.secretName` are no longer required, and you do not need to
create the `currents-email-smtp` secret. The other email settings
(`inviteFrom`, `inviteBcc`, `reportsBcc`, `inviteExpirationDays`, `linksBaseUrl`)
apply to both transports.

Apply it:

```shell
helm upgrade --install currents currents --repo https://currents-dev.github.io/helm-charts/ -f currents-helm-config.yaml
```

**4. Confirm the Pods Picked Up the Change**

Email is sent by the `server` pod (invitations) and the `writer` pod (automated
reports). Confirm both see the SES settings and the IRSA credentials:

```shell
kubectl exec deploy/currents-writer -- env | grep -E 'EMAIL_TRANSPORTER|SES_REGION|AWS_ROLE_ARN|AWS_WEB_IDENTITY_TOKEN_FILE'
```

Expected output:

```
EMAIL_TRANSPORTER=ses
SES_REGION=us-east-1
AWS_ROLE_ARN=arn:aws:iam::111122223333:role/currents-irsa
AWS_WEB_IDENTITY_TOKEN_FILE=/var/run/secrets/eks.amazonaws.com/serviceaccount/token
```
Comment thread
twk3 marked this conversation as resolved.

If `EMAIL_TRANSPORTER` is still `smtp`, the values did not reach the pod — re-run the
upgrade with your full values file rather than `--reuse-values`, and confirm the
deployment rolled.
Comment thread
twk3 marked this conversation as resolved.

**5. Verify Delivery**

Trigger an email (for example, invite a user) and check the logs:

```shell
kubectl logs deploy/currents-writer --tail=100 | grep -i email
```

> **Note:** a successful send logs `Message sent`. Delivery failures are logged as
> `Error sending email with SES` — but they are **followed by a misleading
> `Email sent` line**, so treat the presence of an `Error sending email with SES`
> entry as a failure regardless of what comes after it. Common causes are an
> unverified `from` identity, a region mismatch, or SES sandbox restrictions on the
> recipient.
Loading