diff --git a/.github/workflows/dev.yml b/.github/workflows/dev.yml index 352e3c2d2..b19ba2198 100644 --- a/.github/workflows/dev.yml +++ b/.github/workflows/dev.yml @@ -375,8 +375,10 @@ jobs: # run: ./tests/main.py "docker" # - name: Run Autoconf tests # run: ./tests/main.py "autoconf" - - name: Run Swarm tests - run: ./tests/main.py "swarm" + # - name: Run Swarm tests + # run: ./tests/main.py "swarm" + - name: Run Kubernetes tests + run: ./tests/main.py "kubernetes" - name: Temp stop tests run: exit 1 - name: Run autoconf tests diff --git a/examples/authelia/kubernetes.yml b/examples/authelia/kubernetes.yml index 4abcf689b..e719bf983 100644 --- a/examples/authelia/kubernetes.yml +++ b/examples/authelia/kubernetes.yml @@ -116,6 +116,111 @@ spec: port: 80 targetPort: 80 --- +apiVersion: v1 +kind: ConfigMap +metadata: + name: cfg-authelia +data: + configuration.yml: | + --- + ############################################################### + # Authelia configuration # + ############################################################### + + jwt_secret: a_very_important_secret + default_redirection_url: https://auth.example.com + + ntp: + disable_failure: true + + server: + host: 0.0.0.0 + port: 9091 + + log: + level: debug + # This secret can also be set using the env variables AUTHELIA_JWT_SECRET_FILE + + totp: + issuer: authelia.com + + # duo_api: + # hostname: api-123456789.example.com + # integration_key: ABCDEF + # # This secret can also be set using the env variables AUTHELIA_DUO_API_SECRET_KEY_FILE + # secret_key: 1234567890abcdefghifjkl + + authentication_backend: + file: + path: /config/users_database.yml + + access_control: + default_policy: deny + rules: + # Rules applied to everyone + - domain: auth.example.com + policy: bypass + - domain: app1.example.com + policy: one_factor + - domain: app2.example.com + policy: two_factor + + session: + name: authelia_session + # This secret can also be set using the env variables AUTHELIA_SESSION_SECRET_FILE + secret: unsecure_session_secret + expiration: 3600 # 1 hour + inactivity: 300 # 5 minutes + domain: example.com # Should match whatever your root protected domain is + + redis: + host: svc-redis + port: 6379 + # This secret can also be set using the env variables AUTHELIA_SESSION_REDIS_PASSWORD_FILE + # password: authelia + + regulation: + max_retries: 3 + find_time: 120 + ban_time: 300 + + storage: + encryption_key: you_must_generate_a_random_string_of_more_than_twenty_chars_and_configure_this + local: + path: /config/db.sqlite3 + + notifier: + filesystem: + filename: /config/notification.txt + #notifier: + # smtp: + # username: test + # This secret can also be set using the env variables AUTHELIA_NOTIFIER_SMTP_PASSWORD_FILE + # password: password + # host: mail.example.com + # port: 25 + # sender: admin@example.com + ... + users_database.yml: + --- + ############################################################### + # Users Database # + ############################################################### + + # This file can be used if you do not have an LDAP set up. + + # List of users + users: + authelia: + displayname: "Authelia User" + # Password is authelia + password: "$6$rounds=50000$BpLnfgDsc2WD8F2q$Zis.ixdg9s/UOJYrs56b5QEZFiZECu0qZVNsIYxBaNJ7ucIL.nlxVCT5tqh8KHG8X4tlwCFm5r6NTOZZ5qRFN/" # yamllint disable-line rule:line-length + email: authelia@authelia.com + groups: + - admins + - dev + ... +--- apiVersion: apps/v1 kind: Deployment metadata: @@ -134,4 +239,65 @@ spec: spec: containers: - name: authelia - image: authelia/authelia \ No newline at end of file + image: authelia/authelia + env: + - name: TZ + value: "Europe/Paris" + volumeMounts: + - name: config + mountPath: /config/configuration.yml + subPath: configuration.yml + - name: config + mountPath: /config/users_database.yml + subPath: users_database.yml + volumes: + - name: config + configMap: + name: cfg-authelia +--- +apiVersion: v1 +kind: Service +metadata: + name: svc-authelia +spec: + selector: + app: authelia + ports: + - protocol: TCP + port: 9091 + targetPort: 9091 +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: redis + labels: + app: redis +spec: + replicas: 1 + selector: + matchLabels: + app: redis + template: + metadata: + labels: + app: redis + spec: + containers: + - name: redis + image: redis:alpine + env: + - name: TZ + value: "Europe/Paris" +--- +apiVersion: v1 +kind: Service +metadata: + name: svc-redis +spec: + selector: + app: redis + ports: + - protocol: TCP + port: 6379 + targetPort: 6379 \ No newline at end of file diff --git a/tests/KubernetesTest.py b/tests/KubernetesTest.py new file mode 100644 index 000000000..51860c6f0 --- /dev/null +++ b/tests/KubernetesTest.py @@ -0,0 +1,125 @@ +from Test import Test +from os.path import isdir, join, isfile +from os import chown, walk, getenv, listdir +from shutil import copytree, rmtree, copy +from traceback import format_exc +from subprocess import run +from time import sleep +from logger import log + +class KubernetesTest(Test) : + + def __init__(self, name, timeout, tests) : + super().__init__(name, "kubernetes", timeout, tests) + self._domains = { + r"www\.example\.com": getenv("TEST_DOMAIN1_1"), + r"auth\.example\.com": getenv("TEST_DOMAIN1_2"), + r"app1\.example\.com": getenv("TEST_DOMAIN1"), + r"app2\.example\.com": getenv("TEST_DOMAIN2"), + r"app3\.example\.com": getenv("TEST_DOMAIN3") + } + + def init() : + try : + if not Test.init() : + return False + proc = run("sudo chown -R root:root /tmp/bw-data", shell=True) + if proc.returncode != 0 : + raise(Exception("chown failed (k8s stack)")) + if isdir("/tmp/kubernetes") : + rmtree("/tmp/kubernetes") + copytree("./integrations/kubernetes", "/tmp/kubernetes") + copy("./tests/utils/k8s.yml", "/tmp/kubernetes") + deploy = "/tmp/kubernetes/bunkerweb.yml" + Test.replace_in_file(deploy, r"bunkerity/bunkerweb:.*$", "10.20.1.1:5000/bw-tests:latest") + Test.replace_in_file(deploy, r"bunkerity/bunkerweb-autoconf:.*$", "10.20.1.1:5000/bw-autoconf-tests:latest") + Test.replace_in_file(deploy, r"ifNotPresent", "Always") + proc = run("sudo kubectl apply -f k8s.yml", cwd="/tmp/kubernetes", shell=True) + if proc.returncode != 0 : + raise(Exception("kubectl apply k8s failed (k8s stack)")) + proc = run("sudo kubectl apply -f rbac.yml", cwd="/tmp/kubernetes", shell=True) + if proc.returncode != 0 : + raise(Exception("kubectl apply rbac failed (k8s stack)")) + proc = run("sudo kubectl apply -f bunkerweb.yml", cwd="/tmp/kubernetes", shell=True) + if proc.returncode != 0 : + raise(Exception("kubectl apply bunkerweb failed (k8s stack)")) + i = 0 + while i < 30 : + proc = run('sudo kubectl get pods | grep bunkerweb | grep -v Running', shell=True, capture_output=True) + if "" == proc.stdout.decode() : + healthy = True + break + sleep(1) + i += 1 + if not healthy : + raise(Exception("k8s stack is not healthy")) + sleep(60) + except : + log("KUBERNETES", "❌", "exception while running KubernetesTest.init()\n" + format_exc()) + return False + return True + + def end() : + ret = True + try : + if not Test.end() : + return False + proc = run("sudo kubectl delete -f bunkerweb.yml", cwd="/tmp/kubernetes", shell=True) + if proc.returncode != 0 : + ret = False + proc = run("sudo kubectl delete -f rbac.yml", cwd="/tmp/kubernetes", shell=True) + if proc.returncode != 0 : + ret = False + proc = run("sudo kubectl delete -f k8s.yml", cwd="/tmp/kubernetes", shell=True) + if proc.returncode != 0 : + ret = False + rmtree("/tmp/kubernetes") + except : + log("KUBERNETES", "❌", "exception while running KubernetesTest.end()\n" + format_exc()) + return False + return ret + + def _setup_test(self) : + try : + super()._setup_test() + test = "/tmp/tests/" + self._name + deploy = "/tmp/tests/" + self._name + "/kubernetes.yml" + example_data = "./examples/" + self._name + "/bw-data" + for ex_domain, test_domain in self._domains.items() : + Test.replace_in_files(test, ex_domain, test_domain) + Test.rename(test, ex_domain, test_domain) + Test.replace_in_files(test, "example.com", getenv("ROOT_DOMAIN")) + setup = test + "/setup-kubernetes.sh" + if isfile(setup) : + proc = run("sudo ./setup-kubernetes.sh", cwd=test, shell=True) + if proc.returncode != 0 : + raise(Exception("setup-kubernetes failed")) + if isdir(example_data) : + for cp_dir in listdir(example_data) : + if isdir(join(example_data, cp_dir)) : + copytree(join(example_data, cp_dir), join("/tmp/bw-data", cp_dir)) + proc = run("sudo kubectl apply -f kubernetes.yml", shell=True, cwd=test) + if proc.returncode != 0 : + raise(Exception("kubectl apply failed")) + except : + log("KUBERNETES", "❌", "exception while running KubernetesTest._setup_test()\n" + format_exc()) + self._cleanup_test() + return False + return True + + def _cleanup_test(self) : + try : + proc = run("sudo kubectl delete -f kubernetes.yml", shell=True, cwd="/tmp/tests/" + self._name) + if proc.returncode != 0 : + raise(Exception("kubectl delete failed")) + super()._cleanup_test() + except : + log("KUBERNETES", "❌", "exception while running KubernetesTest._cleanup_test()\n" + format_exc()) + return False + return True + + def _debug_fail(self) : + proc = run('sudo kubectl get pods --no-headers -o custom-columns=":metadata.name"', shell=True, capture_output=True) + if proc.returncode != 0 : + for pod in proc.stdout.decode().splitlines() : + run("sudo kubectl logs " + pod, shell=True) \ No newline at end of file diff --git a/tests/SwarmTest.py b/tests/SwarmTest.py index ada8b4ec6..6a66ca427 100644 --- a/tests/SwarmTest.py +++ b/tests/SwarmTest.py @@ -59,9 +59,6 @@ class SwarmTest(Test) : if not Test.end() : return False proc = run("docker stack rm bunkerweb", shell=True) - if proc.returncode != 0 : - ret = False - proc = run("docker network rm bw-services bw-autoconf", shell=True) if proc.returncode != 0 : ret = False rmtree("/tmp/swarm") diff --git a/tests/main.py b/tests/main.py index eb5713981..199f22bba 100755 --- a/tests/main.py +++ b/tests/main.py @@ -13,6 +13,7 @@ from Test import Test from DockerTest import DockerTest from AutoconfTest import AutoconfTest from SwarmTest import SwarmTest +from KubernetesTest import KubernetesTest from logger import log if len(argv) != 2 : @@ -36,6 +37,9 @@ elif test_type == "autoconf" : elif test_type == "swarm" : ret = SwarmTest.init() end_fun = SwarmTest.end +elif test_type == "kubernetes" : + ret = KubernetesTest.init() + end_fun = KubernetesTest.end if not ret : log("TESTS", "❌", "Test.init() failed") exit(1) @@ -55,6 +59,8 @@ for example in glob("./examples/*") : test_obj = AutoconfTest(tests["name"], tests["timeout"], tests["tests"]) elif test_type == "swarm" : test_obj = SwarmTest(tests["name"], tests["timeout"], tests["tests"]) + elif test_type == "kubernetes" : + test_obj = KubernetesTest(tests["name"], tests["timeout"], tests["tests"]) if not test_obj.run_tests() : log("TESTS", "❌", "Tests failed for " + tests["name"]) end_fun()