Merge branch 'netbox' into 'netbox'

__netbox type improvements

See merge request ungleich-public/cdist-contrib!17
This commit is contained in:
fnux 2020-11-04 09:23:22 +01:00
commit e8c731c384
56 changed files with 1487 additions and 179 deletions

View file

@ -0,0 +1,8 @@
#!/bin/sh -e
# Explorer will output the key if he exists.
secretkey="/opt/netbox/cdist/secretkey"
if [ -f "$secretkey" ]; then
cat "$secretkey"
fi

5
type/__netbox/explorer/version Executable file
View file

@ -0,0 +1,5 @@
#!/bin/sh -e
# output version if exist
version_path="/opt/netbox/cdist/version"
if [ -f "$version_path" ]; then cat "$version_path"; fi

View file

@ -11,7 +11,7 @@ cat << EOF
# 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_HOST' ]
ALLOWED_HOSTS = [$ALLOWED_HOSTS ]
# PostgreSQL database configuration. See the Django documentation for a complete list of available parameters:
# https://docs.djangoproject.com/en/stable/ref/settings/#databases
@ -19,8 +19,8 @@ DATABASE = {
'NAME': '$DATABASE_NAME', # Database name
'USER': '$DATABASE_USER', # PostgreSQL username
'PASSWORD': '$DATABASE_PASSWORD', # PostgreSQL password
'HOST': 'localhost', # Database server
'PORT': '', # Database port (leave blank for default)
'HOST': '$DATABASE_HOST', # Database server
'PORT': '$DATABASE_PORT', # Database port (leave blank for default)
'CONN_MAX_AGE': 300, # Max database connection age
}
@ -29,28 +29,27 @@ DATABASE = {
# to use two separate database IDs.
REDIS = {
'tasks': {
'HOST': 'localhost',
'PORT': 6379,
'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': '',
'DATABASE': 0,
'DEFAULT_TIMEOUT': 300,
'SSL': False,
'PASSWORD': '$REDIS_PASSWORD',
'DATABASE': $((REDIS_DBID_OFFSET + 0)),
'SSL': $REDIS_SSL,
},
'caching': {
'HOST': 'localhost',
'PORT': 6379,
'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': '',
'DATABASE': 1,
'DEFAULT_TIMEOUT': 300,
'SSL': False,
'PASSWORD': '$REDIS_PASSWORD',
'DATABASE': $((REDIS_DBID_OFFSET + 1)),
'SSL': $REDIS_SSL,
}
}
RQ_DEFAULT_TIMEOUT = 300
# 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
@ -86,7 +85,7 @@ 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 = ''
BASE_PATH = '$BASEPATH'
# Cache timeout in seconds. Set to 0 to dissable caching. Defaults to 900 (15 minutes)
CACHE_TIMEOUT = 900
@ -112,14 +111,14 @@ DEBUG = False
# Email settings
EMAIL = {
'SERVER': 'localhost',
'PORT': 25,
'USERNAME': '',
'PASSWORD': '',
'USE_SSL': False,
'USE_TLS': False,
'SERVER': '$SMTP_HOST',
'PORT': $SMTP_PORT,
'USERNAME': '$SMTP_USER',
'PASSWORD': '$SMTP_PASSWORD',
'USE_SSL': $SMTP_USE_SSL,
'USE_TLS': $SMTP_USE_TLS,
'TIMEOUT': 10, # seconds
'FROM_EMAIL': '',
'FROM_EMAIL': '$SMTP_FROM_EMAIL',
}
# Enforcement of unique IP space can be toggled on a per-VRF basis. To enforce unique IP space within the global table
@ -134,12 +133,29 @@ EXEMPT_VIEW_PERMISSIONS = [
# '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',
# }
EOF
if [ "$HTTP_PROXY" != "" ] || [ "$HTTPS_PROXY" != "" ]; then
cat << EOF
# HTTP proxies NetBox should use when sending outbound HTTP requests (e.g. for webhooks).
HTTP_PROXIES = {
EOF
if [ "$HTTP_PROXY" != "" ]; then
cat << EOF
'http': '$HTTP_PROXY',
EOF
fi
if [ "$HTTPS_PROXY" != "" ]; then
cat << EOF
'https': '$HTTPS_PROXY',
EOF
fi
cat << EOF
}
EOF
fi
cat << EOF
# 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')
@ -150,7 +166,7 @@ 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
LOGIN_REQUIRED = $LOGIN_REQUIRED
# 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])
@ -164,10 +180,18 @@ MAINTENANCE_MODE = False
# all objects by specifying "?limit=0".
MAX_PAGE_SIZE = 1000
EOF
if [ "$MEDIA_ROOT" != "" ]; then
cat << EOF
# 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'
MEDIA_ROOT = '$MEDIA_ROOT'
EOF
fi
cat << EOF
# 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'
@ -215,30 +239,67 @@ PREFER_IPV4 = False
RACK_ELEVATION_DEFAULT_UNIT_HEIGHT = 22
RACK_ELEVATION_DEFAULT_UNIT_WIDTH = 220
EOF
if [ "$USE_LDAP" ]; then
cat << EOF
# Remote authentication support with ldap
REMOTE_AUTH_ENABLED = True
REMOTE_AUTH_BACKEND = 'netbox.authentication.LDAPBackend'
EOF
else
cat << EOF
# Remote authentication support
REMOTE_AUTH_ENABLED = False
REMOTE_AUTH_BACKEND = 'utilities.auth_backends.RemoteUserBackend'
REMOTE_AUTH_BACKEND = 'netbox.authentication.RemoteUserBackend'
EOF
fi
cat << EOF
REMOTE_AUTH_HEADER = 'HTTP_REMOTE_USER'
REMOTE_AUTH_AUTO_CREATE_USER = True
REMOTE_AUTH_DEFAULT_GROUPS = []
REMOTE_AUTH_DEFAULT_PERMISSIONS = []
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'
EOF
if [ "$UPDATE_CHECK" != "" ]; then
cat << EOF
RELEASE_CHECK_URL = 'https://api.github.com/repos/netbox-community/netbox/releases'
EOF
else
cat << EOF
RELEASE_CHECK_URL = None
EOF
fi
if [ "$REPORTS_ROOT" != "" ]; then
cat << EOF
# 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 = '/opt/netbox/netbox/reports'
REPORTS_ROOT = '$REPORTS_ROOT'
EOF
fi
if [ "$SCRIPTS_ROOT" != "" ]; then
cat << EOF
# 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 = '/opt/netbox/netbox/scripts'
SCRIPTS_ROOT = '$SCRIPTS_ROOT'
EOF
fi
cat << EOF
# 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.

View file

@ -1,5 +1,19 @@
#!/bin/sh
# no configuration if there are no ldap parameters
if [ -z "$USE_LDAP" ]; then
# skip
cat << EOF
##############################
# LDAP-backed authentication #
##############################
# no options set
EOF
exit 0
fi
cat << EOF
##############################
# LDAP-backed authentication #
@ -29,7 +43,7 @@ AUTH_LDAP_USER_ATTR_MAP = {
EOF
if [ "$LDAP_GROUP_BASE" != "" ]; then
cat << EOF
cat << EOF
# This search ought to return all groups to which the user belongs. django_auth_ldap uses this to determine group
# hierarchy.
@ -39,23 +53,30 @@ AUTH_LDAP_GROUP_TYPE = PosixGroupType()
# Mirror LDAP group assignments.
AUTH_LDAP_MIRROR_GROUPS = True
# For more granular permissions, map LDAP groups to Django groups.
AUTH_LDAP_FIND_GROUP_PERMS = True
EOF
if [ "$LDAP_REQUIRE_GROUP" != "" ]; then
cat << EOF
if [ "$LDAP_REQUIRE_GROUP" != "" ]; then
cat << EOF
# Define a group required to login.
AUTH_LDAP_REQUIRE_GROUP = "$LDAP_REQUIRE_GROUP"
EOF
fi
fi
if [ "$LDAP_SUPERUSER_GROUP" != "" ]; then
cat << EOF
cat << EOF
# Define special user types using groups. Exercise great caution when assigning superuser status.
AUTH_LDAP_USER_FLAGS_BY_GROUP = {
"is_superuser": "$LDAP_SUPERUSER_GROUP",
}
EOF
fi
# superuser
if [ "$LDAP_SUPERUSER_GROUP" != "" ]; then
echo " \"is_superuser\": \"$LDAP_SUPERUSER_GROUP\","
fi
# staff user
if [ "$LDAP_STAFF_GROUP" != "" ]; then
echo " \"is_staff\": \"$LDAP_STAFF_GROUP\","
fi
echo "}"
fi

View file

@ -1,8 +1,11 @@
[Unit]
Description=NetBox Request Queue Worker
Documentation=https://netbox.readthedocs.io/en/stable/
After=network-online.target
Wants=network-online.target
PartOf=netbox.service
Wants=network.target
After=netbox.service
After=network.target
After=redis-server.service postgresql.service
[Service]
Type=simple

View file

@ -1,22 +1,13 @@
[Unit]
Description=NetBox WSGI Service
Description=NetBox Service Wrapper
Documentation=https://netbox.readthedocs.io/en/stable/
After=network-online.target
Wants=network-online.target
Wants=network.target
After=network.target
[Service]
Type=simple
User=netbox
Group=netbox
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
Restart=on-failure
RestartSec=30
PrivateTmp=true
Type=oneshot
RemainAfterExit=yes
ExecStart=/bin/true
[Install]
WantedBy=multi-user.target

View file

@ -0,0 +1,33 @@
#!/bin/sh -e
# __netbox/files/netbox.socket.sh
# This is shared between all WSGI-server types.
# Arguments:
# 1: File which list all sockets to listen on (sepearated by \n)
if [ $# -ne 1 ]; then
printf "netbox.socket.sh: argument \$1 missing or too much given!\n" >&2
exit 1
fi
cat << UNIT
[Unit]
Description=Socket for NetBox via $TYPE
[Socket]
UNIT
# read all sockets to listen to
while read -r line; do
printf "ListenStream=%s\n" "$line"
done < "$1"
cat << UNIT
SocketUser=netbox
SocketGroup=www-data
[Install]
WantedBy=sockets.target
UNIT

View file

@ -1,5 +1,6 @@
#!/bin/sh
#!/bin/sh -e
old_version="$(cat "$__object/explorer/version")"
VERSION=$(cat "$__object/parameter/version")
src="netbox-$VERSION"
@ -7,9 +8,8 @@ archive="v$VERSION.tar.gz"
url="https://github.com/netbox-community/netbox/archive/$archive"
install_dir=/opt/netbox/netbox
cat << EOF
set -e
if [ "$VERSION" != "$old_version" ]; then
cat << EOF
# Ensure that coreutils is installed.
if [ ! -x \$(which mktemp) ]; then
echo "mktemp is not available on the remote host." >&2
@ -21,45 +21,100 @@ tmpdir=\$(mktemp -d)
cd "\$tmpdir"
# Download and extract sources.
curl -L '$url' > '$archive'
curl -sS -L '$url' > '$archive'
tar xf '$archive'
# Save cdist-upload configuration file.
cp '$install_dir/cdist/configuration.py' "\$tmpdir/configuration.py"
cp '$install_dir/cdist/ldap_config.py' "\$tmpdir/ldap_config.py"
# Deploy sources and restore configuration.
rm -r '$install_dir'
cp -r '$src/netbox' '$install_dir'
cp \$tmpdir/configuration.py '$install_dir/netbox/configuration.py'
cp \$tmpdir/ldap_config.py '$install_dir/netbox/ldap_config.py'
# virtualenv is given already by __pyvenv, just using it
# Setup & enter python virtualenv.
virtualenv /opt/netbox/venv
# backup requirement files
if [ -f /opt/netbox/requirements.txt ]; then
mv /opt/netbox/requirements.txt /opt/netbox/old-requirements.txt
else
# preseve file-not-found errors and warnings
touch /opt/netbox/old-requirements.txt
fi
cp '$src/requirements.txt' /opt/netbox/
# Install python dependencies.
/opt/netbox/venv/bin/pip3 install -r "\$tmpdir/$src/requirements.txt"
EOF
# Uninstall packages not required anymore
# if versions not be shortend, they will be ignored by pip, but not by comm
# all of this could be done with grep, too, but it's still must be shortend with awk
awk -F== '{print \$1}' '/opt/netbox/requirements.txt' | sort > "\$tmpdir/curr-reqs.txt"
awk -F== '{print \$1}' '/opt/netbox/old-requirements.txt' | sort > "\$tmpdir/old-reqs.txt"
comm -23 "\$tmpdir/old-reqs.txt" "\$tmpdir/curr-reqs.txt" > "\$tmpdir/pip-uninstall.txt"
if [ -f "$__object/parameter/ldap-server" ]; then
echo "/opt/netbox/venv/bin/pip3 install django-auth-ldap"
# only uninstall if something is available (to avoid errors cause of this)
if [ -s "\$tmpdir/pip-uninstall.txt" ]; then
/opt/netbox/venv/bin/pip3 uninstall -qy -r "\$tmpdir/pip-uninstall.txt"
fi
cat << EOF
# Install python dependencies.
# avoid gunicorn, because it will be done in an other type
grep -v "^gunicorn==" "\$tmpdir/$src/requirements.txt" \
| xargs /opt/netbox/venv/bin/pip3 install -q
EOF
if [ -f "$__object/parameter/ldap-server" ]; then
echo "/opt/netbox/venv/bin/pip3 install -q django-auth-ldap"
else
echo "/opt/netbox/venv/bin/pip3 uninstall -qy django-auth-ldap"
fi
cat << EOF
# Deploy sources and restore configuration.
rm -rf '$install_dir'
cp -r '$src/netbox' '$install_dir'
# force links to the cdist directory
ln -fs /opt/netbox/cdist/configuration.py '$install_dir/netbox/configuration.py'
ln -fs /opt/netbox/cdist/ldap_config.py '$install_dir/netbox/ldap_config.py'
# Set final permissions.
chown -R netbox /opt/netbox
# NetBox manage scripts
# Run database migrations.
sudo -u netbox /opt/netbox/venv/bin/python3 /opt/netbox/netbox/manage.py migrate
# Generate static assets.
sudo -u netbox /opt/netbox/venv/bin/python3 /opt/netbox/netbox/manage.py collectstatic --no-input
# Delete any stale content types
sudo -u netbox /opt/netbox/venv/bin/python3 /opt/netbox/netbox/manage.py remove_stale_contenttypes --no-input
# Delete any expired user sessions
sudo -u netbox /opt/netbox/venv/bin/python3 /opt/netbox/netbox/manage.py clearsessions
# Clear all cached data
sudo -u netbox /opt/netbox/venv/bin/python3 /opt/netbox/netbox/manage.py invalidate all
# Remove temporary working directory.
cd /
rm -r "\$tmpdir"
rm -rf "\$tmpdir"
# Save version after successful installation
printf "%s\\n" "$VERSION" > /opt/netbox/cdist/version
# Restart services.
service netbox restart
service netbox-rq restart
EOF
# meta
printf "installed %s\n" "$VERSION" >> "$__messages_out"
changes=yes
fi
# check if configuration changed
if grep -q "^__file/opt/netbox/" "$__messages_in"; then
# meta
printf "configured\n" >> "$__messages_out"
changes=yes
fi
# Check for changes
if [ "$changes" = "yes" ]; then
# After the upstream upgrade.sh script, it's ok to migrate while the
# application is running ;)
# restarting after changes
cat << EOF
# Restart service. All required services are included with netbox.service.
systemctl restart netbox
EOF
fi

View file

@ -9,54 +9,194 @@ cdist-type__netbox - Install and configure NetBox
DESCRIPTION
-----------
This (singleton) type installs and configures a NetBox instance, a web
application to help manage and document computer networks
application to help manage and document computer networks.
It installs it with the user ``netbox`` at ``/opt/netbox`` with `python-venv`.
It setup systemd unit files for the services `netbox` and `netbox-rq`. The
`netbox` service only wrap all netbox related services, e.g. restarting and
so one will be delegated to all related services.
The application is still not accessable because a WSGI server is required. To
access the application through WSGI, uWSGI or Gunicorn can be used. The setup
can be done via there own types `__netbox_gunicorn` and `__netbox_uwsgi`.
The Gunicorn setup is recommended from the NetBox documentation. Consult each
manual page to decide. The types must be called after the `__netbox` type.
REQUIRED PARAMETERS
-------------------
version
NetBox version to be installed.
NetBox version to be installed. You can find the correct and newest version
on GitHub at the NetBox project page under
"`Releases <https://github.com/netbox-community/netbox/releases>`_".
database
PostgreSQL database name.
database-user
PostgreSQL database user.
database-password
PostgreSQL database password.
secret-key
Random secret key of at least 50 alphanumeric characters. This key must be
unique to this installation and must not be shared outside the local
system.
host
Hostname (domain or IP address) on which the application is served.
Multiple hostnames are possible; given as multiple arguments.
OPTIONAL PARAMETERS
-------------------
secret-key
Random secret key of at least 50 alphanumeric characters and symbols. This
key must be unique to this installation and must not be shared outside the
local system. If no secret key is given, the type generates an own 50 chars
long key and saves it on the remote host to remember it for the next run.
The secret, random string is used to assist in the creation new
cryptographic hashes for passwords and HTTP cookies. It is not directly
used for hasing user passwords or for encrpted storage. It can be changed
at any time, but will invalidate all existing sessions.
database-host
PostgreSQL database hostname. Defaults to ``localhost``.
database-port
PostgreSQL database port. Defaults to empty (uses the default port).
ldap-server
LDAP server URI. Enables LDAP-backed authentication if specified.
LDAP server URI. Enables LDAP-backed authentication if specified.
ldap-bind-dn
DN for the NetBox service account. Required for LDAP authentication.
DN for the NetBox service account. Required for LDAP authentication.
ldap-bind-password
Password for the NetBox service account. Required for LDAP authentication.
Password for the NetBox service account. Required for LDAP authentication.
ldap-user-base
Base used for searching user entries. Required for LDAP authentication.
Base used for searching user entries. Required for LDAP authentication.
ldap-group-base
Base used for searching group entries.
Base used for searching group entries.
ldap-require-group
Group required to login.
Group required to login.
ldap-staff-group
Make members of this group to "staff". This gives the users "Admin Access",
which means access to the "NetBox Administration" site.
ldap-superuser-group
Make members of this groups superusers.
Make members of this groups superusers.
redis-host
Redis database hostname. Defaults to ``localhost``.
redis-port
Redis database port. Defaults to ``6379``.
redis-password
Redis password. Defaults to empty password.
redis-dbid-offset
Offset to set the redis database id's. The `tasks` database id is
`offset + 0` and `caching` is `offset + 1`. The offset defaults to ``0``.
smtp-host
Host of the SMTP email server. Defaults to ``localhost``.
smtp-port
Port of the SMTP email server. Defaults to ``25``.
smtp-user
Username to access the SMTP email server. Defaults to empty.
smtp-password
Password to access the SMTP email server. Defaults to empty.
smtp-from-email
Email from which NetBox will be sent of. Defaults to empty.
basepath
Base URL path if accessing netbox within a directory instead of directly the
webroot ``/``. For example, if installed at https://example.com/netbox/, set
the value ``netbox/``.
http-proxy
https-proxy
Proxy which will be used with any HTTP request like webhooks.
data-root
This parameter set's the media, reports and scripts root to subdirectories
of the given directory. Values can be overwritten by special parameters like
`--media-root` for example. Use this option if you want to store persistant
data of netbox on an other partition. A trailing slash is not needed.
The data directories have following predefined sub-directory names:
media root:
``$data_root/media``
reports root:
``$data_root/reports``
scripts root:
``$data_root/scripts``
To preserve all data from installation upgrades - which just replace the
installation directory - the data will be kept in the netbox home directory
rather than the installation directory by default (``/opt/netbox/data/``).
This way, no data will be deleted after the installation directory
replacement because it remains outside of the installation directory.
media-root
The file path to where media files (like image attachments) are stored.
Change this path if you require to store data on an other partiotion.
A trailing slash is not needed. Defaults to ``$data_root/media``.
reports-root
The file path of where custom reports are kept. Change this path if you
require to store data on an other partition. A trailing slash is not
needed. Defaults to ``$data_root/reports``.
scripts-root
The file path of where custom scripts are kept. Change this path if you
require to store data on an other partition. A trailing slash is not
needed. Defaults to ``$data_root/scripts``.
BOOLEAN PARAMETERS
------------------
None.
redis-ssl
Enables a secure TLS/SSL connection to the redis database. By default, ssl
is disabled.
smtp-use-tls
Uses TLS to connect to the SMTP email server. `See documentation
<https://docs.djangoproject.com/en/3.1/ref/settings/#email-use-tls>`_
for more information.
smtp-use-ssl
Uses implicit TLS with the SMTP email server. `See documentation
<https://docs.djangoproject.com/en/3.1/ref/settings/#email-use-ssl>`_
for more information.
login-required
Sets if a login is required to access all sites. By default, anonymous
users can see most data (excluding secrets) but not make any changes.
update-notify
Enables the NetBox version check for new upstream updates. It checks every
24 hours for new releases and notify the admin users in the gui if any.
MESSAGES
--------
installed $VERSION
Netbox was fresh installed or updated. The new version number is appended.
configured
Some configuration files got updated and therefore the service was
restarted. This message will not be echoed if configuration got updated due
a standard installation.
EXAMPLES
@ -65,30 +205,71 @@ EXAMPLES
.. code-block:: sh
__netbox --version 2.8.7 --database netbox \
--database-password "secretsecretsecret" \
--secret-key "secretsecretsecret" \
--host "${__target_host:?}" \
--ldap-server "ldaps://ldap.domain.tld" \
--ldap-bind-dn "uid=netbox,ou=services,dc=domain,dc=tld" \
--ldap-bind-password "secretsecretsecret" \
--ldap-user-base "ou=users,dc=domain,dc=tld" \
--ldap-group-base "ou=groups,dc=domain,dc=tld" \
--ldap-require-group "cn=netbox-login,ou=groups,dc=domain,dc=tld" \
--ldap-superuser-group "cn=netbox-admin,ou=groups,dc=domain,dc=tld"
--database-password "secretsecretsecret" \
--secret-key "secretsecretsecret" \
--host "${__target_host:?}" \
--host "cool-netbox.xyz" \
--ldap-server "ldaps://ldap.domain.tld" \
--ldap-bind-dn "uid=netbox,ou=services,dc=domain,dc=tld" \
--ldap-bind-password "secretsecretsecret" \
--ldap-user-base "ou=users,dc=domain,dc=tld" \
--ldap-group-base "ou=groups,dc=domain,dc=tld" \
--ldap-require-group "cn=netbox-login,ou=groups,dc=domain,dc=tld" \
--ldap-superuser-group "cn=netbox-admin,ou=groups,dc=domain,dc=tld"
# using recommended gunicorn setup
require="__netbox" __netbox_gunicorn
NOTES
-----
The configuration of NetBox contains more optional settings than that what can
be set with this type. If you think an important setting is missing or there
is a more good way to inject python code for dynamic configuration variables,
you are welcome to contribute!
- `Possible optional settings
<https://netbox.readthedocs.io/en/stable/configuration/optional-settings/>`_
If you not setup ldap authentification, you may be interested into how to
`setting up a super user
<https://netbox.readthedocs.io/en/stable/installation/3-netbox/#create-a-super-user>`_
directly on the machine to be able to access and use NetBox.
You may also be interested of writing a own type which handles the creation of
the super user. To do this non-interactivly, see the ansible role as `reference
<https://github.com/lae/ansible-role-netbox/blob/18f46a3345f100936c5116abe716c480e1886676/vars/main.yml#L15>`_.
If you change the secret key while the netbox instance is running, there is a
time frame where the access to the application corrupts the whole database.
Then, you need to restore a backup or wipe the database.
Currently, the cause is not clear, but it should work if you do not touch
netbox while the configuration is done (do not shut it down, too). It only
applies for changes of the secret key, which not happen normally.
Maybe the `--restart` flag for the `__systemd_unit` types is not the best idea,
but avoids that the changes will not be applied. It could be solved if the type
would send messages from his actions.
SEE ALSO
--------
- `NetBox documentation <https://netbox.readthedocs.io/en/stable/>`_
`NetBox documentation <https://netbox.readthedocs.io/en/stable/>`_
:strong:`cdist-type__netbox_gunicorn`\ (7)
:strong:`cdist-type__netbox_uwsgi`\ (7)
AUTHORS
-------
Timothée Floure <t.floure@e-durable.ch>
Matthias Stecher <matthiasstecher@gmx.de>
COPYING
-------
Copyright \(C) 2020 Timothée Floure. You can redistribute it
and/or modify it under the terms of the GNU General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
Copyright \(C) 2020 Timothée Floure.
Copyright \(C) 2020 Matthias Stecher.
You can redistribute it and/or modify it under the terms of the GNU
General Public License as published by the Free Software Foundation,
either version 3 of the License, or (at your option) any later version.

241
type/__netbox/manifest Normal file → Executable file
View file

@ -1,99 +1,226 @@
#!/bin/sh
#!/bin/sh -e
os=$(cat "$__global/explorer/os")
case "$os" in
debian|ubuntu)
# Install netbox dependencies.
for pkg in python3-pip python3-venv python3-dev build-essential libxml2-dev \
libxslt1-dev libffi-dev libpq-dev libssl-dev zlib1g-dev curl virtualenv; do
__package $pkg
done
debian|ubuntu)
# Install netbox dependencies.
for pkg in python3-pip python3-venv python3-dev build-essential libxml2-dev \
libxslt1-dev libffi-dev libpq-dev libssl-dev zlib1g-dev curl sudo; do
__package $pkg
done
if [ -f "$__object/parameter/ldap-server" ]; then
for pkg in libldap2-dev libsasl2-dev libssl-dev; do
__package $pkg
done
fi
;;
*)
printf "Your operating system (%s) is currently not supported by this type (%s)\n" "$os" "${__type##*/}" >&2
printf "Please contribute an implementation for it if you can.\n" >&2
exit 1
;;
if [ -f "$__object/parameter/ldap-server" ]; then
for pkg in libldap2-dev libsasl2-dev libssl-dev; do
__package $pkg
done
fi
;;
*)
printf "Your operating system (%s) is currently not supported by this type (%s)\n" "$os" "${__type##*/}" >&2
printf "Please contribute an implementation for it if you can.\n" >&2
exit 1
;;
esac
DATABASE_NAME=$(cat "$__object/parameter/database")
export DATABASE_NAME
DATABASE_USER="$(cat "$__object/parameter/database-user")"
export DATABASE_USER
DATABASE_PASSWORD=$(cat "$__object/parameter/database-password")
export DATABASE_PASSWORD
ALLOWED_HOST=$(cat "$__object/parameter/host")
export ALLOWED_HOST
SECRET_KEY=$(cat "$__object/parameter/secret-key")
DATABASE_HOST="$(cat "$__object/parameter/database-host")"
export DATABASE_HOST
DATABASE_PORT="$(cat "$__object/parameter/database-port")"
export DATABASE_PORT
# list of hosts
ALLOWED_HOSTS=""
while read -r hostname; do
# shellcheck disable=SC2089
ALLOWED_HOSTS="$ALLOWED_HOSTS '$hostname',"
done < "$__object/parameter/host"
# shellcheck disable=SC2090
export ALLOWED_HOSTS
if [ -f "$__object/parameter/secret-key" ]; then
SECRET_KEY=$(cat "$__object/parameter/secret-key")
elif [ -s "$__object/explorer/secretkey" ]; then
# take the key that is already used
SECRET_KEY="$(cat "$__object/explorer/secretkey")"
else
# Can be done over netbox/generate_secret_key.py too, but it can't be
# generated right now where it's required (only if it's preloaded for
# this type to execute it now).
# Generates a 50-character long key with the same character set like
# the helper script. Must escape the '-' to be no character range.
SECRET_KEY="$(tr -cd '!@#$%^&*(\-_=+)[:alnum:]' < /dev/urandom | head -c50)"
fi
export SECRET_KEY
if [ -f "$__object/parameter/ldap-server" ]; then
LDAP_SERVER=$(cat "$__object/parameter/ldap-server")
export LDAP_SERVER
LDAP_SERVER=$(cat "$__object/parameter/ldap-server")
USE_LDAP=yes
export LDAP_SERVER
fi
if [ -f "$__object/parameter/ldap-bind-dn" ]; then
LDAP_BIND_DN=$(cat "$__object/parameter/ldap-bind-dn")
export LDAP_BIND_DN
LDAP_BIND_DN=$(cat "$__object/parameter/ldap-bind-dn")
USE_LDAP=yes
export LDAP_BIND_DN
fi
if [ -f "$__object/parameter/ldap-bind-password" ]; then
LDAP_BIND_PASSWORD=$(cat "$__object/parameter/ldap-bind-password")
export LDAP_BIND_PASSWORD
LDAP_BIND_PASSWORD=$(cat "$__object/parameter/ldap-bind-password")
USE_LDAP=yes
export LDAP_BIND_PASSWORD
fi
if [ -f "$__object/parameter/ldap-user-base" ]; then
LDAP_USER_BASE=$(cat "$__object/parameter/ldap-user-base")
export LDAP_USER_BASE
LDAP_USER_BASE=$(cat "$__object/parameter/ldap-user-base")
USE_LDAP=yes
export LDAP_USER_BASE
fi
if [ -f "$__object/parameter/ldap-group-base" ]; then
LDAP_GROUP_BASE=$(cat "$__object/parameter/ldap-group-base")
export LDAP_GROUP_BASE
LDAP_GROUP_BASE=$(cat "$__object/parameter/ldap-group-base")
export LDAP_GROUP_BASE
fi
if [ -f "$__object/parameter/ldap-require-group" ]; then
LDAP_REQUIRE_GROUP=$(cat "$__object/parameter/ldap-require-group")
export LDAP_REQUIRE_GROUP
LDAP_REQUIRE_GROUP=$(cat "$__object/parameter/ldap-require-group")
export LDAP_REQUIRE_GROUP
fi
if [ -f "$__object/parameter/ldap-superuser-group" ]; then
LDAP_SUPERUSER_GROUP=$(cat "$__object/parameter/ldap-superuser-group")
export LDAP_SUPERUSER_GROUP
fi
if [ -f "$__object/parameter/ldap-staff-group" ]; then
LDAP_STAFF_GROUP="$(cat "$__object/parameter/ldap-staff-group")"
export LDAP_STAFF_GROUP
fi
# export if base ldap parameters are used
export USE_LDAP
# have default values
REDIS_HOST="$(cat "$__object/parameter/redis-host")"
export REDIS_HOST
REDIS_PORT="$(cat "$__object/parameter/redis-port")"
export REDIS_PORT
REDIS_PASSWORD="$(cat "$__object/parameter/redis-password")"
export REDIS_PASSWORD
REDIS_DBID_OFFSET="$(cat "$__object/parameter/redis-dbid-offset")"
export REDIS_DBID_OFFSET
if [ -f "$__object/parameter/redis-ssl" ]; then
REDIS_SSL="True"
else
REDIS_SSL="False"
fi
export REDIS_SSL
SMTP_HOST="$(cat "$__object/parameter/smtp-host")"
export SMTP_HOST
SMTP_PORT="$(cat "$__object/parameter/smtp-port")"
export SMTP_PORT
SMTP_USER="$(cat "$__object/parameter/smtp-user")"
export SMTP_USER
SMTP_PASSWORD="$(cat "$__object/parameter/smtp-password")"
export SMTP_PASSWORD
SMTP_FROM_EMAIL="$(cat "$__object/parameter/smtp-from-email")"
export SMTP_FROM_EMAIL
if [ -f "$__object/parameter/smtp-use-ssl" ]; then
SMTP_USE_SSL="True"
else
SMTP_USE_SSL="False"
fi
export SMTP_USE_SSL
if [ -f "$__object/parameter/smtp-use-tls" ]; then
if [ "$SMTP_USE_SSL" = "True" ]; then
echo "options --smtp-use-ssl and --smtp-use-tls are not compatible" >&2
exit 2
fi
SMTP_USE_TLS="True"
else
SMTP_USE_TLS="False"
fi
export SMTP_USE_TLS
BASEPATH="$(cat "$__object/parameter/basepath")"
export BASEPATH
if [ -f "$__object/parameter/http-proxy" ]; then
HTTP_PROXY=$(cat "$__object/parameter/http-proxy")
export HTTP_PROXY
fi
if [ -f "$__object/parameter/https-proxy" ]; then
HTTPS_PROXY=$(cat "$__object/parameter/https-proxy")
export HTTPS_PROXY
fi
if [ -f "$__object/parameter/ldap-superuser-group" ]; then
LDAP_SUPERUSER_GROUP=$(cat "$__object/parameter/ldap-superuser-group")
export LDAP_SUPERUSER_GROUP
if [ -f "$__object/parameter/login-required" ]; then
LOGIN_REQUIRED="True"
else
LOGIN_REQUIRED="False"
fi
export LOGIN_REQUIRED
data_root="$(cat "$__object/parameter/data-root")"
MEDIA_ROOT="$data_root/media"
REPORTS_ROOT="$data_root/reports"
SCRIPTS_ROOT="$data_root/scripts"
if [ -f "$__object/parameter/media-root" ]; then
MEDIA_ROOT="$(cat "$__object/parameter/media-root")"
fi
export MEDIA_ROOT
if [ -f "$__object/parameter/reports-root" ]; then
REPORTS_ROOT="$(cat "$__object/parameter/reports-root")"
fi
export REPORTS_ROOT
if [ -f "$__object/parameter/scripts-root" ]; then
SCRIPTS_ROOT="$(cat "$__object/parameter/scripts-root")"
fi
export SCRIPTS_ROOT
if [ -f "$__object/parameter/update-notify" ]; then
UPDATE_CHECK="yes"
export UPDATE_CHECK
fi
# Create system user used to run netbox.
__user netbox --system --home /opt/netbox --create-home
# Generate python environment (user will be set by gencode-remote)
require="__user/netbox" __pyvenv /opt/netbox/venv/
# Generate and upload netbox configuration.
mkdir -p "$__object/files"
"$__type/files/configuration.py.sh" > "$__object/files/configuration.py"
"$__type/files/ldap_config.py.sh" > "$__object/files/ldap_config.py"
require="__user/netbox" __directory /opt/netbox/netbox/cdist --parents
require="__directory/opt/netbox/netbox/cdist " __file \
/opt/netbox/netbox/cdist/configuration.py --mode 640 --owner netbox \
--source "$__object/files/configuration.py"
require="__user/netbox" __directory /opt/netbox/cdist
require="__directory/opt/netbox/cdist" __file \
/opt/netbox/cdist/configuration.py --mode 640 --owner netbox \
--source "$__object/files/configuration.py"
if [ -f "$__object/parameter/ldap-server" ]; then
require="__directory/opt/netbox/netbox/cdist " __file \
/opt/netbox/netbox/cdist/ldap_config.py --mode 640 --owner netbox \
--source "$__object/files/ldap_config.py"
require="__directory/opt/netbox/cdist" __file \
/opt/netbox/cdist/ldap_config.py --mode 640 --owner netbox \
--source "$__object/files/ldap_config.py"
else
require="__directory/opt/netbox/cdist" __file \
/opt/netbox/cdist/ldap_config.py --state absent
fi
# save secret
require="__directory/opt/netbox/cdist" __file /opt/netbox/cdist/secretkey \
--mode 400 --owner netbox --source - << SECRET
$SECRET_KEY
SECRET
# Upload systemd units and gunicorn configuration.
for unit in netbox netbox-rq; do
__systemd_unit $unit.service \
--source "$__type/files/$unit.service" \
--enablement-state enabled
done
# Python worker configuration.
require="__user/netbox" __file /opt/netbox/gunicorn.py \
--mode 644 --source "$__type/files/gunicorn.py"
# Upload systemd unit for worker and wsgi service
# does not restart netbox on change cause it only restart all other services
__systemd_unit netbox.service \
--source "$__type/files/netbox.service" \
--enablement-state enabled
__systemd_unit netbox-rq.service \
--source "$__type/files/netbox-rq.service" \
--enablement-state enabled --restart

