Skip to content

Commit 8c41ec6

Browse files
committed
chore: Handle configuration of CRL Distribution Points
Signed-off-by: Teddy Andrieux <teddy.andrieux@scality.com>
1 parent 8fbe7b1 commit 8c41ec6

5 files changed

Lines changed: 447 additions & 11 deletions

File tree

api/v1alpha1/managedcrl_types.go

Lines changed: 195 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,11 +25,15 @@ import (
2525
cmmetav1 "github.com/cert-manager/cert-manager/pkg/apis/meta/v1"
2626
appsv1 "k8s.io/api/apps/v1"
2727
corev1 "k8s.io/api/core/v1"
28+
networkingv1 "k8s.io/api/networking/v1"
2829
"k8s.io/apimachinery/pkg/api/meta"
2930
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
3031
"k8s.io/utils/ptr"
3132
)
3233

34+
// +kubebuilder:validation:Format=ipv4
35+
type IPAddress string
36+
3337
// ImageSpec defines information about the image to expose the CRL.
3438
type ImageSpec struct {
3539
// Repository is the container image repository.
@@ -55,6 +59,35 @@ type ImageSpec struct {
5559
PullSecrets []corev1.LocalObjectReference `json:"pullSecrets,omitempty"`
5660
}
5761

62+
// IngressSpec defines the ingress configuration for exposing the CRL.
63+
type IngressSpec struct {
64+
// Enabled indicates whether to create an Ingress resource to expose the CRL.
65+
// (default: true)
66+
// +optional
67+
Enabled *bool `json:"enabled"`
68+
69+
// Managed indicates whether the operator should manage the Ingress resource.
70+
// If false, the Ingress resource will not be created or updated by the operator.
71+
// (default: true)
72+
// +optional
73+
Managed *bool `json:"managed"`
74+
75+
// Hostname is the hostname to use for the ingress.
76+
// (One of Hostname or IPAddresses must be specified)
77+
// +kubebuilder:validation:MinLength=1
78+
// +optional
79+
Hostname *string `json:"hostname,omitempty"`
80+
81+
// ClassName is the ingress class name to use for the ingress.
82+
// +optional
83+
ClassName *string `json:"className,omitempty"`
84+
85+
// IPAddresses is a list of IP addresses to use for the ingress.
86+
// (One of Hostname or IPAddresses must be specified)
87+
// +optional
88+
IPAddresses []IPAddress `json:"ipAddresses,omitempty"`
89+
}
90+
5891
// CRLExposeSpec defines how the CRL should be exposed.
5992
type CRLExposeSpec struct {
6093
// Enabled indicates whether the CRL should be exposed.
@@ -69,6 +102,17 @@ type CRLExposeSpec struct {
69102
// Tolerations to deploy the Virtual IPs manager
70103
// +optional
71104
Tolerations []corev1.Toleration `json:"tolerations,omitempty"`
105+
106+
// Internal indicates whether the CRL should be exposed internally within the cluster.
107+
// (default: true)
108+
// +optional
109+
Internal *bool `json:"internal"`
110+
111+
// Ingress indicates whether the CRL should be exposed externally outside the cluster
112+
// using an Ingress resource.
113+
// (default: Disabled)
114+
// +optional
115+
Ingress *IngressSpec `json:"ingress"`
72116
}
73117

74118
// RevocationSpec defines a certificate to be revoked.
@@ -112,8 +156,12 @@ type ManagedCRLStatus struct {
112156
// SecretReady indicates whether the CRL is built and available in the Secret.
113157
SecretReady *bool `json:"secretReady,omitempty"`
114158
// PodExposed indicates whether the CRL expose Pod is running.
115-
PodExposed *bool `json:"podExposed,omitempty"`
116-
Conditions []metav1.Condition `json:"conditions,omitempty"`
159+
PodExposed *bool `json:"podExposed,omitempty"`
160+
// IngressExposed indicates whether the CRL Ingress is available.
161+
IngressExposed *bool `json:"ingressExposed,omitempty"`
162+
// IssuerConfigured indicates whether the Issuer is properly configured.
163+
IssuerConfigured *bool `json:"issuerConfigured,omitempty"`
164+
Conditions []metav1.Condition `json:"conditions,omitempty"`
117165

118166
// CRLValidUntil is the time until which the CRL is valid.
119167
CRLValidUntil metav1.Time `json:"crlValidUntil,omitempty"`
@@ -163,6 +211,21 @@ func (mcrl *ManagedCRL) IsExposed() bool {
163211
return mcrl.Spec.Expose != nil && mcrl.Spec.Expose.Enabled
164212
}
165213

214+
// IsIngressEnabled returns true if the CRL is configured to be exposed via Ingress.
215+
func (mcrl *ManagedCRL) IsIngressEnabled() bool {
216+
return mcrl.IsExposed() && mcrl.Spec.Expose.Ingress != nil && *mcrl.Spec.Expose.Ingress.Enabled
217+
}
218+
219+
// IsIngressManaged returns true if the Ingress is managed by the operator.
220+
func (mcrl *ManagedCRL) IsIngressManaged() bool {
221+
return mcrl.IsIngressEnabled() && mcrl.Spec.Expose.Ingress.Managed != nil && *mcrl.Spec.Expose.Ingress.Managed
222+
}
223+
224+
// IsInternalEnabled returns true if the CRL is configured to be exposed internally.
225+
func (mcrl *ManagedCRL) IsInternalEnabled() bool {
226+
return mcrl.IsExposed() && mcrl.Spec.Expose.Internal != nil && *mcrl.Spec.Expose.Internal
227+
}
228+
166229
// GetSecret returns the name of the Secret used to store the CRL.
167230
func (mcrl *ManagedCRL) GetSecret() *corev1.Secret {
168231
return &corev1.Secret{
@@ -203,6 +266,16 @@ func (mcrl *ManagedCRL) GetService() *corev1.Service {
203266
}
204267
}
205268

269+
// GetIngress returns the name of the Ingress used to expose the CRL.
270+
func (mcrl *ManagedCRL) GetIngress() *networkingv1.Ingress {
271+
return &networkingv1.Ingress{
272+
ObjectMeta: metav1.ObjectMeta{
273+
Name: fmt.Sprintf("%s-server", mcrl.Name),
274+
Namespace: mcrl.Namespace,
275+
},
276+
}
277+
}
278+
206279
// WithDefaults sets default values on the ManagedCRL resource.
207280
func (mcrl *ManagedCRL) WithDefaults() {
208281
mcrl.Spec.withDefaults()
@@ -236,6 +309,14 @@ func (ces *CRLExposeSpec) withDefaults() {
236309
ces.Image = &ImageSpec{}
237310
}
238311
ces.Image.withDefaults()
312+
313+
if ces.Ingress != nil {
314+
ces.Ingress.withDefaults()
315+
}
316+
317+
if ces.Internal == nil {
318+
ces.Internal = ptr.To(true)
319+
}
239320
}
240321

241322
func (is *ImageSpec) withDefaults() {
@@ -247,6 +328,15 @@ func (is *ImageSpec) withDefaults() {
247328
}
248329
}
249330

331+
func (is *IngressSpec) withDefaults() {
332+
if is.Enabled == nil {
333+
is.Enabled = ptr.To(true)
334+
}
335+
if is.Managed == nil {
336+
is.Managed = ptr.To(true)
337+
}
338+
}
339+
250340
// Validate validates the ManagedCRL resource.
251341
func (mcrl *ManagedCRL) Validate() error {
252342
err := mcrl.Spec.validate()
@@ -304,6 +394,14 @@ func (ces *CRLExposeSpec) validate() error {
304394
if err != nil {
305395
return fmt.Errorf("invalid image configuration: %w", err)
306396
}
397+
398+
if ces.Ingress != nil {
399+
err := ces.Ingress.validate()
400+
if err != nil {
401+
return fmt.Errorf("invalid ingress configuration: %w", err)
402+
}
403+
}
404+
307405
return nil
308406
}
309407

@@ -312,6 +410,18 @@ func (is *ImageSpec) validate() error {
312410
return nil
313411
}
314412

413+
func (is *IngressSpec) validate() error {
414+
if !*is.Enabled {
415+
return nil
416+
}
417+
418+
if is.Hostname == nil && len(is.IPAddresses) == 0 {
419+
return fmt.Errorf("either hostname or ipAddresses must be specified")
420+
}
421+
422+
return nil
423+
}
424+
315425
// ToRevocationListEntry converts a RevocationSpec to an x509.RevocationListEntry.
316426
func (rs RevocationSpec) ToRevocationListEntry() (x509.RevocationListEntry, error) {
317427
cleanSerial := strings.ReplaceAll(rs.SerialNumber, ":", "")
@@ -358,6 +468,33 @@ func (is *ImageSpec) GetImage() string {
358468
return image
359469
}
360470

471+
// GetCRLDistributionPoint returns the CRL distribution point URL based on the Ingress configuration.
472+
func (mcrl *ManagedCRL) GetCRLDistributionPoint() []string {
473+
var urls []string
474+
475+
// Add Ingress URLs if enabled
476+
if mcrl.IsIngressEnabled() {
477+
if mcrl.Spec.Expose.Ingress.Hostname != nil {
478+
urls = append(urls, fmt.Sprintf("http://%s/crl.der", *mcrl.Spec.Expose.Ingress.Hostname))
479+
}
480+
for _, ip := range mcrl.Spec.Expose.Ingress.IPAddresses {
481+
urls = append(urls, fmt.Sprintf("http://%s/crl.der", ip))
482+
}
483+
}
484+
485+
// Add internal URL if enabled
486+
if mcrl.IsInternalEnabled() {
487+
urls = append(urls, fmt.Sprintf("http://%s.%s.svc/crl.der", mcrl.GetName(), mcrl.GetNamespace()))
488+
}
489+
490+
return urls
491+
}
492+
493+
// NeedsIssuerConfiguration returns true if the Issuer needs to be configured.
494+
func (mcrl *ManagedCRL) NeedsIssuerConfiguration() bool {
495+
return len(mcrl.GetCRLDistributionPoint()) > 0
496+
}
497+
361498
// SetSecretReady sets the ManagedCRL status to SecretReady.
362499
func (mcrl *ManagedCRL) SetSecretReady() {
363500
condition := metav1.Condition{
@@ -413,3 +550,59 @@ func (mcrl *ManagedCRL) SetPodNotExposed(reason, message string) {
413550
meta.SetStatusCondition(&mcrl.Status.Conditions, condition)
414551
mcrl.Status.PodExposed = ptr.To(false)
415552
}
553+
554+
// SetIngressExposed sets the ManagedCRL status to IngressExposed.
555+
func (mcrl *ManagedCRL) SetIngressExposed() {
556+
condition := metav1.Condition{
557+
Type: "IngressExposed",
558+
Status: metav1.ConditionTrue,
559+
LastTransitionTime: metav1.Now(),
560+
Reason: "CRLIngressExposed",
561+
Message: "The ingress exposing the CRL is available",
562+
ObservedGeneration: mcrl.Generation,
563+
}
564+
meta.SetStatusCondition(&mcrl.Status.Conditions, condition)
565+
mcrl.Status.IngressExposed = ptr.To(true)
566+
}
567+
568+
// SetIngressNotExposed sets the ManagedCRL status to IngressNotExposed with the given reason and message.
569+
func (mcrl *ManagedCRL) SetIngressNotExposed(reason, message string) {
570+
condition := metav1.Condition{
571+
Type: "IngressExposed",
572+
Status: metav1.ConditionFalse,
573+
LastTransitionTime: metav1.Now(),
574+
Reason: reason,
575+
Message: message,
576+
ObservedGeneration: mcrl.Generation,
577+
}
578+
meta.SetStatusCondition(&mcrl.Status.Conditions, condition)
579+
mcrl.Status.IngressExposed = ptr.To(false)
580+
}
581+
582+
// SetIssuerConfigured sets the ManagedCRL status to IssuerConfigured.
583+
func (mcrl *ManagedCRL) SetIssuerConfigured() {
584+
condition := metav1.Condition{
585+
Type: "IssuerConfigured",
586+
Status: metav1.ConditionTrue,
587+
LastTransitionTime: metav1.Now(),
588+
Reason: "CRLIssuerConfigured",
589+
Message: "The issuer is properly configured",
590+
ObservedGeneration: mcrl.Generation,
591+
}
592+
meta.SetStatusCondition(&mcrl.Status.Conditions, condition)
593+
mcrl.Status.IssuerConfigured = ptr.To(true)
594+
}
595+
596+
// SetIssuerNotConfigured sets the ManagedCRL status to IssuerNotConfigured with the given reason and message.
597+
func (mcrl *ManagedCRL) SetIssuerNotConfigured(reason, message string) {
598+
condition := metav1.Condition{
599+
Type: "IssuerConfigured",
600+
Status: metav1.ConditionFalse,
601+
LastTransitionTime: metav1.Now(),
602+
Reason: reason,
603+
Message: message,
604+
ObservedGeneration: mcrl.Generation,
605+
}
606+
meta.SetStatusCondition(&mcrl.Status.Conditions, condition)
607+
mcrl.Status.IssuerConfigured = ptr.To(false)
608+
}

api/v1alpha1/zz_generated.deepcopy.go

Lines changed: 60 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)