From dcbf5a949a880f1af57cbb040889bb7038fd7b0a Mon Sep 17 00:00:00 2001 From: Andrew Ying Date: Sun, 19 Nov 2023 18:18:48 +0000 Subject: [PATCH] Add Netbox setup actions --- roles/netbox_installation/defaults/main.yml | 58 +++++++- roles/netbox_installation/tasks/main.yml | 109 ++++++++++++++- .../templates/configuration.py.j2 | 131 +++++++++--------- .../templates/gunicorn.py.j2 | 6 + .../templates/netbox-housekeeping.service.j2 | 17 +++ .../templates/netbox-housekeeping.timer.j2 | 13 ++ .../templates/netbox-rq.service.j2 | 21 +++ .../templates/netbox.service.j2 | 22 +++ roles/netbox_installation/vars/main.yml | 3 +- 9 files changed, 302 insertions(+), 78 deletions(-) create mode 100644 roles/netbox_installation/templates/gunicorn.py.j2 create mode 100644 roles/netbox_installation/templates/netbox-housekeeping.service.j2 create mode 100644 roles/netbox_installation/templates/netbox-housekeeping.timer.j2 create mode 100644 roles/netbox_installation/templates/netbox-rq.service.j2 create mode 100644 roles/netbox_installation/templates/netbox.service.j2 diff --git a/roles/netbox_installation/defaults/main.yml b/roles/netbox_installation/defaults/main.yml index ae7bf3c..27eb797 100644 --- a/roles/netbox_installation/defaults/main.yml +++ b/roles/netbox_installation/defaults/main.yml @@ -1,6 +1,62 @@ --- netbox_user: netbox +netbox_user_uid: 4030 netbox_group: netbox +netbox_group_gid: 4030 netbox_install_target: "{{ netbox_latest_release }}" -netbox_allowed_hosts: [] +netbox_allowed_hosts: + - '*' + +netbox_database_host: localhost +netbox_database_port: '' +netbox_database_user: netbox +netbox_database_password: '' +netbox_database_name: netbox + +netbox_redis_host: localhost +netbox_redis_port: 6379 +netbox_redis_user: '' +netbox_redis_password: '' +netbox_redis_ssl: false +netbox_redis_tasks_database: 0 +netbox_redis_caching_database: 1 + +netbox_admins: [] +netbox_basepath: '' + +netbox_cors_allow_all: false +netbox_cors_origin_whitelist: [] +netbox_cors_origin_regex_whitelist: [] +netbox_csrf_cookie: 'csrftoken' + +netbox_debug: false + +netbox_localization: false +netbox_default_locale: 'en-us' + +netbox_email_server: 'localhost' +netbox_email_port: 25 +netbox_email_user: '' +netbox_email_password: '' +netbox_from_email: '' + +netbox_internal_ips: + - 127.0.0.1 + - '::1' + +netbox_login_persistence: false +netbox_login_required: false +netbox_logout_redirect: home +netbox_session_cookie: 'sessionid' +netbox_metrics: false +netbox_remote_auth: false +netbox_remote_auth_backend: netbox.authentication.RemoteUserBackend +netbox_remote_auth_auto_create: true +netbox_social_auth_config: {} + +netbox_timezone: GMT + +netbox_server_host: 127.0.0.1 +netbox_server_port: 8001 +netbox_server_workers: "{{ ansible_processor_nproc * 2 + 1 }}" diff --git a/roles/netbox_installation/tasks/main.yml b/roles/netbox_installation/tasks/main.yml index a6c29df..39d1aed 100644 --- a/roles/netbox_installation/tasks/main.yml +++ b/roles/netbox_installation/tasks/main.yml @@ -1,19 +1,28 @@ --- +- name: "Ensure {{ item }} directory is present" + ansible.builtin.file: + path: "{{ item }}" + state: directory + loop: + - /etc/ansible + - /etc/ansible/facts.d + - name: "Gather facts from remote host" ansible.builtin.setup: filter: ansible_local - name: "Set netbox_install to True if no remote installation was detected" ansible.builtin.set_fact: - netbox_install: True + netbox_install: true + netbox_present: true when: "ansible_local is not defined or 'witine' is not in ansible_local or 'netbox' is not in ansible_local['witine'] or 'installed_version' is not in ansible_local['witine']['netbox']" - + - name: "Set netbox_install to True if a newer version is available" ansible.builtin.set_fact: - netbox_install: True + netbox_install: true when: - not netbox_install - - "netbox_install_target['version'] is version(ansible_local['witine']['netbox']['installed_version'], '>')" + - "netbox_install_target.version is version(ansible_local['witine']['netbox']['installed_version'], '>')" - name: "Ensure that Netbox install target has URL and version information" ansible.builtin.assert: @@ -24,14 +33,14 @@ - name: "Download and extract Netbox release" ansible.builtin.unarchive: - src: "{{ netbox_install_target['url'] }}" + src: "{{ netbox_install_target.url }}" dest: "/opt" - remote_src: yes + remote_src: true when: netbox_install - name: "Create symbolic link" ansible.builtin.file: - src: "/opt/netbox-{{ netbox_install_target['version'] }}" + src: "/opt/netbox-{{ netbox_install_target.version }}" dest: /opt/netbox state: link when: netbox_install @@ -44,3 +53,89 @@ - netbox_install - ansible_local['witine']['netbox']['installed_version'] is defined +- name: "Create {{ netbox_group }} group" + ansible.builtin.group: + name: "{{ netbox_group }}" + gid: "{{ netbox_group_gid }}" + system: true + state: present + +- name: "Create {{ netbox_user }} user" + ansible.builtin.user: + name: "{{ netbox_user }}" + uid: "{{ netbox_user_uid }}" + group: "{{ netbox_group }}" + comment: "Netbox user" + system: true + state: present + +- name: "Set {{ item }} directory to be owned by {{ netbox_user }}" + ansible.builtin.file: + path: "{{ item }}" + owner: "{{ netbox_user }}" + group: "{{ netbox_group }}" + recurse: true + mode: '755' + state: directory + loop: + - /opt/netbox/netbox/media + - /opt/netbox/netbox/reports + - /opt/netbox/netbox/scripts + +- name: "Validate Netbox secret meets requirements" + ansible.builtin.assert: + that: + - netbox_secret_key is defined + - netbox_secret_key|length >= 50 + +- name: "Configure Netbox configuration" + ansible.builtin.template: + src: configuration.py.j2 + dest: /opt/netbox/netbox/netbox/configuration.py + owner: "{{ netbox_user }}" + group: "{{ netbox_group }}" + mode: '640' + register: netbox_config + +- name: "Run Netbox installation/upgrade script" + ansible.builtin.shell: + cmd: /opt/netbox/upgrade.sh + when: netbox_install or netbox_config.changed + +- name: "Record installed Netbox version" + community.general.ini_file: + path: /etc/ansible/facts.d/witine.fact + section: netbox + option: installed_version + value: "{{ netbox_install_target.version }}" + mode: 0644 + backup: false + when: netbox_install + +- name: "Create /opt/netbox/gunicorn.py file" + ansible.builtin.template: + src: gunicorn.py.j2 + dest: /opt/netbox/gunicorn.py + owner: "{{ netbox_user }}" + group: "{{ netbox_group }}" + mode: '755' + +- name: "Create /etc/systemd/system/{{ item }} file" + ansible.builtin.template: + src: "{{ item }}.j2" + dest: "/etc/systemd/system/{{ item }}" + loop: + - netbox.service + - netbox-rq.service + - netbox-housekeeping.service + - netbox-housekeeping.timer + +- name: "Start {{ item }}" + ansible.builtin.systemd_service: + name: "{{ item }}" + enabled: true + state: started + loop: + - netbox.service + - netbox-rq.service + - netbox-housekeeping.timer diff --git a/roles/netbox_installation/templates/configuration.py.j2 b/roles/netbox_installation/templates/configuration.py.j2 index 6488cef..186bace 100644 --- a/roles/netbox_installation/templates/configuration.py.j2 +++ b/roles/netbox_installation/templates/configuration.py.j2 @@ -2,20 +2,18 @@ # This is a list of valid fully-qualified domain names (FQDNs) for the NetBox server. NetBox will not permit write # access to the server via any other hostnames. The first FQDN in the list will be treated as the preferred name. -# -# Example: ALLOWED_HOSTS = ['netbox.example.com', 'netbox.internal.local'] -ALLOWED_HOSTS = {{ netbox_allowed_hosts }} +ALLOWED_HOSTS = {{ netbox_allowed_hosts | tojson(4) }} # PostgreSQL database configuration. See the Django documentation for a complete list of available parameters: # https://docs.djangoproject.com/en/stable/ref/settings/#databases DATABASE = { - 'ENGINE': 'django.db.backends.postgresql', # Database engine - 'NAME': 'netbox', # Database name - 'USER': '', # PostgreSQL username - 'PASSWORD': '', # PostgreSQL password - 'HOST': 'localhost', # Database server - 'PORT': '', # Database port (leave blank for default) - 'CONN_MAX_AGE': 300, # Max database connection age + 'ENGINE': 'django.db.backends.postgresql', + 'NAME': '{{ netbox_database_name }}', + 'USER': '{{ netbox_database_user }}', + 'PASSWORD': '{{ netbox_database_password }}', + 'HOST': '{{ netbox_database_host }}', + 'PORT': '{{ netbox_database_port }}', + 'CONN_MAX_AGE': 300, } # Redis database settings. Redis is used for caching and for queuing background tasks such as webhook events. A separate @@ -23,36 +21,23 @@ DATABASE = { # to use two separate database IDs. REDIS = { 'tasks': { - 'HOST': 'localhost', - 'PORT': 6379, - # Comment out `HOST` and `PORT` lines and uncomment the following if using Redis Sentinel - # 'SENTINELS': [('mysentinel.redis.example.com', 6379)], - # 'SENTINEL_SERVICE': 'netbox', - 'USERNAME': '', - 'PASSWORD': '', - 'DATABASE': 0, - 'SSL': False, - # Set this to True to skip TLS certificate verification - # This can expose the connection to attacks, be careful - # 'INSECURE_SKIP_TLS_VERIFY': False, - # Set a path to a certificate authority, typically used with a self signed certificate. - # 'CA_CERT_PATH': '/etc/ssl/certs/ca.crt', + 'HOST': '{{ netbox_redis_host }}', + 'PORT': {{ netbox_redis_port }}, + 'USERNAME': '{{ netbox_redis_user }}', + 'PASSWORD': '{{ netbox_redis_password }}', + 'DATABASE': {{ netbox_redis_tasks_database }}, + 'SSL': {{ netbox_redis_ssl }}, }, 'caching': { - 'HOST': 'localhost', - 'PORT': 6379, + 'HOST': '{{ netbox_redis_host }}', + 'PORT': {{ netbox_redis_port }}, # Comment out `HOST` and `PORT` lines and uncomment the following if using Redis Sentinel # 'SENTINELS': [('mysentinel.redis.example.com', 6379)], # 'SENTINEL_SERVICE': 'netbox', - 'USERNAME': '', - 'PASSWORD': '', - 'DATABASE': 1, - 'SSL': False, - # Set this to True to skip TLS certificate verification - # This can expose the connection to attacks, be careful - # 'INSECURE_SKIP_TLS_VERIFY': False, - # Set a path to a certificate authority, typically used with a self signed certificate. - # 'CA_CERT_PATH': '/etc/ssl/certs/ca.crt', + 'USERNAME': '{{ netbox_redis_user }}', + 'PASSWORD': '{{ netbox_redis_password }}', + 'DATABASE': {{ netbox_redis_caching_database }}, + 'SSL': {{ netbox_redis_ssl }}, } } @@ -60,19 +45,14 @@ REDIS = { # For optimal security, SECRET_KEY should be at least 50 characters in length and contain a mix of letters, numbers, and # symbols. NetBox will not run without this defined. For more information, see # https://docs.djangoproject.com/en/stable/ref/settings/#std:setting-SECRET_KEY -SECRET_KEY = '' - - -######################### -# # -# Optional settings # -# # -######################### +SECRET_KEY = '{{ netbox_secret_key }}' # Specify one or more name and email address tuples representing NetBox administrators. These people will be notified of # application errors (assuming correct email settings are provided). ADMINS = [ - # ('John Doe', 'jdoe@example.com'), +{% for admin in netbox_admins %} + ('{{ admin.name }}', '{{ admin.email }}'), +{% endfor %} ] # Permit the retrieval of API tokens after their creation. @@ -91,44 +71,44 @@ AUTH_PASSWORD_VALIDATORS = [ # Base URL path if accessing NetBox within a directory. For example, if installed at https://example.com/netbox/, set: # BASE_PATH = 'netbox/' -BASE_PATH = '' +BASE_PATH = '{{ netbox_basepath }}' # API Cross-Origin Resource Sharing (CORS) settings. If CORS_ORIGIN_ALLOW_ALL is set to True, all origins will be # allowed. Otherwise, define a list of allowed origins using either CORS_ORIGIN_WHITELIST or # CORS_ORIGIN_REGEX_WHITELIST. For more information, see https://github.com/ottoyiu/django-cors-headers -CORS_ORIGIN_ALLOW_ALL = False -CORS_ORIGIN_WHITELIST = [ - # 'https://hostname.example.com', -] +CORS_ORIGIN_ALLOW_ALL = {{ netbox_cors_allow_all }} +CORS_ORIGIN_WHITELIST = {{ netbox_cors_origin_whitelist | tojson(4) }} CORS_ORIGIN_REGEX_WHITELIST = [ - # r'^(https?://)?(\w+\.)?example\.com$', +{% for wl in netbox_cors_origin_regex_whitelist %} + {{ wl | safe }}, +{% endfor %} ] # The name to use for the CSRF token cookie. -CSRF_COOKIE_NAME = 'csrftoken' +CSRF_COOKIE_NAME = '{{ netbox_csrf_cookie }}' # Set to True to enable server debugging. WARNING: Debugging introduces a substantial performance penalty and may reveal # sensitive information about your installation. Only enable debugging while performing testing. Never enable debugging # on a production system. -DEBUG = False +DEBUG = {{ netbox_debug | capitalize }} # Set the default preferred language/locale -DEFAULT_LANGUAGE = 'en-us' +DEFAULT_LANGUAGE = '{{ netbox_default_locale }}' # Email settings EMAIL = { - 'SERVER': 'localhost', - 'PORT': 25, - 'USERNAME': '', - 'PASSWORD': '', + 'SERVER': '{{ netbox_email_server }}', + 'PORT': {{ netbox_email_port }}, + 'USERNAME': '{{ netbox_email_user }}', + 'PASSWORD': '{{ netbox_email_password }}', 'USE_SSL': False, 'USE_TLS': False, 'TIMEOUT': 10, # seconds - 'FROM_EMAIL': '', + 'FROM_EMAIL': '{{ netbox_from_email }}', } # Localization -ENABLE_LOCALIZATION = False +ENABLE_LOCALIZATION = {{ netbox_localization }} # Exempt certain models from the enforcement of view permissions. Models listed here will be viewable by all users and # by anonymous users. List models in the form `.`. Add '*' to this list to exempt all models. @@ -143,10 +123,20 @@ EXEMPT_VIEW_PERMISSIONS = [ # 'http': 'http://10.10.1.10:3128', # 'https': 'http://10.10.1.10:1080', # } +{% if netbox_http_proxies is defined %} +HTTP_PROXIES = { + 'http': '{{ netbox_http_proxies.http }}', + 'https': '{{ netbox_http_proxies.https }}', +} +{% endif %} # IP addresses recognized as internal to the system. The debugging toolbar will be available only to clients accessing # NetBox from an internal IP. -INTERNAL_IPS = ('127.0.0.1', '::1') +INTERNAL_IPS = ( +{% for ip in netbox_internal_ips %} + '{{ ip }}', +{% endfor %} +) # Enable custom logging. Please see the Django documentation for detailed guidance on configuring custom logs: # https://docs.djangoproject.com/en/stable/topics/logging/ @@ -154,25 +144,25 @@ LOGGING = {} # Automatically reset the lifetime of a valid session upon each authenticated request. Enables users to remain # authenticated to NetBox indefinitely. -LOGIN_PERSISTENCE = False +LOGIN_PERSISTENCE = {{ netbox_login_persistence | title }} # Setting this to True will permit only authenticated users to access any part of NetBox. By default, anonymous users # are permitted to access most data in NetBox but not make any changes. -LOGIN_REQUIRED = False +LOGIN_REQUIRED = {{ netbox_login_required | capitalize }} # The length of time (in seconds) for which a user will remain logged into the web UI before being prompted to # re-authenticate. (Default: 1209600 [14 days]) LOGIN_TIMEOUT = None # The view name or URL to which users are redirected after logging out. -LOGOUT_REDIRECT_URL = 'home' +LOGOUT_REDIRECT_URL = '{{ netbox_logout_redirect }}' # The file path where uploaded media such as image attachments are stored. A trailing slash is not needed. Note that # the default value of this setting is derived from the installed location. # MEDIA_ROOT = '/opt/netbox/netbox/media' # Expose Prometheus monitoring metrics at the HTTP endpoint '/metrics' -METRICS_ENABLED = False +METRICS_ENABLED = {{ netbox_metrics | capitalize }} # Enable installed plugins. Add the name of each plugin to the list. PLUGINS = [] @@ -187,15 +177,18 @@ PLUGINS = [] # } # Remote authentication support -REMOTE_AUTH_ENABLED = False -REMOTE_AUTH_BACKEND = 'netbox.authentication.RemoteUserBackend' +REMOTE_AUTH_ENABLED = {{ netbox_remote_auth | capitalize }} +REMOTE_AUTH_BACKEND = '{{ netbox_remote_auth_backend }}' REMOTE_AUTH_HEADER = 'HTTP_REMOTE_USER' REMOTE_AUTH_USER_FIRST_NAME = 'HTTP_REMOTE_USER_FIRST_NAME' REMOTE_AUTH_USER_LAST_NAME = 'HTTP_REMOTE_USER_LAST_NAME' REMOTE_AUTH_USER_EMAIL = 'HTTP_REMOTE_USER_EMAIL' -REMOTE_AUTH_AUTO_CREATE_USER = True +REMOTE_AUTH_AUTO_CREATE_USER = {{ netbox_remote_auth_auto_create | capitalize }} REMOTE_AUTH_DEFAULT_GROUPS = [] REMOTE_AUTH_DEFAULT_PERMISSIONS = {} +{% for key, value in netbox_social_auth_config.items() %} +SOCIAL_AUTH_{{ key | upper }} = '{{ value }}' +{% endfor %} # This repository is used to check whether there is a new release of NetBox available. Set to None to disable the # version check or use the URL below to check for release in the official NetBox repository. @@ -214,7 +207,7 @@ RQ_DEFAULT_TIMEOUT = 300 # SCRIPTS_ROOT = '/opt/netbox/netbox/scripts' # The name to use for the session cookie. -SESSION_COOKIE_NAME = 'sessionid' +SESSION_COOKIE_NAME = '{{ netbox_session_cookie }}' # By default, NetBox will store session data in the database. Alternatively, a file path can be specified here to use # local file storage instead. (This can be useful for enabling authentication on a standby instance with read-only @@ -232,7 +225,7 @@ SESSION_FILE_PATH = None # } # Time zone (default: UTC) -TIME_ZONE = 'UTC' +TIME_ZONE = '{{ netbox_timezone }}' # Date/time formatting. See the following link for supported formats: # https://docs.djangoproject.com/en/stable/ref/templates/builtins/#date diff --git a/roles/netbox_installation/templates/gunicorn.py.j2 b/roles/netbox_installation/templates/gunicorn.py.j2 new file mode 100644 index 0000000..ea45dac --- /dev/null +++ b/roles/netbox_installation/templates/gunicorn.py.j2 @@ -0,0 +1,6 @@ +bind = '{{ netbox_server_host }}:{{ netbox_server_port }}' +workers = {{ netbox_server_workers }} +threads = 3 +timeout = 120 +max_requests = 5000 +max_requests_jitter = 500 diff --git a/roles/netbox_installation/templates/netbox-housekeeping.service.j2 b/roles/netbox_installation/templates/netbox-housekeeping.service.j2 new file mode 100644 index 0000000..c5c2860 --- /dev/null +++ b/roles/netbox_installation/templates/netbox-housekeeping.service.j2 @@ -0,0 +1,17 @@ +[Unit] +Description=NetBox Housekeeping Service +Documentation=https://docs.netbox.dev/ +After=network-online.target +Wants=network-online.target + +[Service] +Type=simple + +User={{ netbox_user }} +Group={{ netbox_group }} +WorkingDirectory=/opt/netbox + +ExecStart=/opt/netbox/venv/bin/python /opt/netbox/netbox/manage.py housekeeping + +[Install] +WantedBy=multi-user.target diff --git a/roles/netbox_installation/templates/netbox-housekeeping.timer.j2 b/roles/netbox_installation/templates/netbox-housekeeping.timer.j2 new file mode 100644 index 0000000..16facb0 --- /dev/null +++ b/roles/netbox_installation/templates/netbox-housekeeping.timer.j2 @@ -0,0 +1,13 @@ +[Unit] +Description=NetBox Housekeeping Timer +Documentation=https://docs.netbox.dev/ +After=network-online.target +Wants=network-online.target + +[Timer] +OnCalendar=daily +AccuracySec=1h +Persistent=true + +[Install] +WantedBy=multi-user.target diff --git a/roles/netbox_installation/templates/netbox-rq.service.j2 b/roles/netbox_installation/templates/netbox-rq.service.j2 new file mode 100644 index 0000000..cb41cf6 --- /dev/null +++ b/roles/netbox_installation/templates/netbox-rq.service.j2 @@ -0,0 +1,21 @@ +[Unit] +Description=NetBox Request Queue Worker +Documentation=https://docs.netbox.dev/ +After=network-online.target +Wants=network-online.target + +[Service] +Type=simple + +User={{ netbox_user }} +Group={{ netbox_group }} +WorkingDirectory=/opt/netbox + +ExecStart=/opt/netbox/venv/bin/python3 /opt/netbox/netbox/manage.py rqworker high default low + +Restart=on-failure +RestartSec=30 +PrivateTmp=true + +[Install] +WantedBy=multi-user.target diff --git a/roles/netbox_installation/templates/netbox.service.j2 b/roles/netbox_installation/templates/netbox.service.j2 new file mode 100644 index 0000000..2efc6c2 --- /dev/null +++ b/roles/netbox_installation/templates/netbox.service.j2 @@ -0,0 +1,22 @@ +[Unit] +Description=NetBox WSGI Service +Documentation=https://docs.netbox.dev/ +After=network-online.target +Wants=network-online.target + +[Service] +Type=notify + +User={{ netbox_user }} +Group={{ netbox_group }} +PIDFile=/var/tmp/netbox.pid +WorkingDirectory=/opt/netbox + +ExecStart=/opt/netbox/venv/bin/gunicorn --pid /var/tmp/netbox.pid --pythonpath /opt/netbox/netbox --config /opt/netbox/gunicorn.py netbox.wsgi +ExecReload=/bin/kill -s HUP $MAINPID +KillMode=mixed +TimeoutStopSec=5 +PrivateTmp=true + +[Install] +WantedBy=multi-user.target diff --git a/roles/netbox_installation/vars/main.yml b/roles/netbox_installation/vars/main.yml index 57f6b3b..1564fd1 100644 --- a/roles/netbox_installation/vars/main.yml +++ b/roles/netbox_installation/vars/main.yml @@ -1,5 +1,6 @@ --- +ansible_command_timeout: 600 netbox_latest_release: url: https://github.com/netbox-community/netbox/archive/refs/tags/v3.6.5.tar.gz version: 3.6.5 -netbox_install: False +netbox_install: false