diff --git a/host_vars/proxy.pub.infra.auro.re.yml b/host_vars/proxy.pub.infra.auro.re.yml index 149c61d..d0ef62b 100644 --- a/host_vars/proxy.pub.infra.auro.re.yml +++ b/host_vars/proxy.pub.infra.auro.re.yml @@ -31,17 +31,21 @@ caddy__routes_https: status: 301 www3.test.auro.re: reverse: - - "[2a09:6840:128::98]:3000" - - 10.128.0.98:3000 + - "[2a09:6840:128::198]:3000" + - 10.128.0.198:3000 + +caddy__errors: + - root: /var/www + - rewrite: /error.html + - file_server: true + templates: true caddy__servers: https: listen: ":443" routes: "{{ caddy__routes_https }}" + errors: "{{ caddy__errors }}" http: listen: ":80" -#caddy__error_handlers: -#"*": -# - error_template: "/var/www/error.html" ... diff --git a/roles/caddy/defaults/main.yml b/roles/caddy/defaults/main.yml new file mode 100644 index 0000000..69e5d7f --- /dev/null +++ b/roles/caddy/defaults/main.yml @@ -0,0 +1,3 @@ +--- +caddy__error_dir: /var/www/error +... diff --git a/roles/caddy/filter_plugins/caddy.py b/roles/caddy/filter_plugins/caddy.py index 732bb9c..05c74e0 100644 --- a/roles/caddy/filter_plugins/caddy.py +++ b/roles/caddy/filter_plugins/caddy.py @@ -14,8 +14,8 @@ from pydantic import ( T = TypeVar("T") -def flatten(iterable: Iterable[Iterable[T]]) -> Iterable[T]: - return itertools.chain.from_iterable(iterable) +def flatten_list(iterable: Iterable[Iterable[T]]) -> list[T]: + return list(itertools.chain.from_iterable(iterable)) class AutoList(list[T], Generic[T]): @@ -43,7 +43,8 @@ class FilesHandler(BaseHandler): root: str def to_caddy(self): - yield {"handle": [{"handler": "vars", "root": self.root}]} + handler = {"handler": "vars", "root": self.root} + yield {"handle": [handler]} class StaticHandler(BaseHandler): @@ -51,35 +52,60 @@ class StaticHandler(BaseHandler): body: str | None = None def to_caddy(self): - response = {"handler": "static_response"} + handler = {"handler": "static_response"} if self.status is not None: - response["status_code"] = self.status + handler["status_code"] = self.status if self.body is not None: - response["body"] = self.body - yield {"handle": [response]} + handler["body"] = self.body + yield {"handle": [handler]} class ReverseHandler(BaseHandler): reverse: AutoList[str] def to_caddy(self): - yield { - "handle": [ - { - "handler": "reverse_proxy", - "upstreams": [{"dial": s} for s in self.reverse], - } - ] + handler = { + "handler": "reverse_proxy", + "upstreams": [{"dial": s} for s in self.reverse], } + yield {"handle": [handler]} -Handler = FilesHandler | ReverseHandler | StaticHandler +class RewriteHandler(BaseHandler): + rewrite: str + + def to_caddy(self): + handler = {"handler": "rewrite", "uri": self.rewrite} + yield {"handle": [handler]} + + +class FileServerHandler(BaseHandler): + file_server: Literal[True] + templates: bool = False + + def to_caddy(self): + handlers = [ + {"handler": "templates"}, + ] + if self.templates: + handlers.append({"handler": "file_server"}) + yield {"handle": handlers} + + +Handler = ( + FilesHandler + | ReverseHandler + | RewriteHandler + | FileServerHandler + | StaticHandler +) Routes = dict[str, AutoList[Handler]] class Server(BaseModel): listen: AutoList[str] routes: Routes = {} + errors: AutoList[Handler] = {} Config = dict[str, Server] @@ -96,23 +122,22 @@ class Context: def strip_path_prefix(prefix: str) -> Any: return { - "strip_path_prefix": prefix, "handler": "rewrite", + "strip_path_prefix": prefix, } def handler_to_caddy(handler: Handler, ctx: Context) -> Any: def to_caddy_inner(): if handler.headers: - yield { - "handle": [ - { - "handler": "headers", - "response": {"set": {name: [value]}}, - } - for name, value in handler.headers.items() - ] - } + handlers = [ + { + "handler": "headers", + "response": {"set": {name: [value]}}, + } + for name, value in handler.headers.items() + ] + yield {"handle": handlers} yield from handler.to_caddy() if handler.path is None: @@ -133,30 +158,29 @@ def handler_to_caddy(handler: Handler, ctx: Context) -> Any: } -def route_to_caddy(host: str, handlers: list[Handler], ctx: Context) -> Any: - return { - "match": [{"host": [host]}], - "handle": [ - { - "handler": "subroute", - "routes": list( - flatten(handler_to_caddy(h, ctx) for h in handlers) - ), - } - ], - "terminal": True, +def route_to_caddy( + host: str | None, handlers: list[Handler], ctx: Context +) -> Any: + handler = { + "handler": "subroute", + "routes": flatten_list(handler_to_caddy(h, ctx) for h in handlers), } + route = {"handle": [handler], "terminal": True} + if host is not None: + route["match"] = [{"host": [host]}] + return route def server_to_caddy(server: Server, ctx: Context) -> Any: + routes = [ + route_to_caddy(host, handlers, ctx) + for host, handlers in server.routes.items() + ] return { "listen": server.listen, - "errors": {}, # TODO - "logs": {}, # TODO - "routes": [ - route_to_caddy(host, handlers, ctx) - for host, handlers in server.routes.items() - ], + "errors": {"routes": [route_to_caddy(None, server.errors, ctx)]}, + "logs": {}, + "routes": routes, } diff --git a/roles/caddy/tasks/main.yml b/roles/caddy/tasks/main.yml index 4c7b837..cd35f57 100644 --- a/roles/caddy/tasks/main.yml +++ b/roles/caddy/tasks/main.yml @@ -23,7 +23,7 @@ - name: Configure caddy copy: - content: "{{ caddy__servers | caddy__of_servers | to_json }}" + content: "{{ caddy__servers | caddy__of_servers | to_nice_json }}" dest: /etc/caddy/caddy.json owner: root group: root @@ -31,6 +31,22 @@ notify: - Reload caddy +- name: Create error directory + file: + path: "{{ caddy__error_dir }}" + state: directory + owner: root + group: root + mode: u=rwx=,g=rx,o=rx + +- name: Configure error page + template: + src: error.html.j2 + dest: "{{ caddy__error_dir }}/error.html" + owner: root + group: root + mode: u=rw,g=r,o=r + - name: Enable caddy systemd: daemon_reload: true diff --git a/roles/caddy/templates/caddy.json.j2 b/roles/caddy/templates/caddy.json.j2 deleted file mode 100644 index b0af213..0000000 --- a/roles/caddy/templates/caddy.json.j2 +++ /dev/null @@ -1 +0,0 @@ -{{ caddy__servers | caddy__of_servers | to_nice_json }} diff --git a/roles/caddy/templates/error.html.j2 b/roles/caddy/templates/error.html.j2 new file mode 100644 index 0000000..0c86f9c --- /dev/null +++ b/roles/caddy/templates/error.html.j2 @@ -0,0 +1,107 @@ + +{{ ansible_managed | comment("xml") }} + +
+ +Whoops, le service est temporairement indisponible ou en maintenance…
Essayez de rafraîchir la page plus tard.
Whoops, the service is temporarily unavailable or under maintenance…
Try refreshing the page later.
Whoops, le service prend trop de temps à répondre…
Essayez de rafraîchir la page plus tard.
Whoops, the service takes too long to respond…
Try refreshing the page later.
Si le problème persiste, contactez l’équipe technique d’Aurore.
+If the problem persists, contact the Aurore technical team.
+ {{ '{{end}}' }} + {{ '{{if eq $http_code 403}}' }} +Whoops, cette page vous est interdite.
+Whoops, you are not allowed on this page.
+ {{ '{{else if eq $http_code 404}}' }} +Whoops, cette page n’existe pas.
+Whoops, this page doesn't exist.
+ {{ '{{else if and (lt $http_code 499) (gt $http_code 399)}}' }} +Whoops, requête malformée.
+Whoops, malformed request.
+ {{ '{{end}}' }} +ID pour le support : {{ '{{placeholder "http.error.id"}}' }}
Support ID: {{ '{{placeholder "http.error.id"}}' }}