Initial commit, squashed

This commit is contained in:
Timotej Lazar 2023-12-18 11:22:14 +01:00
commit 158e8740b8
83 changed files with 2718 additions and 0 deletions

View file

@ -0,0 +1,16 @@
table inet filter {
set fri {
typeof ip saddr; flags interval
elements = { 10.32.0.0/14, 192.168.0.0/16, 141.255.211.0/24, 193.2.76.0/24 }
}
set fri/6 {
typeof ip6 saddr; flags interval
elements = { 2001:1470:fffd::/48 }
}
chain input {
ip saddr @fri tcp dport { ssh, http, https } accept
ip6 saddr @fri/6 tcp dport { ssh, http, https } accept
}
}

View file

@ -0,0 +1,16 @@
[uwsgi]
uid = friwall
gid = friwall
socket = /run/friwall.socket
chown-socket = friwall:nginx
chmod-socket = 660
plugin = python3
chdir = /srv/friwall/app
mount = /=wsgi:app
env = PYTHONUSERBASE=/srv/friwall/.local
env = HOME=/srv/friwall
# Microsoft OIDC endpoint sends some fat‐ass headers.
buffer-size = 16384

View file

@ -0,0 +1,18 @@
#!/sbin/openrc-run
command="/srv/friwall/app/$RC_SVCNAME"
command_background="yes"
command_user="friwall"
command_group="nogroup"
directory="/srv/friwall"
pidfile="/run/$RC_SVCNAME.pid"
depend() {
need net
}
stop() {
ebegin "Stopping $RC_SVCNAME"
pkill -INT -g $(cat "$pidfile") && rm -f "$pidfile"
eend $?
}

View file

@ -0,0 +1,2 @@
[uwsgi]
emperor = /etc/uwsgi/conf.d

View file

@ -0,0 +1,39 @@
- name: restart interfaces
shell: ifdown --force --auto && ifup --auto
when: "'handler' not in ansible_skip_tags"
- name: reload nftables
service:
name: nftables
state: reloaded
when: "'handler' not in ansible_skip_tags"
- name: reload nginx
service:
name: nginx
state: reloaded
when: "'handler' not in ansible_skip_tags"
- name: restart nginx
service:
name: nginx
state: restarted
when: "'handler' not in ansible_skip_tags"
- name: restart pusher
service:
name: pusher
state: restarted
when: "'handler' not in ansible_skip_tags"
- name: reload uwsgi
service:
name: uwsgi
state: reloaded
when: "'handler' not in ansible_skip_tags"
- name: restart uwsgi
service:
name: uwsgi
state: restarted
when: "'handler' not in ansible_skip_tags"

View file

@ -0,0 +1,9 @@
- name: Install mail server
package:
name: opensmtpd
- name: Enable mail server
service:
name: smtpd
enabled: yes
state: started

View file

@ -0,0 +1,42 @@
- name: Set up network interfaces
template:
dest: /etc/network/interfaces
src: interfaces.j2
mode: 0644
notify: restart interfaces
- name: Install nftables
package:
name: nftables
- name: Accept connections from FRI addresses
copy:
dest: /etc/nftables.d/
src: accept-fri.nft
notify: reload nftables
- name: Enable nftables
service:
name: nftables
enabled: yes
state: started
- name: Install qemu guest agent
package:
name: qemu-guest-agent
- name: Enable qemu guest agent
service:
name: qemu-guest-agent
enabled: yes
runlevel: boot
state: started
- name: Set up mail server
import_tasks: mail.yml
- name: Set up friwall user
import_tasks: user.yml
- name: Set up web UI
import_tasks: web.yml

View file

@ -0,0 +1,14 @@
- name: Create friwall group
group:
name: friwall
system: yes
- name: Create friwall user
user:
name: friwall
system: yes
home: /srv/friwall
shell: /sbin/nologin
generate_ssh_key: yes
ssh_key_comment: "{{ inventory_hostname }}"
ssh_key_type: ed25519

View file

