Skip to content

Commit fd741d7

Browse files
committed
dexop: make Secrets compatible with Understack
This changes how the automatically generated Kubernetes Secret looks like: - the `secret` key was renamed to `client-secret` - the `client-id` key is now populated - the `issuer` is populated Caveat: The Issuer information can be obtained from Dex dynamically only starting from newest version (2.42) of Dex. To avoid triggering failed discovery, the same information can be provided as a command line argument.
1 parent 9b699c2 commit fd741d7

File tree

8 files changed

+80
-18
lines changed

8 files changed

+80
-18
lines changed

go/dexop/api/v1alpha1/client_types.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ type ClientSpec struct {
2929
SecretName string `json:"secretName,omitempty"`
3030
SecretNamespace string `json:"secretNamespace,omitempty"`
3131
SecretValue string `json:"-"`
32+
Issuer string `json:"-"`
3233
GenerateSecret bool `json:"generateSecret,omitempty"`
3334
RedirectURIs []string `json:"redirectURIs"`
3435
LogoUrl string `json:"logoURL,omitempty"`

go/dexop/cmd/main.go

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ func main() {
7979
"Path to the client certificate for Dex API")
8080
flag.StringVar(&dexConf.ClientKeyPath, "dex-key-path", "./grpc_client.key",
8181
"Path to the client key for Dex API")
82+
flag.StringVar(&dexConf.Issuer, "dex-issuer", "", "force dex Issuer value to be inserted into created secrets.")
8283
opts := zap.Options{
8384
Development: true,
8485
}
@@ -191,10 +192,11 @@ type DexConfig struct {
191192
CAPath string
192193
ClientKeyPath string
193194
ClientCertPath string
195+
Issuer string
194196
}
195197

196-
func newDexManager(config *DexConfig) (*dexmgr.DexManager, error) {
197-
mgr, err := dexmgr.NewDexManager(config.Address, config.CAPath, config.ClientKeyPath, config.ClientCertPath)
198+
func newDexManager(cfg *DexConfig) (*dexmgr.DexManager, error) {
199+
mgr, err := dexmgr.NewDexManager(cfg.Address, cfg.CAPath, cfg.ClientKeyPath, cfg.ClientCertPath, cfg.Issuer)
198200
if err != nil {
199201
setupLog.Error(err, "While getting the DexManager")
200202
}

go/dexop/dex/client.go

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import (
1616

1717
type DexManager struct {
1818
Client dexapi.DexClient
19+
Issuer string
1920
}
2021

2122
// Creates new Oauth2 client in Dex
@@ -86,6 +87,19 @@ func (d *DexManager) UpdateOauth2Client(clientSpec *dexv1alpha1.Client) error {
8687
return err
8788
}
8889

90+
func (d *DexManager) GetIssuer() (string, error) {
91+
// fallback for dex versions that don't support Discovery over GRPC
92+
if d.Issuer != "" {
93+
return d.Issuer, nil
94+
}
95+
96+
result, err := d.Client.GetDiscovery(context.TODO(), &dexapi.DiscoveryReq{})
97+
if err != nil {
98+
return "", err
99+
}
100+
return result.GetIssuer(), nil
101+
}
102+
89103
func newDexClient(hostAndPort, caPath, clientKey, clientCrt string) (dexapi.DexClient, error) {
90104
cPool := x509.NewCertPool()
91105
caCert, err := os.ReadFile(caPath)
@@ -120,13 +134,13 @@ func NewInsecureTestManager(grpcAddr string) (*DexManager, error) {
120134
if err != nil {
121135
return nil, err
122136
}
123-
return &DexManager{Client: dexapi.NewDexClient(grpcConn)}, nil
137+
return &DexManager{Client: dexapi.NewDexClient(grpcConn), Issuer: "http://127.0.0.1:15556/dex"}, nil
124138
}
125139

126-
func NewDexManager(host, caCert, clientKey, clientCert string) (*DexManager, error) {
140+
func NewDexManager(host, caCert, clientKey, clientCert, issuer string) (*DexManager, error) {
127141
client, err := newDexClient(host, caCert, clientKey, clientCert)
128142
if err != nil {
129143
return nil, fmt.Errorf("failed creating dex client: %w", err)
130144
}
131-
return &DexManager{Client: client}, nil
145+
return &DexManager{Client: client, Issuer: issuer}, nil
132146
}

go/dexop/helm/templates/deployment.yaml.tpl

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,9 @@ spec:
4040
- --dex-cert-path=/run/secrets/dex/tls.crt
4141
- --dex-key-path=/run/secrets/dex/tls.key
4242
- --dex-host={{ .Values.dex.address }}
43+
{{ with .Values.dex.issuer }}
44+
- --dex-issuer={{ . }}
45+
{{- end }}
4346
command:
4447
- /manager
4548
securityContext:

go/dexop/helm/values.yaml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,3 +104,6 @@ volumeMounts: []
104104
dex:
105105
address: dex-api.dex.cluster.local:5557
106106
secret: dexop-dex-client
107+
# 'issuer' must be set explicitly if using dex version older than 2.42
108+
#
109+
# issuer: http://dex.instance.local/dex

go/dexop/internal/controller/client_controller.go

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,12 @@ func (r *ClientReconciler) getClientSpec(ctx context.Context, namespacedName typ
110110
}
111111
return nil, err
112112
}
113+
// populate issuer
114+
issuer, err := r.DexManager.GetIssuer()
115+
if err != nil {
116+
return nil, err
117+
}
118+
clientSpec.Spec.Issuer = issuer
113119
return clientSpec, nil
114120
}
115121

@@ -190,7 +196,7 @@ func (r *ClientReconciler) readOrGenerateSecret(ctx context.Context, secretmgr *
190196

191197
// generateSecret creates a Kubernetes secret with randomly generated password.
192198
func (r *ClientReconciler) generateSecret(ctx context.Context, secretmgr *SecretManager, clientSpec *dexv1alpha1.Client, reqLogger logr.Logger) (string, error) {
193-
secret, err := secretmgr.generateSecret(r, ctx, clientSpec.Spec.SecretName, clientSpec.Spec.SecretNamespace)
199+
secret, err := secretmgr.generateSecret(r, ctx, clientSpec)
194200
if err != nil {
195201
reqLogger.Error(err, "Unable to write secret", "secretName", clientSpec.Spec.SecretName)
196202
return "", err

go/dexop/internal/controller/client_controller_test.go

Lines changed: 36 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,7 @@ var _ = Describe("Client Controller", func() {
110110

111111
secretObj := &v1.Secret{}
112112
err = k8sClient.Get(ctx, types.NamespacedName{Namespace: typeNamespacedName.Namespace, Name: secretName}, secretObj)
113-
Expect(len(secretObj.Data["secret"])).To(Equal(48))
113+
Expect(len(secretObj.Data["client-secret"])).To(Equal(48))
114114
Expect(err).NotTo(HaveOccurred())
115115
})
116116
It("should successfully reconcile the resource", func() {
@@ -140,7 +140,7 @@ var _ = Describe("Client Controller", func() {
140140
Expect(err).NotTo(HaveOccurred())
141141
secretObj := &v1.Secret{}
142142
Expect(k8sClient.Get(ctx, typesNamespacedSecretName, secretObj)).To(Succeed())
143-
secretObj.Data["secret"] = []byte("newSecret")
143+
secretObj.Data["client-secret"] = []byte("newSecret")
144144

145145
By("reconcile after changing the secret")
146146
Expect(k8sClient.Update(ctx, secretObj)).To(Succeed())
@@ -208,7 +208,39 @@ var _ = Describe("Client Controller", func() {
208208

209209
By("checking if the Secret has been recreated")
210210
newSecret := &v1.Secret{ObjectMeta: metav1.ObjectMeta{Name: typesNamespacedSecretName.Name, Namespace: typesNamespacedSecretName.Namespace}}
211-
Expect(k8sClient.Get(ctx, typesNamespacedSecretName, newSecret))
211+
Expect(k8sClient.Get(ctx, typesNamespacedSecretName, newSecret)).To(Succeed())
212+
})
213+
214+
It("populates the issuer in Secret", func() {
215+
By("reconciling the first time")
216+
controllerReconciler := &ClientReconciler{
217+
Client: k8sClient,
218+
Scheme: k8sClient.Scheme(),
219+
DexManager: dex,
220+
}
221+
_, err := controllerReconciler.Reconcile(ctx, reconcile.Request{
222+
NamespacedName: typeNamespacedName,
223+
})
224+
Expect(err).NotTo(HaveOccurred())
225+
secret := &v1.Secret{ObjectMeta: metav1.ObjectMeta{Name: typesNamespacedSecretName.Name, Namespace: typesNamespacedSecretName.Namespace}}
226+
Expect(k8sClient.Get(ctx, typesNamespacedSecretName, secret)).To(Succeed())
227+
Expect(string(secret.Data["issuer"])).To(Equal("http://127.0.0.1:15556/dex"))
228+
})
229+
230+
It("populates the client-id in Secret", func() {
231+
By("reconciling the first time")
232+
controllerReconciler := &ClientReconciler{
233+
Client: k8sClient,
234+
Scheme: k8sClient.Scheme(),
235+
DexManager: dex,
236+
}
237+
_, err := controllerReconciler.Reconcile(ctx, reconcile.Request{
238+
NamespacedName: typeNamespacedName,
239+
})
240+
Expect(err).NotTo(HaveOccurred())
241+
secret := &v1.Secret{ObjectMeta: metav1.ObjectMeta{Name: typesNamespacedSecretName.Name, Namespace: typesNamespacedSecretName.Namespace}}
242+
Expect(k8sClient.Get(ctx, typesNamespacedSecretName, secret)).To(Succeed())
243+
Expect(string(secret.Data["client-id"])).To(Equal("fred-client"))
212244
})
213245
})
214246

@@ -239,7 +271,7 @@ var _ = Describe("Client Controller", func() {
239271
By("creating secret")
240272
secret := &v1.Secret{
241273
ObjectMeta: metav1.ObjectMeta{Name: testSecretName, Namespace: "default"},
242-
Data: map[string][]byte{"secret": []byte("abc")},
274+
Data: map[string][]byte{"client-secret": []byte("abc")},
243275
}
244276
Expect(k8sClient.Create(ctx, secret)).To(Succeed())
245277

go/dexop/internal/controller/secret_manager.go

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

7+
dexv1alpha1 "github.com/rackerlabs/understack/go/dexop/api/v1alpha1"
78
"github.com/sethvargo/go-password/password"
89
v1 "k8s.io/api/core/v1"
910
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@@ -21,20 +22,20 @@ func (s SecretManager) readSecret(r *ClientReconciler, ctx context.Context, name
2122
return "", err
2223
}
2324

24-
if value, ok := secret.Data["secret"]; ok {
25+
if value, ok := secret.Data["client-secret"]; ok {
2526
return string(value), nil
2627
}
27-
return "", fmt.Errorf("secret key not found")
28+
return "", fmt.Errorf("client-secret key not found")
2829
}
2930

30-
func (s SecretManager) writeSecret(r *ClientReconciler, ctx context.Context, name, namespace, value string) (*v1.Secret, error) {
31+
func (s SecretManager) writeSecret(r *ClientReconciler, ctx context.Context, clientSpec *dexv1alpha1.Client, value string) (*v1.Secret, error) {
3132
secret := &v1.Secret{
3233
TypeMeta: metav1.TypeMeta{},
3334
ObjectMeta: metav1.ObjectMeta{
34-
Name: name,
35-
Namespace: namespace,
35+
Name: clientSpec.Spec.SecretName,
36+
Namespace: clientSpec.Spec.SecretNamespace,
3637
},
37-
Data: map[string][]byte{"secret": []byte(value)},
38+
Data: map[string][]byte{"client-secret": []byte(value), "issuer": []byte(clientSpec.Spec.Issuer), "client-id": []byte(clientSpec.Spec.Name)},
3839
Type: "Opaque",
3940
}
4041

@@ -45,10 +46,10 @@ func (s SecretManager) writeSecret(r *ClientReconciler, ctx context.Context, nam
4546
return secret, nil
4647
}
4748

48-
func (s SecretManager) generateSecret(r *ClientReconciler, ctx context.Context, name, namespace string) (*v1.Secret, error) {
49+
func (s SecretManager) generateSecret(r *ClientReconciler, ctx context.Context, clientSpec *dexv1alpha1.Client) (*v1.Secret, error) {
4950
res, err := password.Generate(48, 10, 10, false, false)
5051
if err != nil {
5152
return nil, err
5253
}
53-
return s.writeSecret(r, ctx, name, namespace, res)
54+
return s.writeSecret(r, ctx, clientSpec, res)
5455
}

0 commit comments

Comments
 (0)