From aa62b997a1ade75589370e55dd398c70acc0a702 Mon Sep 17 00:00:00 2001 From: antarcticrainforest Date: Thu, 26 Oct 2023 07:34:54 +0200 Subject: [PATCH 01/10] Always use RESTApi for vault. --- .../share/freva/deployment/vault/Dockerfile | 10 +- .../share/freva/deployment/vault/runserver.py | 282 ++++++++++++------ 2 files changed, 203 insertions(+), 89 deletions(-) mode change 100644 => 100755 assets/share/freva/deployment/vault/runserver.py diff --git a/assets/share/freva/deployment/vault/Dockerfile b/assets/share/freva/deployment/vault/Dockerfile index a1871154..e9484e96 100644 --- a/assets/share/freva/deployment/vault/Dockerfile +++ b/assets/share/freva/deployment/vault/Dockerfile @@ -4,19 +4,21 @@ LABEL maintainer="DRKZ-CLINT" LABEL repository="https://github.com/FREVA-CLINT/freva.git" ENV VAULT_ADDR='http://127.0.0.1:8200' -COPY runserver.py /bin/runserver +COPY runserver.py /bin/runserver.py COPY --chown=vault:vault vault-server-tls.hcl /vault COPY --chown=vault:vault policy-file.hcl /vault RUN set -ex &&\ - chmod +x /bin/runserver &&\ + chmod +x /bin/runserver.py &&\ mkdir -p /data && chown -R vault:vault /data &&\ apk add --update --no-cache python3 mysql mysql-client &&\ ln -sf python3 /usr/bin/python RUN python3 -m ensurepip RUN pip3 install --no-cache --upgrade pip setuptools &&\ - pip3 install requests flask flask_restful + pip3 install requests pyopenssl flask flask_restful gunicorn EXPOSE 5002 VOLUME /vault/file -CMD ["/bin/runserver"] +CMD /bin/runserver.py &&\ + gunicorn -w 2 --threads 3 --pythonpath /bin runserver:app \ + --bind 0.0.0.0:5002 --access-logfile - diff --git a/assets/share/freva/deployment/vault/runserver.py b/assets/share/freva/deployment/vault/runserver.py old mode 100644 new mode 100755 index aeb0d40a..afba5459 --- a/assets/share/freva/deployment/vault/runserver.py +++ b/assets/share/freva/deployment/vault/runserver.py @@ -1,82 +1,195 @@ #!/usr/bin/python3 -from flask import Flask, request, jsonify -from flask_restful import Resource, Api -from json import dumps +import base64 +import binascii +import json +import logging +import os from pathlib import Path -import sys -from subprocess import run, PIPE, Popen +import random +from subprocess import Popen, PIPE, run import shlex import time -import os -import hashlib -from functools import cached_property +import threading +from typing import Any, Literal, List, Optional, TypedDict, cast + +from flask import Flask +from flask_restful import Resource, Api import requests -app = Flask(__name__) -api = Api(app) +KeyType = TypedDict("KeyType", {"keys": List[str], "token": str}) KEY_FILE = Path("/vault/file/keys") CERT_DIR = Path("/data") +ThreadLock = threading.Lock() +VAULT_ADDR = os.environ.get("VAULT_ADDR", "http://127.0.0.1:8200") +PHRASES = [ + "There seems to be a noose around this request.", + "Our guns jammed! Something went wrong.", + "When you have to shoot, shoot, don't talk.", + "The bullets didn't fire.", + "I've never seen so many men wasted so badly.", + "You're not digging.", + "We're in the desert without a horse.", + "We faced a showdown, and it didn't end well." + "Blondie! You know what you are? Just a dirty son of a...", +] + +app = Flask("secret-reader") +api = Api(app) +logging.basicConfig( + format="%(asctime)s - %(name)s - [%(levelname)s] - %(message)s", + level=logging.INFO, + datefmt="%Y-%m-%dT%H:%M:%S%z", +) +logger = logging.getLogger("secret-reader") +app.logger = logger -def read_key(): - unseal_keys = [] - token = "" - with KEY_FILE.open("r") as f: - for line in f.readlines(): - if "unseal key" in line.lower(): - unseal_keys.append(line.split(":")[-1].strip()) - if "initial root token" in line.lower(): - token = line.split(":")[-1].strip() - return unseal_keys, token - - -def unseal(): - env = os.environ.copy() - env["VAULT_ADDR"] = "http://127.0.0.1:8200" - env["VAULT_SKIP_VERIFY"] = "true" - unseal_keys, token = read_key() - for key in unseal_keys[:3]: - run(shlex.split(f"vault operator unseal {key}"), env=env) - if token: - run(shlex.split(f"vault login {token}"), env=env) - run( - shlex.split(f"vault secrets enable -version=2 -path=kv kv"), - env=env, +def interact_with_vault( + endpoint: str, + method: Literal["GET", "POST", "PUT", "DELETE"] = "GET", + loglevel: int = logging.ERROR, + **kwargs: Any, +) -> Optional[requests.Response]: + """Interact with the vault rest api. + + Parameters + --------- + url: str + The vault endpoint. + method: str, default: GET + Use a GET, PUT or POST method + loglevel: int, default: 40 + Which log level to emit if errors occur. + kwargs: + Additional arguments for the request. + """ + text = f"Failed to connect to {endpoint}" + for _ in range(10): + try: + response = requests.request( + method, f"{VAULT_ADDR}/{endpoint}", **kwargs + ) + text = response.text + if response.status_code >= 200 and response.status_code < 300: + return response + break + except requests.exceptions.ConnectionError: + logger.critical("Vault not active: sleeping for 1 second") + time.sleep(1) + logger.log(loglevel, text) + return None + + +def read_key() -> KeyType: + """Read the login token and the vault seal keys from a secret file.""" + auth: KeyType = {"keys": [], "token": ""} + try: + return cast( + KeyType, json.loads(base64.b64decode(KEY_FILE.read_bytes())) ) - run( - shlex.split(f"vault policy write read-eval /vault/policy-file.hcl"), - env=env, + except FileNotFoundError: + return auth + except (json.JSONDecodeError, binascii.Error): + pass + auth_content = KEY_FILE.read_text(encoding="utf-8") + for line in auth_content.splitlines(): + if "unseal key" in line.lower(): + auth["keys"].append(line.split(":")[-1].strip()) + if "initial root token" in line.lower(): + auth["token"] = line.split(":")[-1].strip() + KEY_FILE.write_bytes(base64.b64encode(json.dumps(auth).encode("utf-8"))) + return auth + + +def unseal(auth: KeyType): + """Open the vault. + + Parameters + ---------- + auth: dict + Dictionary holding the unseal keys and login token + """ + for key in auth["keys"]: + response = interact_with_vault( + f"v1/sys/unseal", "PUT", json={"key": key} ) + data = response.json() + if data.get("sealed", False) is False: + logger.info("Vault has been successfully unsealed!") + login(auth["token"]) + return + logger.error( + "Failed to unseal Vault. Please check the provided unseal keys and Vault status." + ) -def deploy_vault(): +def login(token: str) -> None: + """Login to the vault and adjust the correct settings. + + Parameters + ---------- + token: str + The login token for the opened vault. + """ + if not token: + return + auth_header = {"X-Vault-Token": token, "Content-Type": "application/json"} + # Make vault commands possible and login from the cmd. + run(shlex.split(f"vault login {token}"), stderr=PIPE, stdout=PIPE) + interact_with_vault(f"v1/sys/health", "GET", headers=auth_header) + interact_with_vault( + f"v1/sys/mounts/kv", + "PUT", + loglevel=logging.WARNING, + headers=auth_header, + json={"type": "kv", "options": {"version": "2"}}, + ) try: - key_text = KEY_FILE.read_text() - except FileNotFoundError: - key_text = "" - if not key_text: - cmd = shlex.split("vault operator init") - env = os.environ.copy() - env["VAULT_ADDR"] = "http://127.0.0.1:8200" - env["VAULT_SKIP_VERIFY"] = "true" - res = run(cmd, stdout=PIPE, stderr=PIPE, env=env) - stdout = res.stdout.decode() - with KEY_FILE.open("w") as f: - f.write(stdout) - unseal() + policy_content = Path("/vault/policy-file.hcl").read_text() + except (FileNotFoundError, PermissionError): + return + interact_with_vault( + f"v1/sys/policies/acl/read-eval", + "PUT", + loglevel=logging.WARNING, + headers=auth_header, + json={"policy": policy_content}, + ) + + +def deploy_vault(): + """Deploy a new vault if it hasn't been deployed yet.""" + auth = read_key() + if not auth["keys"]: + response = interact_with_vault( + f"v1/sys/init", + "PUT", + headers={"Content-Type": "application/json"}, + json={ + "secret_shares": 5, # Total number of share keys + "secret_threshold": 3, # Number of keys required to unseal + }, + ) + data = response.json() + auth["keys"] = data.get("keys", []) + auth["token"] = data.get("token", "") + KEY_FILE.write_bytes( + base64.b64encode(json.dumps(auth).encode("utf-8")) + ) + unseal(auth) class Vault(Resource): - @staticmethod - def _read_key(suffix): - with (CERT_DIR / f"freva.{suffix}").open() as f: - key = [k.strip() for k in f.readlines() if not k.startswith("-")] - return ("".join(key)).encode() + _token: str = "" - @cached_property - def public_key(self): - return hashlib.sha512(self._read_key("crt")).hexdigest() + @property + def token(self) -> str: + """Get the vault login token.""" + if not self._token: + with ThreadLock: + self._token = read_key()["token"] + return self._token def post(self, entry, public_key): """Post method, for setting a new db_password. @@ -92,14 +205,17 @@ def post(self, entry, public_key): -------- dict: status information """ - out, status = {}, 401 + out, status = {}, 400 if public_key != os.environ["ROOT_PW"]: return out, status - _, token = read_key() # Get the information from the vault url = f"http://127.0.0.1:8200/v1/kv/data/read-eval" - headers = {"X-Vault-Token": token} - out = requests.get(url, headers=headers, timeout=3).json().get("data", {}) + headers = {"X-Vault-Token": self.token} + out = ( + requests.get(url, headers=headers, timeout=3) + .json() + .get("data", {}) + ) try: out["data"]["db.passwd"] = entry except KeyError: @@ -126,42 +242,38 @@ def get(self, entry, public_key): dict: Vault information """ out = {} - status = 401 - _, token = read_key() - if entry == "token": - return {"X-Vault-Token": token}, 200 + status = 400 + if len(public_key) != 128: # This is not a checksum of a cert. + is_sealed = getattr( + interact_with_vault(f"v1/sys/seal-status"), "json", lambda: {} + )().get("sealed", True) + vault_status = {True: "sealdd", False: "unsealed"}[is_sealed] + text = f"But the vault is {vault_status}" + return f"{random.choice(PHRASES)} {text}", status if entry == "data": # Get the information from the vault - req = requests.get( - "http://127.0.0.1:8200/v1/kv/data/read-eval", - headers={"X-Vault-Token": token}, - timeout=3, + req = interact_with_vault( + f"v1/kv/data/read-eval", + headers={"X-Vault-Token": self.token}, ) else: - req = requests.get( - f"http://127.0.0.1:8200/v1/kv/data/{entry}", - headers={"X-Vault-Token": token}, - timeout=3, + req = interact_with_vault( + f"v1/kv/data/{entry}", + headers={"X-Vault-Token": self.token}, ) try: out = req.json()["data"]["data"] status = req.status_code except KeyError: out = req.json() + status = req.status_code + except (json.JSONDecodeError, AttributeError): + out = {} return out, status api.add_resource(Vault, "/vault//") # Route_3 if __name__ == "__main__": - try: - cmd = sys.argv[1:] - except IndexError: - cmd = ["vault", "server", "-config", "/vault/vault-server-tls.hcl"] - if len(cmd) == 0: - cmd = ["vault", "server", "-config", "/vault/vault-server-tls.hcl"] - p = Popen(cmd, env=os.environ.copy()) - time.sleep(1) + Popen(["vault", "server", "-config", "/vault/vault-server-tls.hcl"]) deploy_vault() - app.run(host="0.0.0.0", port="5002") - p.wait() From bbbaa9d9c85c447aeda25617f1982c08aef531bf Mon Sep 17 00:00:00 2001 From: antarcticrainforest Date: Sat, 28 Oct 2023 00:28:18 +0200 Subject: [PATCH 02/10] Use fastAPI --- .../share/freva/deployment/vault/Dockerfile | 6 +- .../share/freva/deployment/vault/runserver.py | 177 +++++++----------- 2 files changed, 72 insertions(+), 111 deletions(-) diff --git a/assets/share/freva/deployment/vault/Dockerfile b/assets/share/freva/deployment/vault/Dockerfile index e9484e96..ed2c455b 100644 --- a/assets/share/freva/deployment/vault/Dockerfile +++ b/assets/share/freva/deployment/vault/Dockerfile @@ -15,10 +15,10 @@ RUN set -ex &&\ RUN python3 -m ensurepip RUN pip3 install --no-cache --upgrade pip setuptools &&\ - pip3 install requests pyopenssl flask flask_restful gunicorn + pip3 install requests pyopenssl fastapi uvicorn EXPOSE 5002 VOLUME /vault/file CMD /bin/runserver.py &&\ - gunicorn -w 2 --threads 3 --pythonpath /bin runserver:app \ - --bind 0.0.0.0:5002 --access-logfile - + uvicorn --workers 2 --app-dir /bin runserver:app \ + --host 0.0.0.0 --port 5002 diff --git a/assets/share/freva/deployment/vault/runserver.py b/assets/share/freva/deployment/vault/runserver.py index afba5459..86e794db 100755 --- a/assets/share/freva/deployment/vault/runserver.py +++ b/assets/share/freva/deployment/vault/runserver.py @@ -11,10 +11,11 @@ import shlex import time import threading -from typing import Any, Literal, List, Optional, TypedDict, cast +from typing import Any, Dict, Literal, List, Tuple, TypedDict, Union, cast -from flask import Flask -from flask_restful import Resource, Api + +from fastapi import FastAPI, HTTPException +from fastapi.responses import JSONResponse import requests KeyType = TypedDict("KeyType", {"keys": List[str], "token": str}) @@ -34,15 +35,32 @@ "Blondie! You know what you are? Just a dirty son of a...", ] -app = Flask("secret-reader") -api = Api(app) +app = FastAPI( + title="secret-reader", + description="Read information from a vault", + version="2023.10.1", +) logging.basicConfig( format="%(asctime)s - %(name)s - [%(levelname)s] - %(message)s", level=logging.INFO, datefmt="%Y-%m-%dT%H:%M:%S%z", ) logger = logging.getLogger("secret-reader") -app.logger = logger + + +class VaultCore: + _token: str = "" + + @property + def token(self) -> str: + """Get the vault login token.""" + if not self._token: + with ThreadLock: + self._token = read_key()["token"] + return self._token + + +Vault = VaultCore() def interact_with_vault( @@ -50,7 +68,7 @@ def interact_with_vault( method: Literal["GET", "POST", "PUT", "DELETE"] = "GET", loglevel: int = logging.ERROR, **kwargs: Any, -) -> Optional[requests.Response]: +) -> Dict[str, Any]: """Interact with the vault rest api. Parameters @@ -72,13 +90,15 @@ def interact_with_vault( ) text = response.text if response.status_code >= 200 and response.status_code < 300: - return response + return cast(Dict[str, Any], response.json()) break except requests.exceptions.ConnectionError: logger.critical("Vault not active: sleeping for 1 second") time.sleep(1) + except json.JSONDecodeError: + break logger.log(loglevel, text) - return None + return {} def read_key() -> KeyType: @@ -102,7 +122,7 @@ def read_key() -> KeyType: return auth -def unseal(auth: KeyType): +def unseal(auth: KeyType) -> None: """Open the vault. Parameters @@ -111,10 +131,7 @@ def unseal(auth: KeyType): Dictionary holding the unseal keys and login token """ for key in auth["keys"]: - response = interact_with_vault( - f"v1/sys/unseal", "PUT", json={"key": key} - ) - data = response.json() + data = interact_with_vault("v1/sys/unseal", "PUT", json={"key": key}) if data.get("sealed", False) is False: logger.info("Vault has been successfully unsealed!") login(auth["token"]) @@ -137,9 +154,9 @@ def login(token: str) -> None: auth_header = {"X-Vault-Token": token, "Content-Type": "application/json"} # Make vault commands possible and login from the cmd. run(shlex.split(f"vault login {token}"), stderr=PIPE, stdout=PIPE) - interact_with_vault(f"v1/sys/health", "GET", headers=auth_header) + interact_with_vault("v1/sys/health", "GET", headers=auth_header) interact_with_vault( - f"v1/sys/mounts/kv", + "v1/sys/mounts/kv", "PUT", loglevel=logging.WARNING, headers=auth_header, @@ -150,7 +167,7 @@ def login(token: str) -> None: except (FileNotFoundError, PermissionError): return interact_with_vault( - f"v1/sys/policies/acl/read-eval", + "v1/sys/policies/acl/read-eval", "PUT", loglevel=logging.WARNING, headers=auth_header, @@ -158,12 +175,12 @@ def login(token: str) -> None: ) -def deploy_vault(): +def deploy_vault() -> None: """Deploy a new vault if it hasn't been deployed yet.""" auth = read_key() if not auth["keys"]: - response = interact_with_vault( - f"v1/sys/init", + data = interact_with_vault( + "v1/sys/init", "PUT", headers={"Content-Type": "application/json"}, json={ @@ -171,7 +188,6 @@ def deploy_vault(): "secret_threshold": 3, # Number of keys required to unseal }, ) - data = response.json() auth["keys"] = data.get("keys", []) auth["token"] = data.get("token", "") KEY_FILE.write_bytes( @@ -180,99 +196,44 @@ def deploy_vault(): unseal(auth) -class Vault(Resource): - _token: str = "" +@app.get("/vault/data/{public_key}") +async def read_secret(public_key: str) -> JSONResponse: + """Read the secrets from the vault. - @property - def token(self) -> str: - """Get the vault login token.""" - if not self._token: - with ThreadLock: - self._token = read_key()["token"] - return self._token - - def post(self, entry, public_key): - """Post method, for setting a new db_password. + Parameters + ---------- + public_key: str + hexdigest representation of the sha512 public key that is stored + on the docker container. - Parameters: - ----------- - entry: str - the new database password - public_key: str - mysql root password to - Returns: - -------- - dict: status information - """ - out, status = {}, 400 - if public_key != os.environ["ROOT_PW"]: - return out, status - # Get the information from the vault - url = f"http://127.0.0.1:8200/v1/kv/data/read-eval" - headers = {"X-Vault-Token": self.token} - out = ( - requests.get(url, headers=headers, timeout=3) - .json() - .get("data", {}) + Returns + ------- + dict: A key-value paired dictionary containing the secrets. + """ + out = {} + status = 400 + if len(public_key) != 128: # This is not a checksum of a cert. + is_sealed = interact_with_vault("v1/sys/seal-status").get("sealed") + vault_status = {True: "sealed", False: "unsealed", None: "down"}[ + is_sealed + ] + text = f"But the vault is {vault_status}" + raise HTTPException( + detail=f"{random.choice(PHRASES)} {text}", status_code=status ) - try: - out["data"]["db.passwd"] = entry - except KeyError: - pass - _ = requests.post(url, json=out, headers=headers) - return {}, 201 - - def get(self, entry, public_key): - """Get method, for getting vault information. - - Parameters: - ----------- - entry: str - the kind of inormation that is returned, can be - key#N : to get the unseal key #N - token: to get the login token - data: to get the key-value data that is stored in the vault - public_key: hashlib.sha512 - hexdigest representation of the sha512 public key that is stored - on the docker container. - - Returns: - -------- - dict: Vault information - """ + # Get the information from the vault + req = requests.get( + f"{VAULT_ADDR}/v1/kv/data/read-eval", + headers={"X-Vault-Token": Vault.token}, + ) + try: + out = req.json().get("data", {}).get("data", {}) + status = req.status_code + except (json.JSONDecodeError, AttributeError): out = {} - status = 400 - if len(public_key) != 128: # This is not a checksum of a cert. - is_sealed = getattr( - interact_with_vault(f"v1/sys/seal-status"), "json", lambda: {} - )().get("sealed", True) - vault_status = {True: "sealdd", False: "unsealed"}[is_sealed] - text = f"But the vault is {vault_status}" - return f"{random.choice(PHRASES)} {text}", status - if entry == "data": - # Get the information from the vault - req = interact_with_vault( - f"v1/kv/data/read-eval", - headers={"X-Vault-Token": self.token}, - ) - else: - req = interact_with_vault( - f"v1/kv/data/{entry}", - headers={"X-Vault-Token": self.token}, - ) - try: - out = req.json()["data"]["data"] - status = req.status_code - except KeyError: - out = req.json() - status = req.status_code - except (json.JSONDecodeError, AttributeError): - out = {} - return out, status - + return JSONResponse(content=out, status_code=status) -api.add_resource(Vault, "/vault//") # Route_3 if __name__ == "__main__": Popen(["vault", "server", "-config", "/vault/vault-server-tls.hcl"]) From 2b00ade50464650e5c80d0e410d8f018f6b3ac7c Mon Sep 17 00:00:00 2001 From: antarcticrainforest Date: Thu, 7 Dec 2023 06:16:05 +0100 Subject: [PATCH 03/10] Addd mypy.ini for type checking. --- assets/share/freva/deployment/vault/mypy.ini | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 assets/share/freva/deployment/vault/mypy.ini diff --git a/assets/share/freva/deployment/vault/mypy.ini b/assets/share/freva/deployment/vault/mypy.ini new file mode 100644 index 00000000..398caf41 --- /dev/null +++ b/assets/share/freva/deployment/vault/mypy.ini @@ -0,0 +1,9 @@ +[mypy] +strict = True +install_types = True +non_interactive = True +disallow_untyped_defs = True +disallow_incomplete_defs = True +check_untyped_defs = True +disallow_untyped_decorators = True +warn_redundant_casts = True From 96597e4f3ba8e04bd5ab5bef8c84ddfc45ff2cb1 Mon Sep 17 00:00:00 2001 From: antarcticrainforest Date: Thu, 7 Dec 2023 07:48:45 +0100 Subject: [PATCH 04/10] Update policy --- .../freva/deployment/vault/policy-file.hcl | 2 +- .../share/freva/deployment/vault/runserver.py | 25 ++++++++++++++++--- 2 files changed, 23 insertions(+), 4 deletions(-) diff --git a/assets/share/freva/deployment/vault/policy-file.hcl b/assets/share/freva/deployment/vault/policy-file.hcl index eb4574dc..9a7eb08a 100644 --- a/assets/share/freva/deployment/vault/policy-file.hcl +++ b/assets/share/freva/deployment/vault/policy-file.hcl @@ -1,3 +1,3 @@ path "kv/*" { - capabilities = ["read", "list"] + capabilities = ["read", "list", "delete", "create", "update"] } diff --git a/assets/share/freva/deployment/vault/runserver.py b/assets/share/freva/deployment/vault/runserver.py index 86e794db..b713c38e 100755 --- a/assets/share/freva/deployment/vault/runserver.py +++ b/assets/share/freva/deployment/vault/runserver.py @@ -11,7 +11,16 @@ import shlex import time import threading -from typing import Any, Dict, Literal, List, Tuple, TypedDict, Union, cast +from typing import ( + Any, + Dict, + Literal, + List, + Tuple, + TypedDict, + Union, + cast, +) from fastapi import FastAPI, HTTPException @@ -34,11 +43,12 @@ "We faced a showdown, and it didn't end well." "Blondie! You know what you are? Just a dirty son of a...", ] +__version__ = "2023.10.1" app = FastAPI( title="secret-reader", description="Read information from a vault", - version="2023.10.1", + version=__version__, ) logging.basicConfig( format="%(asctime)s - %(name)s - [%(levelname)s] - %(message)s", @@ -189,7 +199,7 @@ def deploy_vault() -> None: }, ) auth["keys"] = data.get("keys", []) - auth["token"] = data.get("token", "") + auth["token"] = data.get("token", data.get("root_token", "")) KEY_FILE.write_bytes( base64.b64encode(json.dumps(auth).encode("utf-8")) ) @@ -238,3 +248,12 @@ async def read_secret(public_key: str) -> JSONResponse: if __name__ == "__main__": Popen(["vault", "server", "-config", "/vault/vault-server-tls.hcl"]) deploy_vault() + Popen( + [ + "vault", + "policy", + "write", + "eval-policy", + "/vault/policy-file.hcl", + ] + ) From 86a55a39df013299a8869e85cbb683c78b2d4fc5 Mon Sep 17 00:00:00 2001 From: antarcticrainforest Date: Thu, 7 Dec 2023 07:51:46 +0100 Subject: [PATCH 05/10] Use network alias names inside the containers. --- assets/share/freva/deployment/playbooks/web-server-playbook.yml | 2 +- src/freva_deployment/deploy.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/assets/share/freva/deployment/playbooks/web-server-playbook.yml b/assets/share/freva/deployment/playbooks/web-server-playbook.yml index a31573e1..5419087c 100644 --- a/assets/share/freva/deployment/playbooks/web-server-playbook.yml +++ b/assets/share/freva/deployment/playbooks/web-server-playbook.yml @@ -88,7 +88,7 @@ -e GID=$(id -g {{ansible_user}}) --name "{{project_name}}-httpd" --security-opt label=disable - -e FREVA_HOST={{web_server_name}} -p 80:80 -p 443:443 + -e FREVA_HOST="{{project_name}}-web" -p 80:80 -p 443:443 httpd:latest /usr/local/bin/prepare-httpd redis_name: "{{ project_name }}-redis" apache_name: "{{project_name}}-httpd" diff --git a/src/freva_deployment/deploy.py b/src/freva_deployment/deploy.py index f2e62941..2e6d0f61 100644 --- a/src/freva_deployment/deploy.py +++ b/src/freva_deployment/deploy.py @@ -232,7 +232,7 @@ def _prep_web(self) -> None: "No web config section given, please configure the web.config" ) from error _webserver_items["ALLOWED_HOSTS"].append(self.cfg["web"]["hosts"]) - _webserver_items["REDIS_HOST"] = self.cfg["web"]["hosts"] + _webserver_items["REDIS_HOST"] = "{self.project_name}-web" try: with Path(_webserver_items["homepage_text"]).open("r") as f_obj: _webserver_items["homepage_text"] = f_obj.read() From 3b09d74c381263a0c009558782496c6d15a7eff1 Mon Sep 17 00:00:00 2001 From: antarcticrainforest Date: Tue, 23 Jan 2024 23:26:12 +0100 Subject: [PATCH 06/10] Add missing f-string --- src/freva_deployment/deploy.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/freva_deployment/deploy.py b/src/freva_deployment/deploy.py index 2e6d0f61..bc2f5117 100644 --- a/src/freva_deployment/deploy.py +++ b/src/freva_deployment/deploy.py @@ -232,7 +232,7 @@ def _prep_web(self) -> None: "No web config section given, please configure the web.config" ) from error _webserver_items["ALLOWED_HOSTS"].append(self.cfg["web"]["hosts"]) - _webserver_items["REDIS_HOST"] = "{self.project_name}-web" + _webserver_items["REDIS_HOST"] = f"{self.project_name}-web" try: with Path(_webserver_items["homepage_text"]).open("r") as f_obj: _webserver_items["homepage_text"] = f_obj.read() From e36c7756c0890da8ac4d26245bf7d91f12c33a71 Mon Sep 17 00:00:00 2001 From: antarcticrainforest Date: Tue, 23 Jan 2024 23:28:38 +0100 Subject: [PATCH 07/10] rename redis host -redis --- src/freva_deployment/deploy.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/freva_deployment/deploy.py b/src/freva_deployment/deploy.py index bc2f5117..fba00989 100644 --- a/src/freva_deployment/deploy.py +++ b/src/freva_deployment/deploy.py @@ -232,7 +232,7 @@ def _prep_web(self) -> None: "No web config section given, please configure the web.config" ) from error _webserver_items["ALLOWED_HOSTS"].append(self.cfg["web"]["hosts"]) - _webserver_items["REDIS_HOST"] = f"{self.project_name}-web" + _webserver_items["REDIS_HOST"] = f"{self.project_name}-redis" try: with Path(_webserver_items["homepage_text"]).open("r") as f_obj: _webserver_items["homepage_text"] = f_obj.read() From 1e176ceb001cae3e52c8ac871520eb5ef3cf8c15 Mon Sep 17 00:00:00 2001 From: antarcticrainforest Date: Tue, 23 Jan 2024 23:38:53 +0100 Subject: [PATCH 08/10] revert playbook --- assets/share/freva/deployment/playbooks/web-server-playbook.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets/share/freva/deployment/playbooks/web-server-playbook.yml b/assets/share/freva/deployment/playbooks/web-server-playbook.yml index 5419087c..3c9f207d 100644 --- a/assets/share/freva/deployment/playbooks/web-server-playbook.yml +++ b/assets/share/freva/deployment/playbooks/web-server-playbook.yml @@ -88,7 +88,7 @@ -e GID=$(id -g {{ansible_user}}) --name "{{project_name}}-httpd" --security-opt label=disable - -e FREVA_HOST="{{project_name}}-web" -p 80:80 -p 443:443 + -e FREVA_HOST="{{web_server_name}}" -p 80:80 -p 443:443 httpd:latest /usr/local/bin/prepare-httpd redis_name: "{{ project_name }}-redis" apache_name: "{{project_name}}-httpd" From e2e8b0650113e5a0a914a105e799deca878f8b6f Mon Sep 17 00:00:00 2001 From: antarcticrainforest Date: Wed, 24 Jan 2024 00:14:08 +0100 Subject: [PATCH 09/10] Catch all errors when interacting with vault. --- assets/share/freva/deployment/vault/runserver.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/assets/share/freva/deployment/vault/runserver.py b/assets/share/freva/deployment/vault/runserver.py index b713c38e..156223c5 100755 --- a/assets/share/freva/deployment/vault/runserver.py +++ b/assets/share/freva/deployment/vault/runserver.py @@ -83,7 +83,7 @@ def interact_with_vault( Parameters --------- - url: str + endpoint: str The vault endpoint. method: str, default: GET Use a GET, PUT or POST method @@ -105,7 +105,8 @@ def interact_with_vault( except requests.exceptions.ConnectionError: logger.critical("Vault not active: sleeping for 1 second") time.sleep(1) - except json.JSONDecodeError: + except Exception as error: + text = str(error) break logger.log(loglevel, text) return {} From e54e8605d004c98e46d6e1dbdf7552ffd27120ba Mon Sep 17 00:00:00 2001 From: antarcticrainforest Date: Wed, 24 Jan 2024 00:16:32 +0100 Subject: [PATCH 10/10] Remove unused imports. --- .../share/freva/deployment/vault/runserver.py | 18 ++++-------------- 1 file changed, 4 insertions(+), 14 deletions(-) diff --git a/assets/share/freva/deployment/vault/runserver.py b/assets/share/freva/deployment/vault/runserver.py index 156223c5..57846ccc 100755 --- a/assets/share/freva/deployment/vault/runserver.py +++ b/assets/share/freva/deployment/vault/runserver.py @@ -16,9 +16,7 @@ Dict, Literal, List, - Tuple, TypedDict, - Union, cast, ) @@ -95,9 +93,7 @@ def interact_with_vault( text = f"Failed to connect to {endpoint}" for _ in range(10): try: - response = requests.request( - method, f"{VAULT_ADDR}/{endpoint}", **kwargs - ) + response = requests.request(method, f"{VAULT_ADDR}/{endpoint}", **kwargs) text = response.text if response.status_code >= 200 and response.status_code < 300: return cast(Dict[str, Any], response.json()) @@ -116,9 +112,7 @@ def read_key() -> KeyType: """Read the login token and the vault seal keys from a secret file.""" auth: KeyType = {"keys": [], "token": ""} try: - return cast( - KeyType, json.loads(base64.b64decode(KEY_FILE.read_bytes())) - ) + return cast(KeyType, json.loads(base64.b64decode(KEY_FILE.read_bytes()))) except FileNotFoundError: return auth except (json.JSONDecodeError, binascii.Error): @@ -201,9 +195,7 @@ def deploy_vault() -> None: ) auth["keys"] = data.get("keys", []) auth["token"] = data.get("token", data.get("root_token", "")) - KEY_FILE.write_bytes( - base64.b64encode(json.dumps(auth).encode("utf-8")) - ) + KEY_FILE.write_bytes(base64.b64encode(json.dumps(auth).encode("utf-8"))) unseal(auth) @@ -226,9 +218,7 @@ async def read_secret(public_key: str) -> JSONResponse: status = 400 if len(public_key) != 128: # This is not a checksum of a cert. is_sealed = interact_with_vault("v1/sys/seal-status").get("sealed") - vault_status = {True: "sealed", False: "unsealed", None: "down"}[ - is_sealed - ] + vault_status = {True: "sealed", False: "unsealed", None: "down"}[is_sealed] text = f"But the vault is {vault_status}" raise HTTPException( detail=f"{random.choice(PHRASES)} {text}", status_code=status