Skip to content

Commit ee99b9d

Browse files
committed
Fix autodiscovery of Fulcio Root CA in CTlog
1 parent 7f1d0c2 commit ee99b9d

File tree

3 files changed

+318
-22
lines changed

3 files changed

+318
-22
lines changed
+38
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
package action
2+
3+
import (
4+
"github.com/go-logr/logr"
5+
consolev1 "github.com/openshift/api/console/v1"
6+
routev1 "github.com/openshift/api/route/v1"
7+
monitoringv1 "github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring/v1"
8+
"github.com/securesign/operator/api/v1alpha1"
9+
"github.com/securesign/operator/controllers/common/action"
10+
v1 "k8s.io/api/core/v1"
11+
"k8s.io/apiextensions-apiserver/pkg/apis/apiextensions"
12+
"k8s.io/apimachinery/pkg/runtime"
13+
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
14+
clientgoscheme "k8s.io/client-go/kubernetes/scheme"
15+
"k8s.io/client-go/tools/record"
16+
"sigs.k8s.io/controller-runtime/pkg/client"
17+
"sigs.k8s.io/controller-runtime/pkg/client/fake"
18+
)
19+
20+
func FakeClientBuilder() *fake.ClientBuilder {
21+
scheme := runtime.NewScheme()
22+
utilruntime.Must(clientgoscheme.AddToScheme(scheme))
23+
utilruntime.Must(monitoringv1.AddToScheme(scheme))
24+
utilruntime.Must(v1alpha1.AddToScheme(scheme))
25+
utilruntime.Must(routev1.AddToScheme(scheme))
26+
utilruntime.Must(v1.AddToScheme(scheme))
27+
utilruntime.Must(consolev1.AddToScheme(scheme))
28+
utilruntime.Must(apiextensions.AddToScheme(scheme))
29+
cl := fake.NewClientBuilder().WithScheme(scheme)
30+
return cl
31+
}
32+
33+
func PrepareAction[T interface{}](c client.Client, a action.Action[T]) action.Action[T] {
34+
a.InjectClient(c)
35+
a.InjectLogger(logr.Logger{})
36+
a.InjectRecorder(record.NewFakeRecorder(10))
37+
return a
38+
}

controllers/ctlog/actions/handle_fulcio_root.go

