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
8 changes: 8 additions & 0 deletions contrib/aws/lambdaworker/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,14 @@ or Security.

## [Unreleased]

### Added

- Client credentials can be loaded from AWS Secrets Manager. `RunWorker` now
loads client options with the `go.temporal.io/sdk/contrib/aws/secretsmanager`
envconfig extension, so the API key and TLS cert/key/CA can be configured as
secret IDs via `TEMPORAL_EXT_AWS_SECRETS_MANAGER_*` env vars or
`profile.<profile>.ext.aws_secrets_manager` in the config file.

### Breaking Changes

- Raised the minimum supported Go version from 1.25.4 to 1.26.0.
22 changes: 22 additions & 0 deletions contrib/aws/lambdaworker/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,28 @@ Set the public fields on `Options` (e.g. `ClientOptions`, `WorkerOptions`) in
the configure callback to override any defaults programmatically. User overrides
are applied last, so they always win.

### Credentials from AWS Secrets Manager

To keep credentials out of env vars and the Lambda package, store them in AWS
Secrets Manager and configure only the secret IDs (names or ARNs). The
[`secretsmanager`](../secretsmanager/README.md) envconfig extension is always
enabled, so no code changes are needed:

| Env var | TOML (under `profile.<profile>.ext.aws_secrets_manager`) |
|---|---|
| `TEMPORAL_EXT_AWS_SECRETS_MANAGER_API_KEY_ID` | `api_key_id` |
| `TEMPORAL_EXT_AWS_SECRETS_MANAGER_TLS_CLIENT_CERT_ID` | `tls.client_cert_id` |
| `TEMPORAL_EXT_AWS_SECRETS_MANAGER_TLS_CLIENT_KEY_ID` | `tls.client_key_id` |
| `TEMPORAL_EXT_AWS_SECRETS_MANAGER_TLS_SERVER_CA_CERT_ID` | `tls.server_ca_cert_id` |

