diff --git a/Puppetfile b/Puppetfile --- a/Puppetfile +++ b/Puppetfile @@ -163,6 +163,10 @@ :git => 'https://forge.softwareheritage.org/source/puppet-puppet-rabbitmq', :ref => 'v10.0.0' +mod 'redis', + :git => 'https://forge.softwareheritage.org/source/puppet-puppet-redis', + :ref => 'v6.1.0' + mod 'resolv_conf', :git => 'https://forge.softwareheritage.org/source/puppet-saz-resolv_conf', :ref => 'v4.1.0' diff --git a/data/defaults.yaml b/data/defaults.yaml --- a/data/defaults.yaml +++ b/data/defaults.yaml @@ -814,7 +814,12 @@ - sponsorship.softwareheritage.org - testimonials.softwareheritage.org deploy_hook: gandi_paas - + netbox: + domain: + - inventory.internal.softwareheritage.org + netbox-vagrant: + domain: + - inventory-vagrant.inventory.softwareheritage.org bind::update_key: local-update @@ -4205,3 +4210,34 @@ - localhost - "::1" +netbox::version: 2.9.3 +netbox::user: netbox +netbox::db::host: localhost +netbox::db::port: 5432 +netbox::db::database: netbox +netbox::db::username: netbox +# netbox::db::password: in private-data +netbox::mail::host: "%{lookup('smtp::relay_hostname')}" +netbox::mail::from: inventory@softwareheritage.org +netbox::redis::host: localhost +netbox::redis::port: 6379 +# netbox::redis::password in private-data +# netbox::redis_cache::password in private-data +# netbox::secret_key in private-data +netbox::admin::email: sysop+netbox@softwareheritage.org +# netbox::admin::password in private-data +# netbox::admin::api_token in private-data +netbox::webhook_enabled: true +netbox::gunicorn::binding: 127.0.0.1 +netbox::gunicorn::port: 8001 +netbox::data_directory: /var/lib/netbox +netbox::allowed_hosts: + - "*" + - "localhost" +netbox::vhost::letsencrypt_cert: netbox +netbox::vhost::name: inventory.softwareheritage.org +netbox::backend::url: "http://localhost/%{hiera('netbox::gunicorn::port')}" +netbox::vhost::ssl_protocol: "%{hiera('apache::ssl_protocol')}" +netbox::vhost::ssl_honorcipherorder: "%{hiera('apache::ssl_honorcipherorder')}" +netbox::vhost::ssl_cipher: "%{hiera('apache::ssl_cipher')}" +netbox::vhost::hsts_header: "%{hiera('apache::hsts_header')}" diff --git a/data/location/vagrant.yaml b/data/location/vagrant.yaml --- a/data/location/vagrant.yaml +++ b/data/location/vagrant.yaml @@ -32,3 +32,6 @@ - loader_svn dns::forward_zones: { } + +netbox::mail::from: inventory+vagrant@softwareheritage.org +netbox::admin::email: sysop+vagrant@softwareheritage.org diff --git a/data/vagrant/bojimans.internal.softwareheritage.org.yaml b/data/vagrant/bojimans.internal.softwareheritage.org.yaml new file mode 100644 --- /dev/null +++ b/data/vagrant/bojimans.internal.softwareheritage.org.yaml @@ -0,0 +1,4 @@ +networks: + eth0: + # vagrant admin interface + type: dhcp diff --git a/manifests/site.pp b/manifests/site.pp --- a/manifests/site.pp +++ b/manifests/site.pp @@ -179,6 +179,10 @@ include role::swh_journal_allinone } +node 'bojimans.internal.softwareheritage.org' { + include role::swh_netbox +} + node default { include role::swh_base } diff --git a/site-modules/profile/manifests/netbox.pp b/site-modules/profile/manifests/netbox.pp new file mode 100644 --- /dev/null +++ b/site-modules/profile/manifests/netbox.pp @@ -0,0 +1,150 @@ +# deploy a netbox instance +class profile::netbox { + + $version = lookup('netbox::version') + $netbox_user = lookup('netbox::user') + $db_host = lookup('netbox::db::host') + $db_port = lookup('netbox::db::port') + $db_database = lookup('netbox::db::database') + $db_username = lookup('netbox::db::username') + $db_password = lookup('netbox::db::password') + $secret_key = lookup('netbox::secret_key') + $allowed_hosts = lookup('netbox::allowed_hosts') + $redis_host = lookup('netbox::redis::host') + $redis_port = lookup('netbox::redis::port') + $redis_password = lookup('netbox::redis::password') + $smtp_host = lookup('netbox::mail::host') + $email_from = lookup('netbox::mail::from') + $gunicorn_binding = lookup('netbox::gunicorn::binding') + $gunicorn_port = lookup('netbox::gunicorn::port') + $data_directory = lookup('netbox::data_directory') + $media_directory = "${data_directory}/media" + $reports_directory = "${data_directory}/reports" + $scripts_directory = "${data_directory}/scripts" + + $archive_url = "https://github.com/netbox-community/netbox/archive/v${version}.tar.gz" + $archive_path = "/opt/netbox-v${version}.tar.gz" + $install_path = "/opt/netbox-${version}" + $upgrade_flag_path = "${install_path}/.upgrade_done" + + ensure_packages ('python3-venv') + + include ::postgresql::server + + ::postgresql::server::db {$db_database: + user => $db_username, + password => postgresql_password($db_username, $db_password), + require => [Class['Postgresql::Server']], + } + + class { '::redis' : + requirepass => $redis_password, + bind => '127.0.0.1', + port => $redis_port, + } + + user {$netbox_user: + ensure => present, + system => true, + shell => '/bin/bash', + home => $data_directory, + } + + archive { 'netbox': + path => $archive_path, + source => $archive_url, + extract => true, + extract_path => '/opt', + creates => $install_path, + cleanup => true, + user => 'root', + group => 'root', + } + file { '/opt/netbox' : + ensure => link, + target => $install_path, + owner => 'root', + group => 'root', + require => Archive['netbox'], + } + + file { 'netbox-configuration': + ensure => present, + path => "${install_path}/netbox/netbox/configuration.py", + owner => 'root', + group => 'root', + mode => '0644', + content => template('profile/netbox/configuration.py.erb'), + require => Archive['netbox'], + notify => Service['netbox'], + } + + file { 'netbox-gunicorn-config': + ensure => present, + path => "${install_path}/gunicorn.py", + owner => 'root', + group => 'root', + content => template('profile/netbox/gunicorn.py.erb'), + require => Archive['netbox'], + notify => Service['netbox'], + } + + file { $data_directory : + ensure => directory, + owner => $netbox_user, + group => $netbox_user, + mode => '0750', + require => User[$netbox_user] + } + file { $media_directory: + ensure => directory, + owner => $netbox_user, + group => $netbox_user, + mode => '0750', + require => File[$data_directory], + } + file { $scripts_directory: + ensure => directory, + owner => $netbox_user, + group => $netbox_user, + mode => '0750', + require => File[$data_directory], + } + file { $reports_directory: + ensure => directory, + owner => $netbox_user, + group => $netbox_user, + mode => '0750', + require => File[$data_directory], + } + + exec { 'netbox-upgrade': + command => "${install_path}/upgrade.sh", + cwd => $install_path, + creates => $upgrade_flag_path, + require => [File['netbox-configuration'], + File[$media_directory], + Package['python3-venv'], + Postgresql::Server::Db[$db_database], + ], + notify => Exec['netbox-flag-upgrade-done'], + } + + exec {'netbox-flag-upgrade-done': + command => "touch ${upgrade_flag_path}", + path => '/usr/bin', + refreshonly => true, + notify => Systemd::Unit_file['netbox.service'], + } + + ::systemd::unit_file {'netbox.service': + ensure => present, + content => template('profile/netbox/netbox.service.erb'), + } ~> service {'netbox': + ensure => 'running', + enable => true, + require => [File['netbox-gunicorn-config'], + File['netbox-configuration']], + } + +} diff --git a/site-modules/profile/manifests/netbox/reverse_proxy.pp b/site-modules/profile/manifests/netbox/reverse_proxy.pp new file mode 100644 --- /dev/null +++ b/site-modules/profile/manifests/netbox/reverse_proxy.pp @@ -0,0 +1,15 @@ +# Reverseproxy for netbox +class profile::netbox::reverse_proxy { + ::profile::reverse_proxy {'netbox': + extra_proxy_pass => [{ patch => '/static', url => '!'}], + extra_apache_opts => { + proxy_preserve_host => true, + aliases => [ + { aliasmatch => '^/static', + path => "%{lookup('profile::netbox::install_path')}", + }, + ], + }, + icinga_check_uri => '/login', + } +} diff --git a/site-modules/profile/templates/netbox/configuration.py.erb b/site-modules/profile/templates/netbox/configuration.py.erb new file mode 100644 --- /dev/null +++ b/site-modules/profile/templates/netbox/configuration.py.erb @@ -0,0 +1,257 @@ +######################### +# # +# Required settings # +# # +######################### + +# 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 = [ +<%- @allowed_hosts.each do |host| -%> +"<%= host %>", +<%- end -%>] + +# PostgreSQL database configuration. See the Django documentation for a complete list of available parameters: +# https://docs.djangoproject.com/en/stable/ref/settings/#databases +DATABASE = { + 'NAME': '<%= @db_database %>', # Database name + 'USER': '<%= @db_username %>', # PostgreSQL username + 'PASSWORD': '<%= @db_password %>', # PostgreSQL password + 'HOST': '<%= @db_host %>', # Database server + 'PORT': '<%= @db_port %>', # Database port (leave blank for default) + 'CONN_MAX_AGE': 300, # Max database connection age +} + +# Redis database settings. Redis is used for caching and for queuing background tasks such as webhook events. A separate +# configuration exists for each. Full connection details are required in both sections, and it is strongly recommended +# to use two separate database IDs. +REDIS = { + 'tasks': { + 'HOST': '<%= @redis_host %>', + 'PORT': <%= @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', + 'PASSWORD': '<%= @redis_password %>', + 'DATABASE': 0, + 'DEFAULT_TIMEOUT': 300, + 'SSL': False, + }, + 'caching': { + 'HOST': '<%= @redis_host %>', + 'PORT': <%= @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', + 'PASSWORD': '<%= @redis_password %>', + 'DATABASE': 1, + 'DEFAULT_TIMEOUT': 300, + 'SSL': False, + } +} + +# This key is used for secure generation of random numbers and strings. It must never be exposed outside of this file. +# 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 = '<%= @secret_key %>' + + +######################### +# # +# Optional settings # +# # +######################### + +# 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'], +] + +# URL schemes that are allowed within links in NetBox +ALLOWED_URL_SCHEMES = ( + 'file', 'ftp', 'ftps', 'http', 'https', 'irc', 'mailto', 'sftp', 'ssh', 'tel', 'telnet', 'tftp', 'vnc', 'xmpp', +) + +# Optionally display a persistent banner at the top and/or bottom of every page. HTML is allowed. To display the same +# content in both banners, define BANNER_TOP and set BANNER_BOTTOM = BANNER_TOP. +BANNER_TOP = '' +BANNER_BOTTOM = '' + +# Text to include on the login page above the login form. HTML is allowed. +BANNER_LOGIN = '' + +# Base URL path if accessing NetBox within a directory. For example, if installed at http://example.com/netbox/, set: +# BASE_PATH = 'netbox/' +BASE_PATH = '' + +# Cache timeout in seconds. Set to 0 to dissable caching. Defaults to 900 (15 minutes) +CACHE_TIMEOUT = 900 + +# Maximum number of days to retain logged changes. Set to 0 to retain changes indefinitely. (Default: 90) +CHANGELOG_RETENTION = 90 + +# 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_REGEX_WHITELIST = [ + # r'^(https?://)?(\w+\.)?example\.com$', +] + +# 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 + +# Email settings +EMAIL = { + 'SERVER': '<%= @smtp_host %>', + 'PORT': 25, + 'USERNAME': '', + 'PASSWORD': '', + 'USE_SSL': False, + 'USE_TLS': False, + 'TIMEOUT': 10, # seconds + 'FROM_EMAIL': '<%= @email_from %>', +} + +# Enforcement of unique IP space can be toggled on a per-VRF basis. To enforce unique IP space within the global table +# (all prefixes and IP addresses not assigned to a VRF), set ENFORCE_GLOBAL_UNIQUE to True. +ENFORCE_GLOBAL_UNIQUE = False + +# 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. +EXEMPT_VIEW_PERMISSIONS = [ + # 'dcim.site', + # 'dcim.region', + # 'ipam.prefix', +] + +# HTTP proxies NetBox should use when sending outbound HTTP requests (e.g. for webhooks). +# HTTP_PROXIES = { +# 'http': 'http://10.10.1.10:3128', +# 'https': 'http://10.10.1.10:1080', +# } + +# 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') + +# Enable custom logging. Please see the Django documentation for detailed guidance on configuring custom logs: +# https://docs.djangoproject.com/en/stable/topics/logging/ +LOGGING = {} + +# 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 (excluding secrets) but not make any changes. +LOGIN_REQUIRED = False + +# 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 + +# Setting this to True will display a "maintenance mode" banner at the top of every page. +MAINTENANCE_MODE = False + +# An API consumer can request an arbitrary number of objects =by appending the "limit" parameter to the URL (e.g. +# "?limit=1000"). This setting defines the maximum limit. Setting it to 0 or None will allow an API consumer to request +# all objects by specifying "?limit=0". +MAX_PAGE_SIZE = 1000 + +# 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 = '<%= @media_directory %>' + +# By default uploaded media is stored on the local filesystem. Using Django-storages is also supported. Provide the +# class path of the storage driver in STORAGE_BACKEND and any configuration options in STORAGE_CONFIG. For example: +# STORAGE_BACKEND = 'storages.backends.s3boto3.S3Boto3Storage' +# STORAGE_CONFIG = { +# 'AWS_ACCESS_KEY_ID': 'Key ID', +# 'AWS_SECRET_ACCESS_KEY': 'Secret', +# 'AWS_STORAGE_BUCKET_NAME': 'netbox', +# 'AWS_S3_REGION_NAME': 'eu-west-1', +# } + +# Expose Prometheus monitoring metrics at the HTTP endpoint '/metrics' +METRICS_ENABLED = True + +# Credentials that NetBox will uses to authenticate to devices when connecting via NAPALM. +NAPALM_USERNAME = '' +NAPALM_PASSWORD = '' + +# NAPALM timeout (in seconds). (Default: 30) +NAPALM_TIMEOUT = 30 + +# NAPALM optional arguments (see http://napalm.readthedocs.io/en/latest/support/#optional-arguments). Arguments must +# be provided as a dictionary. +NAPALM_ARGS = {} + +# Determine how many objects to display per page within a list. (Default: 50) +PAGINATE_COUNT = 50 + +# Enable installed plugins. Add the name of each plugin to the list. +PLUGINS = [] + +# Plugins configuration settings. These settings are used by various plugins that the user may have installed. +# Each key in the dictionary is the name of an installed plugin and its value is a dictionary of settings. +# PLUGINS_CONFIG = { +# 'my_plugin': { +# 'foo': 'bar', +# 'buzz': 'bazz' +# } +# } + +# When determining the primary IP address for a device, IPv6 is preferred over IPv4 by default. Set this to True to +# prefer IPv4 instead. +PREFER_IPV4 = False + +# Rack elevation size defaults, in pixels. For best results, the ratio of width to height should be roughly 10:1. +RACK_ELEVATION_DEFAULT_UNIT_HEIGHT = 22 +RACK_ELEVATION_DEFAULT_UNIT_WIDTH = 220 + +# Remote authentication support +REMOTE_AUTH_ENABLED = False +REMOTE_AUTH_BACKEND = 'netbox.authentication.RemoteUserBackend' +REMOTE_AUTH_HEADER = 'HTTP_REMOTE_USER' +REMOTE_AUTH_AUTO_CREATE_USER = True +REMOTE_AUTH_DEFAULT_GROUPS = [] +REMOTE_AUTH_DEFAULT_PERMISSIONS = {} + +# This determines how often the GitHub API is called to check the latest release of NetBox. Must be at least 1 hour. +RELEASE_CHECK_TIMEOUT = 24 * 3600 + +# 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. +RELEASE_CHECK_URL = None +# RELEASE_CHECK_URL = 'https://api.github.com/repos/netbox-community/netbox/releases' + +# The file path where custom reports will be stored. A trailing slash is not needed. Note that the default value of +# this setting is derived from the installed location. +REPORTS_ROOT = '<%= @reports_dir %>' + +# The file path where custom scripts will be stored. A trailing slash is not needed. Note that the default value of +# this setting is derived from the installed location. +SCRIPTS_ROOT = '<%= @scripts_directory %>' + +# 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 +# database access.) Note that the user as which NetBox runs must have read and write permissions to this path. +SESSION_FILE_PATH = None + +# Time zone (default: UTC) +TIME_ZONE = 'UTC' + +# Date/time formatting. See the following link for supported formats: +# https://docs.djangoproject.com/en/stable/ref/templates/builtins/#date +DATE_FORMAT = 'N j, Y' +SHORT_DATE_FORMAT = 'Y-m-d' +TIME_FORMAT = 'g:i a' +SHORT_TIME_FORMAT = 'H:i:s' +DATETIME_FORMAT = 'N j, Y g:i a' +SHORT_DATETIME_FORMAT = 'Y-m-d H:i' diff --git a/site-modules/profile/templates/netbox/gunicorn.py.erb b/site-modules/profile/templates/netbox/gunicorn.py.erb new file mode 100644 --- /dev/null +++ b/site-modules/profile/templates/netbox/gunicorn.py.erb @@ -0,0 +1,17 @@ + +# The IP address (typically localhost) and port that the Netbox WSGI process should listen on +bind = '<%= @gunicorn_binding %>:<%= @gunicorn_port %>' + +# Number of gunicorn workers to spawn. This should typically be 2n+1, where +# n is the number of CPU cores present. +workers = 2 + +# Number of threads per worker process +threads = 3 + +# Timeout (in seconds) for a request to complete +timeout = 120 + +# The maximum number of requests a worker can handle before being respawned +max_requests = 5000 +max_requests_jitter = 500 diff --git a/site-modules/profile/templates/netbox/netbox.service.erb b/site-modules/profile/templates/netbox/netbox.service.erb new file mode 100644 --- /dev/null +++ b/site-modules/profile/templates/netbox/netbox.service.erb @@ -0,0 +1,22 @@ +[Unit] +Description=NetBox WSGI Service +Documentation=https://netbox.readthedocs.io/en/stable/ +After=network-online.target +Wants=network-online.target + +[Service] +Type=simple + +User=netbox +Group=netbox +PIDFile=/var/tmp/netbox.pid +WorkingDirectory=<%= @install_path %> + +ExecStart=<%= @install_path %>/venv/bin/gunicorn --pid /var/tmp/netbox.pid --pythonpath <%= @install_path %>/netbox --config <%= @install_path %>/gunicorn.py netbox.wsgi + +Restart=on-failure +RestartSec=30 +PrivateTmp=true + +[Install] +WantedBy=multi-user.target diff --git a/site-modules/role/manifests/swh_netbox.pp b/site-modules/role/manifests/swh_netbox.pp new file mode 100644 --- /dev/null +++ b/site-modules/role/manifests/swh_netbox.pp @@ -0,0 +1,7 @@ +# Netbox template +class role::swh_netbox inherits role::swh_server { + include profile::postgresql + + include profile::netbox + include profile::netbox::reverse_proxy +}