Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,30 @@ Bootable environment to bootstrap my infrastructure.

TODO

## Usage

Install collection and run locally:

```shell
ansible-galaxy collection build
ansible-galaxy collection install nekrohaven-bootstrap-*.tar.gz --force
ansible-playbook orchestrator/stage_1/playbook.yaml -i orchestrator/stage_1/inventory.yaml
```

## Development

Using nix, which spins up a nix-shell containing all tools required:

```shell
nix develop .#default
```

## TODO

* DNS:
* Resilience: top-level DNS (router) specifies an Unbound zonefile
* Internal DNS fetches from master
* Clients should add both the hypervisor and the router as nameservers
* DNSSEC:
* IPsec:

2 changes: 2 additions & 0 deletions ansible.cfg
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
[defaults]
collections_paths = ./collections
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
# Generated unbound.conf

server:

# Disable Unbound's own recursion
# do-not-query-localhost: no
# do-ip6: no
# do-daemonize: no
# hide-identity: yes
# harden-glue: yes
# harden-dnssec-stripped: yes
# use-caps-for-ids: yes
# prefetch: yes
# cache-max-ttl: 86400
# cache-min-ttl: 3600
#
# # Enable DNSSEC
# auto-trust-anchor-file: "/var/unbound/root.key" # NOTE: can be fetched/updated with `unbound-anchor -a "/var/unbound/root.key"`
# val-clean-additional: yes
# val-log-level: 1
# ipsecmod-enabled: yes
# module-config: "ipsecmod validator iterator"
# validator:
# validator-config: yes
#
# local-zone: "{{ domain }}" static
# {% for host in hosts %}
# local-data: "{{ host.fqdn }} IN A {{ host.ipv4 }}"
# {% endfor %}

server:
verbosity: 1
# Listens for LAN clients
#interface: 0.0.0.0
# Listens for DNSCrypt-proxy
interface: 127.0.0.1
access-control: 127.0.0.0/8 allow
logfile: /var/log/unbound.log

auth-zone:
name: "{{ dns_internal_domain }}"
# The master config, typically /etc/unbound/zones/internal.lan.zone
zonefile: "{{ dns_internal_zonefile }}"
{% dns_host in dns_internal_hosts %}
allow-notify: "{{ dns_host }}"
allow-transfer: "{{ dns_host }}"
{% endfor %}
allow-notify: "{{ dns_internal_hosts }}"

forward-zone:
name: "."
forward-addr: 127.0.0.1@5353 # DNSCrypt-proxy
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
$TTL 3600
@ IN SOA {{ dns_authoritative_host }}. (
2026021001 ; serial
3600 ; refresh
900 ; retry
1209600 ; expire
3600 ) ; minimum

{% dns_host in dns_service_hosts %}
IN NS {{ dns_host.hostaddr }}.
{% endfor %}

{% dns_host in dns_service_hosts %}
{{ dns_host.hostname }} IN A {{ dns_host.ipv4 }}
{% endfor %}


Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# roles/dns/defaults/main.yaml
---
dns_internal_domain: "{{ lookup('ansible.builtin.vars', 'internal_domain') }}"
dns_internal_port: 53
# This is the top-level authoritative DNS (usually the router)
dns_internal_authoritative_ipv4: "{{ lookup('ansible.builtin.vars', 'dns_authoritative') }}"
dns_internal_host_fqdn: "{{ ansible_facts['nodename'] }}.{{ dns_internal_domain }}"
dns_internal_host_ipv4: "{{ ansible_facts['default_ipv4']['address'] }}"
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,3 @@
galaxy_info:
author: rslangl
description: DNS role
dependencies:
- role: runtime_security
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
# dns/tasks/unbound.yaml
---
- name: Get service facts
ansible.builtin.service_facts:

- name: Install unbound
ansible.builtin.package:
name: "{{ item }}"
state: present
update_cache: true
with_items:
- unbound
- unbound-anchor

- name: Configure AppArmor for Unbound
block:
- name: Configure AppArmor for access to logfile
ansible.builtin.lineinfile:
path: /etc/apparmor.d/usr.sbin.unbound
line: ' /var/log/unbound.log rw,'
insertbefore: '}$'
state: present
register: aa_unbound

