Skip to content

Commit 5e899f9

Browse files
committed
runtimeconfig: Encapsulate options as 'secret' strings to make accidental exposure harder
Signed-off-by: Danielle Lancashire <[email protected]>
1 parent d0b0794 commit 5e899f9

File tree

5 files changed

+70
-7
lines changed

5 files changed

+70
-7
lines changed

go.mod

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ toolchain go1.22.0
66

77
require (
88
github.com/go-logr/logr v1.4.1
9+
github.com/pelletier/go-toml/v2 v2.1.1
910
github.com/prometheus/common v0.49.0
1011
github.com/stretchr/testify v1.9.0
1112
golang.org/x/sync v0.5.0

go.sum

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,8 @@ github.com/onsi/ginkgo/v2 v2.14.0 h1:vSmGj2Z5YPb9JwCWT6z6ihcUvDhuXLc3sJiqd3jMKAY
7676
github.com/onsi/ginkgo/v2 v2.14.0/go.mod h1:JkUdW7JkN0V6rFvsHcJ478egV3XH9NxpD27Hal/PhZw=
7777
github.com/onsi/gomega v1.30.0 h1:hvMK7xYz4D3HapigLTeGdId/NcfQx1VHMJc60ew99+8=
7878
github.com/onsi/gomega v1.30.0/go.mod h1:9sxs+SwGrKI0+PWe4Fxa9tFQQBG5xSsSbMXOI8PPpoQ=
79+
github.com/pelletier/go-toml/v2 v2.1.1 h1:LWAJwfNvjQZCFIDKWYQaM62NcYeYViCmWIwmOStowAI=
80+
github.com/pelletier/go-toml/v2 v2.1.1/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc=
7981
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
8082
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
8183
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=

internal/runtimeconfig/types.go

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"fmt"
55

66
spinv1 "github.com/spinkube/spin-operator/api/v1"
7+
"github.com/spinkube/spin-operator/pkg/secret"
78
corev1 "k8s.io/api/core/v1"
89
"k8s.io/apimachinery/pkg/types"
910
)
@@ -26,9 +27,9 @@ type VariablesProvider struct {
2627
EnvVariablesProviderOptions
2728
}
2829

29-
type KeyValueStoreOptions map[string]string
30+
type KeyValueStoreOptions map[string]secret.String
3031

31-
type SQLiteDatabaseOptions map[string]string
32+
type SQLiteDatabaseOptions map[string]secret.String
3233

3334
type Spin struct {
3435
Variables []VariablesProvider `toml:"config_provider,omitempty"`
@@ -37,7 +38,7 @@ type Spin struct {
3738

3839
SQLiteDatabases map[string]SQLiteDatabaseOptions `toml:"sqlite_database,omitempty"`
3940

40-
LLMCompute map[string]string `toml:"llm_compute,omitempty"`
41+
LLMCompute map[string]secret.String `toml:"llm_compute,omitempty"`
4142
}
4243

4344
func (s *Spin) AddKeyValueStore(
@@ -98,9 +99,9 @@ func (s *Spin) AddLLMCompute(computeType, namespace string,
9899

99100
func renderOptionsIntoMap(typeOpt, namespace string,
100101
opts []spinv1.RuntimeConfigOption,
101-
secrets map[types.NamespacedName]*corev1.Secret, configMaps map[types.NamespacedName]*corev1.ConfigMap) (map[string]string, error) {
102-
options := map[string]string{
103-
"type": typeOpt,
102+
secrets map[types.NamespacedName]*corev1.Secret, configMaps map[types.NamespacedName]*corev1.ConfigMap) (map[string]secret.String, error) {
103+
options := map[string]secret.String{
104+
"type": secret.String(typeOpt),
104105
}
105106

106107
for _, opt := range opts {
@@ -125,7 +126,7 @@ func renderOptionsIntoMap(typeOpt, namespace string,
125126
value = string(sec.Data[secKeyRef.Key])
126127
}
127128

128-
options[opt.Name] = value
129+
options[opt.Name] = secret.String(value)
129130
}
130131

131132
return options, nil

pkg/secret/secret.go

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
// Secret is a package for types that make it harder to accidentally expose
2+
// secret variables when passing them around.
3+
package secret
4+
5+
type String string
6+
7+
const redacted = "REDACTED"
8+
9+
// String implements fmt.Stringer and redacts the sensitive value.
10+
func (s String) String() string {
11+
return redacted
12+
}
13+
14+
// GoString implements fmt.GoStringer and redacts the sensitive value.
15+
func (s String) GoString() string {
16+
return redacted
17+
}
18+
19+
// Value returns the sensitive value as a string.
20+
func (s String) Value() string {
21+
return string(s)
22+
}
23+
24+
func (s String) MarshalJSON() ([]byte, error) {
25+
return []byte(`"` + redacted + `"`), nil
26+
}

pkg/secret/secret_test.go

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
package secret
2+
3+
import (
4+
"encoding/json"
5+
"fmt"
6+
"testing"
7+
8+
toml "github.com/pelletier/go-toml/v2"
9+
"github.com/stretchr/testify/require"
10+
)
11+
12+
func TestSecret(t *testing.T) {
13+
s := String("secret")
14+
require.Equal(t, "secret", s.Value(), "secret")
15+
require.Equal(t, "REDACTED", fmt.Sprintf("%v", s))
16+
require.Equal(t, "REDACTED", s.String())
17+
require.Equal(t, "REDACTED", s.GoString())
18+
19+
b, err := json.Marshal(s)
20+
require.NoError(t, err)
21+
require.Equal(t, `"REDACTED"`, string(b))
22+
}
23+
24+
func TestSecret_TOMLMarshal(t *testing.T) {
25+
toMarshal := map[string]String{
26+
"some_key": String("some_secret_value"),
27+
}
28+
data, err := toml.Marshal(toMarshal)
29+
require.NoError(t, err)
30+
// If `toml` changes its default marshal formatting it is fine to update this
31+
// test to match - we only care that the secret is rendered in plain text.
32+
require.Equal(t, "some_key = 'some_secret_value'\n", string(data))
33+
}

0 commit comments

Comments
 (0)