Skip to content
Draft
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
5 changes: 4 additions & 1 deletion test/fakeintake/cmd/server/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ func main() {
rcStatePath := flag.String("rc-state", "", "Remote Config: YAML file with initial config to preload")
rcVersion := flag.Uint64("rc-version", 0, "Remote Config: initial version counter (default 1)")
rcKeyPath := flag.String("rc-key-path", "", "Remote Config: ed25519 signing key path (default ~/.fakeintake/signing.key)")
rcKeyData := flag.String("rc-key-data", "", "Remote Config: hex-encoded 32-byte ed25519 seed (takes precedence over --rc-key-path; use for ephemeral envs)")

flag.Parse()

Expand All @@ -46,7 +47,9 @@ func main() {

if *remoteConfig {
fiOptions = append(fiOptions, fakeintake.WithRemoteConfig(*rcOrgUUID))
if *rcKeyPath != "" {
if *rcKeyData != "" {
fiOptions = append(fiOptions, fakeintake.WithRemoteConfigKeyData(*rcKeyData))
} else if *rcKeyPath != "" {
fiOptions = append(fiOptions, fakeintake.WithRemoteConfigKeyPath(*rcKeyPath))
}
if *rcVersion != 0 {
Expand Down
40 changes: 34 additions & 6 deletions test/fakeintake/server/rc.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ type rcServerState struct {
rootJSON []byte

keyPath string
keyData string // hex-encoded seed; takes precedence over keyPath when non-empty
initialStatePath string
}

Expand Down Expand Up @@ -142,6 +143,20 @@ func WithRemoteConfigKeyPath(path string) Option {
}
}

// WithRemoteConfigKeyData supplies the ed25519 signing key as a hex-encoded
// 32-byte seed string. When set, the key is never written to disk and
// WithRemoteConfigKeyPath is ignored. Use this for ephemeral environments
// (e.g. ECS Fargate) where a fixed, pre-known key is required so the agent's
// config_root/director_root can be set at provisioning time.
func WithRemoteConfigKeyData(hexSeed string) Option {
return func(fi *Server) {
if fi.rc == nil {
return
}
fi.rc.keyData = hexSeed
}
}

// WithRemoteConfigVersion seeds the version counter (default 1). Used to keep
// the agent's remote-config.db in sync across restarts.
func WithRemoteConfigVersion(v uint64) Option {
Expand Down Expand Up @@ -183,9 +198,25 @@ func (fi *Server) initRC() error {
return nil
}

priv, generated, err := rcstore.LoadOrCreateSigningKey(rc.keyPath)
if err != nil {
return fmt.Errorf("rc signing key: %w", err)
var (
priv ed25519.PrivateKey
err error
)
if rc.keyData != "" {
priv, err = rcstore.KeyFromHexSeed(rc.keyData)
if err != nil {
return fmt.Errorf("rc signing key (from --rc-key-data): %w", err)
}
log.Println("Remote Config: loaded signing key from --rc-key-data")
} else {
var generated bool
priv, generated, err = rcstore.LoadOrCreateSigningKey(rc.keyPath)
if err != nil {
return fmt.Errorf("rc signing key: %w", err)
}
if generated {
log.Println("Remote Config: generated new signing key — agent's remote-config.db must be flushed")
}
}
rc.signing = priv

Expand All @@ -203,9 +234,6 @@ func (fi *Server) initRC() error {
rc.rootJSON = root

log.Printf("Remote Config: keyid=%s pubkey=%s", keyID, pubHex)
if generated {
log.Println("Remote Config: generated new signing key — agent's remote-config.db must be flushed")
}
log.Printf("Remote Config: paste into datadog.yaml:\n remote_configuration.config_root: '%s'\n remote_configuration.director_root: '%s'", root, root)

if rc.initialStatePath != "" {
Expand Down
14 changes: 14 additions & 0 deletions test/fakeintake/server/rcstore/keystore.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ package rcstore
import (
"crypto/ed25519"
"crypto/rand"
"encoding/hex"
"errors"
"fmt"
"os"
Expand All @@ -24,6 +25,19 @@ func DefaultKeyPath() (string, error) {
return filepath.Join(home, ".fakeintake", "signing.key"), nil
}

// KeyFromHexSeed derives an ed25519 private key from a 64-character hex-encoded
// 32-byte seed. Returns an error when hexSeed is malformed.
func KeyFromHexSeed(hexSeed string) (ed25519.PrivateKey, error) {
seed, err := hex.DecodeString(hexSeed)
if err != nil {
return nil, fmt.Errorf("decode seed: %w", err)
}
if len(seed) != ed25519.SeedSize {
return nil, fmt.Errorf("expected %d-byte seed, got %d", ed25519.SeedSize, len(seed))
}
return ed25519.NewKeyFromSeed(seed), nil
}

// LoadOrCreateSigningKey reads a 32-byte ed25519 seed from path. If path is
// empty, falls back to DefaultKeyPath. If the file does not exist, a fresh
// key is generated and written. The returned bool reports whether a new key
Expand Down
Loading