Skip to content

✨ Add openshift-serviceca certificate provider #1969

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions cmd/operator-controller/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -431,6 +431,9 @@ func run() error {
if features.OperatorControllerFeatureGate.Enabled(features.WebhookProviderCertManager) {
certProvider = certproviders.CertManagerCertificateProvider{}
isWebhookSupportEnabled = true
} else if features.OperatorControllerFeatureGate.Enabled(features.WebhookProviderOpenshiftServiceCA) {
certProvider = certproviders.OpenshiftServiceCaCertificateProvider{}
isWebhookSupportEnabled = true
}

// now initialize the helmApplier, assigning the potentially nil preAuth
Expand Down
19 changes: 15 additions & 4 deletions internal/operator-controller/features/features.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,11 @@ import (
const (
// Add new feature gates constants (strings)
// Ex: SomeFeature featuregate.Feature = "SomeFeature"
PreflightPermissions featuregate.Feature = "PreflightPermissions"
SingleOwnNamespaceInstallSupport featuregate.Feature = "SingleOwnNamespaceInstallSupport"
SyntheticPermissions featuregate.Feature = "SyntheticPermissions"
WebhookProviderCertManager featuregate.Feature = "WebhookProviderCertManager"
PreflightPermissions featuregate.Feature = "PreflightPermissions"
SingleOwnNamespaceInstallSupport featuregate.Feature = "SingleOwnNamespaceInstallSupport"
SyntheticPermissions featuregate.Feature = "SyntheticPermissions"
WebhookProviderCertManager featuregate.Feature = "WebhookProviderCertManager"
WebhookProviderOpenshiftServiceCA featuregate.Feature = "WebhookProviderOpenshiftServiceCA"
)

var operatorControllerFeatureGates = map[featuregate.Feature]featuregate.FeatureSpec{
Expand Down Expand Up @@ -52,6 +53,16 @@ var operatorControllerFeatureGates = map[featuregate.Feature]featuregate.Feature
PreRelease: featuregate.Alpha,
LockToDefault: false,
},

// WebhookProviderCertManager enables support for installing
// registry+v1 cluster extensions that include validating,
// mutating, and/or conversion webhooks with Openshift Service CA
// as the certificate provider.
WebhookProviderOpenshiftServiceCA: {
Default: false,
PreRelease: featuregate.Alpha,
LockToDefault: false,
},
}

var OperatorControllerFeatureGate featuregate.MutableFeatureGate = featuregate.NewFeatureGate()
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package certproviders

import (
admissionregistrationv1 "k8s.io/api/admissionregistration/v1"
corev1 "k8s.io/api/core/v1"
apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"sigs.k8s.io/controller-runtime/pkg/client"

"github.com/operator-framework/operator-controller/internal/operator-controller/rukpak/render"
"github.com/operator-framework/operator-controller/internal/operator-controller/rukpak/util"
)

const (
openshiftServiceCAServingCertNameAnnotation = "service.beta.openshift.io/serving-cert-secret-name"
openshiftServiceCAInjectCABundleAnnotation = "service.beta.openshift.io/inject-cabundle"
)

var _ render.CertificateProvider = (*OpenshiftServiceCaCertificateProvider)(nil)

type OpenshiftServiceCaCertificateProvider struct{}

func (p OpenshiftServiceCaCertificateProvider) InjectCABundle(obj client.Object, cfg render.CertificateProvisionerConfig) error {
switch obj.(type) {
case *admissionregistrationv1.ValidatingWebhookConfiguration:
p.addInjectCABundleAnnotation(obj)
case *admissionregistrationv1.MutatingWebhookConfiguration:
p.addInjectCABundleAnnotation(obj)
case *apiextensionsv1.CustomResourceDefinition:
p.addInjectCABundleAnnotation(obj)
case *corev1.Service:
p.addServingCertSecretNameAnnotation(obj, cfg.CertName)
}
return nil
}

func (p OpenshiftServiceCaCertificateProvider) AdditionalObjects(_ render.CertificateProvisionerConfig) ([]unstructured.Unstructured, error) {
return nil, nil
}

func (p OpenshiftServiceCaCertificateProvider) GetCertSecretInfo(cfg render.CertificateProvisionerConfig) render.CertSecretInfo {
return render.CertSecretInfo{
SecretName: cfg.CertName,
PrivateKeyKey: "tls.key",
CertificateKey: "tls.crt",
}
}

func (p OpenshiftServiceCaCertificateProvider) addServingCertSecretNameAnnotation(obj client.Object, certName string) {
injectionAnnotation := map[string]string{
openshiftServiceCAServingCertNameAnnotation: certName,
}
obj.SetAnnotations(util.MergeMaps(obj.GetAnnotations(), injectionAnnotation))
}

func (p OpenshiftServiceCaCertificateProvider) addInjectCABundleAnnotation(obj client.Object) {
injectionAnnotation := map[string]string{
openshiftServiceCAInjectCABundleAnnotation: "true",
}
obj.SetAnnotations(util.MergeMaps(obj.GetAnnotations(), injectionAnnotation))
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
package certproviders_test

import (
"testing"

"github.com/stretchr/testify/require"
admissionregistrationv1 "k8s.io/api/admissionregistration/v1"
corev1 "k8s.io/api/core/v1"
apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"sigs.k8s.io/controller-runtime/pkg/client"

"github.com/operator-framework/operator-controller/internal/operator-controller/rukpak/render"
"github.com/operator-framework/operator-controller/internal/operator-controller/rukpak/render/certproviders"
)

func Test_OpenshiftServiceCAProvider_InjectCABundle(t *testing.T) {
for _, tc := range []struct {
name string
obj client.Object
cfg render.CertificateProvisionerConfig
expectedObj client.Object
}{
{
name: "injects inject-cabundle annotation in validating webhook configuration",
obj: &admissionregistrationv1.ValidatingWebhookConfiguration{},
cfg: render.CertificateProvisionerConfig{
WebhookServiceName: "webhook-service",
Namespace: "namespace",
CertName: "cert-name",
},
expectedObj: &admissionregistrationv1.ValidatingWebhookConfiguration{
ObjectMeta: metav1.ObjectMeta{
Annotations: map[string]string{
"service.beta.openshift.io/inject-cabundle": "true",
},
},
},
},
{
name: "injects inject-cabundle annotation in mutating webhook configuration",
obj: &admissionregistrationv1.MutatingWebhookConfiguration{},
cfg: render.CertificateProvisionerConfig{
WebhookServiceName: "webhook-service",
Namespace: "namespace",
CertName: "cert-name",
},
expectedObj: &admissionregistrationv1.MutatingWebhookConfiguration{
ObjectMeta: metav1.ObjectMeta{
Annotations: map[string]string{
"service.beta.openshift.io/inject-cabundle": "true",
},
},
},
},
{
name: "injects inject-cabundle annotation in custom resource definition",
obj: &apiextensionsv1.CustomResourceDefinition{},
cfg: render.CertificateProvisionerConfig{
WebhookServiceName: "webhook-service",
Namespace: "namespace",
CertName: "cert-name",
},
expectedObj: &apiextensionsv1.CustomResourceDefinition{
ObjectMeta: metav1.ObjectMeta{
Annotations: map[string]string{
"service.beta.openshift.io/inject-cabundle": "true",
},
},
},
},
{
name: "injects serving-cert-secret-name annotation in service resource referencing the certificate name",
obj: &corev1.Service{},
cfg: render.CertificateProvisionerConfig{
WebhookServiceName: "webhook-service",
Namespace: "namespace",
CertName: "cert-name",
},
expectedObj: &corev1.Service{
ObjectMeta: metav1.ObjectMeta{
Annotations: map[string]string{
"service.beta.openshift.io/serving-cert-secret-name": "cert-name",
},
},
},
},
{
name: "ignores other objects",
obj: &corev1.Secret{},
cfg: render.CertificateProvisionerConfig{
WebhookServiceName: "webhook-service",
Namespace: "namespace",
CertName: "cert-name",
},
expectedObj: &corev1.Secret{},
},
} {
t.Run(tc.name, func(t *testing.T) {
certProvider := certproviders.OpenshiftServiceCaCertificateProvider{}
require.NoError(t, certProvider.InjectCABundle(tc.obj, tc.cfg))
require.Equal(t, tc.expectedObj, tc.obj)
})
}
}

func Test_OpenshiftServiceCAProvider_AdditionalObjects(t *testing.T) {
certProvider := certproviders.OpenshiftServiceCaCertificateProvider{}
objs, err := certProvider.AdditionalObjects(render.CertificateProvisionerConfig{
WebhookServiceName: "webhook-service",
Namespace: "namespace",
CertName: "cert-name",
})
require.NoError(t, err)
require.Nil(t, objs)
}

func Test_OpenshiftServiceCAProvider_GetCertSecretInfo(t *testing.T) {
certProvider := certproviders.OpenshiftServiceCaCertificateProvider{}
certInfo := certProvider.GetCertSecretInfo(render.CertificateProvisionerConfig{
WebhookServiceName: "webhook-service",
Namespace: "namespace",
CertName: "cert-name",
})
require.Equal(t, render.CertSecretInfo{
SecretName: "cert-name",
PrivateKeyKey: "tls.key",
CertificateKey: "tls.crt",
}, certInfo)
}
Loading