View file

@ -0,0 +1,5 @@
redis-ssl
smtp-use-ssl
smtp-use-tls
login-required
update-notify

View file

@ -0,0 +1 @@

View file

@ -0,0 +1 @@
/opt/netbox/data

View file

@ -0,0 +1 @@
localhost

View file

@ -0,0 +1 @@

View file

@ -0,0 +1 @@
0

View file

@ -0,0 +1 @@
localhost

View file

@ -0,0 +1 @@

View file

@ -0,0 +1 @@
6379

View file

@ -0,0 +1 @@

View file

@ -0,0 +1 @@
localhost

View file

@ -0,0 +1 @@

View file

@ -0,0 +1 @@
25

View file

@ -0,0 +1 @@

View file

@ -1,7 +1,27 @@
secret-key
database-host
database-port
ldap-server
ldap-bind-dn
ldap-bind-password
ldap-user-base
ldap-group-base
ldap-require-group
ldap-staff-group
ldap-superuser-group
redis-host
redis-port
redis-password
redis-dbid-offset
smtp-host
smtp-port
smtp-user
smtp-password
smtp-from-email
basepath
http-proxy
https-proxy
data-root
media-root
reports-root
scripts-root

View file

@ -1,5 +1,4 @@
version
database
database-user
database-password
secret-key
host

