GRM-144: feat(runner): enable IPv6 in rootless Docker via pasta network driver
This commit is contained in:
@@ -57,7 +57,7 @@ CI also runs a best-effort `make workflow-dryrun` step (skipped if act_runner is
|
|||||||
## Architecture
|
## Architecture
|
||||||
|
|
||||||
- **Python CLI** (`src/grm/`) — Click-based CLI that delegates to Ansible
|
- **Python CLI** (`src/grm/`) — Click-based CLI that delegates to Ansible
|
||||||
- **Ansible Role** (`ansible/roles/gitea-runner/`) — Idempotent role for rootless Docker runner setup
|
- **Ansible Role** (`ansible/roles/gitea-runner/`) — Idempotent role for rootless Docker runner setup with pasta networking (IPv6 support)
|
||||||
- **devx package** (installed from git) — Reusable CI/CD tools: auto-merge, post-merge, release, publishing, molecule distribution, PR reviews, failure notifications
|
- **devx package** (installed from git) — Reusable CI/CD tools: auto-merge, post-merge, release, publishing, molecule distribution, PR reviews, failure notifications
|
||||||
- **Versioning** (`cliff.toml`) — git-cliff configuration for automated semver versioning from conventional commits
|
- **Versioning** (`cliff.toml`) — git-cliff configuration for automated semver versioning from conventional commits
|
||||||
|
|
||||||
|
|||||||
@@ -53,3 +53,10 @@ docker_apt_source_line: >-
|
|||||||
{{ ansible_facts['distribution_release'] }} stable
|
{{ ansible_facts['distribution_release'] }} stable
|
||||||
# Set to false in CI/molecule to skip rootless daemon startup (needs kernel userns)
|
# Set to false in CI/molecule to skip rootless daemon startup (needs kernel userns)
|
||||||
docker_rootless_setup: true
|
docker_rootless_setup: true
|
||||||
|
|
||||||
|
# Rootless Docker network driver: "pasta" (IPv6 support) or "slirp4netns" (IPv4 only)
|
||||||
|
# pasta has proper outgoing IPv6 support; slirp4netns does not (known limitation).
|
||||||
|
docker_rootless_net_driver: "pasta"
|
||||||
|
|
||||||
|
# IPv6 subnet for rootless Docker containers (ULA range, not routable on internet)
|
||||||
|
docker_ipv6_cidr: "fd00:dead:beef::/48"
|
||||||
|
|||||||
@@ -35,6 +35,7 @@
|
|||||||
name:
|
name:
|
||||||
- uidmap
|
- uidmap
|
||||||
- slirp4netns
|
- slirp4netns
|
||||||
|
- passt
|
||||||
- fuse-overlayfs
|
- fuse-overlayfs
|
||||||
- docker-ce
|
- docker-ce
|
||||||
- docker-ce-cli
|
- docker-ce-cli
|
||||||
@@ -57,6 +58,7 @@
|
|||||||
- docker
|
- docker
|
||||||
- docker-compose
|
- docker-compose
|
||||||
- slirp4netns
|
- slirp4netns
|
||||||
|
- passt
|
||||||
- fuse-overlayfs
|
- fuse-overlayfs
|
||||||
- rsync
|
- rsync
|
||||||
state: present
|
state: present
|
||||||
@@ -106,12 +108,48 @@
|
|||||||
group: "{{ gitea_runner_service_user }}"
|
group: "{{ gitea_runner_service_user }}"
|
||||||
when: docker_rootless_setup
|
when: docker_rootless_setup
|
||||||
|
|
||||||
- name: Configure rootless Docker to disable IPv6
|
- name: Ensure systemd user override directory exists
|
||||||
|
ansible.builtin.file:
|
||||||
|
path: "{{ gitea_runner_home }}/.config/systemd/user/docker.service.d"
|
||||||
|
state: directory
|
||||||
|
mode: "0755"
|
||||||
|
owner: "{{ gitea_runner_service_user }}"
|
||||||
|
group: "{{ gitea_runner_service_user }}"
|
||||||
|
when: docker_rootless_setup
|
||||||
|
|
||||||
|
- name: Configure rootless Docker to use pasta with IPv6
|
||||||
|
ansible.builtin.copy:
|
||||||
|
dest: "{{ gitea_runner_home }}/.config/systemd/user/docker.service.d/override.conf"
|
||||||
|
content: |
|
||||||
|
[Service]
|
||||||
|
Environment="DOCKERD_ROOTLESS_ROOTLESSKIT_NET={{ docker_rootless_net_driver }}"
|
||||||
|
Environment="DOCKERD_ROOTLESS_ROOTLESSKIT_PORT_DRIVER=implicit"
|
||||||
|
Environment="DOCKERD_ROOTLESS_ROOTLESSKIT_FLAGS=--ipv6"
|
||||||
|
mode: "0644"
|
||||||
|
owner: "{{ gitea_runner_service_user }}"
|
||||||
|
group: "{{ gitea_runner_service_user }}"
|
||||||
|
register: docker_network_override
|
||||||
|
when: docker_rootless_setup
|
||||||
|
|
||||||
|
- name: Reload systemd user daemon if network config changed
|
||||||
|
ansible.builtin.command: systemctl --user daemon-reload
|
||||||
|
become: true
|
||||||
|
become_user: "{{ gitea_runner_service_user }}"
|
||||||
|
environment:
|
||||||
|
XDG_RUNTIME_DIR: "/run/user/{{ gitea_runner_uid }}"
|
||||||
|
changed_when: true
|
||||||
|
when:
|
||||||
|
- docker_rootless_setup
|
||||||
|
- docker_network_override is changed
|
||||||
|
|
||||||
|
- name: Configure rootless Docker daemon with IPv6 enabled
|
||||||
ansible.builtin.copy:
|
ansible.builtin.copy:
|
||||||
dest: "{{ gitea_runner_home }}/.config/docker/daemon.json"
|
dest: "{{ gitea_runner_home }}/.config/docker/daemon.json"
|
||||||
content: |
|
content: |
|
||||||
{
|
{
|
||||||
"ipv6": false
|
"ipv6": true,
|
||||||
|
"ip6tables": true,
|
||||||
|
"fixed-cidr-v6": "{{ docker_ipv6_cidr }}"
|
||||||
}
|
}
|
||||||
mode: "0644"
|
mode: "0644"
|
||||||
owner: "{{ gitea_runner_service_user }}"
|
owner: "{{ gitea_runner_service_user }}"
|
||||||
@@ -128,7 +166,7 @@
|
|||||||
changed_when: true
|
changed_when: true
|
||||||
when:
|
when:
|
||||||
- docker_rootless_setup
|
- docker_rootless_setup
|
||||||
- docker_ipv6_config is changed
|
- docker_ipv6_config is changed or docker_network_override is changed
|
||||||
|
|
||||||
- name: Wait for rootless Docker daemon to be ready
|
- name: Wait for rootless Docker daemon to be ready
|
||||||
ansible.builtin.command: docker version
|
ansible.builtin.command: docker version
|
||||||
|
|||||||
@@ -173,9 +173,21 @@ Each runner operates under a dedicated unprivileged system user. The Docker daem
|
|||||||
|
|
||||||
- User namespace mapping via `/etc/subuid` and `/etc/subgid` (range: 100000-165535)
|
- User namespace mapping via `/etc/subuid` and `/etc/subgid` (range: 100000-165535)
|
||||||
- Rootless Docker socket at `/run/user/<UID>/docker.sock`
|
- Rootless Docker socket at `/run/user/<UID>/docker.sock`
|
||||||
- `slirp4netns` for user-mode networking
|
- `pasta` for user-mode networking with IPv6 support (replaces `slirp4netns`, which lacks outgoing IPv6)
|
||||||
- `fuse-overlayfs` for rootless container storage
|
- `fuse-overlayfs` for rootless container storage
|
||||||
|
|
||||||
|
The rootless Docker daemon is configured via a systemd user override
|
||||||
|
(`docker.service.d/override.conf`) that sets:
|
||||||
|
|
||||||
|
- `DOCKERD_ROOTLESS_ROOTLESSKIT_NET=pasta` — use pasta as the network driver
|
||||||
|
- `DOCKERD_ROOTLESS_ROOTLESSKIT_PORT_DRIVER=implicit` — pasta's native port forwarding
|
||||||
|
- `DOCKERD_ROOTLESS_ROOTLESSKIT_FLAGS=--ipv6` — enable IPv6 routing
|
||||||
|
|
||||||
|
The daemon.json enables IPv6 with a ULA subnet (`fd00:dead:beef::/48`)
|
||||||
|
for container addressing. This ensures runner containers can reach
|
||||||
|
both IPv4 and IPv6 services (e.g., the Gitea registry) without
|
||||||
|
per-workaround DNS hacks.
|
||||||
|
|
||||||
Containers launched by the runner never have root access to the host. The rootless Docker daemon is started as a systemd user service and persists via lingering.
|
Containers launched by the runner never have root access to the host. The rootless Docker daemon is started as a systemd user service and persists via lingering.
|
||||||
|
|
||||||
### Secret handling
|
### Secret handling
|
||||||
|
|||||||
Reference in New Issue
Block a user