WIP: caddy: very early role
This commit is contained in:
parent
66e6c960d3
commit
d20c89defe
9 changed files with 293 additions and 1 deletions
|
@ -113,7 +113,7 @@ firewall__zones:
|
||||||
- 10.128.1.5
|
- 10.128.1.5
|
||||||
proxy.pub:
|
proxy.pub:
|
||||||
addrs:
|
addrs:
|
||||||
- 2a09:6840:214::1:1
|
- 2a09:6840:215::1:1
|
||||||
- 45.66.111.206
|
- 45.66.111.206
|
||||||
|
|
||||||
firewall__input:
|
firewall__input:
|
||||||
|
@ -259,6 +259,20 @@ firewall__forward:
|
||||||
- 80
|
- 80
|
||||||
- 443
|
- 443
|
||||||
verdict: accept
|
verdict: accept
|
||||||
|
- src: proxy.pub
|
||||||
|
dst: grafana.adm
|
||||||
|
protocols:
|
||||||
|
tcp:
|
||||||
|
dport: 3000
|
||||||
|
verdict: accept
|
||||||
|
- src: proxy.pub
|
||||||
|
dst: adm-legacy
|
||||||
|
protocols:
|
||||||
|
tcp:
|
||||||
|
dport:
|
||||||
|
- 80
|
||||||
|
- 443
|
||||||
|
verdict: accept
|
||||||
# ICMP to public vlan
|
# ICMP to public vlan
|
||||||
- dst: pub
|
- dst: pub
|
||||||
protocols:
|
protocols:
|
||||||
|
|
|
@ -337,6 +337,12 @@ knotd__zones:
|
||||||
mx:
|
mx:
|
||||||
- exchange: mx
|
- exchange: mx
|
||||||
preference: 5
|
preference: 5
|
||||||
|
cname:
|
||||||
|
- name:
|
||||||
|
- www1
|
||||||
|
- www2
|
||||||
|
- www3
|
||||||
|
target: proxy.pub.infra.auro.re.
|
||||||
hosts:
|
hosts:
|
||||||
mx:
|
mx:
|
||||||
- 2a09:6840:211::1:5
|
- 2a09:6840:211::1:5
|
||||||
|
|
|
@ -8,4 +8,40 @@ ifupdown2__interfaces:
|
||||||
- 2a09:6840:215::1:1/64
|
- 2a09:6840:215::1:1/64
|
||||||
- 45.66.111.206/30
|
- 45.66.111.206/30
|
||||||
gateways: "{{ ifupdown2__gateways.pub }}"
|
gateways: "{{ ifupdown2__gateways.pub }}"
|
||||||
|
|
||||||
|
caddy__matrix_headers:
|
||||||
|
access-control-allow-headers: "Origin, X-Requested-With, Content-Type, Accept, Authorization"
|
||||||
|
access-control-allow-methods: "GET, POST, PUT, DELETE, OPTIONS"
|
||||||
|
access-control-allow-origin: "*"
|
||||||
|
|
||||||
|
caddy__routes_https:
|
||||||
|
www1.test.auro.re:
|
||||||
|
- root: /var/www/auro.re
|
||||||
|
- path: /.well-known/matrix/server
|
||||||
|
headers: "{{ caddy__matrix_headers }}"
|
||||||
|
body: '{"m.server": "matrix.auro.re:8448"}'
|
||||||
|
status: 200
|
||||||
|
- path: /.well-known/matrix/client
|
||||||
|
headers: "{{ caddy__matrix_headers }}"
|
||||||
|
body: '{"m.homeserver": {"base_url": "https://matrix.auro.re"}}'
|
||||||
|
status: 200
|
||||||
|
www2.test.auro.re:
|
||||||
|
headers:
|
||||||
|
location: "https://auro.re{http.request.uri}"
|
||||||
|
status: 301
|
||||||
|
www3.test.auro.re:
|
||||||
|
reverse:
|
||||||
|
- "[2a09:6840:128::98]:3000"
|
||||||
|
- 10.128.0.98:3000
|
||||||
|
|
||||||
|
caddy__servers:
|
||||||
|
https:
|
||||||
|
listen: ":443"
|
||||||
|
routes: "{{ caddy__routes_https }}"
|
||||||
|
http:
|
||||||
|
listen: ":80"
|
||||||
|
|
||||||
|
#caddy__error_handlers:
|
||||||
|
#"*":
|
||||||
|
# - error_template: "/var/www/error.html"
|
||||||
...
|
...
|
||||||
|
|
7
playbooks/caddy.yml
Executable file
7
playbooks/caddy.yml
Executable file
|
@ -0,0 +1,7 @@
|
||||||
|
#!/usr/bin/env ansible-playbook
|
||||||
|
---
|
||||||
|
- hosts:
|
||||||
|
- proxy.pub.infra.auro.re
|
||||||
|
roles:
|
||||||
|
- caddy
|
||||||
|
...
|
174
roles/caddy/filter_plugins/caddy.py
Normal file
174
roles/caddy/filter_plugins/caddy.py
Normal file
|
@ -0,0 +1,174 @@
|
||||||
|
import itertools
|
||||||
|
import json
|
||||||
|
from collections.abc import Iterable
|
||||||
|
from typing import Any, Generic, Iterator, Literal, TypeVar
|
||||||
|
|
||||||
|
from pydantic import (
|
||||||
|
BaseModel,
|
||||||
|
Field,
|
||||||
|
IPvAnyAddress,
|
||||||
|
ValidationError,
|
||||||
|
parse_obj_as,
|
||||||
|
)
|
||||||
|
|
||||||
|
T = TypeVar("T")
|
||||||
|
|
||||||
|
|
||||||
|
def flatten(iterable: Iterable[Iterable[T]]) -> Iterable[T]:
|
||||||
|
return itertools.chain.from_iterable(iterable)
|
||||||
|
|
||||||
|
|
||||||
|
class AutoList(list[T], Generic[T]):
|
||||||
|
@classmethod
|
||||||
|
def __get_validators__(cls):
|
||||||
|
yield cls.__validator__
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def __validator__(cls, value):
|
||||||
|
try:
|
||||||
|
return parse_obj_as(list[T], value)
|
||||||
|
except ValidationError:
|
||||||
|
return [parse_obj_as(T, value)]
|
||||||
|
|
||||||
|
|
||||||
|
class BaseHandler(BaseModel):
|
||||||
|
headers: dict[str, str] = {}
|
||||||
|
path: str | None = None
|
||||||
|
|
||||||
|
def to_caddy(self):
|
||||||
|
raise StopIteration
|
||||||
|
|
||||||
|
|
||||||
|
class FilesHandler(BaseHandler):
|
||||||
|
root: str
|
||||||
|
|
||||||
|
def to_caddy(self):
|
||||||
|
yield {"handle": [{"handler": "vars", "root": self.root}]}
|
||||||
|
|
||||||
|
|
||||||
|
class StaticHandler(BaseHandler):
|
||||||
|
status: int | None = None
|
||||||
|
body: str | None = None
|
||||||
|
|
||||||
|
def to_caddy(self):
|
||||||
|
response = {"handler": "static_response"}
|
||||||
|
if self.status is not None:
|
||||||
|
response["status_code"] = self.status
|
||||||
|
if self.body is not None:
|
||||||
|
response["body"] = self.body
|
||||||
|
yield {"handle": [response]}
|
||||||
|
|
||||||
|
|
||||||
|
class ReverseHandler(BaseHandler):
|
||||||
|
reverse: AutoList[str]
|
||||||
|
|
||||||
|
def to_caddy(self):
|
||||||
|
yield {
|
||||||
|
"handle": [
|
||||||
|
{
|
||||||
|
"handler": "reverse_proxy",
|
||||||
|
"upstreams": [{"dial": s} for s in self.reverse],
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Handler = FilesHandler | ReverseHandler | StaticHandler
|
||||||
|
Routes = dict[str, AutoList[Handler]]
|
||||||
|
|
||||||
|
|
||||||
|
class Server(BaseModel):
|
||||||
|
listen: AutoList[str]
|
||||||
|
routes: Routes = {}
|
||||||
|
|
||||||
|
|
||||||
|
Config = dict[str, Server]
|
||||||
|
|
||||||
|
|
||||||
|
class Context:
|
||||||
|
def __init__(self):
|
||||||
|
self._group = 0
|
||||||
|
|
||||||
|
def next_group(self):
|
||||||
|
self._group += 1
|
||||||
|
return self._group
|
||||||
|
|
||||||
|
|
||||||
|
def strip_path_prefix(prefix: str) -> Any:
|
||||||
|
return {
|
||||||
|
"strip_path_prefix": prefix,
|
||||||
|
"handler": "rewrite",
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
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()
|
||||||
|
]
|
||||||
|
}
|
||||||
|
yield from handler.to_caddy()
|
||||||
|
|
||||||
|
if handler.path is None:
|
||||||
|
yield from to_caddy_inner()
|
||||||
|
else:
|
||||||
|
yield {
|
||||||
|
"group": f"group{ctx.next_group()}",
|
||||||
|
"match": [{"path": [handler.path]}],
|
||||||
|
"handle": [
|
||||||
|
{
|
||||||
|
"handler": "subroute",
|
||||||
|
"routes": [
|
||||||
|
{"handle": [strip_path_prefix(handler.path)]},
|
||||||
|
*to_caddy_inner(),
|
||||||
|
],
|
||||||
|
}
|
||||||
|
],
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
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 server_to_caddy(server: Server, ctx: Context) -> Any:
|
||||||
|
return {
|
||||||
|
"listen": server.listen,
|
||||||
|
"errors": {}, # TODO
|
||||||
|
"logs": {}, # TODO
|
||||||
|
"routes": [
|
||||||
|
route_to_caddy(host, handlers, ctx)
|
||||||
|
for host, handlers in server.routes.items()
|
||||||
|
],
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def to_caddy(config: Any) -> Any:
|
||||||
|
ctx = Context()
|
||||||
|
parsed = parse_obj_as(Config, config)
|
||||||
|
servers = {
|
||||||
|
name: server_to_caddy(server, ctx) for name, server in parsed.items()
|
||||||
|
}
|
||||||
|
return {"apps": {"http": {"servers": servers}}}
|
||||||
|
|
||||||
|
|
||||||
|
class FilterModule:
|
||||||
|
def filters(self):
|
||||||
|
return {"caddy__of_servers": to_caddy}
|
7
roles/caddy/handlers/main.yml
Normal file
7
roles/caddy/handlers/main.yml
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
---
|
||||||
|
- name: Reload caddy
|
||||||
|
systemd:
|
||||||
|
name: caddy.service
|
||||||
|
daemon_reload: true
|
||||||
|
state: restarted
|
||||||
|
...
|
40
roles/caddy/tasks/main.yml
Normal file
40
roles/caddy/tasks/main.yml
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
---
|
||||||
|
- name: Install caddy
|
||||||
|
apt:
|
||||||
|
name: caddy
|
||||||
|
|
||||||
|
- name: Create override directory
|
||||||
|
file:
|
||||||
|
path: /etc/systemd/system/caddy.service.d
|
||||||
|
state: directory
|
||||||
|
owner: root
|
||||||
|
group: root
|
||||||
|
mode: u=rwx,g=rx,o=rx
|
||||||
|
|
||||||
|
- name: Override caddy.service
|
||||||
|
template:
|
||||||
|
src: override.conf.j2
|
||||||
|
dest: /etc/systemd/system/caddy.service.d/override.conf
|
||||||
|
owner: root
|
||||||
|
group: root
|
||||||
|
mode: u=rw,g=r,o=r
|
||||||
|
notify:
|
||||||
|
- Reload caddy
|
||||||
|
|
||||||
|
- name: Configure caddy
|
||||||
|
copy:
|
||||||
|
content: "{{ caddy__servers | caddy__of_servers | to_json }}"
|
||||||
|
dest: /etc/caddy/caddy.json
|
||||||
|
owner: root
|
||||||
|
group: root
|
||||||
|
mode: u=rw,g=r,o=r
|
||||||
|
notify:
|
||||||
|
- Reload caddy
|
||||||
|
|
||||||
|
- name: Enable caddy
|
||||||
|
systemd:
|
||||||
|
daemon_reload: true
|
||||||
|
name: caddy.service
|
||||||
|
enabled: true
|
||||||
|
state: started
|
||||||
|
...
|
1
roles/caddy/templates/caddy.json.j2
Normal file
1
roles/caddy/templates/caddy.json.j2
Normal file
|
@ -0,0 +1 @@
|
||||||
|
{{ caddy__servers | caddy__of_servers | to_nice_json }}
|
7
roles/caddy/templates/override.conf.j2
Normal file
7
roles/caddy/templates/override.conf.j2
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
{{ ansible_managed | comment }}
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
ExecStart=
|
||||||
|
ExecStart=/usr/bin/caddy run --environ --config /etc/caddy/caddy.json
|
||||||
|
ExecReload=
|
||||||
|
ExecReload=/usr/bin/caddy reload --config /etc/caddy/caddy.json --force
|
Loading…
Reference in a new issue