Skip to content
This repository was archived by the owner on Jul 11, 2023. It is now read-only.

Commit baff85f

Browse files
authored
feat(certs): get Vault token from Secret (#4753)
If the Vault token option is not set, the token will be obtained from the secret referenced in the SecretKeyRef. Currently, the Vault token must still be set as an option if Vault is the cert provider. In a subsequent change, all cert provider flags will be removed from the osm-bootstrap, osm-controller, and osm-injector. These values will instead be obtained from the MRC created by the osm-bootstrap and populated using the preset-mesh-root-certificate Signed-off-by: jaellio <[email protected]>
1 parent 4a3d57d commit baff85f

File tree

7 files changed

+181
-27
lines changed

7 files changed

+181
-27
lines changed

charts/osm/README.md

+3-3
Original file line numberDiff line numberDiff line change
@@ -176,9 +176,9 @@ The following table lists the configurable parameters of the osm chart and their
176176
| osm.vault.port | int | `8200` | port to use to connect to Vault |
177177
| osm.vault.protocol | string | `"http"` | protocol to use to connect to Vault |
178178
| osm.vault.role | string | `"openservicemesh"` | Vault role to be used by Open Service Mesh |
179-
| osm.vault.secret | object | `{"key":"token","name":"osm-vault-token"}` | The Kubernetes secret storing the Vault token used in OSM |
180-
| osm.vault.secret.key | string | `"token"` | The Kubernetes secret key with the value bring the Vault token |
181-
| osm.vault.secret.name | string | `"osm-vault-token"` | The Kubernetes secret name storing the Vault token used in OSM |
179+
| osm.vault.secret | object | `{"key":"","name":""}` | The Kubernetes secret storing the Vault token used in OSM |
180+
| osm.vault.secret.key | string | `""` | The Kubernetes secret key with the value bring the Vault token |
181+
| osm.vault.secret.name | string | `""` | The Kubernetes secret name storing the Vault token used in OSM |
182182
| osm.vault.token | string | `""` | token that should be used to connect to Vault |
183183
| osm.webhookConfigNamePrefix | string | `"osm-webhook"` | Prefix used in name of the webhook configuration resources |
184184
| smi.validateTrafficTarget | bool | `true` | Enables validation of SMI Traffic Target |

charts/osm/values.yaml

+2-2
Original file line numberDiff line numberDiff line change
@@ -135,9 +135,9 @@ osm:
135135
# -- The Kubernetes secret storing the Vault token used in OSM
136136
secret:
137137
# -- The Kubernetes secret name storing the Vault token used in OSM
138-
name: osm-vault-token
138+
name: ""
139139
# -- The Kubernetes secret key with the value bring the Vault token
140-
key: token
140+
key: ""
141141

142142
#
143143
# -- cert-manager.io configuration

pkg/certificate/providers/config.go

+28-2
Original file line numberDiff line numberDiff line change
@@ -188,10 +188,21 @@ func (c *MRCProviderGenerator) getHashiVaultOSMCertificateManager(mrc *v1alpha2.
188188

189189
// A Vault address would have the following shape: "http://vault.default.svc.cluster.local:8200"
190190
vaultAddr := fmt.Sprintf("%s://%s:%d", provider.Protocol, provider.Host, provider.Port)
191-
// TODO(#4502): If the DefaultVaultToken is empty, query the mrc.provider.vault.token.secretRef.
191+
192+
// If the DefaultVaultToken is empty, query Vault token secret
193+
var err error
194+
vaultToken := c.DefaultVaultToken
195+
if vaultToken == "" {
196+
log.Debug().Msgf("Attempting to get Vault token from secret %s", provider.Token.SecretKeyRef.Name)
197+
vaultToken, err = getHashiVaultOSMToken(&provider.Token.SecretKeyRef, c.kubeClient)
198+
if err != nil {
199+
return nil, "", err
200+
}
201+
}
202+
192203
vaultClient, err := vault.New(
193204
vaultAddr,
194-
c.DefaultVaultToken,
205+
vaultToken,
195206
provider.Role,
196207
)
197208
if err != nil {
@@ -204,6 +215,21 @@ func (c *MRCProviderGenerator) getHashiVaultOSMCertificateManager(mrc *v1alpha2.
204215
return vaultClient, id, nil
205216
}
206217

218+
// getHashiVaultOSMToken returns the Hashi Vault token from the secret specified in the provided secret key reference
219+
func getHashiVaultOSMToken(secretKeyRef *v1alpha2.SecretKeyReferenceSpec, kubeClient kubernetes.Interface) (string, error) {
220+
tokenSecret, err := kubeClient.CoreV1().Secrets(secretKeyRef.Namespace).Get(context.TODO(), secretKeyRef.Name, metav1.GetOptions{})
221+
if err != nil {
222+
return "", fmt.Errorf("error retrieving Hashi Vault token secret %s/%s: %w", secretKeyRef.Namespace, secretKeyRef.Name, err)
223+
}
224+
225+
token, ok := tokenSecret.Data[secretKeyRef.Key]
226+
if !ok {
227+
return "", fmt.Errorf("key %s not found in Hashi Vault token secret %s/%s", secretKeyRef.Key, secretKeyRef.Namespace, secretKeyRef.Name)
228+
}
229+
230+
return string(token), nil
231+
}
232+
207233
// getCertManagerOSMCertificateManager returns a certificate manager instance with cert-manager as the certificate provider
208234
func (c *MRCProviderGenerator) getCertManagerOSMCertificateManager(mrc *v1alpha2.MeshRootCertificate) (certificate.Issuer, string, error) {
209235
provider := mrc.Spec.Provider.CertManager

pkg/certificate/providers/config_test.go

+132
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import (
1111
"github.com/stretchr/testify/require"
1212
v1 "k8s.io/api/core/v1"
1313
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
14+
"k8s.io/apimachinery/pkg/runtime"
1415
"k8s.io/client-go/kubernetes"
1516
"k8s.io/client-go/kubernetes/fake"
1617
"k8s.io/client-go/rest"
@@ -88,6 +89,62 @@ func TestGetCertificateManager(t *testing.T) {
8889
},
8990
cfg: mockConfigurator,
9091
},
92+
{
93+
name: "Valid Vault protocol using vault secret defined in MRC",
94+
options: VaultOptions{
95+
VaultHost: "vault.default.svc.cluster.local",
96+
VaultRole: "role",
97+
VaultPort: 8200,
98+
VaultProtocol: "http",
99+
},
100+
kubeClient: fake.NewSimpleClientset(&v1.Secret{
101+
ObjectMeta: metav1.ObjectMeta{
102+
Name: "vault-token",
103+
Namespace: "osm-system",
104+
},
105+
Data: map[string][]byte{
106+
"token": []byte("secret"),
107+
},
108+
}),
109+
configClient: fakeConfigClientset.NewSimpleClientset(&v1alpha2.MeshRootCertificate{
110+
ObjectMeta: metav1.ObjectMeta{
111+
Name: "osm-mesh-root-certificate",
112+
Namespace: "osm-system",
113+
Annotations: map[string]string{
114+
constants.MRCVersionAnnotation: "0",
115+
},
116+
},
117+
Spec: v1alpha2.MeshRootCertificateSpec{
118+
Provider: v1alpha2.ProviderSpec{
119+
Vault: &v1alpha2.VaultProviderSpec{
120+
Host: "vault.default.svc.cluster.local",
121+
Role: "role",
122+
Port: 8200,
123+
Protocol: "http",
124+
Token: v1alpha2.VaultTokenSpec{
125+
SecretKeyRef: v1alpha2.SecretKeyReferenceSpec{
126+
Name: "vault-token",
127+
Namespace: "osm-system",
128+
Key: "token",
129+
},
130+
},
131+
},
132+
},
133+
},
134+
Status: v1alpha2.MeshRootCertificateStatus{
135+
State: constants.MRCStateActive,
136+
},
137+
}),
138+
informerCollectionFunc: func(tc testCase) (*informers.InformerCollection, error) {
139+
ic, err := informers.NewInformerCollection("osm", nil, informers.WithKubeClient(tc.kubeClient), informers.WithConfigClient(tc.configClient))
140+
if err != nil {
141+
return nil, err
142+
}
143+
144+
return ic, nil
145+
},
146+
cfg: mockConfigurator,
147+
},
91148
{
92149
name: "Not a valid Vault protocol",
93150
options: VaultOptions{
@@ -185,3 +242,78 @@ func TestGetCertificateManager(t *testing.T) {
185242
})
186243
}
187244
}
245+
246+
func TestGetHashiVaultOSMToken(t *testing.T) {
247+
validVaultTokenSecret := &v1.Secret{
248+
ObjectMeta: metav1.ObjectMeta{
249+
Namespace: "osm-system",
250+
Name: "osm-vault-token",
251+
},
252+
Data: map[string][]byte{
253+
"token": []byte("token"),
254+
},
255+
}
256+
257+
invalidVaultTokenSecret := &v1.Secret{
258+
ObjectMeta: metav1.ObjectMeta{
259+
Namespace: "osm-system",
260+
Name: "osm-vault-token",
261+
},
262+
Data: map[string][]byte{
263+
"noop": []byte("noop"),
264+
},
265+
}
266+
267+
testCases := []struct {
268+
name string
269+
secretKeyRef *v1alpha2.SecretKeyReferenceSpec
270+
kubeClient kubernetes.Interface
271+
expectError bool
272+
}{
273+
{
274+
name: "No Vault token secret",
275+
secretKeyRef: &v1alpha2.SecretKeyReferenceSpec{
276+
Name: "osm-vault-token",
277+
Namespace: "osm-system",
278+
Key: "token",
279+
},
280+
kubeClient: fake.NewSimpleClientset(),
281+
expectError: true,
282+
},
283+
{
284+
name: "Invalid Vault token secret",
285+
secretKeyRef: &v1alpha2.SecretKeyReferenceSpec{
286+
Name: "osm-vault-token",
287+
Namespace: "osm-system",
288+
Key: "token",
289+
},
290+
kubeClient: fake.NewSimpleClientset([]runtime.Object{invalidVaultTokenSecret}...),
291+
expectError: true,
292+
},
293+
{
294+
name: "Valid Vault token secret",
295+
secretKeyRef: &v1alpha2.SecretKeyReferenceSpec{
296+
Name: "osm-vault-token",
297+
Namespace: "osm-system",
298+
Key: "token",
299+
},
300+
kubeClient: fake.NewSimpleClientset([]runtime.Object{validVaultTokenSecret}...),
301+
expectError: false,
302+
},
303+
}
304+
305+
for _, tc := range testCases {
306+
t.Run(tc.name, func(t *testing.T) {
307+
assert := tassert.New(t)
308+
309+
token, err := getHashiVaultOSMToken(tc.secretKeyRef, tc.kubeClient)
310+
if tc.expectError {
311+
assert.Empty(token)
312+
assert.Error(err)
313+
} else {
314+
assert.NotEmpty(token)
315+
assert.NoError(err)
316+
}
317+
})
318+
}
319+
}

pkg/certificate/providers/options.go

+4-4
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,6 @@ import (
99
"github.com/openservicemesh/osm/pkg/apis/config/v1alpha2"
1010
)
1111

12-
const vaultTokenSecretName = "osm-vault-token" // #nosec G101: Potential hardcoded credentials
13-
1412
// Validate validates the options for Tresor certificate provider
1513
func (options TresorOptions) Validate() error {
1614
if options.SecretName == "" {
@@ -39,7 +37,7 @@ func (options VaultOptions) Validate() error {
3937
}
4038

4139
if options.VaultToken == "" {
42-
return errors.New("VaultToken not specified in Hashi Vault options")
40+
log.Warn().Msg("VaultToken is not specified in Hashi Vault options. The token secret reference option must be specified.")
4341
}
4442

4543
if options.VaultRole == "" {
@@ -61,7 +59,9 @@ func (options VaultOptions) AsProviderSpec() v1alpha2.ProviderSpec {
6159
Host: options.VaultHost,
6260
Token: v1alpha2.VaultTokenSpec{
6361
SecretKeyRef: v1alpha2.SecretKeyReferenceSpec{
64-
Name: vaultTokenSecretName,
62+
Name: options.VaultTokenSecretName,
63+
Namespace: options.VaultTokenSecretNamespace,
64+
Key: options.VaultTokenSecretKey,
6565
},
6666
},
6767
Role: options.VaultRole,

pkg/certificate/providers/options_test.go

+1-11
Original file line numberDiff line numberDiff line change
@@ -98,17 +98,7 @@ func TestValidateVaultOptions(t *testing.T) {
9898
VaultToken: "",
9999
VaultRole: "vault-role",
100100
},
101-
expectErr: true,
102-
},
103-
{
104-
testName: "Empty role",
105-
options: VaultOptions{
106-
VaultProtocol: "http",
107-
VaultHost: "vault-host",
108-
VaultToken: "vault-token",
109-
VaultRole: "",
110-
},
111-
expectErr: true,
101+
expectErr: false,
112102
},
113103
{
114104
testName: "Empty role",

pkg/certificate/providers/types.go

+11-5
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,11 @@ import (
88
"github.com/openservicemesh/osm/pkg/apis/config/v1alpha2"
99
"github.com/openservicemesh/osm/pkg/certificate"
1010
"github.com/openservicemesh/osm/pkg/certificate/pem"
11+
"github.com/openservicemesh/osm/pkg/logger"
1112
)
1213

14+
var log = logger.New("certificate/provider")
15+
1316
// Kind specifies the certificate provider kind
1417
type Kind string
1518

@@ -51,11 +54,14 @@ type TresorOptions struct {
5154

5255
// VaultOptions is a type that specifies 'Hashicorp Vault' certificate provider options
5356
type VaultOptions struct {
54-
VaultProtocol string
55-
VaultHost string
56-
VaultToken string // TODO(#4745): Remove after deprecating the osm.vault.token option. Replace with VaultTokenSecretName
57-
VaultRole string
58-
VaultPort int
57+
VaultProtocol string
58+
VaultHost string
59+
VaultToken string // TODO(#4745): Remove after deprecating the osm.vault.token option. Replace with VaultTokenSecretName
60+
VaultRole string
61+
VaultPort int
62+
VaultTokenSecretNamespace string
63+
VaultTokenSecretName string
64+
VaultTokenSecretKey string
5965
}
6066

6167
// CertManagerOptions is a type that specifies 'cert-manager.io' certificate provider options

0 commit comments

Comments
 (0)