Skip to content

Commit 9c4e3c2

Browse files
committed
[AWSX-2144] feat(go-forwarder): add basic api key and config logic
1 parent 20ab64d commit 9c4e3c2

5 files changed

Lines changed: 311 additions & 4 deletions

File tree

aws/logs_monitoring_go/cmd/forwarder/main.go

Lines changed: 37 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,15 +9,48 @@ import (
99
"context"
1010
"encoding/json"
1111
"log"
12+
"log/slog"
13+
"os"
14+
"strings"
15+
16+
"github.com/DataDog/datadog-serverless-functions/aws/logs_monitoring_go/internal/config"
1217

1318
"github.com/aws/aws-lambda-go/lambda"
1419
)
1520

16-
func handleRequest(ctx context.Context, event json.RawMessage) error {
17-
log.Printf("Received event: %s", string(event))
18-
return nil
21+
func initLogger(level string) {
22+
var slogLevel slog.Level
23+
switch strings.ToUpper(level) {
24+
case "DEBUG":
25+
slogLevel = slog.LevelDebug
26+
case "INFO":
27+
slogLevel = slog.LevelInfo
28+
case "WARNING", "WARN":
29+
slogLevel = slog.LevelWarn
30+
case "ERROR":
31+
slogLevel = slog.LevelError
32+
default:
33+
slogLevel = slog.LevelInfo
34+
}
35+
slog.SetDefault(slog.New(slog.NewTextHandler(os.Stderr, &slog.HandlerOptions{
36+
Level: slogLevel,
37+
})))
38+
}
39+
40+
func handleRequest(cfg *config.Config) func(context.Context, json.RawMessage) error {
41+
return func(ctx context.Context, event json.RawMessage) error {
42+
slog.Info("received event", "event", string(event))
43+
return nil
44+
}
1945
}
2046

2147
func main() {
22-
lambda.Start(handleRequest)
48+
ctx := context.Background()
49+
cfg, err := config.Load(ctx)
50+
if err != nil {
51+
log.Fatalf("config: %v", err)
52+
}
53+
// TODO: exit if forwading disabled ?
54+
initLogger(cfg.LogLevel)
55+
lambda.Start(handleRequest(cfg))
2356
}

aws/logs_monitoring_go/go.mod

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,23 @@ module github.com/DataDog/datadog-serverless-functions/aws/logs_monitoring_go
33
go 1.26
44

55
require github.com/aws/aws-lambda-go v1.53.0
6+
7+
require (
8+
github.com/aws/aws-sdk-go-v2 v1.41.4 // indirect
9+
github.com/aws/aws-sdk-go-v2/config v1.32.12 // indirect
10+
github.com/aws/aws-sdk-go-v2/credentials v1.19.12 // indirect
11+
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.20 // indirect
12+
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.20 // indirect
13+
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.20 // indirect
14+
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.6 // indirect
15+
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.7 // indirect
16+
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.20 // indirect
17+
github.com/aws/aws-sdk-go-v2/service/kms v1.50.3 // indirect
18+
github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.41.4 // indirect
19+
github.com/aws/aws-sdk-go-v2/service/signin v1.0.8 // indirect
20+
github.com/aws/aws-sdk-go-v2/service/ssm v1.68.3 // indirect
21+
github.com/aws/aws-sdk-go-v2/service/sso v1.30.13 // indirect
22+
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.17 // indirect
23+
github.com/aws/aws-sdk-go-v2/service/sts v1.41.9 // indirect
24+
github.com/aws/smithy-go v1.24.2 // indirect
25+
)

aws/logs_monitoring_go/go.sum

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,39 @@
11
github.com/aws/aws-lambda-go v1.53.0 h1:uAMv6W/vCP/L494BAUSxe+8KVBIPK+SGPyapFt3FuMk=
22
github.com/aws/aws-lambda-go v1.53.0/go.mod h1:dpMpZgvWx5vuQJfBt0zqBha60q7Dd7RfgJv23DymV8A=
3+
github.com/aws/aws-sdk-go-v2 v1.41.4 h1:10f50G7WyU02T56ox1wWXq+zTX9I1zxG46HYuG1hH/k=
4+
github.com/aws/aws-sdk-go-v2 v1.41.4/go.mod h1:mwsPRE8ceUUpiTgF7QmQIJ7lgsKUPQOUl3o72QBrE1o=
5+
github.com/aws/aws-sdk-go-v2/config v1.32.12 h1:O3csC7HUGn2895eNrLytOJQdoL2xyJy0iYXhoZ1OmP0=
6+
github.com/aws/aws-sdk-go-v2/config v1.32.12/go.mod h1:96zTvoOFR4FURjI+/5wY1vc1ABceROO4lWgWJuxgy0g=
7+
github.com/aws/aws-sdk-go-v2/credentials v1.19.12 h1:oqtA6v+y5fZg//tcTWahyN9PEn5eDU/Wpvc2+kJ4aY8=
8+
github.com/aws/aws-sdk-go-v2/credentials v1.19.12/go.mod h1:U3R1RtSHx6NB0DvEQFGyf/0sbrpJrluENHdPy1j/3TE=
9+
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.20 h1:zOgq3uezl5nznfoK3ODuqbhVg1JzAGDUhXOsU0IDCAo=
10+
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.20/go.mod h1:z/MVwUARehy6GAg/yQ1GO2IMl0k++cu1ohP9zo887wE=
11+
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.20 h1:CNXO7mvgThFGqOFgbNAP2nol2qAWBOGfqR/7tQlvLmc=
12+
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.20/go.mod h1:oydPDJKcfMhgfcgBUZaG+toBbwy8yPWubJXBVERtI4o=
13+
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.20 h1:tN6W/hg+pkM+tf9XDkWUbDEjGLb+raoBMFsTodcoYKw=
14+
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.20/go.mod h1:YJ898MhD067hSHA6xYCx5ts/jEd8BSOLtQDL3iZsvbc=
15+
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.6 h1:qYQ4pzQ2Oz6WpQ8T3HvGHnZydA72MnLuFK9tJwmrbHw=
16+
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.6/go.mod h1:O3h0IK87yXci+kg6flUKzJnWeziQUKciKrLjcatSNcY=
17+
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.7 h1:5EniKhLZe4xzL7a+fU3C2tfUN4nWIqlLesfrjkuPFTY=
18+
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.7/go.mod h1:x0nZssQ3qZSnIcePWLvcoFisRXJzcTVvYpAAdYX8+GI=
19+
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.20 h1:2HvVAIq+YqgGotK6EkMf+KIEqTISmTYh5zLpYyeTo1Y=
20+
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.20/go.mod h1:V4X406Y666khGa8ghKmphma/7C0DAtEQYhkq9z4vpbk=
21+
github.com/aws/aws-sdk-go-v2/service/kms v1.50.3 h1:s/zDSG/a/Su9aX+v0Ld9cimUCdkr5FWPmBV8owaEbZY=
22+
github.com/aws/aws-sdk-go-v2/service/kms v1.50.3/go.mod h1:/iSgiUor15ZuxFGQSTf3lA2FmKxFsQoc2tADOarQBSw=
23+
github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.41.4 h1:9aZbO86sraeCIHHCpZhxwN9tnVy9POkSKzi4/TpT54A=
24+
github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.41.4/go.mod h1:cxiXDhEzIq7Xx1BtmC4lGBK3SwAZ79+EUWiKawYHo14=
25+
github.com/aws/aws-sdk-go-v2/service/signin v1.0.8 h1:0GFOLzEbOyZABS3PhYfBIx2rNBACYcKty+XGkTgw1ow=
26+
github.com/aws/aws-sdk-go-v2/service/signin v1.0.8/go.mod h1:LXypKvk85AROkKhOG6/YEcHFPoX+prKTowKnVdcaIxE=
27+
github.com/aws/aws-sdk-go-v2/service/ssm v1.68.3 h1:bBoWhx8lsFLTXintRX64ZBXcmFZbGqUmaPUrjXECqIc=
28+
github.com/aws/aws-sdk-go-v2/service/ssm v1.68.3/go.mod h1:rcRkKbUJ2437WuXdq9fbj+MjTudYWzY9Ct8kiBbN8a8=
29+
github.com/aws/aws-sdk-go-v2/service/sso v1.30.13 h1:kiIDLZ005EcKomYYITtfsjn7dtOwHDOFy7IbPXKek2o=
30+
github.com/aws/aws-sdk-go-v2/service/sso v1.30.13/go.mod h1:2h/xGEowcW/g38g06g3KpRWDlT+OTfxxI0o1KqayAB8=
31+
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.17 h1:jzKAXIlhZhJbnYwHbvUQZEB8KfgAEuG0dc08Bkda7NU=
32+
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.17/go.mod h1:Al9fFsXjv4KfbzQHGe6V4NZSZQXecFcvaIF4e70FoRA=
33+
github.com/aws/aws-sdk-go-v2/service/sts v1.41.9 h1:Cng+OOwCHmFljXIxpEVXAGMnBia8MSU6Ch5i9PgBkcU=
34+
github.com/aws/aws-sdk-go-v2/service/sts v1.41.9/go.mod h1:LrlIndBDdjA/EeXeyNBle+gyCwTlizzW5ycgWnvIxkk=
35+
github.com/aws/smithy-go v1.24.2 h1:FzA3bu/nt/vDvmnkg+R8Xl46gmzEDam6mZ1hzmwXFng=
36+
github.com/aws/smithy-go v1.24.2/go.mod h1:YE2RhdIuDbA5E5bTdciG9KrW3+TiEONeUWCqxX9i1Fc=
337
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
438
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
539
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
// Unless explicitly stated otherwise all files in this repository are licensed
2+
// under the Apache License Version 2.0.
3+
// This product includes software developed at Datadog (https://www.datadoghq.com/).
4+
// Copyright 2026-Present Datadog, Inc.
5+
6+
package config
7+
8+
import (
9+
"context"
10+
"errors"
11+
"fmt"
12+
"log/slog"
13+
"os"
14+
"time"
15+
16+
"github.com/aws/aws-sdk-go-v2/aws"
17+
awshttp "github.com/aws/aws-sdk-go-v2/aws/transport/http"
18+
awsconfig "github.com/aws/aws-sdk-go-v2/config"
19+
)
20+
21+
const (
22+
TIMEOUT = 5
23+
)
24+
25+
type resolveOptions struct {
26+
AWSCfg aws.Config
27+
Value string
28+
UseFIPS bool
29+
}
30+
31+
type apiKeyResolver func(ctx context.Context, opts resolveOptions) (string, error)
32+
33+
var resolvers = []struct {
34+
envVar string
35+
resolve apiKeyResolver
36+
}{
37+
{"DD_API_KEY_SECRET_ARN", resolveFromSecretsManager},
38+
{"DD_API_KEY_SSM_NAME", resolveFromSSM},
39+
{"DD_KMS_API_KEY", resolveFromKMS},
40+
{"DD_API_KEY", resolveFromEnv},
41+
}
42+
43+
func resolveAPIKey(ctx context.Context, useFIPS bool) (string, error) {
44+
awsCfg, err := awsconfig.LoadDefaultConfig(ctx,
45+
awsconfig.WithHTTPClient(awshttp.NewBuildableClient().WithTimeout(time.Second*TIMEOUT)),
46+
)
47+
48+
if err != nil {
49+
return "", fmt.Errorf("loading AWS config: %w", err)
50+
}
51+
52+
for _, resolver := range resolvers {
53+
if v, ok := os.LookupEnv(resolver.envVar); ok {
54+
slog.Debug("resolving API key", "source", resolver.envVar)
55+
resolver.resolve(ctx, resolveOptions{
56+
AWSCfg: awsCfg,
57+
Value: v,
58+
UseFIPS: useFIPS,
59+
})
60+
}
61+
}
62+
return "", errors.New("no API key configured: set DD_API_KEY, DD_API_KEY_SECRET_ARN, DD_API_KEY_SSM_NAME, or DD_KMS_API_KEY. See: https://docs.datadoghq.com/serverless/forwarder/")
63+
}
64+
65+
func resolveFromSecretsManager(ctx context.Context, opts resolveOptions) (string, error) {
66+
return "", nil
67+
}
68+
69+
func resolveFromSSM(ctx context.Context, opts resolveOptions) (string, error) {
70+
return "", nil
71+
}
72+
73+
func resolveFromKMS(ctx context.Context, opts resolveOptions) (string, error) {
74+
return "", nil
75+
}
76+
77+
func resolveFromEnv(ctx context.Context, opts resolveOptions) (string, error) {
78+
// if len(opts.Value) != 32 {
79+
// return "", fmt.Errorf("invalid datadog api key format")
80+
// }
81+
82+
// client := &http.Client{
83+
// Timeout: TIMEOUT * time.Second,
84+
// Transport: &http.Transport{
85+
// TLSClientConfig: &tls.Config{
86+
// InsecureSkipVerify: skipSSLValidation,
87+
// },
88+
// },
89+
// }
90+
91+
// res, err := http.Get()
92+
// if err != nil {
93+
94+
// }
95+
96+
return opts.Value, nil
97+
}
98+
99+
func validateAPIKey(cfg Config) {
100+
101+
}
Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
// Unless explicitly stated otherwise all files in this repository are licensed
2+
// under the Apache License Version 2.0.
3+
// This product includes software developed at Datadog (https://www.datadoghq.com/).
4+
// Copyright 2026-Present Datadog, Inc.
5+
6+
package config
7+
8+
import (
9+
"context"
10+
"fmt"
11+
"log/slog"
12+
"os"
13+
"strconv"
14+
"strings"
15+
)
16+
17+
var deprecatedEnvironmentVariables = []string{
18+
"DD_ADDITIONAL_TARGET_LAMBDAS",
19+
"DD_ENRICH_CLOUDWATCH_TAGS",
20+
"DD_ENRICH_S3_TAGS",
21+
"DD_FETCH_LAMBDA_TAGS",
22+
"DD_FETCH_LOG_GROUP_TAGS",
23+
"DD_FETCH_S3_TAGS",
24+
"DD_FETCH_STEP_FUNCTIONS_TAGS",
25+
"DD_TAGS_CACHE_TTL_SECONDS",
26+
"DD_TRACE_INTAKE_URL",
27+
"DD_USE_VPC",
28+
}
29+
30+
type Config struct {
31+
APIKey string
32+
Site string
33+
URL string
34+
Port int
35+
APIURL string
36+
ForwardLog bool
37+
UseCompression bool
38+
CompressionLevel int
39+
NoSSL bool
40+
SkipSSLValidation bool
41+
Tags string
42+
Source string
43+
LogLevel string
44+
UseFIPS bool
45+
}
46+
47+
func Load(ctx context.Context) (*Config, error) {
48+
cfg := &Config{
49+
Site: envOrDefault("DD_SITE", "datadoghq.com"),
50+
Port: envOrDefaultInt("DD_PORT", 443),
51+
ForwardLog: envOrDefaultBool("DD_FORWARD_LOG", true),
52+
UseCompression: envOrDefaultBool("DD_USE_COMPRESSION", true),
53+
CompressionLevel: envOrDefaultInt("DD_COMPRESSION_LEVEL", 6),
54+
NoSSL: envOrDefaultBool("DD_NO_SSL", false),
55+
SkipSSLValidation: envOrDefaultBool("DD_SKIP_SSL_VALIDATION", false),
56+
Tags: envOrDefault("DD_TAGS", ""),
57+
Source: envOrDefault("DD_SOURCE", ""),
58+
LogLevel: envOrDefault("DD_LOG_LEVEL", "INFO"),
59+
}
60+
61+
scheme := "https"
62+
if cfg.NoSSL {
63+
scheme = "http"
64+
}
65+
cfg.URL = envOrDefault("DD_URL", "http-intake.logs."+cfg.Site)
66+
cfg.APIURL = envOrDefault("DD_API_URL", fmt.Sprintf("%s://api.%s", scheme, cfg.Site))
67+
68+
logDroppedEnvVars()
69+
70+
useFIPS := envOrDefaultBool("DD_USE_FIPS", false)
71+
cfg.UseFIPS = useFIPS
72+
apiKey, err := resolveAPIKey(ctx, useFIPS)
73+
if err != nil {
74+
return nil, fmt.Errorf("resolving API key: %w", err)
75+
}
76+
cfg.APIKey = apiKey
77+
78+
if err := validateAPIKey(cfg); err != nil {
79+
return nil, fmt.Errorf("validating API key: %w", err)
80+
}
81+
82+
return cfg, nil
83+
}
84+
85+
func logDroppedEnvVars() {
86+
for _, name := range deprecatedEnvironmentVariables {
87+
if _, ok := os.LookupEnv(name); ok {
88+
slog.Warn("deprecated env var set, will be ignored", "name", name)
89+
}
90+
}
91+
}
92+
93+
func envOrDefault(key, fallback string) string {
94+
if v, ok := os.LookupEnv(key); ok {
95+
return v
96+
}
97+
return fallback
98+
}
99+
100+
func envOrDefaultBool(key string, fallback bool) bool {
101+
v, ok := os.LookupEnv(key)
102+
if !ok {
103+
return fallback
104+
}
105+
return strings.EqualFold(v, "true")
106+
}
107+
108+
func envOrDefaultInt(key string, fallback int) int {
109+
v, ok := os.LookupEnv(key)
110+
if !ok {
111+
return fallback
112+
}
113+
n, err := strconv.Atoi(v)
114+
if err != nil {
115+
slog.Warn("invalid integer for env var, using default", "key", key, "value", v, "default", fallback)
116+
return fallback
117+
}
118+
return n
119+
}

0 commit comments

Comments
 (0)