View file

@ -0,0 +1 @@
host

View file

@ -0,0 +1,4 @@
#!/bin/sh -e
# print version if available
/opt/netbox/venv/bin/pip3 show gunicorn | awk '/Version:/{print $2}'

View file

@ -0,0 +1,3 @@
#!/bin/sh -e
awk -v FS="==" '$1 ~ /gunicorn/{print $2}' /opt/netbox/requirements.txt

View file

@ -1,9 +1,23 @@
#!/bin/sh -e
# Generates gunicorn config
# see https://docs.gunicorn.org/en/stable/settings.html
# fix missing $__explorer
# see https://code.ungleich.ch/ungleich-public/cdist/-/issues/834
__explorer="$__global/explorer"
# size workes by cpu
cores="$(cat "$__explorer/cpu_cores")"
cat << EOF
# The IP address (typically localhost) and port that the Netbox WSGI process should listen on
bind = '127.0.0.1:8001'
#bind = done via systemd socket 'gunicorn-netbox.socket'
# Number of gunicorn workers to spawn. This should typically be 2n+1, where
# n is the number of CPU cores present.
workers = 3
workers = $(( 2*cores + 1 ))
# Number of threads per worker process
threads = 3
@ -14,3 +28,4 @@ timeout = 120
# The maximum number of requests a worker can handle before being respawned
max_requests = 5000
max_requests_jitter = 500
EOF

