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
17 changes: 17 additions & 0 deletions build/resources.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"strings"

templatev1 "github.com/openshift/api/template/v1"
operatorconfig "github.com/openshift/managed-cluster-validating-webhooks/config"
"github.com/openshift/managed-cluster-validating-webhooks/pkg/syncset"
webhooks "github.com/openshift/managed-cluster-validating-webhooks/pkg/webhooks"
utils "github.com/openshift/managed-cluster-validating-webhooks/pkg/webhooks/utils"
Expand Down Expand Up @@ -633,6 +634,17 @@ func createDaemonSet() *appsv1.DaemonSet {
RestartPolicy: corev1.RestartPolicyAlways,
ServiceAccountName: serviceAccountName,
Volumes: []corev1.Volume{
{
Name: "validation-webhook-config",
VolumeSource: corev1.VolumeSource{
ConfigMap: &corev1.ConfigMapVolumeSource{
LocalObjectReference: corev1.LocalObjectReference{
Name: operatorconfig.ValidationWebhookConfigMapName,
},
Optional: pointer.Bool(true),
},
},
},
{
Name: "service-certs",
VolumeSource: corev1.VolumeSource{
Expand Down Expand Up @@ -661,6 +673,11 @@ func createDaemonSet() *appsv1.DaemonSet {
Name: "webhooks",
Image: "${REGISTRY_IMG}@${IMAGE_DIGEST}",
VolumeMounts: []corev1.VolumeMount{
{
Name: "validation-webhook-config",
MountPath: operatorconfig.ValidationWebhookConfigMount,
ReadOnly: true,
},
{
Name: "service-certs",
MountPath: "/service-certs",
Expand Down
36 changes: 36 additions & 0 deletions build/resources_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package main

import (
"testing"

operatorconfig "github.com/openshift/managed-cluster-validating-webhooks/config"
)

func TestCreateDaemonSetConfigMapMount(t *testing.T) {
daemonSet := createDaemonSet()
container := daemonSet.Spec.Template.Spec.Containers[0]

if len(container.Env) != 0 {
t.Fatalf("environment = %#v, want none", container.Env)
}

var foundMount bool
for _, mount := range container.VolumeMounts {
if mount.Name == "validation-webhook-config" {
foundMount = mount.MountPath == operatorconfig.ValidationWebhookConfigMount && mount.ReadOnly
}
}
if !foundMount {
t.Fatalf("volume mounts = %#v, want read-only validation webhook config mount", container.VolumeMounts)
}

var foundVolume bool
for _, volume := range daemonSet.Spec.Template.Spec.Volumes {
if volume.Name == "validation-webhook-config" && volume.ConfigMap != nil {
foundVolume = volume.ConfigMap.Name == operatorconfig.ValidationWebhookConfigMapName && volume.ConfigMap.Optional != nil && *volume.ConfigMap.Optional
}
}
if !foundVolume {
t.Fatalf("volumes = %#v, want optional validation webhook config ConfigMap", daemonSet.Spec.Template.Spec.Volumes)
}
}
7 changes: 7 additions & 0 deletions build/selectorsyncset.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,9 @@ objects:
resources: {}
terminationMessagePolicy: FallbackToLogsOnError
volumeMounts:
- mountPath: /etc/validation-webhook-config
name: validation-webhook-config
readOnly: true
- mountPath: /service-certs
name: service-certs
readOnly: true
Expand All @@ -215,6 +218,10 @@ objects:
- effect: NoExecute
key: node-role.kubernetes.io/master
volumes:
- configMap:
name: validation-webhook-config
optional: true
name: validation-webhook-config
- name: service-certs
secret:
secretName: webhook-cert
Expand Down
4 changes: 4 additions & 0 deletions config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,8 @@ const (
// I know this isn't the operator's name but so much stuff has been coded to use this...
OperatorName = "validation-webhook"
OperatorNamespace = "openshift-validation-webhook"

ValidationWebhookConfigMapName = "validation-webhook-config"
CCSCPMSResizeConfigKey = "enableCCSCPMSResize"
ValidationWebhookConfigMount = "/etc/validation-webhook-config"
)
20 changes: 18 additions & 2 deletions docs/webhooks-short.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,22 @@
"webhookName": "customresourcedefinitions-validation",
"documentString": "Managed OpenShift Customers may not change CustomResourceDefinitions managed by Red Hat."
},
{
"webhookName": "hcpnamespace-validation",
"documentString": "Validates that only authorized users and service accounts can delete protected HCP namespaces"
},
{
"webhookName": "hiveownership-validation",
"documentString": "Managed OpenShift customers may not edit certain managed resources. A managed resource has a \"hive.openshift.io/managed\": \"true\" label."
},
{
"webhookName": "hostedcluster-validation",
"documentString": "Validates HostedCluster deletion operations are only performed by authorized service accounts"
},
{
"webhookName": "hostedcontrolplane-validation",
"documentString": "Validates HostedControlPlane deletion operations are only performed by authorized service accounts"
},
{
"webhookName": "imagecontentpolicies-validation",
"documentString": "Managed OpenShift customers may not create ImageContentSourcePolicy, ImageDigestMirrorSet, or ImageTagMirrorSet resources that configure mirrors that would conflict with system registries (e.g. quay.io, registry.redhat.io, registry.access.redhat.com, etc). For more details, see https://docs.openshift.com/"
Expand All @@ -31,13 +43,17 @@
"webhookName": "ingresscontroller-validation",
"documentString": "Managed OpenShift Customer may create IngressControllers without necessary taints. This can cause those workloads to be provisioned on master nodes."
},
{
"webhookName": "manifestworks-validation",
"documentString": "Validates ManifestWorks deletion operations are only performed by authorized service accounts"
},
{
"webhookName": "namespace-validation",
"documentString": "Managed OpenShift Customers may not modify namespaces specified in the [openshift-monitoring/managed-namespaces openshift-monitoring/ocp-namespaces] ConfigMaps because customer workloads should be placed in customer-created namespaces. Customers may not create namespaces identified by this regular expression (^com$|^io$|^in$) because it could interfere with critical DNS resolution. Additionally, customers may not set or change the values of these Namespace labels [managed.openshift.io/storage-pv-quota-exempt managed.openshift.io/service-lb-quota-exempt]."
},
{
"webhookName": "network-operator-validation",
"documentString": "Managed OpenShift customers may not modify critical fields in the network.operator CRD (such as spec.migration.networkType) because it can disrupt Cluster Network Operator operations and CNI migrations. Even cluster-admin users are blocked from modifying these critical fields."
"documentString": "Managed OpenShift customers may not modify critical fields in the network.operator CRD (such as spec.migration.networkType) because it can disrupt Cluster Network Operator operations and CNI migrations. Only backplane-cluster-admin, SRE, Cluster Network Operator (CNO), and Managed Upgrade Operator (MUO) service accounts are allowed to modify these critical fields. Regular cluster-admin users (system:admin) are explicitly blocked."
},
{
"webhookName": "networkpolicies-validation",
Expand All @@ -61,7 +77,7 @@
},
{
"webhookName": "regular-user-validation",
"documentString": "Managed OpenShift customers may not manage any objects in the following APIGroups [upgrade.managed.openshift.io config.openshift.io operator.openshift.io network.openshift.io admissionregistration.k8s.io addons.managed.openshift.io cloudingress.managed.openshift.io managed.openshift.io splunkforwarder.managed.openshift.io autoscaling.openshift.io machineconfiguration.openshift.io cloudcredential.openshift.io machine.openshift.io ocmagent.managed.openshift.io], nor may Managed OpenShift customers alter the APIServer, KubeAPIServer, OpenShiftAPIServer, ClusterVersion, Proxy or SubjectPermission objects."
"documentString": "Managed OpenShift customers may not manage any objects in the following APIGroups [cloudcredential.openshift.io admissionregistration.k8s.io addons.managed.openshift.io cloudingress.managed.openshift.io managed.openshift.io ocmagent.managed.openshift.io splunkforwarder.managed.openshift.io upgrade.managed.openshift.io machine.openshift.io autoscaling.openshift.io config.openshift.io machineconfiguration.openshift.io operator.openshift.io network.openshift.io], nor may Managed OpenShift customers alter the APIServer, KubeAPIServer, OpenShiftAPIServer, ClusterVersion, Proxy or SubjectPermission objects. On CCS clusters, when the trimmed enableCCSCPMSResize configuration value is exactly true, cluster administrators and dedicated administrators may update a ControlPlaneMachineSet AWS instance type from a non-metal m5 or m6i type to an equivalent or larger non-metal m5 or m6i type."
},
{
"webhookName": "scc-validation",
Expand Down
88 changes: 86 additions & 2 deletions docs/webhooks.json
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,27 @@
],
"documentString": "Managed OpenShift Customers may not change CustomResourceDefinitions managed by Red Hat."
},
{
"webhookName": "hcpnamespace-validation",
"rules": [
{
"operations": [
"DELETE"
],
"apiGroups": [
""
],
"apiVersions": [
"*"
],
"resources": [
"namespaces"
],
"scope": "Cluster"
}
],
"documentString": "Validates that only authorized users and service accounts can delete protected HCP namespaces"
},
{
"webhookName": "hiveownership-validation",
"rules": [
Expand Down Expand Up @@ -113,6 +134,48 @@
},
"documentString": "Managed OpenShift customers may not edit certain managed resources. A managed resource has a \"hive.openshift.io/managed\": \"true\" label."
},
{
"webhookName": "hostedcluster-validation",
"rules": [
{
"operations": [
"DELETE"
],
"apiGroups": [
"hypershift.openshift.io"
],
"apiVersions": [
"*"
],
"resources": [
"hostedclusters"
],
"scope": "Namespaced"
}
],
"documentString": "Validates HostedCluster deletion operations are only performed by authorized service accounts"
},
{
"webhookName": "hostedcontrolplane-validation",
"rules": [
{
"operations": [
"DELETE"
],
"apiGroups": [
"hypershift.openshift.io"
],
"apiVersions": [
"*"
],
"resources": [
"hostedcontrolplanes"
],
"scope": "Namespaced"
}
],
"documentString": "Validates HostedControlPlane deletion operations are only performed by authorized service accounts"
},
{
"webhookName": "imagecontentpolicies-validation",
"rules": [
Expand Down Expand Up @@ -198,6 +261,27 @@
],
"documentString": "Managed OpenShift Customer may create IngressControllers without necessary taints. This can cause those workloads to be provisioned on master nodes."
},
{
"webhookName": "manifestworks-validation",
"rules": [
{
"operations": [
"DELETE"
],
"apiGroups": [
"work.open-cluster-management.io"
],
"apiVersions": [
"*"
],
"resources": [
"manifestworks"
],
"scope": "Namespaced"
}
],
"documentString": "Validates ManifestWorks deletion operations are only performed by authorized service accounts"
},
{
"webhookName": "namespace-validation",
"rules": [
Expand Down Expand Up @@ -241,7 +325,7 @@
"scope": "Cluster"
}
],
"documentString": "Managed OpenShift customers may not modify critical fields in the network.operator CRD (such as spec.migration.networkType) because it can disrupt Cluster Network Operator operations and CNI migrations. Even cluster-admin users are blocked from modifying these critical fields."
"documentString": "Managed OpenShift customers may not modify critical fields in the network.operator CRD (such as spec.migration.networkType) because it can disrupt Cluster Network Operator operations and CNI migrations. Only backplane-cluster-admin, SRE, Cluster Network Operator (CNO), and Managed Upgrade Operator (MUO) service accounts are allowed to modify these critical fields. Regular cluster-admin users (system:admin) are explicitly blocked."
},
{
"webhookName": "networkpolicies-validation",
Expand Down Expand Up @@ -498,7 +582,7 @@
"scope": "*"
}
],
"documentString": "Managed OpenShift customers may not manage any objects in the following APIGroups [splunkforwarder.managed.openshift.io autoscaling.openshift.io ocmagent.managed.openshift.io upgrade.managed.openshift.io config.openshift.io machineconfiguration.openshift.io operator.openshift.io network.openshift.io cloudcredential.openshift.io machine.openshift.io admissionregistration.k8s.io addons.managed.openshift.io cloudingress.managed.openshift.io managed.openshift.io], nor may Managed OpenShift customers alter the APIServer, KubeAPIServer, OpenShiftAPIServer, ClusterVersion, Proxy or SubjectPermission objects."
"documentString": "Managed OpenShift customers may not manage any objects in the following APIGroups [config.openshift.io machineconfiguration.openshift.io operator.openshift.io cloudcredential.openshift.io addons.managed.openshift.io cloudingress.managed.openshift.io managed.openshift.io autoscaling.openshift.io network.openshift.io machine.openshift.io admissionregistration.k8s.io ocmagent.managed.openshift.io splunkforwarder.managed.openshift.io upgrade.managed.openshift.io], nor may Managed OpenShift customers alter the APIServer, KubeAPIServer, OpenShiftAPIServer, ClusterVersion, Proxy or SubjectPermission objects. On CCS clusters, when the trimmed enableCCSCPMSResize configuration value is exactly true, cluster administrators and dedicated administrators may update a ControlPlaneMachineSet AWS instance type from a non-metal m5 or m6i type to an equivalent or larger non-metal m5 or m6i type."
},
{
"webhookName": "scc-validation",
Expand Down
2 changes: 1 addition & 1 deletion pkg/webhooks/hcpnamespace/hcpnamespace.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import (

const (
WebhookName string = "hcpnamespace-validation"
docString string = "Validates HCP namespace deletion operations are only performed by authorized service accounts"
docString string = "Validates that only authorized users and service accounts can delete protected HCP namespaces"
)

var (
Expand Down
2 changes: 1 addition & 1 deletion pkg/webhooks/hostedcontrolplane/hostedcontrolplane.go
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ func (s *HostedControlPlaneWebhook) authorized(request admissionctl.Request) adm
}

saName := strings.Split(request.UserInfo.Username, ":")
if len(saName) > 0 && slices.Contains(allowedServiceAccountsNames, saName[len(saName)-1]) {
if len(saName) == 4 && saName[0] == "system" && saName[1] == "serviceaccount" && saName[2] != "" && slices.Contains(allowedServiceAccountsNames, saName[3]) {
ret = admissionctl.Allowed("Service account is authorized to delete HostedControlPlane resources")
ret.UID = request.AdmissionRequest.UID
return ret
Expand Down
24 changes: 24 additions & 0 deletions pkg/webhooks/hostedcontrolplane/hostedcontrolplane_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,30 @@ func TestHostedControlPlaneAuthorized(t *testing.T) {
operation: admissionv1.Delete,
shouldBeAllowed: true,
},
{
name: "Allowed short-name service account can delete hostedcontrolplane",
username: "system:serviceaccount:openshift-cluster-api:cluster-api",
operation: admissionv1.Delete,
shouldBeAllowed: true,
},
{
name: "Short name without service account username format cannot delete hostedcontrolplane",
username: "oidc:cluster-api",
operation: admissionv1.Delete,
shouldBeAllowed: false,
},
{
name: "Short name with an empty namespace cannot delete hostedcontrolplane",
username: "system:serviceaccount::cluster-api",
operation: admissionv1.Delete,
shouldBeAllowed: false,
},
{
name: "Short name with extra username components cannot delete hostedcontrolplane",
username: "system:serviceaccount:openshift-cluster-api:cluster-api:extra",
operation: admissionv1.Delete,
shouldBeAllowed: false,
},
{
name: "Random user cannot delete hostedcontrolplane",
username: "unknown-user",
Expand Down
Loading