Add pod security admission controller labels

Pod security admission controller labels on namespaces are needed
for pod security admission controller to know how restrictive each
namespace is. This commit adds labels for pod security admission
controller to our namespaces. Pod security admission controller
is enabled by default on kubernetes 1.23. These labels do nothing
harmful or beneficial on the lower versions of kubernetes.

Test Plan:
PASS: Bootstrap system and ensure the pod security admission
      controller labels are present on our namespaces (kube-system,
      armada, deployment, and any namespaces created by applications
      such as cert-manager)
PASS: Upgrade an old system and ensure the labels are added after
      the upgrade is finished
PASS: Try to bring up privileged pods in a baseline namespace,
      ensure it fails. This was done on a developer iso, since
      we do not have kubernetes 1.23 ready yet. The same labels
      were applied to the developer iso's namespaces.
PASS: Deploy a privileged pod in a baseline namespace in the
      current kubernetes version. Ensure it is NOT rejected

Change-Id: Ib909eaacb6bba3b5c3327e2f9998a5ecdcb70e9b
Story: 2009833
Task: 44764
Signed-off-by: Jerry Sun <jerry.sun@windriver.com>
This commit is contained in:
Jerry Sun 2022-03-11 19:54:02 -05:00
parent f9dc544340
commit aa93e03b10
5 changed files with 150 additions and 0 deletions

View File

@ -0,0 +1,87 @@
#!/usr/bin/python
# Copyright (c) 2022 Wind River Systems, Inc.
#
# SPDX-License-Identifier: Apache-2.0
#
# This script adds pod security admission controller labels to a system
# after upgrades. These are applied by ansible and sysinv when a new
# namespace is created during application deployment. Upgrades needs
# to apply these labels to existing namespaces
import subprocess
import sys
from controllerconfig.common import log
from sysinv.helm import common
LOG = log.get_logger(__name__)
def main():
action = None
from_release = None
to_release = None
arg = 1
while arg < len(sys.argv):
if arg == 1:
from_release = sys.argv[arg]
elif arg == 2:
to_release = sys.argv[arg]
elif arg == 3:
action = sys.argv[arg]
else:
print("Invalid option %s." % sys.argv[arg])
return 1
arg += 1
log.configure()
if from_release == '21.12' and action == 'activate':
LOG.info("%s invoked from_release = %s to_release = %s action = %s"
% (sys.argv[0], from_release, to_release, action))
add_pod_security_admission_controller_labels()
def add_pod_security_admission_controller_labels():
try:
cmd = ["kubectl", "--kubeconfig=/etc/kubernetes/admin.conf",
"get", "namespaces", "-o=name"]
namespaces_output = subprocess.check_output(cmd)
except Exception as exc:
LOG.error('Command failed:\n %s' % (cmd))
raise Exception('Cannot get namespaces for pod security labels')
for line in namespaces_output.splitlines():
# we add pod security admission controller labels to namespaces that
# we create
namespace = line.replace("namespace/", "")
if namespace not in common.PRIVILEGED_NS \
and namespace not in common.BASELINE_NS:
continue
security_version = 'v1.23'
security_level = 'baseline'
if namespace in common.PRIVILEGED_NS:
security_level = 'privileged'
try:
cmd = ["kubectl", "--kubeconfig=/etc/kubernetes/admin.conf",
"label", "--overwrite", "namespaces", namespace,
"pod-security.kubernetes.io/enforce=%s"
% (security_level),
"pod-security.kubernetes.io/warn=%s"
% (security_level),
"pod-security.kubernetes.io/audit=%s"
% (security_level),
"pod-security.kubernetes.io/enforce-version=%s"
% (security_version),
"pod-security.kubernetes.io/warn-version=%s"
% (security_version),
"pod-security.kubernetes.io/audit-version=%s"
% (security_version)]
subprocess.call(cmd)
except Exception as exc:
LOG.error('Command failed:\n %s\n%s' % (cmd, exc))
raise Exception('Cannot assign pod security label')
if __name__ == "__main__":
sys.exit(main())

