mirror of
https://github.com/zalando/postgres-operator.git
synced 2026-09-30 06:43:48 +02:00
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:
@@ -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)" .
|
||||
|
||||
@@ -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 $@
|
||||
|
||||
@@ -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],
|
||||
|
||||
@@ -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',
|
||||
|
||||
Reference in New Issue
Block a user