Secrets are fetched through the
[AWS Parameters and Secrets Lambda Extension](https://docs.aws.amazon.com/secretsmanager/latest/userguide/retrieving-secrets_lambda.html)
if the function has it, and otherwise directly with the AWS SDK. Either way they
are cached across warm invocations. The Lambda execution role needs
`secretsmanager:GetSecretValue` on the configured secrets. See the
[`secretsmanager` README](../secretsmanager/README.md) for the full set of
options, validation rules, caching behavior, and observability metrics.

## Lambda-tuned worker defaults

The package applies conservative concurrency limits suited to Lambda's resource constraints, see the
Expand Down
9 changes: 7 additions & 2 deletions contrib/aws/lambdaworker/doc.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,13 @@
// Client connection options (address, namespace, TLS, API key) are loaded automatically from
// a TOML config file and environment variables via
// [go.temporal.io/sdk/contrib/envconfig.LoadClientOptions]. See more at
// https://docs.temporal.io/references/client-environment-configuration. In production, we recommend
// using a secret manager of some sort and pulling your credentials from there.
// https://docs.temporal.io/references/client-environment-configuration.
//
// In production, we recommend keeping credentials in AWS Secrets Manager rather than in env vars
// or files in the Lambda package. The [go.temporal.io/sdk/contrib/aws/secretsmanager] extension is
// always enabled, so the API key and TLS cert/key/CA can instead be configured as Secrets Manager
// secret IDs, for example with the TEMPORAL_EXT_AWS_SECRETS_MANAGER_API_KEY_ID env var. The Lambda
// execution role needs secretsmanager:GetSecretValue on those secrets. See that package for details.
//
// Because AWS Lambda does not have a standard config file directory, it resolves the config file
// path in the following order:
Expand Down
19 changes: 18 additions & 1 deletion contrib/aws/lambdaworker/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,27 @@ require (
github.com/nexus-rpc/sdk-go v0.7.0
github.com/stretchr/testify v1.10.0
go.temporal.io/sdk v1.39.0
go.temporal.io/sdk/contrib/envconfig v1.0.0
go.temporal.io/sdk/contrib/aws/secretsmanager v0.0.0-00010101000000-000000000000
go.temporal.io/sdk/contrib/envconfig v1.0.2
)

require (
github.com/BurntSushi/toml v1.4.0 // indirect
github.com/aws/aws-sdk-go-v2 v1.47.2 // indirect
github.com/aws/aws-sdk-go-v2/config v1.33.8 // indirect
github.com/aws/aws-sdk-go-v2/credentials v1.20.8 // indirect
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.20.2 // indirect
github.com/aws/aws-sdk-go-v2/internal/configsources v1.5.5 // indirect
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.8.5 // indirect
github.com/aws/aws-sdk-go-v2/internal/v4a v1.5.5 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.20 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.14.5 // indirect
github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.50.3 // indirect
github.com/aws/aws-sdk-go-v2/service/signin v1.10.3 // indirect
github.com/aws/aws-sdk-go-v2/service/sso v1.38.3 // indirect
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.43.3 // indirect
github.com/aws/aws-sdk-go-v2/service/sts v1.51.3 // indirect
github.com/aws/smithy-go v1.28.4 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/facebookgo/clock v0.0.0-20150410010913-600d898af40a // indirect
github.com/gogo/protobuf v1.3.2 // indirect
Expand All @@ -38,5 +54,6 @@ require (

replace (
go.temporal.io/sdk => ../../../
go.temporal.io/sdk/contrib/aws/secretsmanager => ../secretsmanager
go.temporal.io/sdk/contrib/envconfig => ../../envconfig
)
30 changes: 30 additions & 0 deletions contrib/aws/lambdaworker/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,36 @@ github.com/BurntSushi/toml v1.4.0 h1:kuoIxZQy2WRRk1pttg9asf+WVv6tWQuBNVmK8+nqPr0
github.com/BurntSushi/toml v1.4.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
github.com/aws/aws-lambda-go v1.47.0 h1:0H8s0vumYx/YKs4sE7YM0ktwL2eWse+kfopsRI1sXVI=
github.com/aws/aws-lambda-go v1.47.0/go.mod h1:dpMpZgvWx5vuQJfBt0zqBha60q7Dd7RfgJv23DymV8A=
github.com/aws/aws-sdk-go-v2 v1.47.2 h1:4vrR4S7MUdvPiv/fxlYI/FaZCgwKuir2LpXxwCUtwWY=
github.com/aws/aws-sdk-go-v2 v1.47.2/go.mod h1:LYKdgB/k1/omdrfoQuyt6T10CvF1yw27ByBjd+ku1jk=
github.com/aws/aws-sdk-go-v2/config v1.33.8 h1:I9S23gMufs97T0CE0ZUzvdl/i2TDnxOJzgxw18d1RBo=
github.com/aws/aws-sdk-go-v2/config v1.33.8/go.mod h1:/WlDmOxSaE2dhWUmEcFF3QnckmOPH49GxsQI0CzRAtg=
github.com/aws/aws-sdk-go-v2/credentials v1.20.8 h1:ibO101be+3jJOMBCJphBXzXm9UzIq+zxr8plM2V3FHw=
github.com/aws/aws-sdk-go-v2/credentials v1.20.8/go.mod h1:gAFU+h+59h6frPO/KUG7sgcToVf1ti8i/VMImKiFpcM=
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.20.2 h1:b4FhKL3xhR6BVxUTU+h1K8gK2XYhb+pU/sRiwJwLuCs=
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.20.2/go.mod h1:FprVEOVKR2fKunYpdjOCpt1/n76MnL72PcTKylXlYRs=
github.com/aws/aws-sdk-go-v2/internal/configsources v1.5.5 h1:JwchTNw8oqkFWMYrmFmdoS4sYgE6JJcnO+ElvME+uH4=
github.com/aws/aws-sdk-go-v2/internal/configsources v1.5.5/go.mod h1:8pc+vcibD0We5t3f/LqHxjBPkXXfgcjYQN3upxqz/Nw=
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.8.5 h1:CNgkOhhgJ1anuboBDmjRLCpVlBKFV9cEvT16d4QNl/w=
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.8.5/go.mod h1:SS3HIANtO8edxa4QkX+8SvbMxRm+k78QoE6EIRI81tU=
github.com/aws/aws-sdk-go-v2/internal/v4a v1.5.5 h1:rkxirozlOuMs3QuaGsDV3ll5suBdeeH6iSLFPyA5zNk=
github.com/aws/aws-sdk-go-v2/internal/v4a v1.5.5/go.mod h1:OQjoYoCdNx6tLyZNEuD0HRfHQVStBIfQyigewExdEVU=
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.20 h1:XBYeuBbMY5LRAKOdZgzUQO1BVj3B07uYAROSapq/IWg=
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.20/go.mod h1:XL6ZfC3ePnooN2u2LT4RNhpr/xs5TiboydyUbgr8Dmg=
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.14.5 h1:Q2MWL+9cTKHHiXWcsPobHiOD+wx8tTc8sWZaFvFSk9Q=
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.14.5/go.mod h1:U8tTityyPbgc90QwGOha8XsrQpLIjpGUS7+Q+4prM+Q=
github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.50.3 h1:oPQLY2D2sJowKzZl1qHMH9Uo8C/rF3xCqRh5ORYg6fA=
github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.50.3/go.mod h1:6ZMEilrwLy1zFaG2zndlY7cXtOL8Y6JsOGykkcSOALE=
github.com/aws/aws-sdk-go-v2/service/signin v1.10.3 h1:lRyGlN6mXianMWMQdvTFCnJE5DkmD54N9PC3m+U/GgA=
github.com/aws/aws-sdk-go-v2/service/signin v1.10.3/go.mod h1:9Y78vT13MZ36q2uxrSKqHJnk/6n9sbSgGp3ZOZS1s4w=
github.com/aws/aws-sdk-go-v2/service/sso v1.38.3 h1:NYlE1/vPhIhdPTLUnmvz/PSXS3klsToqQY6KVtsynhs=
github.com/aws/aws-sdk-go-v2/service/sso v1.38.3/go.mod h1:nVwtZaAwWFpiXGfNE0GPxbpjhWNVGJbJEZDOfrDCEm4=
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.43.3 h1:Glo50xPemPwHaioR66Rl5BTEq62Ohet0dyxzt6L0H1M=
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.43.3/go.mod h1:LaCSuxpt5Z2a8oThoMtWWB71u2MOmylEqHaQdp3J4aw=
github.com/aws/aws-sdk-go-v2/service/sts v1.51.3 h1:L5w+LXVzMaacz3iotdx2fWtU/O+TIrplsK2JVMNbXeA=
github.com/aws/aws-sdk-go-v2/service/sts v1.51.3/go.mod h1:8E586vK6OTj+qVzsTygGzw1GlZnOgNjul8toJEdtX5s=
github.com/aws/smithy-go v1.28.4 h1:vzBmFpcRUtotqGPfmnro2W2Pgg3mk103n9sZhNJY9YU=
github.com/aws/smithy-go v1.28.4/go.mod h1:YE2RhdIuDbA5E5bTdciG9KrW3+TiEONeUWCqxX9i1Fc=
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
Expand Down
2 changes: 2 additions & 0 deletions contrib/aws/lambdaworker/runworker.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"github.com/aws/aws-lambda-go/lambdacontext"

"go.temporal.io/sdk/client"
"go.temporal.io/sdk/contrib/aws/secretsmanager"
"go.temporal.io/sdk/contrib/envconfig"
"go.temporal.io/sdk/worker"
)
Expand All @@ -36,6 +37,7 @@ func defaultDeps() workerDeps {
loadConfig: func() (client.Options, error) {
return envconfig.LoadClientOptions(envconfig.LoadClientOptionsRequest{
ConfigFilePath: lambdaDefaultConfigFilePath(os.Getenv),
Extensions: []envconfig.ClientConfigExtension{secretsmanager.Extension{}},
})
},
getenv: os.Getenv,
Expand Down
22 changes: 22 additions & 0 deletions contrib/aws/lambdaworker/runworker_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package lambdaworker
import (
"context"
"errors"
"path/filepath"
"testing"
"testing/synctest"
"time"
Expand Down Expand Up @@ -637,3 +638,24 @@ func TestRunWorkerInternal_PerInvocationLifecycle(t *testing.T) {
w.AssertExpectations(t)
c.AssertExpectations(t)
}

func TestDefaultLoadConfig_SecretsManagerExtension(t *testing.T) {
t.Setenv("TEMPORAL_CONFIG_FILE", filepath.Join(t.TempDir(), "temporal.toml"))
t.Setenv("TEMPORAL_API_KEY", "")
t.Setenv("TEMPORAL_EXT_AWS_SECRETS_MANAGER_API_KEY_ID", "")

opts, err := defaultDeps().loadConfig()
require.NoError(t, err)
assert.Empty(t, opts.Plugins)

// Secrets aren't fetched until a client is created, so loading needs no AWS access
t.Setenv("TEMPORAL_EXT_AWS_SECRETS_MANAGER_API_KEY_ID", "my-api-key-secret")
opts, err = defaultDeps().loadConfig()
require.NoError(t, err)
require.Len(t, opts.Plugins, 1)
assert.Equal(t, "go.temporal.io/sdk/contrib/aws/secretsmanager", opts.Plugins[0].Name())

t.Setenv("TEMPORAL_API_KEY", "my-api-key")
_, err = defaultDeps().loadConfig()
require.ErrorContains(t, err, "API key secret ID cannot be set with an API key")
}
Loading