- name: Reload AppArmor
ansible.builtin.command: apparmor_parser -r /etc/apparmor.d/usr.sbin.unbound
when: aa_unbound.changed
when: ansible_facts['services']['apparmor.service']['status'] | default('not-found') != 'not-found'

- name: Setup trust anchor
ansible.builtin.command: unbound-anchor -a /var/lib/unbound/root.key

- name: Ensure presence of log file
ansible.builtin.file:
path: /var/log/unbound.log
state: touch
owner: unbound
group: unbound
mode: "0640"

- name: Configure unbound
ansible.builtin.template:
src: unbound.conf.j2
dest: /etc/unbound/unbound.conf

- name: Configure interface resolver
ansible.builtin.lineinfile:
path: /etc/network/interfaces
line: '\tdns-nameservers 127.0.0.1'
insertafter: '^iface vmbr0 inet static'
state: present

- name: Configure resolv.conf
ansible.builtin.template:
src: resolv.conf.j2
dest: /etc/resolv.conf
backup: true
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@
DNS=127.0.0.1
DNSSEC=yes
DNSStubListener=no
Domains={{ dns_domain }}
Domains={{ dns_internal_domain }}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# Generated unbound.conf

server:
do-ip6: no
verbosity: 1
interface: 0.0.0.0
port: "{{ dns_internal_port }}"
access-control: 127.0.0.0/8 allow
access-control: 10.0.0.0/8 allow

logfile: /var/log/unbound.log

cache-max-ttl: 86400
cache-min-ttl: 3600

auto-trust-anchor-file: "/var/lib/unbound/root.key"

# Internal-only zone
local-zone: "{{ dns_internal_domain }}" static
local-data: "{{ dns_internal_host_fqdn }} A {{ dns_internal_host_ipv4 }}"

auth-zone:
name: "{{ dns_internal_domain }}."
# The top-level DNS, usually the router
master: "{{ dns_internal_master_ipv4 }}"
# forward-zone:
# name: "."
# forward-addr: "{{ dns_internal_authoritative_ipv4 }}@{{ dns_internal_port }}"
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
# dns/tasks/ipsec.yaml
---
- name: Install strongswan
ansible.builtin.apt:
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
---
- name: Ensure apparmor and apparmor-utils is present
ansible.builtin.apt:
name: "{{ item }}"
state: present
update_cache: true
with_items:
- apparmor
- apparmor-utils

- name: Start apparmor service
ansible.builtin.service:
name: apparmor
state: started
enabled: true

# TODO: define custom rules if necessary
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
- name: Setup MAC using AppArmor
import_tasks:
file: apparmor.yaml
when: mac_module is defined and mac_module == "apparmor"

# TODO: SELinux
Original file line number Diff line number Diff line change
@@ -1,3 +1,2 @@
# roles/security/tasks/peripherals.yaml
---
# TODO: disable data transfer/communication peripherals not in use (e.g. USB)
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
---
service_account_name: ansible
service_account_shell: /bin/bash
service_account_groups: []
service_account_groups:
- ansible
service_account_authorized_keys: []
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
name: "{{ service_account_name }}"
uid: "{{ service_account_uid | default(omit) }}"
shell: "{{ service_account_shell }}"
group: "{{ service_account_groups | join(',') }}"
groups: "{{ service_account_groups | join(',') }}"
append: true
create_home: true
password_lock: true
Expand Down
30 changes: 0 additions & 30 deletions collections/bootstrap/playbooks/bootstrap.yaml

This file was deleted.

This file was deleted.

5 changes: 0 additions & 5 deletions collections/bootstrap/roles/dns_internal/defaults/main.yaml

This file was deleted.

7 changes: 0 additions & 7 deletions collections/bootstrap/roles/dns_internal/tasks/main.yaml

This file was deleted.

40 changes: 0 additions & 40 deletions collections/bootstrap/roles/dns_internal/tasks/unbound.yaml

This file was deleted.

Loading
Loading