View file

@ -0,0 +1,29 @@
[Unit]
Description=NetBox Gunicorn WSGI Service
Documentation=https://netbox.readthedocs.io/en/stable/
PartOf=netbox.service
Requires=netbox-rq.service
Requires=gunicorn-netbox.socket
Wants=network.target
After=netbox.service
After=network.target
After=redis-server.service postgresql.service
[Service]
Type=notify
User=netbox
Group=netbox
WorkingDirectory=/opt/netbox
ExecStart=/opt/netbox/venv/bin/gunicorn --pythonpath /opt/netbox/netbox --config /opt/netbox/gunicorn.py netbox.wsgi
# signals: https://docs.gunicorn.org/en/stable/signals.html
ExecReload=kill -HUP $MAINPID
ExecStop=kill -TERM $MAINPID
KillSignal=SIGQUIT
Restart=on-failure
RestartSec=30
[Install]
WantedBy=netbox.service

View file

@ -0,0 +1 @@
../../__netbox/files/netbox.socket.sh

View file

@ -0,0 +1,50 @@
#!/bin/sh -e
# control state
state="$(cat "$__object/parameter/state")"
case "$state" in
# install gunicorn
enabled|disabled)
curr_installed="$(cat "$__object/explorer/installed")"
should_installed="$(cat "$__object/explorer/should_installed")"
# gunicorn version change
if [ "$curr_installed" != "$should_installed" ]; then
# (re)installing gunicorn
echo "/opt/netbox/venv/bin/pip3 install 'gunicorn==$should_installed'"
if [ "$curr_installed" != "" ]; then
printf "updated %s to %s\n" "$curr_installed" "$should_installed" \
>> "$__messages_out"
else
printf "installed\n" >> "$__messages_out"
fi
do_restart=yes
fi
# configuration changes
if grep -q "^__file/opt/netbox/gunicorn.py:" "$__messages_in"; then
do_restart=yes
printf "configured\n" >> "$__messages_out"
fi
# restart gunicorn
if [ "$do_restart" ] && [ "$state" != "disabled" ]; then
cat << EOF
# Restart service
systemctl restart gunicorn-netbox
EOF
fi
;;
# uninstall
absent)
# check if installed
if [ -s "$__object/explorer/installed" ]; then
# service already disabled
echo "/opt/netbox/venv/bin/pip3 uninstall -y gunicorn"
printf "uninstalled\n" >> "$__messages_out"
fi
esac

