Add netbox role

Kinda ouroborosish if you think about it. Better don’t.
This commit is contained in:
Timotej Lazar 2024-05-28 12:32:28 +02:00
parent 43b9010126
commit c7a3513fa1
14 changed files with 379 additions and 0 deletions

View file

@ -11,3 +11,8 @@
cluster: '{{ query("netbox.netbox.nb_lookup", "clusters", raw_data=true, api_filter="name="+cluster) | first }}'
nodes: '{{ groups["cluster_"+cluster] | map("extract", hostvars) | rejectattr("is_virtual") }}'
when: cluster
- name: Get my domain names if any
set_fact:
fqdns: '{{ interfaces | map(attribute="ip_addresses") | flatten
| map(attribute="dns_name") | reject("==", "") | sort | unique }}'

View file

@ -0,0 +1,11 @@
# handle .well-known for all domains
server {
listen 80 default_server;
listen [::]:80 default_server;
location /.well-known/ {
alias /srv/http/.well-known/;
}
location / {
return 301 https://$host$request_uri;
}
}

View file

@ -0,0 +1,2 @@
dulwich # for git data sources
netbox-topology-views

View file

@ -0,0 +1,27 @@
- name: reload nginx
service:
name: nginx
state: reloaded
when: "'handler' not in ansible_skip_tags"
- name: reload package cache
package:
update_cache: yes
when: "'handler' not in ansible_skip_tags"
- name: restart netbox
service:
name: '{{ item }}'
state: restarted
loop:
- netbox
- netbox-rq
when: "'handler' not in ansible_skip_tags"
- name: run migrations
become: yes
become_method: su
become_user: '{{ user }}'
command: sh ~/app/upgrade.sh
notify: restart netbox
when: "'handler' not in ansible_skip_tags"

150
roles/netbox/tasks/app.yml Normal file
View file

