diff --git a/install/0000_80_machine-config_00_networkpolicy-kube-rbac-proxy-crio.yaml b/install/0000_80_machine-config_00_networkpolicy-kube-rbac-proxy-crio.yaml new file mode 100644 index 0000000000..c9bcf22ea4 --- /dev/null +++ b/install/0000_80_machine-config_00_networkpolicy-kube-rbac-proxy-crio.yaml @@ -0,0 +1,16 @@ +apiVersion: networking.k8s.io/v1 +kind: NetworkPolicy +metadata: + name: kube-rbac-proxy-crio-deny-all + namespace: openshift-machine-config-operator + annotations: + include.release.openshift.io/ibm-cloud-managed: "true" + include.release.openshift.io/self-managed-high-availability: "true" + include.release.openshift.io/single-node-developer: "true" +spec: + podSelector: + matchLabels: + k8s-app: kube-rbac-proxy-crio + policyTypes: + - Ingress + - Egress diff --git a/manifests/machineconfigcontroller/networkpolicy.yaml b/manifests/machineconfigcontroller/networkpolicy.yaml new file mode 100644 index 0000000000..7b4ac2f898 --- /dev/null +++ b/manifests/machineconfigcontroller/networkpolicy.yaml @@ -0,0 +1,12 @@ +apiVersion: networking.k8s.io/v1 +kind: NetworkPolicy +metadata: + name: machine-config-controller-deny-all + namespace: {{.TargetNamespace}} +spec: + podSelector: + matchLabels: + k8s-app: machine-config-controller + policyTypes: + - Ingress + - Egress diff --git a/manifests/machineconfigdaemon/networkpolicy.yaml b/manifests/machineconfigdaemon/networkpolicy.yaml new file mode 100644 index 0000000000..0bafea15b2 --- /dev/null +++ b/manifests/machineconfigdaemon/networkpolicy.yaml @@ -0,0 +1,12 @@ +apiVersion: networking.k8s.io/v1 +kind: NetworkPolicy +metadata: + name: machine-config-daemon-deny-all + namespace: {{.TargetNamespace}} +spec: + podSelector: + matchLabels: + k8s-app: machine-config-daemon + policyTypes: + - Ingress + - Egress diff --git a/manifests/machineconfigserver/networkpolicy.yaml b/manifests/machineconfigserver/networkpolicy.yaml new file mode 100644 index 0000000000..3c50cea5e1 --- /dev/null +++ b/manifests/machineconfigserver/networkpolicy.yaml @@ -0,0 +1,12 @@ +apiVersion: networking.k8s.io/v1 +kind: NetworkPolicy +metadata: + name: machine-config-server-deny-all + namespace: {{.TargetNamespace}} +spec: + podSelector: + matchLabels: + k8s-app: machine-config-server + policyTypes: + - Ingress + - Egress diff --git a/pkg/operator/bootstrap.go b/pkg/operator/bootstrap.go index c0b9bed742..092fb25a38 100644 --- a/pkg/operator/bootstrap.go +++ b/pkg/operator/bootstrap.go @@ -148,7 +148,7 @@ func buildSpec(dependencies *BootstrapDependencies, imgs *ctrlcommon.Images, rel } config := getRenderConfig("", dependencies.KubeAPIServerServingCA, spec, - &imgs.RenderConfigImages, dependencies.Infrastructure, nil, nil, "2") + &imgs.RenderConfigImages, dependencies.Infrastructure, nil, nil, "2", "") return config, nil } diff --git a/pkg/operator/operator.go b/pkg/operator/operator.go index d53291fa47..e993c74345 100644 --- a/pkg/operator/operator.go +++ b/pkg/operator/operator.go @@ -570,6 +570,7 @@ func (optr *Operator) sync(key string) error { // "RenderConfig" should be the first one to run (except OSImageStream) as it sets the renderConfig in // the operator for the sync funcs below {"RenderConfig", optr.syncRenderConfig}, + {"NetworkPolicies", optr.syncNetworkPolicies}, {"MachineConfiguration", optr.syncMachineConfiguration}, {"MachineConfigNode", optr.syncMachineConfigNodes}, {"MachineConfigPools", optr.syncMachineConfigPools}, diff --git a/pkg/operator/render.go b/pkg/operator/render.go index 1afcd1e3d9..7cd3c39e25 100644 --- a/pkg/operator/render.go +++ b/pkg/operator/render.go @@ -52,6 +52,7 @@ type renderConfig struct { TLSMinVersion string TLSCipherSuites []string LogLevel string + ClusterNetworkCIDR string } type assetRenderer struct { diff --git a/pkg/operator/sync.go b/pkg/operator/sync.go index 64f8fbc81a..d3bcb261ac 100644 --- a/pkg/operator/sync.go +++ b/pkg/operator/sync.go @@ -653,7 +653,13 @@ func (optr *Operator) syncRenderConfig(_ *renderConfig, _ *configv1.ClusterOpera optr.setOperatorLogLevel(mcop.Spec.OperatorLogLevel) } - optr.renderConfig = getRenderConfig(optr.namespace, string(kubeAPIServerServingCABytes), spec, &imgs.RenderConfigImages, infra, pointerConfigData, apiServer, fmt.Sprintf("%d", optr.logLevel)) + // Get the Cluster Network CIDR for the MCC's allow NetworkPolicy + clusterNetworkCIDR, err := optr.getClusterNetworkCIDR() + if err != nil { + return err + } + + optr.renderConfig = getRenderConfig(optr.namespace, string(kubeAPIServerServingCABytes), spec, &imgs.RenderConfigImages, infra, pointerConfigData, apiServer, fmt.Sprintf("%d", optr.logLevel), clusterNetworkCIDR) return nil } @@ -1994,6 +2000,21 @@ func (optr *Operator) getOsImageURLs(namespace, osImageStreamName string) (strin return cfg.BaseOSContainerImage, cfg.BaseOSExtensionsContainerImage, nil } +func (optr *Operator) getClusterNetworkCIDR() (string, error) { + // Fetch the cluster-wide Network configuration + network, err := optr.networkLister.Get("cluster") + if err != nil { + return "", err + } + + // Always use Status as it represents the current reality of the CNI + if len(network.Status.ClusterNetwork) > 0 { + return network.Status.ClusterNetwork[0].CIDR, nil + } + + return "", fmt.Errorf("no cluster network CIDR found in status") +} + func (optr *Operator) getCAsFromConfigMap(namespace, name, key string) ([]byte, error) { cm, err := optr.clusterCmLister.ConfigMaps(namespace).Get(name) if err != nil { @@ -2138,7 +2159,7 @@ func setGVK(obj runtime.Object, scheme *runtime.Scheme) error { return nil } -func getRenderConfig(tnamespace, kubeAPIServerServingCA string, ccSpec *mcfgv1.ControllerConfigSpec, imgs *ctrlcommon.RenderConfigImages, infra *configv1.Infrastructure, pointerConfigData []byte, apiServer *configv1.APIServer, logLevel string) *renderConfig { +func getRenderConfig(tnamespace, kubeAPIServerServingCA string, ccSpec *mcfgv1.ControllerConfigSpec, imgs *ctrlcommon.RenderConfigImages, infra *configv1.Infrastructure, pointerConfigData []byte, apiServer *configv1.APIServer, logLevel, clusterNetworkCIDR string) *renderConfig { tlsMinVersion, tlsCipherSuites := ctrlcommon.GetSecurityProfileCiphersFromAPIServer(apiServer) return &renderConfig{ TargetNamespace: tnamespace, @@ -2153,6 +2174,7 @@ func getRenderConfig(tnamespace, kubeAPIServerServingCA string, ccSpec *mcfgv1.C TLSMinVersion: tlsMinVersion, TLSCipherSuites: tlsCipherSuites, LogLevel: logLevel, + ClusterNetworkCIDR: clusterNetworkCIDR, } } @@ -2610,3 +2632,52 @@ func (optr *Operator) getOCPVersionFromClusterVersion() string { } return fmt.Sprintf("%d.%d.%d", parsedVersion.Major(), parsedVersion.Minor(), parsedVersion.Patch()) } + +func (optr *Operator) syncNetworkPolicies(config *renderConfig, _ *configv1.ClusterOperator) error { + // 1. Define your manifest list + manifests := []string{ + // "common/network-policies/00-default-deny-all.yaml", + "common/network-policies/machine-config-controller-allow.yaml", + // "common/network-policies/03-allow-operator.yaml", + } + + // if config.IsImageBuildEnabled { + // manifests = append(manifests, "common/network-policies/02-allow-os-builder.yaml") + // } + + // 2. Iterate and Apply using Server-Side Apply + for _, path := range manifests { + // Render the template using the operator's internal asset renderer + npBytes, err := renderAsset(config, path) + if err != nil { + return fmt.Errorf("failed to render %s: %w", path, err) + } + + // Convert raw bytes to a NetworkPolicy object + netPol := resourceread.ReadNetworkPolicyV1OrDie(npBytes) + + // Marshal the NetworkPolicy to JSON for Server-Side Apply + netPolBytes, err := json.Marshal(netPol) + if err != nil { + return fmt.Errorf("failed to marshal network policy %s: %w", netPol.Name, err) + } + + // Apply with ForceOwnership to ensure the Operator is the source of truth + // Apply via Server-Side Apply with ForceOwnership + // This ensures the MCO 'owns' these fields and reverts manual drifts + _, err = optr.kubeClient.NetworkingV1().NetworkPolicies(netPol.Namespace).Patch( + context.TODO(), + netPol.Name, + types.ApplyPatchType, + netPolBytes, + metav1.PatchOptions{ + FieldManager: "machine-config-operator", + Force: ptr.To(true), + }, + ) + if err != nil { + return fmt.Errorf("failed to apply network policy %s: %w", netPol.Name, err) + } + } + return nil +} diff --git a/templates/common/network-policies/machine-config-controller-allow.yaml b/templates/common/network-policies/machine-config-controller-allow.yaml new file mode 100644 index 0000000000..3ba482e0e3 --- /dev/null +++ b/templates/common/network-policies/machine-config-controller-allow.yaml @@ -0,0 +1,43 @@ +apiVersion: networking.k8s.io/v1 +kind: NetworkPolicy +metadata: + name: mco-allow-controller + namespace: {{.TargetNamespace}} +spec: + podSelector: + matchLabels: # TODO: see if the following should be `k8s-app: {{.ControllerAppLabel}}` instead + k8s-app: machine-config-controller + policyTypes: + - Ingress + - Egress + ingress: + - from: # Allow Prometheus Metrics Scraping + - namespaceSelector: + matchLabels: + network.openshift.io/policy-group: monitoring + ports: + - protocol: TCP + port: 9001 + - from: # Allow Kubelet Health Probes (from Node Network) + - ipBlock: + cidr: {{.ClusterNetworkCIDR}} + ports: + - protocol: TCP + port: 9443 + egress: + - to: # Allow API Server Access + - namespaceSelector: + matchLabels: + kubernetes.io/metadata.name: openshift-kube-apiserver + ports: + - protocol: TCP + port: 6443 + - to: # Allow DNS Lookups + - namespaceSelector: + matchLabels: + kubernetes.io/metadata.name: openshift-dns + ports: + - protocol: UDP + port: 53 + - protocol: TCP + port: 53