View file

@ -0,0 +1,117 @@
cdist-type__netbox_gunicorn(7)
==============================
NAME
----
cdist-type__netbox_gunicorn - Run NetBox with Gunicorn
DESCRIPTION
-----------
This (singleton) type installs Gunicorn into the NetBox `python-venv` to host
the NetBox WSGI application. It provides the application as HTTP over the given
sockets. Static content must be served independent of Gunicorn. The Gunicorn
daemon is available as the `gunicorn-netbox` systemd service, but also
available via the `netbox` wrapper service.
It will use systemd socket activation to listen to the given sockets. This
should allow to bind to privileaged ports (all below 1024) and hot reloads.
REQUIRED PARAMETERS
-------------------
None.
OPTIONAL PARAMETERS
-------------------
state
Represents the state of the Gunciron application. Defaults to ``enabled``.
enabled
The Gunicorn service is enabled and running.
disabled
The Gunicorn service is installed, but disabled.
absent
The uWSGI service is not installed and all configuration removed.
This type does not guarantee anything about the running state of the
service. To be sure about the service is stopped or not, use the type
:strong:`cdist-type__systemd_service`\ (7) after this execution.
bind-to
The hosts the gunicorn socket should be bind to. Formats are `IP`,
`IP:PORT`, `PATH` or anything other that systemd socket units will
understand as stream. Parameter can be set multiple times. Defaults
to ``127.0.0.1:8001``.
BOOLEAN PARAMETERS
------------------
None.
MESSAGES
--------
installed
The software was installed.
upgraded $old to $new
The version of the gunicorn software was updated from `$old` to `$new`.
configured
Configuration for gunicorn changed.
uninstalled
The Gunicorn application was removed.
In all cases where the application is still present, it restarts the service to
use the up-to-date version.
EXAMPLES
--------
.. code-block:: sh
# simple
__netbox $args
require="__netbox" __netbox_gunicorn
# with arguments
__netbox $args
require="__netbox" __netbox_gunicorn \
--bind-to 0.0.0.0:8001 \
--bind-to 1.2.3.4:5678
# replace uwsgi with gunicorn
__netbox $args
require="__netbox" __netbox_uwsgi --state absent
# it should depend on __netbox_uwsgi if they use the same socket
require="__netbox_uwsgi" __netbox_gunicorn --state enabled
# be sure the service is disabled
__netbox $args
require="__netbox" __netbox_gunicorn --state disabled
require="__netbox_gunicorn" __systemd_service gunicorn-netbox --state stopped
SEE ALSO
--------
`Gunicorn Documentation <https://docs.gunicorn.org/en/stable/>`_
:strong:`cdist-type__netbox`\ (7)
:strong:`cdist-type__netbox_uwsgi`\ (7)
AUTHORS
-------
Matthias Stecher <matthiasstecher@gmx.de>
COPYING
-------
Copyright \(C) 2020 Matthias Stecher. You can redistribute it
and/or modify it under the terms of the GNU General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.

