Initial IPv6 delployment #2

Merged
CaZzzer merged 2 commits from feature/ipv6 into master 2023-04-04 08:56:13 +00:00
16 changed files with 177 additions and 24 deletions
Showing only changes of commit 40de9b87a1 - Show all commits

1
.idea/alpina.iml generated
View File

@ -28,6 +28,7 @@
<option value="$MODULE_DIR$/roles/nextcloud/templates" />
<option value="$MODULE_DIR$/roles/arrstack/templates" />
<option value="$MODULE_DIR$/roles/jellyfin/templates" />
<option value="$MODULE_DIR$/roles/docker_host/templates" />
</list>
</option>
</component>

23
.idea/jsonSchemas.xml generated
View File

@ -3,6 +3,24 @@
<component name="JsonSchemaMappingsProjectConfiguration">
<state>
<map>
<entry key="Ansible Tasks File">
<value>
<SchemaInfo>
<option name="name" value="Ansible Tasks File" />
<option name="relativePathToSchema" value="https://raw.githubusercontent.com/ansible/ansible-lint/main/src/ansiblelint/schemas/ansible.json#/$defs/tasks" />
<option name="applicationDefined" value="true" />
<option name="patterns">
<list>
<Item>
<option name="pattern" value="true" />
<option name="path" value="*/tasks/*.yml" />
<option name="mappingKind" value="Pattern" />
</Item>
</list>
</option>
</SchemaInfo>
</value>
</entry>
<entry key="Traefik v2">
<value>
<SchemaInfo>
@ -45,6 +63,11 @@
<option name="applicationDefined" value="true" />
<option name="patterns">
<list>
<Item>
<option name="pattern" value="true" />
<option name="path" value="*/docker-compose.yml" />
<option name="mappingKind" value="Pattern" />
</Item>
<Item>
<option name="pattern" value="true" />
<option name="path" value="*/docker-compose.yml.j2" />

View File

@ -1,4 +1,6 @@
.POSIX:
.PHONY: *
.EXPORT_ALL_VARIABLES:
env ?= staging
vault_id ?= alpina@contrib/rbw-client.sh

View File

@ -2,3 +2,17 @@
A home for configuring all of my homelab containers on a Debian Linux machine.
This assumes a Debian Linux machine with Docker and Docker Compose installed.
# Notes
## IPv6
The current configuration is designed to work with IPv6.
However, because of how (not properly) I'm doing the subnetting
from the host's network, NDP doesn't work.
This means that container IPs are not accessible from other hosts on the local network.
I simply have a static route on my router to the container subnet,
that uses the IP of this host as the gateway.
This is a limitation of my current ISP, I only have a single /64 subnet for my lab network.
I'd like to get a /56 or /48, perhaps using Hurricane Electric's tunnel broker.
*Sigh* ISPs being stingy with the 2^48 prefixes they're afraid of running out of.

View File

@ -1,3 +1,11 @@
{% macro default_network(subnet_index) %}
default:
enable_ipv6: true
ipam:
config:
- subnet: {{ docker_ipv6_subnet | ansible.utils.ipsubnet(80, subnet_index) }}
{% endmacro %}
{% macro traefik_labels(host, service="", port="", auth=false) %}
traefik.enable=true
- traefik.http.routers.{{ host }}.rule=Host(`{{ host }}.{{ domain }}`)

View File

@ -28,8 +28,12 @@
when: item.state == "file"
- name: Deploy docker-compose for {{ current_svc_name }}
community.docker.docker_compose:
project_src: "{{ current_svc_path }}"
state: present
pull: true
remove_orphans: true
command: docker compose -f "{{ current_svc_path }}/docker-compose.yml" up -d --pull --remove-orphans
register: docker_compose_output
# Not perfect idempotency, but the built-in docker_compose module doesn't support docker-compose v2
# And of course there's an IPv6 bug in docker-compose v1, smh
# https://github.com/docker/compose/issues/7670
changed_when: "'created' in docker_compose_output.stderr.lower()"
- debug:
var: docker_compose_output

View File

@ -0,0 +1 @@
docker_ipv6_index: 255

View File

@ -0,0 +1 @@
docker_ipv6_index: 254

14
poetry.lock generated
View File

