refactor pooler tls support and set pooler pod security context (#2255)

* bump pooler image
* set pooler pod security context
* use hard coded RunAsUser 100 and RunAsGroup 101 for pooler pod
* unify generation of TLS secret mounts
* extend documentation on tls support
* add unit test for testing TLS support for pooler
* add e2e test for tls support
This commit is contained in:
Felix Kunde
2023-04-17 11:38:56 +02:00
committed by GitHub
parent 87b7ac0806
commit 0e7beb5fe5
16 changed files with 369 additions and 120 deletions
+2
View File
@@ -29,10 +29,12 @@ default: tools
clean:
rm -rf manifests
rm -rf tls
copy: clean
mkdir manifests
cp -r ../manifests .
mkdir tls
docker: scm-source.json
docker build -t "$(IMAGE):$(TAG)" .
+6
View File
@@ -55,6 +55,10 @@ function set_kind_api_server_ip(){
sed -i "s/server.*$/server: https:\/\/$kind_api_server/g" "${kubeconfig_path}"
}
function generate_certificate(){
openssl req -x509 -nodes -newkey rsa:2048 -keyout tls/tls.key -out tls/tls.crt -subj "/CN=acid.zalan.do"
}
function run_tests(){
echo "Running tests... image: ${e2e_test_runner_image}"
# tests modify files in ./manifests, so we mount a copy of this directory done by the e2e Makefile
@@ -62,6 +66,7 @@ function run_tests(){
docker run --rm --network=host -e "TERM=xterm-256color" \
--mount type=bind,source="$(readlink -f ${kubeconfig_path})",target=/root/.kube/config \
--mount type=bind,source="$(readlink -f manifests)",target=/manifests \
--mount type=bind,source="$(readlink -f tls)",target=/tls \
--mount type=bind,source="$(readlink -f tests)",target=/tests \
--mount type=bind,source="$(readlink -f exec.sh)",target=/exec.sh \
--mount type=bind,source="$(readlink -f scripts)",target=/scripts \
@@ -82,6 +87,7 @@ function main(){
[[ ! -f ${kubeconfig_path} ]] && start_kind
load_operator_image
set_kind_api_server_ip
generate_certificate
shift
run_tests $@
+32
View File
@@ -156,6 +156,26 @@ class K8s:
while not get_services():
time.sleep(self.RETRY_TIMEOUT_SEC)
def count_pods_with_volume_mount(self, mount_name, labels, namespace='default'):
pod_count = 0
pods = self.api.core_v1.list_namespaced_pod(namespace, label_selector=labels).items
for pod in pods:
for mount in pod.spec.containers[0].volume_mounts:
if mount.name == mount_name:
pod_count += 1
return pod_count
def count_pods_with_env_variable(self, env_variable_key, labels, namespace='default'):
pod_count = 0
pods = self.api.core_v1.list_namespaced_pod(namespace, label_selector=labels).items
for pod in pods:
for env in pod.spec.containers[0].env:
if env.name == env_variable_key:
pod_count += 1
return pod_count
def count_pods_with_rolling_update_flag(self, labels, namespace='default'):
pods = self.api.core_v1.list_namespaced_pod(namespace, label_selector=labels).items
return len(list(filter(lambda x: "zalando-postgres-operator-rolling-update-required" in x.metadata.annotations, pods)))
@@ -241,6 +261,18 @@ class K8s:
def patch_pod(self, data, pod_name, namespace="default"):
self.api.core_v1.patch_namespaced_pod(pod_name, namespace, data)
def create_tls_secret_with_kubectl(self, secret_name):
return subprocess.run(
["kubectl", "create", "secret", "tls", secret_name, "--key=tls/tls.key", "--cert=tls/tls.crt"],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE)
def create_tls_ca_secret_with_kubectl(self, secret_name):
return subprocess.run(
["kubectl", "create", "secret", "generic", secret_name, "--from-file=ca.crt=tls/ca.crt"],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE)
def create_with_kubectl(self, path):
return subprocess.run(
["kubectl", "apply", "-f", path],
+48
View File
@@ -622,6 +622,49 @@ class EndToEndTestCase(unittest.TestCase):
self.eventuallyEqual(lambda: k8s.count_secrets_with_label("cluster-name=acid-minimal-cluster,application=spilo", self.test_namespace),
1, "Secret not created for user in namespace")
@timeout_decorator.timeout(TEST_TIMEOUT_SEC)
def test_custom_ssl_certificate(self):
'''
Test if spilo uses a custom SSL certificate
'''
k8s = self.k8s
cluster_label = 'application=spilo,cluster-name=acid-minimal-cluster'
tls_secret = "pg-tls"
# get nodes of master and replica(s) (expected target of new master)
_, replica_nodes = k8s.get_pg_nodes(cluster_label)
self.assertNotEqual(replica_nodes, [])
try:
# create secret containing ssl certificate
result = self.k8s.create_tls_secret_with_kubectl(tls_secret)
print("stdout: {}, stderr: {}".format(result.stdout, result.stderr))
# enable load balancer services
pg_patch_tls = {
"spec": {
"spiloFSGroup": 103,
"tls": {
"secretName": tls_secret
}
}
}
k8s.api.custom_objects_api.patch_namespaced_custom_object(
"acid.zalan.do", "v1", "default", "postgresqls", "acid-minimal-cluster", pg_patch_tls)
# wait for switched over
k8s.wait_for_pod_failover(replica_nodes, 'spilo-role=master,' + cluster_label)
k8s.wait_for_pod_start('spilo-role=replica,' + cluster_label)
self.eventuallyEqual(lambda: k8s.count_pods_with_env_variable("SSL_CERTIFICATE_FILE", cluster_label), 2, "TLS env variable SSL_CERTIFICATE_FILE missing in Spilo pods")
self.eventuallyEqual(lambda: k8s.count_pods_with_env_variable("SSL_PRIVATE_KEY_FILE", cluster_label), 2, "TLS env variable SSL_PRIVATE_KEY_FILE missing in Spilo pods")
self.eventuallyEqual(lambda: k8s.count_pods_with_volume_mount(tls_secret, cluster_label), 2, "TLS volume mount missing in Spilo pods")
except timeout_decorator.TimeoutError:
print('Operator log: {}'.format(k8s.get_operator_log()))
raise
@timeout_decorator.timeout(TEST_TIMEOUT_SEC)
def test_enable_disable_connection_pooler(self):
'''
@@ -653,6 +696,11 @@ class EndToEndTestCase(unittest.TestCase):
self.eventuallyEqual(lambda: k8s.count_services_with_label(pooler_label), 2, "No pooler service found")
self.eventuallyEqual(lambda: k8s.count_secrets_with_label(pooler_label), 1, "Pooler secret not created")
# TLS still enabled so check existing env variables and volume mounts
self.eventuallyEqual(lambda: k8s.count_pods_with_env_variable("CONNECTION_POOLER_CLIENT_TLS_CRT", pooler_label), 4, "TLS env variable CONNECTION_POOLER_CLIENT_TLS_CRT missing in pooler pods")
self.eventuallyEqual(lambda: k8s.count_pods_with_env_variable("CONNECTION_POOLER_CLIENT_TLS_KEY", pooler_label), 4, "TLS env variable CONNECTION_POOLER_CLIENT_TLS_KEY missing in pooler pods")
self.eventuallyEqual(lambda: k8s.count_pods_with_volume_mount("pg-tls", pooler_label), 4, "TLS volume mount missing in pooler pods")
k8s.api.custom_objects_api.patch_namespaced_custom_object(
'acid.zalan.do', 'v1', 'default',
'postgresqls', 'acid-minimal-cluster',