Skip to content

Commit da402f3

Browse files
committed
fix: limit secrets encryption to BoltDB-backed deployments
1 parent 80ef3d7 commit da402f3

4 files changed

Lines changed: 39 additions & 3 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
1. [#6211](https://github.com/influxdata/chronograf/pull/6211): Harden secrets-at-rest protections for persisted source and server credentials using envelope encryption.
66
* Add startup migration for legacy plaintext secrets when a secrets master key is configured.
77
* Add `chronoctl` commands for master-key generation, rewrap, and disable workflows.
8+
* Secrets encryption is supported only for BoltDB-backed deployments.
89

910
## v1.11.3 [2026-05-27]
1011

cmd/chronoctl/README.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ Available commands:
1414

1515
### Secrets Encryption Commands
1616

17-
Use these commands when Chronograf secret-at-rest encryption is enabled.
17+
Use these commands when Chronograf secret-at-rest encryption is enabled with BoltDB storage.
1818

1919
##### Generate Secrets Master Key
2020
Generate a base64-encoded 32-byte key:
@@ -58,12 +58,15 @@ After successful disable:
5858
Important:
5959
- `rewrap-secrets-master-key` changes only master-key wrapping and does not re-encrypt secret records.
6060
- `disable-secrets-encryption` decrypts encrypted secrets and stores them as plaintext.
61+
- Secrets encryption management supports BoltDB only; etcd-backed deployments should rely on etcd encryption-at-rest or platform-level disk encryption.
6162

6263

6364
### Migrate
6465

6566
The `migrate` command allows you to migrate your chronograf configuration store. It is highly recommended that you make a backup of all databases involved before running a migration as there is no guarantee that there will be no data loss. When specifying an etcd endpoint, the URI must begin with `etcd://`. It is preferred that you prefix `bolt://` to an absolute path when specifying a local bolt db file, but a lone relative path is also accepted without the prefix. If there is authentication on etcd, use the standard URI format to define a username/password: `[scheme:][//[userinfo@]host][/]path`.
6667
There is currently no cleanup for a failed migration, so keep that in mind before migrating to a db that contains other important data.
68+
If migrating from an encrypted BoltDB, first run `disable-secrets-encryption`.
69+
The `migrate` command does not initialize a secrets DEK and cannot read encrypted source or server secrets.
6770

6871

6972
##### Usage

server/server.go

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -151,8 +151,8 @@ type Server struct {
151151
ReportingDisabled bool `short:"r" long:"reporting-disabled" description:"Disable reporting of usage stats (os,arch,version,cluster_id,uptime) once every 24hr" env:"REPORTING_DISABLED"`
152152
CustomAutoRefresh string `long:"custom-auto-refresh" description:"Adds custom auto refresh options using semicolon separated list of label=milliseconds pairs" env:"CUSTOM_AUTO_REFRESH"`
153153
LogLevel string `short:"l" long:"log-level" value-name:"choice" choice:"debug" choice:"info" choice:"error" default:"info" description:"Set the logging level" env:"LOG_LEVEL"`
154-
SecretsMasterKey string `long:"secrets-master-key" description:"Base64-encoded 32-byte master key used to wrap/unwrap the data encryption key for secret-field encryption" env:"SECRETS_MASTER_KEY"`
155-
SecretsMasterKeyFile flags.Filename `long:"secrets-master-key-file" description:"Path to file containing a base64-encoded 32-byte master key used to wrap/unwrap the data encryption key for secret-field encryption" env:"SECRETS_MASTER_KEY_FILE"`
154+
SecretsMasterKey string `long:"secrets-master-key" description:"Base64-encoded 32-byte master key used to wrap/unwrap the data encryption key for secret-field encryption (BoltDB storage only)" env:"SECRETS_MASTER_KEY"`
155+
SecretsMasterKeyFile flags.Filename `long:"secrets-master-key-file" description:"Path to file containing a base64-encoded 32-byte master key used to wrap/unwrap the data encryption key for secret-field encryption (BoltDB storage only)" env:"SECRETS_MASTER_KEY_FILE"`
156156
Basepath string `short:"p" long:"basepath" description:"A URL path prefix under which all chronograf routes will be mounted. (Note: PREFIX_ROUTES has been deprecated. Now, if basepath is set, all routes will be prefixed with it.)" env:"BASE_PATH"`
157157
ShowVersion bool `short:"v" long:"version" description:"Show Chronograf version info"`
158158
BuildInfo chronograf.BuildInfo
@@ -649,6 +649,12 @@ func (s *Server) loadSecretsMasterKey() ([]byte, error) {
649649
return nil, errors.New("secrets master key must be provided by either --secrets-master-key or --secrets-master-key-file, not both")
650650
}
651651

652+
// Storage topology is validated here because this is the last common point
653+
// before Serve opens either BoltDB or etcd; secrets encryption is BoltDB-only.
654+
if len(s.EtcdEndpoints) > 0 && (s.SecretsMasterKey != "" || s.SecretsMasterKeyFile != "") {
655+
return nil, errors.New("--secrets-master-key/--secrets-master-key-file are supported only with BoltDB storage; remove them, or rely on etcd encryption-at-rest or platform-level disk encryption")
656+
}
657+
652658
if s.SecretsMasterKey == "" && s.SecretsMasterKeyFile == "" {
653659
return nil, nil
654660
}

server/server_test.go

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,13 @@ func Test_loadSecretsMasterKey(t *testing.T) {
145145
require.Nil(t, got)
146146
})
147147

148+
t.Run("etcd config without key returns nil", func(t *testing.T) {
149+
s := Server{EtcdEndpoints: []string{"localhost:2379"}}
150+
got, err := s.loadSecretsMasterKey()
151+
require.NoError(t, err)
152+
require.Nil(t, got)
153+
})
154+
148155
t.Run("fails when both direct and file are set", func(t *testing.T) {
149156
s := Server{
150157
SecretsMasterKey: validB64,
@@ -155,6 +162,25 @@ func Test_loadSecretsMasterKey(t *testing.T) {
155162
require.Contains(t, err.Error(), "either --secrets-master-key or --secrets-master-key-file")
156163
})
157164

165+
t.Run("fails when direct key is set with etcd", func(t *testing.T) {
166+
s := Server{
167+
EtcdEndpoints: []string{"localhost:2379"},
168+
SecretsMasterKey: validB64,
169+
}
170+
_, err := s.loadSecretsMasterKey()
171+
require.Error(t, err)
172+
require.Contains(t, err.Error(), "supported only with BoltDB storage")
173+
})
174+
175+
t.Run("fails when key file is set with etcd", func(t *testing.T) {
176+
f := t.TempDir() + "/master-key.txt"
177+
require.NoError(t, os.WriteFile(f, []byte(validB64+"\n"), 0600))
178+
s := Server{EtcdEndpoints: []string{"localhost:2379"}, SecretsMasterKeyFile: flags.Filename(f)}
179+
_, err := s.loadSecretsMasterKey()
180+
require.Error(t, err)
181+
require.Contains(t, err.Error(), "supported only with BoltDB storage")
182+
})
183+
158184
t.Run("fails on invalid base64", func(t *testing.T) {
159185
s := Server{SecretsMasterKey: "not-base64"}
160186
_, err := s.loadSecretsMasterKey()

0 commit comments

Comments
 (0)