@ -254,6 +254,18 @@ files = [
{file = "MarkupSafe-2.1.2.tar.gz", hash = "sha256:abcabc8c2b26036d62d4c746381a6f7cf60aafcc653198ad678306986b09450d"},
]
[[package]]
name = "netaddr"
version = "0.8.0"
description = "A network address manipulation library for Python"
category = "main"
optional = false
python-versions = "*"
files = [
{file = "netaddr-0.8.0-py2.py3-none-any.whl", hash = "sha256:9666d0232c32d2656e5e5f8d735f58fd6c7457ce52fc21c98d45f2af78f990ac"},
{file = "netaddr-0.8.0.tar.gz", hash = "sha256:d6cc57c7a07b1d9d2e917aa8b36ae8ce61c35ba3fcd1b83ca31c5a0ee2b5a243"},
]
[[package]]
name = "packaging"
version = "23.0"
@ -366,4 +378,4 @@ testing-integration = ["build[virtualenv]", "filelock (>=3.4.0)", "jaraco.envs (
[metadata]
lock-version = "2.0"
python-versions = "^3.10"
content-hash = "4c3656f66006d184debf3777b8df073898df0eb1f53611cdd47ec4c543071595"
content-hash = "e5cad99dc808b0751037fa8d524f2a4c4eac8f6ae5710c6ed5f32def518746b9"

View File

@ -9,6 +9,7 @@ readme = "README.md"
python = "^3.10"
ansible = "^7.3.0"
ansible-vault = "^2.1.0"
netaddr = "^0.8.0"
[build-system]

View File

@ -1,3 +1,12 @@
- name: Install Debian packages
become: yes
ansible.builtin.apt:
name:
- docker-ce
- docker-compose-plugin
- ufw
state: latest
- name: Upgrade Debian packages
become: yes
ansible.builtin.apt:
@ -8,6 +17,26 @@
state: latest
register: apt_upgrades
- name: Allow SSH
become: yes
ufw:
rule: allow
name: OpenSSH
- name: Allow Web
become: yes
ufw:
rule: allow
name: WWW Full
- name: Enable Firewall
become: yes
ufw:
state: enabled
policy: reject
direction: incoming
logging: on
- name: Reboot if needed
become: yes
ansible.builtin.reboot:

View File

@ -3,3 +3,31 @@
state: directory
path: "{{ my_svc_path }}"
mode: "700"
- name: Get IPv6 subnet for Docker
set_fact:
docker_ipv6_subnet: "{{ \
ansible_default_ipv6.address \
| ansible.utils.ipsubnet(64) \
| ansible.utils.ipsubnet(72, docker_ipv6_index) \
}}"
- debug:
var: docker_ipv6_subnet
- name: Configure Docker daemon
become: yes
template:
src: "daemon.json.j2"
dest: "/etc/docker/daemon.json"
owner: root
group: root
mode: "0644"
register: docker_daemon_config
- name: Restart Docker daemon
become: yes
service:
name: docker
state: restarted
when: docker_daemon_config.changed

View File

@ -0,0 +1,4 @@
{
"ipv6": true,
"fixed-cidr-v6": "{{ docker_ipv6_subnet | ansible.utils.ipsubnet(80, 0) }}"
}

View File

@ -1,35 +1,37 @@
{% from "contrib/compose_helpers.j2" import traefik_labels with context %}
{% import 'contrib/compose_helpers.j2' as helpers with context %}
{##}
version: "3.7"
version: "3.9"
networks:
default:
traefik:
internal: true
enable_ipv6: true
ipam:
config:
- subnet: {{ traefik_ip }}/24
- subnet: {{ docker_ipv6_subnet | ansible.utils.ipsubnet(80, 255) }}
services:
traefik:
image: traefik:v2.9
container_name: traefik
labels:
- {{ traefik_labels("traefik", service="api@internal") | indent(6) }}
restart: unless-stopped
ports:
- "80:80"
- "443:443"
- "8080:8080"
env_file:
- .env.traefik
networks:
default:
traefik:
ipv4_address: {{ traefik_ip }}
network_mode: host
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
- ./traefik.yml:/etc/traefik/traefik.yml:ro
- {{ base_volume_path }}/traefik/rules:/rules:ro
- ./rules:/rules:ro
- /var/run/docker.sock:/var/run/docker.sock:ro
- {{ base_volume_path }}/traefik/rules:/rules/extra:ro
- {{ base_volume_path }}/traefik/logs:/logs
- {{ base_volume_path }}/traefik/acme:/acme
# This is mostly just so that the traefik network gets created
whoami:
image: containous/whoami
container_name: whoami
labels:
- {{ helpers.traefik_labels('whoami', port=80) | indent(6) }}
networks:
- traefik

View File

@ -0,0 +1,25 @@
http:
routers:
traefik-dash:
rule: "Host(`traefik.{{ domain }}`)"
entryPoints:
- web
service: traefik-dash
traefik-dash-tls:
rule: "Host(`traefik.{{ domain }}`)"
entryPoints:
- websecure
service: traefik-dash
tls:
certResolver: letsencrypt
domains:
- main: "{{ domain }}"
sans:
- "*.{{ domain }}"
services:
traefik-dash:
loadBalancer:
servers:
- url: "http://localhost:8080"

View File

@ -11,9 +11,7 @@ accessLog:
entryPoints:
web:
address: ":80"
forwardedHeaders:
trustedIPs:
- "172.16.0.0/12"
websecure:
address: ":443"