summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xcreate.py151
-rw-r--r--deploy.yml4
-rw-r--r--group_vars/labdns.yml17
-rw-r--r--group_vars/pihole.yml7
-rw-r--r--inventory3
-rw-r--r--playbooks/labdns.yml7
-rw-r--r--playbooks/pihole.yml7
-rw-r--r--playbooks/roles/labdns/tasks/main.yml (renamed from playbooks/roles/pihole/tasks/main.yml)7
-rw-r--r--playbooks/roles/labdns/templates/stacks/docker-compose.yml30
-rw-r--r--playbooks/roles/labdns/templates/volumes/unbound/a-records.conf4
-rw-r--r--playbooks/roles/labdns/templates/volumes/unbound/forward-records.conf5
-rw-r--r--playbooks/roles/pihole/templates/stacks/docker-compose.yml43
-rw-r--r--playbooks/roles/pihole/templates/volumes/dnsmasq/.gitkeep0
-rw-r--r--playbooks/roles/pihole/templates/volumes/pihole/.gitkeep0
14 files changed, 156 insertions, 129 deletions
diff --git a/create.py b/create.py
index 258b998..c956f1b 100755
--- a/create.py
+++ b/create.py
@@ -15,10 +15,10 @@ from functools import cache
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
+
class Config:
SECRETS = Path("secrets.enc")
ROOT_DOMAIN = "liz.coffee"
- PIHOLE = "https://dns.liz.coffee/api"
CLOUDFLARE = "https://api.cloudflare.com/client/v4"
INVENTORY = Path("inventory")
@@ -28,21 +28,24 @@ class Config:
GROUP_VARS = Path("group_vars/")
NGINX_SITES_ENABLED = ANSIBLE_ROLES / Path("outbound/templates/proxy/nginx/conf.d")
- INTERNAL_LOADBALANCER_HOST = "floating.home.arpa"
+ LABDNS_GROUP_VARS = GROUP_VARS / Path("labdns.yml")
+ LABDNS_DELIMITER = "internal_services:" + "\n"
EXTERNAL_LOADBALANCER_HOST = "outbound.liz.coffee"
- LOADBALANCER_INVENTORY_LINE = textwrap.dedent("""\
+ LOADBALANCER_INVENTORY_LINE = textwrap.dedent(
+ """\
swarm-one ansible_host=10.128.0.201 ansible_user=serve \
ansible_connection=ssh ansible_become_password='{{ swarm_become_password }}'
- """).strip()
+ """
+ ).strip()
@staticmethod
@cache
def read_secrets() -> Dict[str, str]:
logger.info("Reading secrets...")
result = subprocess.run(
- ['ansible-vault', 'view', str(Config.SECRETS)],
+ ["ansible-vault", "view", str(Config.SECRETS)],
stdout=subprocess.PIPE,
- stderr=subprocess.PIPE
+ stderr=subprocess.PIPE,
)
secrets = result.stdout.decode().splitlines()
logger.info("Finished reading secrets.")
@@ -53,14 +56,8 @@ class Config:
return Config.read_secrets().get(name, "")
-class IDns(ABC):
- @abstractmethod
- def put_cname(self, of: str, to: str) -> bool:
- pass
-
-
@dataclass
-class CloudflareDns(IDns):
+class CloudflareDns:
api_token: str
zone_id: str
api_url: str
@@ -69,14 +66,9 @@ class CloudflareDns(IDns):
url = f"{self.api_url}/zones/{self.zone_id}/dns_records"
headers = {
"Authorization": f"Bearer {self.api_token}",
- "Content-Type": "application/json"
- }
- data = {
- "type": "CNAME",
- "name": of,
- "content": to,
- "ttl": 3600
+ "Content-Type": "application/json",
}
+ data = {"type": "CNAME", "name": of, "content": to, "ttl": 3600}
logger.info(f"Putting Cloudflare CNAME {of} -> {to}")
response = requests.post(url, headers=headers, json=data)
if response.ok:
@@ -87,26 +79,29 @@ class CloudflareDns(IDns):
@dataclass
-class PiholeDns(IDns):
- web_password: str
- api_url: str
-
- def __authenticate(self) -> Optional[str]:
- response = requests.post(f"{self.api_url}/auth", json={"password": self.web_password})
- return response.json().get("session", {}).get("sid")
-
- def put_cname(self, of: str, to: str) -> bool:
- sid = self.__authenticate()
- if not sid:
- logger.error("Pi-hole authentication failed.")
+class HomelabDns:
+ group_vars: Path
+ delimiter: str
+
+ def put_internal(self, dns_prefix: str) -> bool:
+ new_lines = []
+ with self.group_vars.open("r") as f:
+ lines = f.readlines()
+ try:
+ internal_services = lines.index(self.delimiter)
+ except ValueError:
+ logger.error(f"Cannot find delimiter {self.delimiter} in {self.group_vars}")
+ return False
+ new_lines = (
+ lines[0:internal_services + 1]
+ + [f" - {dns_prefix}\n"]
+ + lines[internal_services + 1:]
+ )
+ if not new_lines:
return False
- url = f"{self.api_url}/config/dns/cnameRecords/{of},{to}?sid={sid}"
- response = requests.put(url)
- if response.ok:
- logger.info("Pi-hole DNS update successful.")
- return True
- logger.error(f"Pi-hole DNS update failed: {response.text}")
- return False
+ with self.group_vars.open("w") as f:
+ f.write("".join(new_lines))
+ return True
class RoleGenerator:
@@ -126,7 +121,9 @@ class RoleGenerator:
def create_tasks(self):
self.tasks_path.mkdir(parents=True, exist_ok=True)
task_file = self.tasks_path / "main.yml"
- task_file.write_text(textwrap.dedent(f"""\
+ task_file.write_text(
+ textwrap.dedent(
+ f"""\
---
- name: Deploy {self.service}
@@ -135,12 +132,16 @@ class RoleGenerator:
service_name: {self.service}
template_render_dir: "../templates"
service_destination_dir: "{{{{ {self.service}_base }}}}"
- """))
+ """
+ )
+ )
def create_compose_template(self):
(self.templates_path / "stacks").mkdir(parents=True, exist_ok=True)
compose_file = self.templates_path / "stacks" / "docker-compose.yml"
- compose_file.write_text(textwrap.dedent(f"""\
+ compose_file.write_text(
+ textwrap.dedent(
+ f"""\
services:
{self.service}:
image: {self.image}
@@ -156,15 +157,15 @@ class RoleGenerator:
timeout: 15s
interval: 30s
retries: 3
- start_period: 10s
+ start_period: 5s
deploy:
mode: replicated
update_config:
parallelism: 1
failure_action: rollback
order: start-first
- delay: 10s
- monitor: 45s
+ delay: 5s
+ monitor: 30s
replicas: 1
labels:
- traefik.enable=true
@@ -178,16 +179,22 @@ class RoleGenerator:
networks:
proxy:
external: true
- """))
+ """
+ )
+ )
def create_group_vars(self):
path = Config.GROUP_VARS / f"{self.service}.yml"
- path.write_text(textwrap.dedent(f"""\
+ path.write_text(
+ textwrap.dedent(
+ f"""\
---
{self.service}_domain: {self.service}.{Config.ROOT_DOMAIN}
{self.service}_base: "{{{{ swarm_base }}}}/{self.service}"
- """))
+ """
+ )
+ )
def create_volumes(self):
(self.templates_path / "volumes" / "data").mkdir(parents=True, exist_ok=True)
@@ -195,7 +202,9 @@ class RoleGenerator:
def create_deploy_hook(self):
path = Config.ANSIBLE_PLAYBOOKS / f"{self.service}.yml"
- path.write_text(textwrap.dedent(f"""\
+ path.write_text(
+ textwrap.dedent(
+ f"""\
---
- name: {self.service} setup
@@ -203,11 +212,15 @@ class RoleGenerator:
become: true
roles:
- {self.service}
- """))
+ """
+ )
+ )
with open(Config.ANSIBLE_DEPLOY, "a") as f:
f.write("\n")
f.write(f"- name: {self.service}\n")
- f.write(f" ansible.builtin.import_playbook: playbooks/{self.service}.yml\n")
+ f.write(
+ f" ansible.builtin.import_playbook: playbooks/{self.service}.yml\n"
+ )
def create_all(self):
self.create_inventory()
@@ -220,7 +233,9 @@ class RoleGenerator:
def create_nginx_conf(service_name: str):
path = Config.NGINX_SITES_ENABLED / f"{service_name}.conf"
- path.write_text(textwrap.dedent(f"""\
+ path.write_text(
+ textwrap.dedent(
+ f"""\
server {{
listen 80;
server_name {service_name}.liz.coffee;
@@ -240,16 +255,18 @@ def create_nginx_conf(service_name: str):
proxy_set_header Connection "upgrade";
}}
}}
- """))
+ """
+ )
+ )
def main():
parser = argparse.ArgumentParser(description="Service initializer for liz.coffee.")
- parser.add_argument('--service-name', type=str, required=True)
- parser.add_argument('--container-image', type=str, required=True)
- parser.add_argument('--service-port', type=str, default='80')
- parser.add_argument('--external', action='store_true')
- parser.add_argument('--internal', action='store_true')
+ parser.add_argument("--service-name", type=str, required=True)
+ parser.add_argument("--container-image", type=str, required=True)
+ parser.add_argument("--service-port", type=str, default="80")
+ parser.add_argument("--external", action="store_true")
+ parser.add_argument("--internal", action="store_true")
args = parser.parse_args()
logger.info(f"Initializing service setup for '{args.service_name}'")
@@ -272,16 +289,20 @@ def main():
logger.info("External DNS (Cloudflare) CNAME created successfully.")
if args.internal:
- logger.info("Configuring internal DNS via Pi-hole...")
- pi = PiholeDns(web_password=Config.from_secrets("pihole_webpwd"), api_url=Config.PIHOLE)
- success = pi.put_cname(service_fqdn, Config.INTERNAL_LOADBALANCER_HOST)
+ logger.info("Configuring internal DNS via LabDNS...")
+ dns = HomelabDns(
+ group_vars=Config.LABDNS_GROUP_VARS, delimiter=Config.LABDNS_DELIMITER
+ )
+ success = dns.put_internal(args.service_name)
if not success:
- logger.warning("Internal DNS (Pi-hole) CNAME creation failed.")
+ logger.warning("Internal DNS (LabDNS) Record creation failed.")
else:
- logger.info("Internal DNS (Pi-hole) CNAME created successfully.")
+ logger.info("Internal DNS (LabDNS) Record created successfully.")
logger.info("Generating Ansible role and configuration...")
- generator = RoleGenerator(args.service_name, args.container_image, args.service_port)
+ generator = RoleGenerator(
+ args.service_name, args.container_image, args.service_port
+ )
generator.create_all()
logger.info("Role generation complete.")
@@ -292,6 +313,6 @@ def main():
logger.info(f"Service '{args.service_name}' setup complete.")
+
if __name__ == "__main__":
main()
-
diff --git a/deploy.yml b/deploy.yml
index a6ff65f..0484fe8 100644
--- a/deploy.yml
+++ b/deploy.yml
@@ -24,8 +24,8 @@
- name: Keepalived
ansible.builtin.import_playbook: playbooks/keepalived.yml
-- name: Pihole
- ansible.builtin.import_playbook: playbooks/pihole.yml
+- name: LabDNS
+ ansible.builtin.import_playbook: playbooks/labdns.yml
- name: Traextor
ansible.builtin.import_playbook: playbooks/traextor.yml
diff --git a/group_vars/labdns.yml b/group_vars/labdns.yml
new file mode 100644
index 0000000..1209e98
--- /dev/null
+++ b/group_vars/labdns.yml
@@ -0,0 +1,17 @@
+---
+
+labdns_base: "{{ swarm_base }}/labdns"
+
+internal_services:
+ - bin
+ - ci
+ - idm
+ - kanban
+ - loadbalancer
+ - notes
+ - passwd
+ - pihole
+ - proxy
+ - src
+ - swarm
+ - traefik
diff --git a/group_vars/pihole.yml b/group_vars/pihole.yml
deleted file mode 100644
index 7e6c0e2..0000000
--- a/group_vars/pihole.yml
+++ /dev/null
@@ -1,7 +0,0 @@
----
-
-pihole_domain: "dns.{{ domain }}"
-pihole_base: "{{ swarm_base }}/pihole"
-upstream_dns_servers:
- - 1.1.1.1
- - 1.0.0.1
diff --git a/inventory b/inventory
index 712c7a6..2b7107c 100644
--- a/inventory
+++ b/inventory
@@ -35,7 +35,7 @@ swarm-one ansible_host=10.128.0.201 ansible_user=serve ansible_connection=ssh a
[traefik]
swarm-one ansible_host=10.128.0.201 ansible_user=serve ansible_connection=ssh ansible_become_password='{{ swarm_become_password }}'
-[pihole]
+[labdns]
swarm-one ansible_host=10.128.0.201 ansible_user=serve ansible_connection=ssh ansible_become_password='{{ swarm_become_password }}'
[traextor]
@@ -49,6 +49,7 @@ swarm-one ansible_host=10.128.0.201 ansible_user=serve ansible_connection=ssh a
[silverbullet]
swarm-one ansible_host=10.128.0.201 ansible_user=serve ansible_connection=ssh ansible_become_password='{{ swarm_become_password }}'
+
[bin]
swarm-one ansible_host=10.128.0.201 ansible_user=serve ansible_connection=ssh ansible_become_password='{{ swarm_become_password }}'
diff --git a/playbooks/labdns.yml b/playbooks/labdns.yml
new file mode 100644
index 0000000..59e09ec
--- /dev/null
+++ b/playbooks/labdns.yml
@@ -0,0 +1,7 @@
+---
+
+- name: labdns setup
+ hosts: labdns
+ become: true
+ roles:
+ - labdns
diff --git a/playbooks/pihole.yml b/playbooks/pihole.yml
deleted file mode 100644
index 6a8b523..0000000
--- a/playbooks/pihole.yml
+++ /dev/null
@@ -1,7 +0,0 @@
----
-
-- name: pihole setup
- hosts: pihole
- become: true
- roles:
- - pihole
diff --git a/playbooks/roles/pihole/tasks/main.yml b/playbooks/roles/labdns/tasks/main.yml
index fc526dc..20c95f7 100644
--- a/playbooks/roles/pihole/tasks/main.yml
+++ b/playbooks/roles/labdns/tasks/main.yml
@@ -1,9 +1,8 @@
---
-- name: Deploy pihole
+- name: Deploy labdns
ansible.builtin.import_tasks: manage-docker-swarm-service.yml
vars:
- service_name: pihole
+ service_name: labdns
template_render_dir: "../templates"
- service_destination_dir: "{{ pihole_base }}"
-
+ service_destination_dir: "{{ labdns_base }}"
diff --git a/playbooks/roles/labdns/templates/stacks/docker-compose.yml b/playbooks/roles/labdns/templates/stacks/docker-compose.yml
new file mode 100644
index 0000000..3327c18
--- /dev/null
+++ b/playbooks/roles/labdns/templates/stacks/docker-compose.yml
@@ -0,0 +1,30 @@
+---
+
+services:
+ labdns:
+ image: mvance/unbound:latest
+ ports:
+ - "53:53/udp"
+ - "53:53/tcp"
+ volumes:
+ - {{ labdns_base }}/volumes/unbound/forward-records.conf:/opt/unbound/etc/unbound/forward-records.conf:ro
+ - {{ labdns_base }}/volumes/unbound/a-records.conf:/opt/unbound/etc/unbound/a-records.conf:ro
+ environment:
+ - TZ={{ timezone }}
+ - DEPLOYMENT_TIME={{ deployment_time }}
+{% if not homelab_build %}
+ healthcheck:
+ test: ["CMD-SHELL", "drill loadbalancer.{{ domain }} @127.0.0.1 | grep -q {{ loadbalancer_ip }}"]
+ retries: 3
+ timeout: 5s
+ start_period: 8s
+{% endif %}
+ deploy:
+ mode: replicated
+ update_config:
+ parallelism: 1
+ failure_action: rollback
+ order: start-first
+ monitor: 25s
+ replicas: 2
+
diff --git a/playbooks/roles/labdns/templates/volumes/unbound/a-records.conf b/playbooks/roles/labdns/templates/volumes/unbound/a-records.conf
new file mode 100644
index 0000000..9462aab
--- /dev/null
+++ b/playbooks/roles/labdns/templates/volumes/unbound/a-records.conf
@@ -0,0 +1,4 @@
+# {{ domain }}
+{% for service in internal_services %}
+local-data: "{{ service }}.{{ domain }}. A {{ loadbalancer_ip }}"
+{% endfor %}
diff --git a/playbooks/roles/labdns/templates/volumes/unbound/forward-records.conf b/playbooks/roles/labdns/templates/volumes/unbound/forward-records.conf
new file mode 100644
index 0000000..19af327
--- /dev/null
+++ b/playbooks/roles/labdns/templates/volumes/unbound/forward-records.conf
@@ -0,0 +1,5 @@
+forward-zone:
+ name: "."
+ forward-addr: 1.1.1.1@853#cloudflare-dns.com
+ forward-addr: 1.0.0.1@853#cloudflare-dns.com
+ forward-tls-upstream: yes
diff --git a/playbooks/roles/pihole/templates/stacks/docker-compose.yml b/playbooks/roles/pihole/templates/stacks/docker-compose.yml
deleted file mode 100644
index 573121f..0000000
--- a/playbooks/roles/pihole/templates/stacks/docker-compose.yml
+++ /dev/null
@@ -1,43 +0,0 @@
-services:
- pihole:
- image: pihole/pihole:latest
- ports:
- - "53:53/udp"
- - "53:53/tcp"
- volumes:
- - {{ pihole_base }}/volumes/pihole:/etc/pihole
- - {{ pihole_base }}/volumes/dnsmasq:/etc/dnsmasq.d
- environment:
- - DEPLOYMENT_TIME={{ deployment_time }}
- - TZ={{ timezone }}
- - FTLCONF_webserver_api_password={{ pihole_webpwd }}
- - FTLCONF_dns_upstreams={{ upstream_dns_servers | join(';') }}
- networks:
- - proxy
-{% if not homelab_build %}
- healthcheck:
- test: ["CMD-SHELL", "dig loadbalancer.{{ domain }} @127.0.0.1 | grep -q {{ loadbalancer_ip }}"]
- retries: 3
- timeout: 5s
- start_period: 8s
-{% endif %}
- deploy:
- mode: replicated
- update_config:
- parallelism: 1
- order: start-first
- failure_action: rollback
- monitor: 10s
- replicas: 1
- labels:
- - traefik.enable=true
- - traefik.swarm.network=proxy
- - traefik.http.routers.piholeweb.tls=true
- - traefik.http.routers.piholeweb.tls.certResolver=letsencrypt
- - traefik.http.routers.piholeweb.rule=Host(`{{ pihole_domain }}`)
- - traefik.http.routers.piholeweb.entrypoints=websecure
- - traefik.http.services.piholeweb.loadbalancer.server.port=80
-
-networks:
- proxy:
- external: true
diff --git a/playbooks/roles/pihole/templates/volumes/dnsmasq/.gitkeep b/playbooks/roles/pihole/templates/volumes/dnsmasq/.gitkeep
deleted file mode 100644
index e69de29..0000000
--- a/playbooks/roles/pihole/templates/volumes/dnsmasq/.gitkeep
+++ /dev/null
diff --git a/playbooks/roles/pihole/templates/volumes/pihole/.gitkeep b/playbooks/roles/pihole/templates/volumes/pihole/.gitkeep
deleted file mode 100644
index e69de29..0000000
--- a/playbooks/roles/pihole/templates/volumes/pihole/.gitkeep
+++ /dev/null