@ -0,0 +1,150 @@
- name: Install dependencies
package:
name:
- git
- python3
- python3-dev
- py3-pip
- py3-virtualenv
- bash # for upgrade script
- build-base # to build psycopg if not available
- postgresql-dev # likewise
- name: Checkout repo
become: yes
become_method: su
become_user: '{{ user }}'
git:
repo: https://github.com/netbox-community/netbox.git
dest: '{{ user_info.home }}/app'
version: 'v{{ netbox_version }}'
notify: run migrations
- name: Copy default config
copy:
dest: '{{ user_info.home }}/app/netbox/netbox/configuration.py'
src: '{{ user_info.home }}/app/netbox/netbox/configuration_example.py'
remote_src: yes
owner: '{{ user_info.uid }}'
group: '{{ user_info.group }}'
force: no
notify: run migrations
- name: Restrict access to config
file:
path: '{{ user_info.home }}/app/netbox/netbox/configuration.py'
mode: 0600
- name: Configure secret key
lineinfile:
path: '{{ user_info.home }}/app/netbox/netbox/configuration.py'
regexp: "^SECRET_KEY = ''"
line: "SECRET_KEY = '{{ lookup('password', '/dev/null', length=50) }}'"
backrefs: yes # don’t set if set already
- name: Configure base settings and database
lineinfile:
path: '{{ user_info.home }}/app/netbox/netbox/configuration.py'
regexp: '{{ item.key }}'
line: '{{ item.line }}'
loop:
- key: '^ALLOWED_HOSTS = '
line: "ALLOWED_HOSTS = [{{ fqdns | map('regex_replace', '^(.*)$', '\"\\1\"') | join(', ') }}]"
- key: 'USER.*PostgreSQL username'
line: " 'USER': '{{ user }}', # PostgreSQL username"
# XXX unnecessary?
#- key: '(OPTIONS|PASSWORD).*PostgreSQL password'
# line: " 'OPTIONS': { 'passfile': '{{ user_info.home }}/.pgpass' }, # PostgreSQL password"
# not yet compatible, see https://github.com/netbox-community/netbox-topology-views/issues/503
#- key: '^PLUGINS = '
# line: "PLUGINS = ['netbox_topology_views']"
notify: run migrations
- name: Configure OIDC authentication
lineinfile:
path: '{{ user_info.home }}/app/netbox/netbox/configuration.py'
regexp: '{{ item.key }}'
line: '{{ item.line }}'
loop:
- key: "^REMOTE_AUTH_ENABLED ="
line: "REMOTE_AUTH_ENABLED = True"
- key: "^REMOTE_AUTH_BACKEND ="
line: "REMOTE_AUTH_BACKEND = 'social_core.backends.open_id_connect.OpenIdConnectAuth'"
- key: "^SOCIAL_AUTH_OIDC_OIDC_ENDPOINT ="
line: "SOCIAL_AUTH_OIDC_OIDC_ENDPOINT = '{{ lookup('passwordstore', 'vm/'~inventory_hostname, subkey='oidc_endpoint') }}'"
- key: "^SOCIAL_AUTH_OIDC_KEY ="
line: "SOCIAL_AUTH_OIDC_KEY = '{{ lookup('passwordstore', 'vm/'~inventory_hostname, subkey='oidc_client_id') }}'"
- key: "^SOCIAL_AUTH_OIDC_SECRET ="
line: "SOCIAL_AUTH_OIDC_SECRET = '{{ lookup('passwordstore', 'vm/'~inventory_hostname, subkey='oidc_client_secret') }}'"
# TODO the key should really be upn but it doesn’t seem to work
- key: "^SOCIAL_AUTH_OIDC_USERNAME_KEY ="
line: "SOCIAL_AUTH_OIDC_USERNAME_KEY = 'email'"
notify: run migrations
- name: Set additional requirements
become: yes
become_method: su
become_user: '{{ user }}'
copy:
dest: '{{ user_info.home }}/app/'
src: local_requirements.txt
notify: run migrations
- meta: flush_handlers
- name: Create superuser
become: yes
become_method: su
become_user: '{{ user }}'
command:
cmd: '{{ user_info.home }}/app/venv/bin/python {{ user_info.home }}/app/netbox/manage.py shell --interface python'
stdin: |
import sys
from users.models import User
#from django.contrib.auth.models import User
username = '{{ lookup('passwordstore', 'vm/'~inventory_hostname, subkey='admin_user') }}'
if not User.objects.filter(username=username):
User.objects.create_superuser(username, '', # TODO email
'{{ lookup('passwordstore', 'vm/'~inventory_hostname, subkey='admin_pass') }}')
sys.exit(1)
register: result
changed_when: result.rc != 0
- name: Set up gunicorn
copy:
dest: /srv/netbox/gunicorn.py
src: /srv/netbox/app/contrib/gunicorn.py
remote_src: yes
force: no
owner: netbox
group: netbox
- name: Set up cron job
file:
dest: /etc/periodic/daily/netbox-housekeeping.sh
src: /srv/netbox/app/contrib/netbox-housekeeping.sh
state: link
- name: Install services
template:
dest: '/etc/init.d/{{ item }}'
src: '{{ item }}.initd.j2'
mode: 0755
loop:
- netbox
- netbox-rq
- name: Enable services
service:
name: '{{ item }}'
enabled: true
state: started
loop:
- netbox
- netbox-rq
- name: Set up nginx site
template:
dest: '/etc/nginx/http.d/netbox.conf'
src: 'netbox.conf.j2'
notify: reload nginx

55
roles/netbox/tasks/db.yml Normal file
View file

@ -0,0 +1,55 @@
- name: Install packages
package:
name:
- postgresql
- py3-psycopg2
- redis
- name: Enable services
service:
name: '{{ item }}'
enabled: true
state: started
loop:
- postgresql
- redis
- name: Create .pgpass
copy:
dest: '{{ user_info.home }}/.pgpass'
content: |
localhost:5432:{{ database }}:{{ user }}:{{ db_password }}
force: no
mode: 0600
owner: '{{ user_info.uid }}'
group: '{{ user_info.group }}'
- become: yes
become_method: su
become_user: postgres
block:
- name: Create database
postgresql_db:
name: '{{ database }}'
- name: Create database user
postgresql_user:
db: '{{ database }}'
name: '{{ user }}'
password: '{{ db_password }}'
no_password_changes: yes
- name: Set schema owner
postgresql_owner:
db: '{{ database }}'
new_owner: '{{ user }}'
obj_name: public
obj_type: schema
- name: Grant database privileges
postgresql_privs:
db: '{{ database }}'
role: '{{ user }}'
privs: CREATE
type: database