View File

@ -448,6 +448,15 @@ class KubeOperator(object):
LOG.error("Kubernetes exception in kube_get_secret: %s" % e)
raise
def kube_patch_namespace(self, namespace, body):
c = self._get_kubernetesclient_core()
try:
c.patch_namespace(namespace, body)
except Exception as e:
LOG.error("Failed to patch namespace %s with patch %s: %s"
% (namespace, body, e))
raise
def kube_create_secret(self, namespace, body):
c = self._get_kubernetesclient_core()
try:

View File

@ -38,6 +38,16 @@ HELM_NS_STORAGE_PROVISIONER = HELM_NS_KUBE_SYSTEM
HELM_NS_CERT_MANAGER = 'cert-manager'
HELM_NS_VAULT = 'vault'
HELM_NS_NOTIFICATION = 'notification'
HELM_NS_DEPLOYMENT = 'deployment'
HELM_NS_ARMADA = 'armada'
# namespace groups for pod security admission controller
PRIVILEGED_NS = [HELM_NS_KUBE_SYSTEM]
BASELINE_NS = [HELM_NS_CEPH, HELM_NS_NFS, HELM_NS_OPENSTACK, HELM_NS_HELM_TOOLKIT,
HELM_NS_MONITOR, HELM_NS_RBD_PROVISIONER, HELM_NS_STORAGE_PROVISIONER,
HELM_NS_CERT_MANAGER, HELM_NS_VAULT, HELM_NS_NOTIFICATION,
HELM_NS_DEPLOYMENT, HELM_NS_ARMADA]
POD_SECURITY_VERSION = 'v1.23'
# Services
# Matches configassistant.py value => Should change to STARLINGX

View File

@ -81,6 +81,7 @@ class AppLifecycleOperator(object):
if hook_info.operation == constants.APP_APPLY_OP and \
hook_info.relative_timing == constants.APP_LIFECYCLE_TIMING_PRE:
lifecycle_utils.create_local_registry_secrets(app_op, app, hook_info)
lifecycle_utils.add_pod_security_admission_controller_labels(app_op, app, hook_info)
elif hook_info.operation == constants.APP_REMOVE_OP and \
hook_info.relative_timing == constants.APP_LIFECYCLE_TIMING_POST:
lifecycle_utils.delete_local_registry_secrets(app_op, app, hook_info)

View File

@ -199,6 +199,49 @@ def delete_configmap(app_op, namespace, configmap):
raise
def add_pod_security_admission_controller_labels(app_op, app, hook_info):
""" adds labels to newly created namespaces
Pod security admission controllers uses labels to enforce policies
this method adds labels used by pod security admission controller
this is needed because new applications can create new namespaces
as they are deployed.
:param app_op: AppOperator object
:param app: AppOperator.Application object
:param hook_info: LifecycleHookInfo object
"""
app_ns = app_op._helm.get_helm_application_namespaces(app.name)
namespaces = \
list(set([ns for ns_list in app_ns.values() for ns in ns_list]))
for ns in namespaces:
security_level = 'baseline'
if ns in common.PRIVILEGED_NS:
security_level = 'privileged'
body = {
"metadata": {
"labels": {
"pod-security.kubernetes.io/enforce": security_level,
"pod-security.kubernetes.io/warn": security_level,
"pod-security.kubernetes.io/audit": security_level,
"pod-security.kubernetes.io/enforce-version": common.POD_SECURITY_VERSION,
"pod-security.kubernetes.io/warn-version": common.POD_SECURITY_VERSION,
"pod-security.kubernetes.io/audit-version": common.POD_SECURITY_VERSION}
}
}
try:
app_op._kube.kube_patch_namespace(ns, body)
except Exception as e:
LOG.error(e)
raise
def create_local_registry_secrets(app_op, app, hook_info):
# Temporary function to create default registry secret
# which would be used by kubernetes to pull images from