@ -0,0 +1,110 @@
---
- name: Install packages
package:
name: git,inotify-tools,nginx,py3-pip,procps-ng,rsync,uwsgi,uwsgi-python3,wireguard-tools
- name: Clone web files
become: yes
become_user: friwall
become_method: su
become_flags: "-s /bin/sh"
git:
repo: "{{ friwall_repo }}"
dest: /srv/friwall/app
force: yes
notify: reload uwsgi
- name: Install requirements
become: yes
become_user: friwall
become_method: su
become_flags: '-s /bin/sh'
pip:
requirements: /srv/friwall/app/requirements.txt
extra_args: --user
- name: Configure base settings
template:
dest: "/srv/friwall/{{ item }}"
src: "{{ item }}.j2"
owner: friwall
group: friwall
mode: 0600
force: no
loop:
- nodes.json
- settings.json
notify: restart uwsgi
- name: Configure list of networks
template:
dest: "/srv/friwall/networks.json"
src: "networks.json.j2"
owner: friwall
group: friwall
mode: 0600
- name: Configure uwsgi
copy:
dest: /etc/uwsgi/
src: uwsgi.ini
notify: restart uwsgi
- name: Configure uwsgi instance
copy:
dest: /etc/uwsgi/conf.d/
src: friwall.ini
owner: friwall
group: friwall
- name: Enable uwsgi
service:
name: uwsgi
enabled: yes
state: started
- name: Configure nginx instance
template:
dest: /etc/nginx/http.d/friwall.conf
src: nginx.conf.j2
notify: reload nginx
- name: Run nginx in default VRF
lineinfile:
path: /etc/conf.d/nginx
line: "vrf=\"default\""
notify: restart nginx
- name: Enable nginx
service:
name: nginx
enabled: yes
state: started
- name: Install config pusher initscript
copy:
dest: /etc/init.d/pusher
src: pusher.initd
mode: 0755
notify: restart pusher
- name: Enable config pusher service
service:
name: pusher
enabled: true
state: started
- name: Regenerate config daily
cron:
name: "regenerate config"
job: "cd ~/app ; FLASK_APP=web python3 -m flask generate"
user: friwall
hour: "3"
minute: "33"
- name: Try (re-)pushing config periodically
cron:
name: "push config"
job: "cd ~/app ; FLASK_APP=web python3 -m flask push"
user: friwall
minute: "*/15"

View file

@ -0,0 +1,14 @@
auto lo
iface lo inet loopback
{% for iface in interfaces %}
auto {{ iface.name }}
iface {{ iface.name }} inet static
{% for address in iface.ip_addresses %}
address {{ address.address }}
{% endfor %}
{% if iface.custom_fields.gateway %}
gateway {{ iface.custom_fields.gateway.address | ipaddr('address') }}
{% endif %}
{% endfor %}

View file

@ -0,0 +1,21 @@
{% set groups = vlans | map(attribute='name') | select('match', '.+-.+')
| map('split', '-') | map('first') | unique -%}
{% set prefixes = query('netbox.netbox.nb_lookup', 'prefixes', raw_data=true) %}
{
{% for vlan in vlans %}
{% set vlan_prefixes = prefixes | selectattr('vlan') | selectattr('vlan.id', '==', vlan.id) | map(attribute='prefix') %}
"{{ vlan.name }}": {
"ip": {{ vlan_prefixes | ipv4 | to_json }},
"ip6": {{ vlan_prefixes | ipv6 | to_json }}
}{% if not loop.last or groups %},{% endif +%}
{% endfor %}
{% for group in groups %}
{% set group_prefixes = prefixes | selectattr('vlan') | selectattr('vlan.name', 'match', '^'~group) | map(attribute='prefix') %}
"{{ group }}": {
"ip": {{ group_prefixes | ipv4 | to_json }},
"ip6": {{ group_prefixes | ipv6 | to_json }}
}{% if not loop.last %},{% endif +%}
{% endfor %}
}

View file

@ -0,0 +1,21 @@
server {
listen 80;
listen [::]:80;
server_name {{ fqdn }};
return 301 https://$host$request_uri;
}
server {
listen 443 ssl;
listen [::]:443 ssl;
server_name {{ fqdn }};
ssl_certificate /etc/letsencrypt/live/{{ fqdn }}/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/{{ fqdn }}/privkey.pem;
location / {
uwsgi_pass unix:/run/friwall.socket;
include uwsgi_params;
}
}

View file

@ -0,0 +1,10 @@
{% set nodes = query('netbox.netbox.nb_lookup', 'devices', raw_data=true)
| selectattr('config_context') | selectattr('config_context', 'contains', 'master')
| selectattr('config_context.master', '==', inventory_hostname)
| map(attribute='name') -%}
{
{% for node in nodes %}
"{{ hostvars[node].interfaces | selectattr('name', '==', 'lo') | map(attribute='ip_addresses') | first | selectattr('role') | selectattr('role.value', '==', 'loopback') | map(attribute='address') | ipv4 | first | ipaddr('address') }}": -1{{ '' if loop.last else ',' }} {# TODO help my eyes the goggles do nothing +#}
{% endfor %}
}

View file

@ -0,0 +1,10 @@
{
"ldap_host": "{{ domain }}",
"ldap_user": "{{ ldap_user }}",
"ldap_pass": "{{ ldap_pass }}",
"ldap_base_dn": "{{ ldap_base_dn }}",
"oidc_server": "{{ oidc_server }}",
"oidc_client_id": "{{ oidc_client_id }}",
"oidc_client_secret": "{{ oidc_client_secret }}",
"wg_net": "{{ wg_net }}",
}