+27-22
Original file line numberDiff line numberDiff line change
@@ -53,21 +53,6 @@ func (g handleFulcioCert) CanHandle(ctx context.Context, instance *v1alpha1.CTlo
5353

5454
func (g handleFulcioCert) Handle(ctx context.Context, instance *v1alpha1.CTlog) *action.Result {
5555

56-
scr, err := k8sutils.FindSecret(ctx, g.Client, instance.Namespace, actions.FulcioCALabel)
57-
if err != nil {
58-
return g.Failed(err)
59-
}
60-
if scr == nil && len(instance.Spec.RootCertificates) == 0 {
61-
meta.SetStatusCondition(&instance.Status.Conditions, metav1.Condition{
62-
Type: CertCondition,
63-
Status: metav1.ConditionFalse,
64-
Reason: constants.Failure,
65-
Message: "Cert not found",
66-
})
67-
g.StatusUpdate(ctx, instance)
68-
return g.Requeue()
69-
}
70-
7156
if meta.FindStatusCondition(instance.Status.Conditions, constants.Ready).Reason != constants.Creating {
7257
meta.SetStatusCondition(&instance.Status.Conditions, metav1.Condition{
7358
Type: constants.Ready,
@@ -78,16 +63,36 @@ func (g handleFulcioCert) Handle(ctx context.Context, instance *v1alpha1.CTlog)
7863
return g.StatusUpdate(ctx, instance)
7964
}
8065

81-
instance.Status.RootCertificates = append(instance.Spec.RootCertificates, v1alpha1.SecretKeySelector{
82-
LocalObjectReference: v1alpha1.LocalObjectReference{
83-
Name: scr.Name,
84-
},
85-
Key: scr.Labels[actions.FulcioCALabel],
86-
})
66+
if len(instance.Spec.RootCertificates) == 0 {
67+
scr, err := k8sutils.FindSecret(ctx, g.Client, instance.Namespace, actions.FulcioCALabel)
68+
if err != nil {
69+
return g.Failed(err)
70+
}
71+
if scr == nil {
72+
meta.SetStatusCondition(&instance.Status.Conditions, metav1.Condition{
73+
Type: CertCondition,
74+
Status: metav1.ConditionFalse,
75+
Reason: constants.Failure,
76+
Message: "Cert not found",
77+
})
78+
g.StatusUpdate(ctx, instance)
79+
return g.Requeue()
80+
}
81+
instance.Status.RootCertificates = []v1alpha1.SecretKeySelector{
82+
{
83+
LocalObjectReference: v1alpha1.LocalObjectReference{
84+
Name: scr.Name,
85+
},
86+
Key: scr.Labels[actions.FulcioCALabel],
87+
},
88+
}
89+
} else {
90+
instance.Status.RootCertificates = instance.Spec.RootCertificates
91+
}
8792

8893
// invalidate server config
8994
if instance.Status.ServerConfigRef != nil {
90-
if err = g.Client.Delete(ctx, &v1.Secret{
95+
if err := g.Client.Delete(ctx, &v1.Secret{
9196
ObjectMeta: metav1.ObjectMeta{
9297
Name: instance.Status.ServerConfigRef.Name,
9398
Namespace: instance.Namespace,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,253 @@
1+
package actions
2+
3+
import (
4+
"context"
5+
. "github.com/onsi/gomega"
6+
"github.com/securesign/operator/api/v1alpha1"
7+
"github.com/securesign/operator/controllers/common/action"
8+
testAction "github.com/securesign/operator/controllers/common/test/action"
9+
"github.com/securesign/operator/controllers/common/utils/kubernetes"
10+
"github.com/securesign/operator/controllers/constants"
11+
"github.com/securesign/operator/controllers/fulcio/actions"
12+
v1 "k8s.io/api/core/v1"
13+
"k8s.io/apimachinery/pkg/api/meta"
14+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
15+
"k8s.io/apimachinery/pkg/types"
16+
"testing"
17+
)
18+
19+
func Test_HandleFulcioCert_Autodiscover(t *testing.T) {
20+
g := NewWithT(t)
21+
22+
instance := &v1alpha1.CTlog{
23+
ObjectMeta: metav1.ObjectMeta{
24+
Name: "auto",
25+
Namespace: "default",
26+
},
27+
Spec: v1alpha1.CTlogSpec{},
28+
Status: v1alpha1.CTlogStatus{
29+
Conditions: []metav1.Condition{
30+
{
31+
Type: constants.Ready,
32+
Reason: constants.Creating,
33+
Status: metav1.ConditionFalse,
34+
},
35+
},
36+
},
37+
}
38+
39+
c := testAction.FakeClientBuilder().WithObjects(
40+
kubernetes.CreateSecret("secret", "default",
41+
map[string][]byte{"key": nil}, map[string]string{actions.FulcioCALabel: "key"}),
42+
instance,
43+
).Build()
44+
45+
i := &v1alpha1.CTlog{}
46+
if err := c.Get(context.TODO(), types.NamespacedName{Name: instance.GetName(), Namespace: instance.GetNamespace()}, i); err != nil {
47+
t.Error(err)
48+
}
49+
50+
a := testAction.PrepareAction(c, NewHandleFulcioCertAction())
51+
g.Expect(a.CanHandle(context.TODO(), i)).To(BeTrue())
52+
53+
_ = a.Handle(context.TODO(), i)
54+
55+
g.Expect(len(i.Status.RootCertificates)).Should(Equal(1))
56+
g.Expect(i.Status.RootCertificates[0].Key).Should(Equal("key"))
57+
g.Expect(i.Status.RootCertificates[0].Name).Should(Equal("secret"))
58+
59+
g.Expect(meta.IsStatusConditionTrue(i.Status.Conditions, CertCondition)).To(BeTrue())
60+
}
61+
62+
63+
func Test_HandleFulcioCert_Empty(t *testing.T) {
64+
g := NewWithT(t)
65+
66+
instance := &v1alpha1.CTlog{
67+
ObjectMeta: metav1.ObjectMeta{
68+
Name: "empty",
69+
Namespace: "default",
70+
},
71+
Spec: v1alpha1.CTlogSpec{},
72+
Status: v1alpha1.CTlogStatus{
73+
Conditions: []metav1.Condition{
74+
{
75+
Type: constants.Ready,
76+
Reason: constants.Creating,
77+
Status: metav1.ConditionFalse,
78+
},
79+
},
80+
},
81+
}
82+
83+
c := testAction.FakeClientBuilder().WithObjects(
84+
instance,
85+
).Build()
86+
87+
i := &v1alpha1.CTlog{}
88+
if err := c.Get(context.TODO(), types.NamespacedName{Name: instance.GetName(), Namespace: instance.GetNamespace()}, i); err != nil {
89+
t.Error(err)
90+
}
91+
92+
a := testAction.PrepareAction(c, NewHandleFulcioCertAction())
93+
g.Expect(a.CanHandle(context.TODO(), i)).To(BeTrue())
94+
95+
result := a.Handle(context.TODO(), i)
96+
var dummyAction = action.BaseAction{}
97+
g.Expect(result).To(Equal(dummyAction.Requeue()))
98+
}
99+
100+
func Test_HandleFulcioCert_Configured(t *testing.T) {
101+
g := NewWithT(t)
102+
103+
instance := &v1alpha1.CTlog{
104+
ObjectMeta: metav1.ObjectMeta{
105+
Name: "configured",
106+
Namespace: "default",
107+
},
108+
Spec: v1alpha1.CTlogSpec{
109+
RootCertificates: []v1alpha1.SecretKeySelector{
110+
{
111+
Key: "key",
112+
LocalObjectReference: v1alpha1.LocalObjectReference{Name: "secret"},
113+
},
114+
{
115+
Key: "key",
116+
LocalObjectReference: v1alpha1.LocalObjectReference{Name: "secret-2"},
117+
},
118+
},
119+
},
120+
Status: v1alpha1.CTlogStatus{
121+
Conditions: []metav1.Condition{
122+
{
123+
Type: constants.Ready,
124+
Reason: constants.Creating,
125+
Status: metav1.ConditionFalse,
126+
},
127+
},
128+
},
129+
}
130+
131+
c := testAction.FakeClientBuilder().WithObjects(
132+
kubernetes.CreateSecret("secret", "default",
133+
map[string][]byte{"key": nil}, map[string]string{}),
134+
instance,
135+
).Build()
136+
137+
i := &v1alpha1.CTlog{}
138+
if err := c.Get(context.TODO(), types.NamespacedName{Name: instance.GetName(), Namespace: instance.GetNamespace()}, i); err != nil {
139+
t.Error(err)
140+
}
141+
142+
a := testAction.PrepareAction(c, NewHandleFulcioCertAction())
143+
g.Expect(a.CanHandle(context.TODO(), i)).To(BeTrue())
144+
145+
_ = a.Handle(context.TODO(), i)
146+
g.Expect(len(i.Status.RootCertificates)).Should(Equal(2))
147+
g.Expect(i.Status.RootCertificates[0].Key).Should(Equal("key"))
148+
g.Expect(i.Status.RootCertificates[0].Name).Should(Equal("secret"))
149+
g.Expect(i.Status.RootCertificates[1].Key).Should(Equal("key"))
150+
g.Expect(i.Status.RootCertificates[1].Name).Should(Equal("secret-2"))
151+
152+
g.Expect(meta.IsStatusConditionTrue(i.Status.Conditions, CertCondition)).To(BeTrue())
153+
}
154+
155+
func Test_HandleFulcioCert_Configured_Priority(t *testing.T) {
156+
g := NewWithT(t)
157+
158+
instance := &v1alpha1.CTlog{
159+
ObjectMeta: metav1.ObjectMeta{
160+
Name: "configured-priority",
161+
Namespace: "default",
162+
},
163+
Spec: v1alpha1.CTlogSpec{
164+
RootCertificates: []v1alpha1.SecretKeySelector{
165+
{
166+
Key: "key",
167+
LocalObjectReference: v1alpha1.LocalObjectReference{Name: "my-secret"},
168+
},
169+
},
170+
},
171+
Status: v1alpha1.CTlogStatus{
172+
Conditions: []metav1.Condition{
173+
{
174+
Type: constants.Ready,
175+
Reason: constants.Creating,
176+
Status: metav1.ConditionFalse,
177+
},
178+
},
179+
},
180+
}
181+
182+
c := testAction.FakeClientBuilder().WithObjects(
183+
kubernetes.CreateSecret("my-secret", "default",
184+
map[string][]byte{"key": nil}, map[string]string{}),
185+
kubernetes.CreateSecret("incorrect-secret", "default",
186+
map[string][]byte{"key": nil}, map[string]string{actions.FulcioCALabel: "key"}),
187+
instance,
188+
).Build()
189+
190+
i := &v1alpha1.CTlog{}
191+
if err := c.Get(context.TODO(), types.NamespacedName{Name: instance.GetName(), Namespace: instance.GetNamespace()}, i); err != nil {
192+
t.Error(err)
193+
}
194+
195+
a := testAction.PrepareAction(c, NewHandleFulcioCertAction())
196+
g.Expect(a.CanHandle(context.TODO(), i)).To(BeTrue())
197+
198+
_ = a.Handle(context.TODO(), i)
199+
g.Expect(len(i.Status.RootCertificates)).Should(Equal(1))
200+
g.Expect(i.Status.RootCertificates[0].Key).Should(Equal("key"))
201+
g.Expect(i.Status.RootCertificates[0].Name).Should(Equal("my-secret"))
202+
203+
g.Expect(meta.IsStatusConditionTrue(i.Status.Conditions, CertCondition)).To(BeTrue())
204+
}
205+
206+
func Test_HandleFulcioCert_Delete_ServerConfig(t *testing.T) {
207+
g := NewWithT(t)
208+
209+
instance := &v1alpha1.CTlog{
210+
ObjectMeta: metav1.ObjectMeta{
211+
Name: "delete-config",
212+
Namespace: "default",
213+
},
214+
Spec: v1alpha1.CTlogSpec{
215+
RootCertificates: []v1alpha1.SecretKeySelector{
216+
{
217+
Key: "key",
218+
LocalObjectReference: v1alpha1.LocalObjectReference{Name: "secret"},
219+
},
220+
},
221+
},
222+
Status: v1alpha1.CTlogStatus{
223+
ServerConfigRef: &v1alpha1.LocalObjectReference{Name: "ctlog-config"},
224+
Conditions: []metav1.Condition{
225+
{
226+
Type: constants.Ready,
227+
Reason: constants.Creating,
228+
Status: metav1.ConditionFalse,
229+
},
230+
},
231+
},
232+
}
233+
234+
c := testAction.FakeClientBuilder().WithObjects(
235+
kubernetes.CreateImmutableSecret("ctlog-config", instance.Namespace, map[string][]byte{}, map[string]string{}),
236+
instance,
237+
).Build()
238+
239+
i := &v1alpha1.CTlog{}
240+
if err := c.Get(context.TODO(), types.NamespacedName{Name: instance.GetName(), Namespace: instance.GetNamespace()}, i); err != nil {
241+
t.Error(err)
242+
}
243+
244+
a := testAction.PrepareAction(c, NewHandleFulcioCertAction())
245+
g.Expect(a.CanHandle(context.TODO(), i)).To(BeTrue())
246+
247+
_ = a.Handle(context.TODO(), i)
248+
g.Expect(meta.IsStatusConditionTrue(i.Status.Conditions, CertCondition)).To(BeTrue())
249+
250+
g.Expect(i.Status.ServerConfigRef).To(BeNil())
251+
g.Expect(c.Get(context.TODO(), types.NamespacedName{Name: "ctlog-config", Namespace: instance.GetNamespace()}, &v1.Secret{})).To(HaveOccurred())
252+
}
253+

0 commit comments

Comments
 (0)