57
type/__netbox_gunicorn/manifest Executable file
View file

@ -0,0 +1,57 @@
#!/bin/sh -e
# __netbox_gunicorn/manifest
# Check states
state=""
unit_state=""
param_state="$(cat "$__object/parameter/state")"
case "$param_state" in
enabled|disabled)
state="present"
unit_state="$param_state"
;;
absent)
state="absent"
unit_state="disabled"
;;
*)
# does not exist
printf "The state '%s' does not exist, can't continue!\n" "$param_state" >&2
exit 2
;;
esac
mkdir "$__object/files"
if [ "$state" = "present" ]; then
# process template
"$__type/files/gunicorn.py.sh" > "$__object/files/gunicorn.py"
# gunicorn config file
__file /opt/netbox/gunicorn.py \
--mode 644 --owner netbox \
--source "$__object/files/gunicorn.py"
else
# absent config file
__file /opt/netbox/gunicorn.py --state absent
fi
TYPE="Gunicorn"
export TYPE
"$__type/files/netbox.socket.sh" "$__object/parameter/bind-to" \
> "$__object/files/netbox.socket"
# install systemd files
__systemd_unit gunicorn-netbox.socket \
--state "$state" --enablement-state "$unit_state" \
--source "$__object/files/netbox.socket" --restart
__systemd_unit gunicorn-netbox.service \
--state "$state" --enablement-state "$unit_state" \
--source "$__type/files/netbox.service" --restart

View file

