Skip to content
Open
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
7 changes: 7 additions & 0 deletions .github/actions/kind-cluster/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -121,3 +121,10 @@ runs:
shell: bash
run: |
kustomize build --enable-helm ./ci/nfs/overlay/ | kubectl apply -f -

- name: Install Cert-Manager
shell: bash
run: |
kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.19.1/cert-manager.yaml
kubectl wait --for=condition=available deployment/cert-manager-webhook -n cert-manager --timeout=5m
kubectl wait --for=condition=available deployment/cert-manager -n cert-manager --timeout=5m
2 changes: 1 addition & 1 deletion .tekton/rhtas-operator-bundle-pull-request.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ spec:
- name: image-expires-after
value: 5d
- name: manager-pipelinerun-selector
value: appstudio.openshift.io/application=operator,appstudio.openshift.io/component=rhtas-operator,pipelinesascode.tekton.dev/sha={{revision}},pipelinesascode.tekton.dev/event-type in (pull_request,incoming)
value: appstudio.openshift.io/application=operator,appstudio.openshift.io/component=rhtas-operator,pipelinesascode.tekton.dev/sha={{revision}},pipelinesascode.tekton.dev/event-type in (pull_request,incoming,retest-all-comment)
- name: manager-registry-url
value: registry.redhat.io/rhtas/rhtas-rhel9-operator
pipelineRef:
Expand Down
4 changes: 2 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -113,8 +113,8 @@ help: ## Display this help.
##@ Development

.PHONY: manifests
manifests: controller-gen ## Generate WebhookConfiguration, ClusterRole and CustomResourceDefinition objects.
$(CONTROLLER_GEN) rbac:roleName=manager-role crd webhook paths="./..." output:crd:artifacts:config=config/crd/bases
manifests: controller-gen ## Generate ClusterRole and CustomResourceDefinition objects.
$(CONTROLLER_GEN) rbac:roleName=manager-role crd paths="./..." output:crd:artifacts:config=config/crd/bases