View file

@ -0,0 +1,25 @@
- name: Set variables
set_fact:
user: '{{ user | default("netbox") }}'
database: '{{ database | default("netbox") }}'
db_password: '{{ lookup("password", "/dev/null", chars=["ascii_letters", "digits"]) }}'
- name: Create group for web service
group:
name: '{{ user }}'
system: yes
- name: Create user for web service
user:
name: '{{ user }}'
group: '{{ user }}'
home: '/srv/{{ user }}'
shell: /bin/sh
system: yes
register: user_info
- name: Set up database
import_tasks: db.yml
- name: Set up app
import_tasks: app.yml

View file

@ -0,0 +1,10 @@
#!/sbin/openrc-run
description="NetBox request queue worker"
command="{{ user_info.home }}/app/venv/bin/python3"
command_args="{{ user_info.home }}/app/netbox/manage.py rqworker high default low"
command_user="{{ user }}:{{ user }}"
command_background=true
pidfile="/run/${RC_SVCNAME}.pid"

View file

@ -0,0 +1,23 @@
{% for fqdn in fqdns %}
server {
server_name {{ fqdn }};
listen [::]:443 ssl ipv6only=off;
ssl_certificate /etc/letsencrypt/live/{{ fqdn }}/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/{{ fqdn }}/privkey.pem;
client_max_body_size 100m;
location /static/ {
alias {{ user_info.home }}/app/netbox/static/;
}
location / {
proxy_pass http://127.0.0.1:8001;
proxy_set_header X-Forwarded-Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
{% endfor %}

View file

@ -0,0 +1,10 @@
#!/sbin/openrc-run
description="NetBox WSGI service"
command="{{ user_info.home }}/app/venv/bin/gunicorn"
command_args="--pythonpath {{ user_info.home }}/app/netbox --config {{ user_info.home }}/gunicorn.py netbox.wsgi"
command_user="{{ user }}:{{ user }}"
command_background=true
pidfile="/run/${RC_SVCNAME}.pid"

View file

@ -0,0 +1,11 @@
# handle .well-known and HTTPS redirect for all domains
server {
listen 80 default_server;
listen [::]:80 default_server;
location /.well-known/ {
alias /srv/http/.well-known/;
}
location / {
return 301 https://$host$request_uri;
}
}

View file

@ -0,0 +1,5 @@
- name: reload nginx
service:
name: nginx
state: reloaded
when: "'handler' not in ansible_skip_tags"

View file

@ -0,0 +1,39 @@
- name: Install packages
package:
name:
- certbot
- nginx
- name: Create HTTP server directories
file:
path: /srv/http/.well-known
recurse: true
state: directory
owner: nginx
group: nginx
- name: Set up default HTTP server
copy:
dest: /etc/nginx/http.d
src: default.conf
notify: reload nginx
- name: Enable nginx service
service:
name: nginx
enabled: true
state: started
- name: Get LE certificate
command:
cmd: certbot certonly --non-interactive --agree-tos --register-unsafely-without-email --webroot --webroot-path /srv/http -d {{ item }}
creates: '/etc/letsencrypt/renewal/{{ item }}.conf'
loop: '{{ fqdns }}'
- name: Enable certbot renewal
cron:
name: "certbot renew"
job: "certbot renew --quiet"
user: root
hour: "2,14"
minute: "18"

View file

@ -20,6 +20,12 @@
- alpine
- dokuwiki
- hosts: netbox
roles:
- alpine
- nginx
- netbox
- hosts: samba
roles:
- debian