@ -0,0 +1 @@
127.0.0.1:8001

View file

@ -0,0 +1 @@
enabled

View file

@ -0,0 +1 @@
state

View file

@ -0,0 +1 @@
bind-to

View file

View file

@ -0,0 +1,12 @@
#!/bin/sh -e
# explorer/bind-capablility
# Checks if the uWSGI binary have the capability to bind to privileaged ports
# as a non-root user. It's required if no systemd sockets are used (cause of
# the use of multiple protocols etc.)
binary="/opt/netbox/venv/bin/uwsgi"
# -v verifies if capability is set
if setcap -q -v CAP_NET_BIND_SERVICE+ep "$binary"; then
echo set
fi

View file

@ -0,0 +1,4 @@
#!/bin/sh -e
# print version if available
/opt/netbox/venv/bin/pip3 show uwsgi | awk '/Version:/{print $2}'

View file

@ -0,0 +1,4 @@
#!/bin/sh -e
# print latest version if availble
/opt/netbox/venv/bin/pip3 list --outdated | awk '$1 == "uwsgi" {print $3}'

View file

@ -0,0 +1,40 @@
#!/bin/sh -e
cat << EOF
[Unit]
Description=Netbox uWSGI WSGI Service
Documentation=https://netbox.readthedocs.io/en/stable/
PartOf=netbox.service
Requires=netbox-rq.service
EOF
# Add dependency to own socket
if [ "$(cat "$__object/files/systemd_socket")" = "yes" ]; then
echo "Requires=uwsgi-netbox.socket"
fi
cat << EOF
Wants=network.target
After=netbox.service
After=network.target
After=redis-server.service postgresql.service
[Service]
Type=notify
User=netbox
Group=netbox
WorkingDirectory=/opt/netbox
ExecStart=/opt/netbox/venv/bin/uwsgi --master --chdir /opt/netbox/netbox --module netbox.wsgi uwsgi.ini
# signals: https://uwsgi-docs.readthedocs.io/en/latest/Management.html#signals-for-controlling-uwsgi
ExecReload=kill -HUP \$MAINPID
ExecStop=kill -INT \$MAINPID
KillSignal=SIGQUIT
Restart=on-failure
RestartSec=30
[Install]
WantedBy=netbox.service
EOF

View file

@ -0,0 +1 @@
../../__netbox/files/netbox.socket.sh

View file

@ -0,0 +1,60 @@
#!/bin/sh -e
# Generates uwsgi config
# see https://uwsgi-docs.readthedocs.io/en/latest/Options.html
# or https://uwsgi-docs-additions.readthedocs.io/en/latest/Options.html
# params:
# 1: parameter name
# 2: parameter value file
#
# output: the lines for the configuration option
multi_options() {
while read -r line; do
printf "%s = %s\n" "$1" "$line"
done < "$2"
}
# fix missing $__explorer
# see https://code.ungleich.ch/ungleich-public/cdist/-/issues/834
__explorer="$__global/explorer"
# size workes by cpu
cores="$(cat "$__explorer/cpu_cores")"
cat << EOF
[uwsgi]
; socket(s) to bind
EOF
if [ "$SYSTEMD_SOCKET" != "yes" ]; then
# special protocol to bind
find "$__object/parameter/" -maxdepth 1 -name "*-bind" -print \
| while read -r param; do
multi_options "$(basename "$param" | awk -F'-' '{print $1}')-socket" "$param"
done
else
# else, systemd will offer socket
echo "; sockets managed via 'uwsgi-netbox.socket'"
printf "protocol = %s\n" "$PROTOCOL"
fi
# multi-process settings
cat << EOF
; processes and threads
processes = $(( 2*cores + 1 ))
threads = 2
EOF
# optional mapping of static content
if [ "$STATIC_MAP" != "" ]; then
cat << EOF
; map static content
static-map = /static=/opt/netbox/netbox/static
EOF
fi

View file

@ -0,0 +1,101 @@
#!/bin/sh -e
# control state
state="$(cat "$__object/parameter/state")"
# Set capabilities to aquire privileaged ports as netbox user. Two modes are
# available to efficiently set capabilites. Assumes libcap-bin is installed as
# default on debian systems.
#
# Arguments:
# 1: mode to detect if capabilites are required to set ('set' or 'correct')
set_bind_cap() {
cap_mode="" # reset variable from the execution before
# check if capabilites are required after given mode
case "$1" in
# assumes capabilites are not set (cause of new binaries)
set)
if [ "$SYSTEMD_SOCKET" != "yes" ]; then
cap_mode="+ep"
fi
;;
# check if capabilities have changed
correct)
if [ -s "$__object/explorer/bind-capability" ]; then
# capabilites are set
if [ "$SYSTEMD_SOCKET" = "yes" ]; then
cap_mode="-ep" # unset
fi
else
# capabilities are unset
if [ "$SYSTEMD_SOCKET" != "yes" ]; then
cap_mode="+ep" # set
fi
fi
;;
# faulty mode
*)
echo "called set_bind_cap incorrect (\$1 missing)" >&2
;;
esac
# set capabilities if any
if [ "$cap_mode" ]; then
printf "setcap -q CAP_NET_BIND_SERVICE%s /opt/netbox/venv/bin/uwsgi\n" "$cap_mode"
fi
}
SYSTEMD_SOCKET="$(cat "$__object/files/systemd_socket")"
case "$state" in
# install uwsgi
enabled|disabled)
# not installed
if ! [ -s "$__object/explorer/installed" ]; then
echo "/opt/netbox/venv/bin/pip3 install -q uwsgi"
set_bind_cap set
do_restart=yes
printf "installed\n" >> "$__messages_out"
# updates available
elif [ -s "$__object/explorer/upgradeable" ]; then
echo "/opt/netbox/venv/bin/pip3 install -q --upgrade uwsgi"
set_bind_cap set
do_restart=yes
printf "upgraded\n" >> "$__messages_out"
fi
# changed configuration
if grep -q "^__file/opt/netbox/uwsgi.ini:" "$__messages_in"; then
do_restart=yes
printf "configured\n" >> "$__messages_out"
fi
# if no capabilities were set yet, check if any are required
if [ -z "$cap_mode" ]; then
set_bind_cap correct
fi
# restart uwsgi
if [ "$do_restart" ] && [ "$state" != "disabled" ]; then
cat << EOF
# Restart service
systemctl restart uwsgi-netbox
EOF
fi
;;
# uninstall
absent)
# check if installed
if [ -s "$__object/explorer/installed" ]; then
# service already disabled
echo "/opt/netbox/venv/bin/pip3 uninstall -qy uwsgi"
printf "uninstalled\n" >> "$__messages_out"
fi
;;
esac

186
type/__netbox_uwsgi/man.rst Normal file
View file