.PHONY: generate
generate: controller-gen ## Generate code containing DeepCopy, DeepCopyInto, and DeepCopyObject method implementations.
Expand Down
1 change: 1 addition & 0 deletions api/v1alpha1/securesign_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ type SecuresignTSAStatus struct {
//+kubebuilder:printcolumn:name="Rekor URL",type=string,JSONPath=`.status.rekor.url`,description="The rekor url"
//+kubebuilder:printcolumn:name="Fulcio URL",type=string,JSONPath=`.status.fulcio.url`,description="The fulcio url"
//+kubebuilder:printcolumn:name="Tuf URL",type=string,JSONPath=`.status.tuf.url`,description="The tuf url"
//+kubebuilder:webhook:path=/validate,mutating=false,failurePolicy=fail,groups=rhtas.redhat.com,resources=securesigns,verbs=create,versions=v1alpha1,name=securesign.rhtas.redhat.com,sideEffects=None,admissionReviewVersions=v1

// Securesign is the Schema for the securesigns API
type Securesign struct {
Expand Down
12 changes: 12 additions & 0 deletions cmd/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ import (
"github.com/securesign/operator/internal/controller/trillian"
"github.com/securesign/operator/internal/controller/tsa"
"github.com/securesign/operator/internal/controller/tuf"
rhtas_webhook "github.com/securesign/operator/internal/webhook"
//+kubebuilder:scaffold:imports
)

Expand Down Expand Up @@ -195,6 +196,17 @@ func main() {
os.Exit(1)
}

if err := ctrl.NewWebhookManagedBy(mgr).
For(&rhtasv1alpha1.Securesign{}).
WithValidator(&rhtas_webhook.SecureSignValidator{
Client: mgr.GetClient(),
}).
WithValidatorCustomPath("/validate").
Complete(); err != nil {
setupLog.Error(err, "unable to create SecureSign validating webhook")
os.Exit(1)
}

setupController("securesign", securesign.NewReconciler, mgr)
setupController("fulcio", fulcio.NewReconciler, mgr)
setupController("trillian", trillian.NewReconciler, mgr)
Expand Down
4 changes: 1 addition & 3 deletions config/default/kustomization.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,6 @@ namePrefix: rhtas-
#commonLabels:
# someName: someValue

# [WEBHOOK] To enable webhook, uncomment all the sections with [WEBHOOK] prefix including the one in
# crd/kustomization.yaml
#- ../webhook
# [CERTMANAGER] To enable cert-manager, uncomment all sections with 'CERTMANAGER'. 'WEBHOOK' components are required.
#- ../certmanager
# [PROMETHEUS] To enable prometheus monitor, uncomment all sections with 'PROMETHEUS'.
Expand All @@ -26,6 +23,7 @@ resources:
- ../rbac
- ../manager
- ../prometheus
- ../webhook

patches:
- path: manager_metrics_patch.yaml
Expand Down
21 changes: 21 additions & 0 deletions config/env/kubernetes/cert_resources.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
apiVersion: cert-manager.io/v1
kind: Issuer
metadata:
name: selfsigned-issuer
namespace: openshift-rhtas-operator
spec:
selfSigned: {}
---
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: webhook-serving-cert
namespace: openshift-rhtas-operator
spec:
secretName: webhook-server-tls
issuerRef:
name: selfsigned-issuer
kind: Issuer
dnsNames:
- rhtas-controller-manager-webhook-service.openshift-rhtas-operator.svc
- rhtas-controller-manager-webhook-service.openshift-rhtas-operator.svc.cluster.local
6 changes: 6 additions & 0 deletions config/env/kubernetes/kubernetes_webhook_patch.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingWebhookConfiguration
metadata:
name: validation.securesigns.rhtas.redhat.com
annotations:
cert-manager.io/inject-ca-from: openshift-rhtas-operator/webhook-serving-cert
9 changes: 9 additions & 0 deletions config/env/kubernetes/kustomization.yaml
Original file line number Diff line number Diff line change
@@ -1,5 +1,14 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization

namespace: openshift-rhtas-operator

resources:
- ../../default
- cert_resources.yaml

patches:
- path: kubernetes_webhook_patch.yaml
target:
kind: ValidatingWebhookConfiguration
name: validation.securesigns.rhtas.redhat.com
6 changes: 6 additions & 0 deletions config/env/openshift/inject_ca_bundle_annotation_patch.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingWebhookConfiguration
metadata:
name: validation.securesigns.rhtas.redhat.com
annotations:
service.beta.openshift.io/inject-cabundle: "true"
10 changes: 10 additions & 0 deletions config/env/openshift/kustomization.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,13 @@ patches:
target:
kind: Deployment
name: operator-controller-manager

- path: serving_cert_annotation_patch.yaml
target:
kind: Service
name: controller-manager-webhook-service

- path: inject_ca_bundle_annotation_patch.yaml
target:
kind: ValidatingWebhookConfiguration
name: validation.securesigns.rhtas.redhat.com
9 changes: 9 additions & 0 deletions config/env/openshift/serving_cert_annotation_patch.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
apiVersion: v1
kind: Service
metadata:
name: controller-manager-webhook-service
namespace: system
labels:
control-plane: controller-manager
annotations:
service.beta.openshift.io/serving-cert-secret-name: webhook-server-tls
8 changes: 8 additions & 0 deletions config/manager/manager.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,10 @@ spec:
- --leader-elect
image: controller:latest
name: manager
volumeMounts:
- name: webhook-cert
mountPath: /tmp/k8s-webhook-server/serving-certs
readOnly: true
securityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
Expand Down Expand Up @@ -97,3 +101,7 @@ spec:
memory: 64Mi
serviceAccountName: operator-controller-manager
terminationGracePeriodSeconds: 10
volumes:
- name: webhook-cert
secret:
secretName: webhook-server-tls
3 changes: 3 additions & 0 deletions config/webhook/kustomization.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
resources:
- service.yaml
- webhook.yaml
15 changes: 15 additions & 0 deletions config/webhook/service.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
apiVersion: v1
kind: Service
metadata:
name: controller-manager-webhook-service
namespace: system
labels:
control-plane: operator-controller-manager
spec:
ports:
- name: https-webhook
port: 443
targetPort: 9443
protocol: TCP
selector:
control-plane: operator-controller-manager
25 changes: 25 additions & 0 deletions config/webhook/webhook.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
---
apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingWebhookConfiguration
metadata:
name: validation.securesigns.rhtas.redhat.com
webhooks:
- admissionReviewVersions:
- v1
clientConfig:
service:
name: controller-manager-webhook-service
namespace: system
path: /validate
failurePolicy: Fail
name: validation.securesigns.rhtas.redhat.com
rules:
- apiGroups:
- rhtas.redhat.com
apiVersions:
- v1alpha1
operations:
- CREATE
resources:
- securesigns
sideEffects: None
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ require (
github.com/operator-framework/api v0.35.0
github.com/operator-framework/operator-lib v0.19.0
github.com/robfig/cron/v3 v3.0.1
github.com/stretchr/testify v1.11.1
golang.org/x/net v0.46.0
google.golang.org/protobuf v1.36.10
k8s.io/api v0.34.1
Expand Down
64 changes: 64 additions & 0 deletions internal/webhook/securesign_validator.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
package webhooks

import (
"context"
"fmt"

rhtasv1alpha1 "github.com/securesign/operator/api/v1alpha1"
corev1 "k8s.io/api/core/v1"
apierrors "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/types"
logf "sigs.k8s.io/controller-runtime/pkg/log"
admission "sigs.k8s.io/controller-runtime/pkg/webhook/admission"
)

func (v *SecureSignValidator) validateNamespacePolicy(ctx context.Context, operandCR *rhtasv1alpha1.Securesign) (admission.Warnings, error) {
reqLog := logf.FromContext(ctx)
targetNamespace := operandCR.GetNamespace()

if targetNamespace == "default" {
reqLog.Info("Validation failed: Deployment blocked in 'default' namespace.")
return nil, fmt.Errorf("installation into the 'default' namespace is prohibited by RHTAS policy")
}

ns := &corev1.Namespace{}

if err := v.Client.Get(ctx, types.NamespacedName{Name: targetNamespace}, ns); err != nil {
if apierrors.IsNotFound(err) {
return nil, nil
}
reqLog.Error(err, "Failed to retrieve target namespace object for validation.")
return nil, fmt.Errorf("failed to retrieve target namespace %s: %w", targetNamespace, err)
}

runLevel, found := ns.Labels["openshift.io/run-level"]
if found && reservedRunLevels[runLevel] {
reqLog.Info("Validation failed: Deployment blocked in reserved namespace.",
"namespace", targetNamespace, "run-level", runLevel)
return nil, fmt.Errorf("installation into reserved OpenShift namespace '%s' (run-level %s) is prohibited by RHTAS policy", targetNamespace, runLevel)
}

return nil, nil
}

func (v *SecureSignValidator) ValidateCreate(ctx context.Context, obj runtime.Object) (admission.Warnings, error) {
operandCR, ok := obj.(*rhtasv1alpha1.Securesign)
if !ok {
return nil, fmt.Errorf("expected SecureSign CR but got %T", obj)
}
return v.validateNamespacePolicy(ctx, operandCR)
}

func (v *SecureSignValidator) ValidateUpdate(ctx context.Context, oldObj, newObj runtime.Object) (admission.Warnings, error) {
operandCR, ok := newObj.(*rhtasv1alpha1.Securesign)
if !ok {
return nil, fmt.Errorf("expected SecureSign CR but got %T", newObj)
}
return v.validateNamespacePolicy(ctx, operandCR)
}

func (v *SecureSignValidator) ValidateDelete(ctx context.Context, obj runtime.Object) (admission.Warnings, error) {
// Allow all delete operations
return nil, nil
}
Loading
Loading