@ -0,0 +1,186 @@
cdist-type__netbox_uwsgi(7)
===========================
NAME
----
cdist-type__netbox_uwsgi - Run NetBox with uWSGI
DESCRIPTION
-----------
This (singleton) type installs uWSGI into the NetBox `python-venv`. It hosts
the NetBox WSGI application via the WSGI protocol. A further server must be
installed to provide it as HTTP and serve static content. It supports multiple
protocols like uwsgi, fastcgi or HTTP to comunicate with the proxy server. This
application is available via the `uwsgi-netbox` systemd service. It is
controllable via the `netbox` wrapper service, too.
**As uWSGI will be started as netbox user, it does not have privileges to
bind to a privileaged port (all ports below 1024).** Because uWSGI will
drop privileages anyway before binding to a port, solutions are to use
the systemd sockets to activate the ports as root or set linux kernel
capabilites to bind to such a privileaged port.
As systemd sockets (or uwsgi itself) do not allow to distinguish multiple
sockets if different protocols are used for different sockets, this type does
not use systemd sockets if it is requested from the user. Using the
``--bind-to`` and ``--protocol`` parameters, it uses the systemd socket
activation. Else, it set the different sockets and protocols natively to uwsgi
and add kernel capabilities to be able to listen to privileaged ports.
REQUIRED PARAMETERS
-------------------
None.
OPTIONAL PARAMETERS
-------------------
state
Represents the state of the uWSGI application. Defaults to ``enabled``.
enabled
The uWSGI service is enabled and running.
disabled
The uWSGI service is installed, but disabled.
absent
The uWSGI service is not installed and all configuration removed.
This type does not guarantee anything about the running state of the
service. To be sure about the service is stopped or not, use the type
:strong:`cdist-type__systemd_service`\ (7) after this execution.
bind-to
The socket uwsgi should bind to. Must be UNIX/TCP (or anything that
systemd sockets accept as stream). Defaults to ``127.0.0.1:3031``. Can be
set multiple times. The used protocol is defined by ``--protocol``.
**By setting up the socket via this parameter, it uses systemd sockets to
handle these.** This parameter will be ignored if a more detailed paramter
is given (``--$proto-bind``).
protocol
The protocol which should be used for the socket given by the ``--bind-to``
parameter. Possible values are ``uwsgi``, ``http``, ``fastcgi`` and
``scgi``. If nothing given, it defaults to ``uwsgi``.
uwsgi-bind
http-bind
fastcgi-bind
scgi-bind
Bind the application to a specific protocol instead of implicit uwsgi via
``--bind-to``. If such parameter given, ``--bind-to`` will be ignored. Must
be a UNIX/TCP socket. Can be set multiple times.
**By using such parameters instead of ``--bind-to``, no systemd sockets
will be used because it can not handle sockets for multiple protocols.**
Instead, the native socket binding will be used. It will add kernel
capabilites to bind to privileaged ports, too. This allow binds to ports
like 80 as netbox user.
BOOLEAN PARAMETERS
------------------
serve-static
Setup uWSGI to serve the static content, too. This is generally not
recommended for real production setups, as it is the job of the reverse
proxy server, who will thread it as static cachable content. This option
is only recommended for small setups or direct usage of the uWSGI socket
like using it as standalone HTTP server for NetBox.
**Hint**: This parameter does not work in junction with the `__netbox`
parameter ``--basepath``. It is because this type does not know the
parameter value and this case is very unlikly to happen; although an
implementation is not difficult.
MESSAGES
--------
installed
The uwsgi service was installed.
upgraded
The uwsgi service was upgraded.
configured
The uwsgi configuration got updated.
uninstalled
The uWSGI application was removed.
In all cases where the application is still present, it restarts the service to
use the up-to-date version.
EXAMPLES
--------
.. code-block:: sh
# simple
__netbox $args
require="__netbox" __netbox_uwsgi
# with multiple binds
__netbox $args
require="__netbox" __netbox_uwsgi --bind-to 0.0.0.0:3032 \
--bind-to 0.0.0.0:3033
# with multiple protocols
# parameter `--bind-to` will be ignored
# avoids systemd sockets, but can handle multiple protocols
__netbox $args
require="__netbox" __netbox_uwsgi --uwsgi-bind 0.0.0.0:3031 \
--http-bind 0.0.0.0:8080 \
--fastcgi-bind 1.2.3.4:5678
# as standalone server
__netbox $args
require="__netbox" __netbox_uwsgi --serve-static --http-bind 0.0.0.0:80
# replace gunicorn with uwsgi
__netbox $args
require="__netbox" __netbox_gunicorn --state absent
# it should depend on __netbox_gunicorn if they use the same socket
require="__netbox_gunicorn" __netbox_uwsgi --state enabled
# be sure the service is disabled
__netbox $args
require="__netbox" __netbox_uwsgi --state disabled
require="__netbox_uwsgi" __systemd_service uwsgi-netbox --state stopped
NOTES
-----
If systemd sockets are used, uwsgi can not be reloaded because it does not
handle the socket correctly. It works by completly restarting uwsgi (because
it is near the same cause of the systemd socket) or tweaking the service unit
with the line ``StandardInput=socket``, which limits you to only one address
to bind to (else, the service will not start).
Maybe someone is interested in enabling log files, because the "log to stdout"
is not the fanciest approach (because it is shown in the journal). See the
`uwsgi documentation <https://uwsgi.readthedocs.io/en/latest/Logging.html>` for
reference.
SEE ALSO
--------
`uWSGI Documentation <https://uwsgi-docs.readthedocs.io/en/latest/>`_
:strong:`cdist-type__netbox`\ (7)
:strong:`cdist-type__netbox_gunicorn`\ (7)
AUTHORS
-------
Matthias Stecher <matthiasstecher@gmx.de>
COPYING
-------
Copyright \(C) 2020 Matthias Stecher. You can redistribute it
and/or modify it under the terms of the GNU General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.

86
type/__netbox_uwsgi/manifest Executable file
View file

@ -0,0 +1,86 @@
#!/bin/sh -e
# __netbox_uwsgi/manifest
# Check states
state=""
unit_state=""
param_state="$(cat "$__object/parameter/state")"
case "$param_state" in
enabled|disabled)
state="present"
unit_state="$param_state"
;;
absent)
state="absent"
unit_state="disabled"
;;
*)
# does not exist
printf "The state '%s' does not exist, can't continue!\n" "$param_state" >&2
exit 2
;;
esac
mkdir "$__object/files"
# check if systemd sockets will be used
if [ -f "$__object/parameter/bind-to" ]; then
SYSTEMD_SOCKET="yes"
fi
if find "$__object/parameter/" -maxdepth 1 -name "*-bind" -print -quit | grep -q .; then
SYSTEMD_SOCKET="no"
fi
echo "$SYSTEMD_SOCKET" > "$__object/files/systemd_socket"
if [ "$state" = "present" ]; then
# already checked outside this if-clause
export SYSTEMD_SOCKET
PROTOCOL="$(cat "$__object/parameter/protocol")"
export PROTOCOL
if [ -f "$__object/parameter/serve-static" ]; then
STATIC_MAP="yes"
export STATIC_MAP
fi
# process template
"$__type/files/uwsgi.ini.sh" > "$__object/files/uwsgi.ini"
# uwsgi config file
# TODO maybe patching with __key_value cause of .ini ?
__file /opt/netbox/uwsgi.ini \
--mode 644 --owner netbox \
--source "$__object/files/uwsgi.ini"
else
# absent config file
__file /opt/netbox/uwsgi.ini --state absent
fi
# handle the systemd socket
if [ "$SYSTEMD_SOCKET" = "yes" ]; then
TYPE="uWSGI"
export TYPE
# generate and set the socket unit
"$__type/files/netbox.socket.sh" "$__object/parameter/bind-to" \
> "$__object/files/netbox.socket"
__systemd_unit uwsgi-netbox.socket \
--state "$state" --enablement-state "$unit_state" \
--source "$__object/files/netbox.socket" --restart
else
# remove the systemd socket unit
__systemd_unit uwsgi-netbox.socket --state absent
fi
# install service file
"$__type/files/netbox.service.sh" > "$__object/files/netbox.service"
__systemd_unit uwsgi-netbox.service \
--state "$state" --enablement-state "$unit_state" \
--source "$__object/files/netbox.service" --restart

View file

@ -0,0 +1 @@
serve-static

View file

@ -0,0 +1 @@
127.0.0.1:3031

View file

@ -0,0 +1 @@
uwsgi

View file

@ -0,0 +1 @@
enabled

View file

@ -0,0 +1,2 @@
state
protocol

View file

@ -0,0 +1,5 @@
bind-to
uwsgi-bind
http-bind
fastcgi-bind
scgi-bind

View file