diff --git a/VERSION b/VERSION index ccbccc3..276cbf9 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -2.2.0 +2.3.0 diff --git a/build.sh b/build.sh index 169568e..f892e8b 100755 --- a/build.sh +++ b/build.sh @@ -6,96 +6,129 @@ echo "▶️ $0 $*" set -e if [ "${1}x" == "x" ] || [ "${1}" == "--help" ] || [ "${1}" == "-h" ]; then - echo "Usage: ${0} [--push]" - echo " branch The branch or tag to build. Required." - echo " --push Pushes the built Docker image to the registry." - echo "" - echo "You can use the following ENV variables to customize the build:" - echo " SRC_ORG Which fork of netbox to use (i.e. github.com/\${SRC_ORG}/\${SRC_REPO})." - echo " Default: netbox-community" - echo " SRC_REPO The name of the repository to use (i.e. github.com/\${SRC_ORG}/\${SRC_REPO})." - echo " Default: netbox" - echo " URL Where to fetch the code from." - echo " Must be a git repository. Can be private." - echo " Default: https://github.com/\${SRC_ORG}/\${SRC_REPO}.git" - echo " NETBOX_PATH The path where netbox will be checkout out." - echo " Must not be outside of the netbox-docker repository (because of Docker)!" - echo " Default: .netbox" - echo " SKIP_GIT If defined, git is not invoked and \${NETBOX_PATH} will not be altered." - echo " This may be useful, if you are manually managing the NETBOX_PATH." - echo " Default: undefined" - echo " TAG The version part of the docker tag." - echo " Default:" - echo " When =master: latest" - echo " When =develop: snapshot" - echo " Else: same as " - echo " IMAGE_NAMES The names used for the image including the registry" - echo " Used for tagging the image." - echo " Default: docker.io/netboxcommunity/netbox" - echo " Example: 'docker.io/netboxcommunity/netbox quay.io/netboxcommunity/netbox'" - echo " DOCKER_TAG The name of the tag which is applied to the image." - echo " Useful for pushing into another registry than hub.docker.com." - echo " Default: \${DOCKER_REGISTRY}/\${DOCKER_ORG}/\${DOCKER_REPO}:\${TAG}" - echo " DOCKER_SHORT_TAG The name of the short tag which is applied to the" - echo " image. This is used to tag all patch releases to their" - echo " containing version e.g. v2.5.1 -> v2.5" - echo " Default: \${DOCKER_REGISTRY}/\${DOCKER_ORG}/\${DOCKER_REPO}:." - echo " DOCKERFILE The name of Dockerfile to use." - echo " Default: Dockerfile" - echo " DOCKER_FROM The base image to use." - echo " Default: 'ubuntu:22.04'" - echo " BUILDX_PLATFORMS" - echo " Specifies the platform(s) to build the image for." - echo " Example: 'linux/amd64,linux/arm64'" - echo " Default: 'linux/amd64'" - echo " BUILDX_BUILDER_NAME" - echo " If defined, the image build will be assigned to the given builder." - echo " If you specify this variable, make sure that the builder exists." - echo " If this value is not defined, a new builx builder with the directory name of the" - echo " current directory (i.e. '$(basename "${PWD}")') is created." - echo " Example: 'clever_lovelace'" - echo " Default: undefined" - echo " BUILDX_REMOVE_BUILDER" - echo " If defined (and only if BUILDX_BUILDER_NAME is undefined)," - echo " then the buildx builder created by this script will be removed after use." - echo " This is useful if you build NetBox Docker on an automated system that does" - echo " not manage the builders for you." - echo " Example: 'on'" - echo " Default: undefined" - echo " HTTP_PROXY The proxy to use for http requests." - echo " Example: http://proxy.domain.tld:3128" - echo " Default: undefined" - echo " NO_PROXY Comma-separated list of domain extensions proxy should not be used for." - echo " Example: .domain1.tld,.domain2.tld" - echo " Default: undefined" - echo " DEBUG If defined, the script does not stop when certain checks are unsatisfied." - echo " Default: undefined" - echo " DRY_RUN Prints all build statements instead of running them." - echo " Default: undefined" - echo " GH_ACTION If defined, special 'echo' statements are enabled that set the" - echo " following environment variables in Github Actions:" - echo " - FINAL_DOCKER_TAG: The final value of the DOCKER_TAG env variable" - echo " Default: undefined" - echo "" - echo "Examples:" - echo " ${0} master" - echo " This will fetch the latest 'master' branch, build a Docker Image and tag it" - echo " 'netboxcommunity/netbox:latest'." - echo " ${0} develop" - echo " This will fetch the latest 'develop' branch, build a Docker Image and tag it" - echo " 'netboxcommunity/netbox:snapshot'." - echo " ${0} v2.6.6" - echo " This will fetch the 'v2.6.6' tag, build a Docker Image and tag it" - echo " 'netboxcommunity/netbox:v2.6.6' and 'netboxcommunity/netbox:v2.6'." - echo " ${0} develop-2.7" - echo " This will fetch the 'develop-2.7' branch, build a Docker Image and tag it" - echo " 'netboxcommunity/netbox:develop-2.7'." - echo " SRC_ORG=cimnine ${0} feature-x" - echo " This will fetch the 'feature-x' branch from https://github.com/cimnine/netbox.git," - echo " build a Docker Image and tag it 'netboxcommunity/netbox:feature-x'." - echo " SRC_ORG=cimnine DOCKER_ORG=cimnine ${0} feature-x" - echo " This will fetch the 'feature-x' branch from https://github.com/cimnine/netbox.git," - echo " build a Docker Image and tag it 'cimnine/netbox:feature-x'." + _BOLD=$(tput bold) + _GREEN=$(tput setaf 2) + _CYAN=$(tput setaf 6) + _CLEAR=$(tput sgr0) + + cat < [--push] + +branch The branch or tag to build. Required. +--push Pushes the built container image to the registry. + +${_BOLD}You can use the following ENV variables to customize the build:${_CLEAR} + +SRC_ORG Which fork of netbox to use (i.e. github.com/\${SRC_ORG}/\${SRC_REPO}). + ${_GREEN}Default:${_CLEAR} netbox-community + +SRC_REPO The name of the repository to use (i.e. github.com/\${SRC_ORG}/\${SRC_REPO}). + ${_GREEN}Default:${_CLEAR} netbox + +URL Where to fetch the code from. + Must be a git repository. Can be private. + ${_GREEN}Default:${_CLEAR} https://github.com/\${SRC_ORG}/\${SRC_REPO}.git + +NETBOX_PATH The path where netbox will be checkout out. + Must not be outside of the netbox-docker repository (because of Docker)! + ${_GREEN}Default:${_CLEAR} .netbox + +SKIP_GIT If defined, git is not invoked and \${NETBOX_PATH} will not be altered. + This may be useful, if you are manually managing the NETBOX_PATH. + ${_GREEN}Default:${_CLEAR} undefined + +TAG The version part of the image tag. + ${_GREEN}Default:${_CLEAR} + When =master: latest + When =develop: snapshot + Else: same as + +IMAGE_NAMES The names used for the image including the registry + Used for tagging the image. + ${_GREEN}Default:${_CLEAR} docker.io/netboxcommunity/netbox + ${_CYAN}Example:${_CLEAR} 'docker.io/netboxcommunity/netbox quay.io/netboxcommunity/netbox' + +DOCKER_TAG The name of the tag which is applied to the image. + Useful for pushing into another registry than hub.docker.com. + ${_GREEN}Default:${_CLEAR} \${DOCKER_REGISTRY}/\${DOCKER_ORG}/\${DOCKER_REPO}:\${TAG} + +DOCKER_SHORT_TAG The name of the short tag which is applied to the + image. This is used to tag all patch releases to their + containing version e.g. v2.5.1 -> v2.5 + ${_GREEN}Default:${_CLEAR} \${DOCKER_REGISTRY}/\${DOCKER_ORG}/\${DOCKER_REPO}:. + +DOCKERFILE The name of Dockerfile to use. + ${_GREEN}Default:${_CLEAR} Dockerfile + +DOCKER_FROM The base image to use. + ${_GREEN}Default:${_CLEAR} 'ubuntu:22.04' + +BUILDX_PLATFORMS + Specifies the platform(s) to build the image for. + ${_CYAN}Example:${_CLEAR} 'linux/amd64,linux/arm64' + ${_GREEN}Default:${_CLEAR} 'linux/amd64' + +BUILDX_BUILDER_NAME + If defined, the image build will be assigned to the given builder. + If you specify this variable, make sure that the builder exists. + If this value is not defined, a new builx builder with the directory name of the + current directory (i.e. '$(basename "${PWD}")') is created." + ${_CYAN}Example:${_CLEAR} 'clever_lovelace' + ${_GREEN}Default:${_CLEAR} undefined + +BUILDX_REMOVE_BUILDER + If defined (and only if BUILDX_BUILDER_NAME is undefined), + then the buildx builder created by this script will be removed after use. + This is useful if you build NetBox Docker on an automated system that does + not manage the builders for you. + ${_CYAN}Example:${_CLEAR} 'on' + ${_GREEN}Default:${_CLEAR} undefined + +HTTP_PROXY The proxy to use for http requests. + ${_CYAN}Example:${_CLEAR} http://proxy.domain.tld:3128 + ${_GREEN}Default:${_CLEAR} undefined + +NO_PROXY Comma-separated list of domain extensions proxy should not be used for. + ${_CYAN}Example:${_CLEAR} .domain1.tld,.domain2.tld + ${_GREEN}Default:${_CLEAR} undefined + +DEBUG If defined, the script does not stop when certain checks are unsatisfied. + ${_GREEN}Default:${_CLEAR} undefined + +DRY_RUN Prints all build statements instead of running them. + ${_GREEN}Default:${_CLEAR} undefined + +GH_ACTION If defined, special 'echo' statements are enabled that set the + following environment variables in Github Actions: + - FINAL_DOCKER_TAG: The final value of the DOCKER_TAG env variable + ${_GREEN}Default:${_CLEAR} undefined + +${_BOLD}Examples:${_CLEAR} + +${0} master + This will fetch the latest 'master' branch, build a Docker Image and tag it + 'netboxcommunity/netbox:latest'. + +${0} develop + This will fetch the latest 'develop' branch, build a Docker Image and tag it + 'netboxcommunity/netbox:snapshot'. + +${0} v2.6.6 + This will fetch the 'v2.6.6' tag, build a Docker Image and tag it + 'netboxcommunity/netbox:v2.6.6' and 'netboxcommunity/netbox:v2.6'. + +${0} develop-2.7 + This will fetch the 'develop-2.7' branch, build a Docker Image and tag it + 'netboxcommunity/netbox:develop-2.7'. + +SRC_ORG=cimnine ${0} feature-x + This will fetch the 'feature-x' branch from https://github.com/cimnine/netbox.git, + build a Docker Image and tag it 'netboxcommunity/netbox:feature-x'. + +SRC_ORG=cimnine DOCKER_ORG=cimnine ${0} feature-x + This will fetch the 'feature-x' branch from https://github.com/cimnine/netbox.git, + build a Docker Image and tag it 'cimnine/netbox:feature-x'. +END_OF_HELP if [ "${1}x" == "x" ]; then exit 1 diff --git a/configuration/configuration.py b/configuration/configuration.py index c8ddd14..8d19fd9 100644 --- a/configuration/configuration.py +++ b/configuration/configuration.py @@ -7,12 +7,17 @@ import re from os import environ from os.path import abspath, dirname, join +from typing import Any, Callable, Tuple -# For reference see https://netbox.readthedocs.io/en/stable/configuration/ -# Based on https://github.com/netbox-community/netbox/blob/master/netbox/netbox/configuration.example.py +# For reference see https://docs.netbox.dev/en/stable/configuration/ +# Based on https://github.com/netbox-community/netbox/blob/develop/netbox/netbox/configuration_example.py + +### +# NetBox-Docker Helper functions +### # Read secret from file -def _read_secret(secret_name, default = None): +def _read_secret(secret_name: str, default: str | None = None) -> str | None: try: f = open('/run/secrets/' + secret_name, 'r', encoding='utf-8') except EnvironmentError: @@ -21,6 +26,25 @@ def _read_secret(secret_name, default = None): with f: return f.readline().strip() +# If the `map_fn` isn't defined, then the value that is read from the environment (or the default value if not found) is returned. +# If the `map_fn` is defined, then `map_fn` is invoked and the value (that was read from the environment or the default value if not found) +# is passed to it as a parameter. The value returned from `map_fn` is then the return value of this function. +# The `map_fn` is not invoked, if the value (that was read from the environment or the default value if not found) is None. +def _environ_get_and_map(variable_name: str, default: str | None = None, map_fn: Callable[[str], Any | None] = None) -> Any | None: + env_value = environ.get(variable_name, default) + + if env_value == None: + return env_value + + if not map_fn: + return env_value + + return map_fn(env_value) + +_AS_BOOL = lambda value : value.lower() == 'true' +_AS_INT = lambda value : int(value) +_AS_LIST = lambda value : list(filter(None, value.split(' '))) + _BASE_DIR = dirname(dirname(abspath(__file__))) ######################### @@ -46,9 +70,9 @@ DATABASE = { 'PORT': environ.get('DB_PORT', ''), # Database port (leave blank for default) 'OPTIONS': {'sslmode': environ.get('DB_SSLMODE', 'prefer')}, # Database connection SSLMODE - 'CONN_MAX_AGE': int(environ.get('DB_CONN_MAX_AGE', '300')), + 'CONN_MAX_AGE': _environ_get_and_map('DB_CONN_MAX_AGE', '300', _AS_INT), # Max database connection age - 'DISABLE_SERVER_SIDE_CURSORS': environ.get('DB_DISABLE_SERVER_SIDE_CURSORS', 'False').lower() == 'true', + 'DISABLE_SERVER_SIDE_CURSORS': _environ_get_and_map('DB_DISABLE_SERVER_SIDE_CURSORS', 'False', _AS_BOOL), # Disable the use of server-side cursors transaction pooling } @@ -58,19 +82,19 @@ DATABASE = { REDIS = { 'tasks': { 'HOST': environ.get('REDIS_HOST', 'localhost'), - 'PORT': int(environ.get('REDIS_PORT', 6379)), + 'PORT': _environ_get_and_map('REDIS_PORT', 6379, _AS_INT), 'PASSWORD': _read_secret('redis_password', environ.get('REDIS_PASSWORD', '')), - 'DATABASE': int(environ.get('REDIS_DATABASE', 0)), - 'SSL': environ.get('REDIS_SSL', 'False').lower() == 'true', - 'INSECURE_SKIP_TLS_VERIFY': environ.get('REDIS_INSECURE_SKIP_TLS_VERIFY', 'False').lower() == 'true', + 'DATABASE': _environ_get_and_map('REDIS_DATABASE', 0, _AS_INT), + 'SSL': _environ_get_and_map('REDIS_SSL', 'False', _AS_BOOL), + 'INSECURE_SKIP_TLS_VERIFY': _environ_get_and_map('REDIS_INSECURE_SKIP_TLS_VERIFY', 'False', _AS_BOOL), }, 'caching': { 'HOST': environ.get('REDIS_CACHE_HOST', environ.get('REDIS_HOST', 'localhost')), - 'PORT': int(environ.get('REDIS_CACHE_PORT', environ.get('REDIS_PORT', 6379))), + 'PORT': _environ_get_and_map('REDIS_CACHE_PORT', environ.get('REDIS_PORT', '6379'), _AS_INT), 'PASSWORD': _read_secret('redis_cache_password', environ.get('REDIS_CACHE_PASSWORD', environ.get('REDIS_PASSWORD', ''))), - 'DATABASE': int(environ.get('REDIS_CACHE_DATABASE', 1)), - 'SSL': environ.get('REDIS_CACHE_SSL', environ.get('REDIS_SSL', 'False')).lower() == 'true', - 'INSECURE_SKIP_TLS_VERIFY': environ.get('REDIS_CACHE_INSECURE_SKIP_TLS_VERIFY', environ.get('REDIS_INSECURE_SKIP_TLS_VERIFY', 'False')).lower() == 'true', + 'DATABASE': _environ_get_and_map('REDIS_CACHE_DATABASE', '1', _AS_INT), + 'SSL': _environ_get_and_map('REDIS_CACHE_SSL', environ.get('REDIS_SSL', 'False'), _AS_BOOL), + 'INSECURE_SKIP_TLS_VERIFY': _environ_get_and_map('REDIS_CACHE_INSECURE_SKIP_TLS_VERIFY', environ.get('REDIS_INSECURE_SKIP_TLS_VERIFY', 'False'), _AS_BOOL), }, } @@ -87,161 +111,217 @@ SECRET_KEY = _read_secret('secret_key', environ.get('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'], -] +# # 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', -) +if 'ALLOWED_URL_SCHEMES' in environ: + ALLOWED_URL_SCHEMES = _environ_get_and_map('ALLOWED_URL_SCHEMES', None, _AS_LIST) # 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 = environ.get('BANNER_TOP', '') -BANNER_BOTTOM = environ.get('BANNER_BOTTOM', '') +if 'BANNER_TOP' in environ: + BANNER_TOP = environ.get('BANNER_TOP', None) +if 'BANNER_BOTTOM' in environ: + BANNER_BOTTOM = environ.get('BANNER_BOTTOM', None) # Text to include on the login page above the login form. HTML is allowed. -BANNER_LOGIN = environ.get('BANNER_LOGIN', '') +if 'BANNER_LOGIN' in environ: + BANNER_LOGIN = environ.get('BANNER_LOGIN', None) # Base URL path if accessing NetBox within a directory. For example, if installed at http://example.com/netbox/, set: # BASE_PATH = 'netbox/' BASE_PATH = environ.get('BASE_PATH', '') # Maximum number of days to retain logged changes. Set to 0 to retain changes indefinitely. (Default: 90) -CHANGELOG_RETENTION = int(environ.get('CHANGELOG_RETENTION', 90)) +if 'CHANGELOG_RETENTION' in environ: + CHANGELOG_RETENTION = _environ_get_and_map('CHANGELOG_RETENTION', None, _AS_INT) + +# Maximum number of days to retain job results (scripts and reports). Set to 0 to retain job results in the database indefinitely. (Default: 90) +if 'JOBRESULT_RETENTION' in environ: + JOBRESULT_RETENTION = _environ_get_and_map('JOBRESULT_RETENTION', None, _AS_INT) # 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 = environ.get('CORS_ORIGIN_ALLOW_ALL', 'False').lower() == 'true' -CORS_ORIGIN_WHITELIST = list(filter(None, environ.get('CORS_ORIGIN_WHITELIST', 'https://localhost').split(' '))) -CORS_ORIGIN_REGEX_WHITELIST = [re.compile(r) for r in list(filter(None, environ.get('CORS_ORIGIN_REGEX_WHITELIST', '').split(' ')))] - -# Cross-Site-Request-Forgery-Attack settings. If Netbox is sitting behind a reverse proxy, you might need to set the CSRF_TRUSTED_ORIGINS flag. -# Django 4.0 requires to specify the URL Scheme in this setting. An example environment variable could be specified like: -# CSRF_TRUSTED_ORIGINS=https://demo.netbox.dev http://demo.netbox.dev -CSRF_TRUSTED_ORIGINS = list(filter(None, environ.get('CSRF_TRUSTED_ORIGINS', '').split(' '))) +CORS_ORIGIN_ALLOW_ALL = _environ_get_and_map('CORS_ORIGIN_ALLOW_ALL', 'False', _AS_BOOL) +CORS_ORIGIN_WHITELIST = _environ_get_and_map('CORS_ORIGIN_WHITELIST', 'https://localhost', _AS_LIST) +CORS_ORIGIN_REGEX_WHITELIST = [re.compile(r) for r in _environ_get_and_map('CORS_ORIGIN_REGEX_WHITELIST', '', _AS_LIST)] # 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 = environ.get('DEBUG', 'False').lower() == 'true' +# sensitive information about your installation. Only enable debugging while performing testing. +# Never enable debugging on a production system. +DEBUG = _environ_get_and_map('DEBUG', 'False', _AS_BOOL) + +# This parameter serves as a safeguard to prevent some potentially dangerous behavior, +# such as generating new database schema migrations. +# Set this to True only if you are actively developing the NetBox code base. +DEVELOPER = _environ_get_and_map('DEVELOPER', 'False', _AS_BOOL) # Email settings EMAIL = { 'SERVER': environ.get('EMAIL_SERVER', 'localhost'), - 'PORT': int(environ.get('EMAIL_PORT', 25)), + 'PORT': _environ_get_and_map('EMAIL_PORT', 25, _AS_INT), 'USERNAME': environ.get('EMAIL_USERNAME', ''), 'PASSWORD': _read_secret('email_password', environ.get('EMAIL_PASSWORD', '')), - 'USE_SSL': environ.get('EMAIL_USE_SSL', 'False').lower() == 'true', - 'USE_TLS': environ.get('EMAIL_USE_TLS', 'False').lower() == 'true', + 'USE_SSL': _environ_get_and_map('EMAIL_USE_SSL', 'False', _AS_BOOL), + 'USE_TLS': _environ_get_and_map('EMAIL_USE_TLS', 'False', _AS_BOOL), 'SSL_CERTFILE': environ.get('EMAIL_SSL_CERTFILE', ''), 'SSL_KEYFILE': environ.get('EMAIL_SSL_KEYFILE', ''), - 'TIMEOUT': int(environ.get('EMAIL_TIMEOUT', 10)), # seconds + 'TIMEOUT': _environ_get_and_map('EMAIL_TIMEOUT', 10, _AS_INT), # seconds 'FROM_EMAIL': environ.get('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 = environ.get('ENFORCE_GLOBAL_UNIQUE', 'False').lower() == 'true' +if 'ENFORCE_GLOBAL_UNIQUE' in environ: + ENFORCE_GLOBAL_UNIQUE = _environ_get_and_map('ENFORCE_GLOBAL_UNIQUE', None, _AS_BOOL) # 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 = list(filter(None, environ.get('EXEMPT_VIEW_PERMISSIONS', '').split(' '))) +EXEMPT_VIEW_PERMISSIONS = _environ_get_and_map('EXEMPT_VIEW_PERMISSIONS', '', _AS_LIST) + +# 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 = _environ_get_and_map('INTERNAL_IPS', '127.0.0.1 ::1', _AS_LIST) # Enable GraphQL API. -GRAPHQL_ENABLED = environ.get('GRAPHQL_ENABLED', 'True').lower() == 'true' +if 'GRAPHQL_ENABLED' in environ: + GRAPHQL_ENABLED = _environ_get_and_map('GRAPHQL_ENABLED', None, _AS_BOOL) -# Enable custom logging. Please see the Django documentation for detailed guidance on configuring custom logs: -# https://docs.djangoproject.com/en/stable/topics/logging/ -LOGGING = {} +# # Enable custom logging. Please see the Django documentation for detailed guidance on configuring custom logs: +# # https://docs.djangoproject.com/en/stable/topics/logging/ +# LOGGING = {} + +# Automatically reset the lifetime of a valid session upon each authenticated request. Enables users to remain +# authenticated to NetBox indefinitely. +LOGIN_PERSISTENCE = _environ_get_and_map('LOGIN_PERSISTENCE', 'False', _AS_BOOL) # 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 = environ.get('LOGIN_REQUIRED', 'False').lower() == 'true' +LOGIN_REQUIRED = _environ_get_and_map('LOGIN_REQUIRED', 'False', _AS_BOOL) # 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 = int(environ.get('LOGIN_TIMEOUT', 1209600)) +LOGIN_TIMEOUT = _environ_get_and_map('LOGIN_TIMEOUT', 1209600, _AS_INT) # Setting this to True will display a "maintenance mode" banner at the top of every page. -MAINTENANCE_MODE = environ.get('MAINTENANCE_MODE', 'False').lower() == 'true' +if 'MAINTENANCE_MODE' in environ: + MAINTENANCE_MODE = _environ_get_and_map('MAINTENANCE_MODE', None, _AS_BOOL) # Maps provider -MAPS_URL = environ.get('MAPS_URL', None) +if 'MAPS_URL' in environ: + MAPS_URL = environ.get('MAPS_URL', None) # 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 = int(environ.get('MAX_PAGE_SIZE', 1000)) +if 'MAX_PAGE_SIZE' in environ: + MAX_PAGE_SIZE = _environ_get_and_map('MAX_PAGE_SIZE', None, _AS_INT) # 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 = environ.get('MEDIA_ROOT', join(_BASE_DIR, 'media')) # Expose Prometheus monitoring metrics at the HTTP endpoint '/metrics' -METRICS_ENABLED = environ.get('METRICS_ENABLED', 'False').lower() == 'true' +METRICS_ENABLED = _environ_get_and_map('METRICS_ENABLED', 'False', _AS_BOOL) # Credentials that NetBox will uses to authenticate to devices when connecting via NAPALM. -NAPALM_USERNAME = environ.get('NAPALM_USERNAME', '') -NAPALM_PASSWORD = _read_secret('napalm_password', environ.get('NAPALM_PASSWORD', '')) +if 'NAPALM_USERNAME' in environ: + NAPALM_USERNAME = environ.get('NAPALM_USERNAME', None) +if 'NAPALM_PASSWORD' in environ: + NAPALM_PASSWORD = _read_secret('napalm_password', environ.get('NAPALM_PASSWORD', None)) # NAPALM timeout (in seconds). (Default: 30) -NAPALM_TIMEOUT = int(environ.get('NAPALM_TIMEOUT', 30)) +if 'NAPALM_TIMEOUT' in environ: + NAPALM_TIMEOUT = _environ_get_and_map('NAPALM_TIMEOUT', None, _AS_INT) -# NAPALM optional arguments (see http://napalm.readthedocs.io/en/latest/support/#optional-arguments). Arguments must -# be provided as a dictionary. -NAPALM_ARGS = {} +# # NAPALM optional arguments (see http://napalm.readthedocs.io/en/latest/support/#optional-arguments). Arguments must +# # be provided as a dictionary. +# NAPALM_ARGS = None # Determine how many objects to display per page within a list. (Default: 50) -PAGINATE_COUNT = int(environ.get('PAGINATE_COUNT', 50)) +if 'PAGINATE_COUNT' in environ: + PAGINATE_COUNT = _environ_get_and_map('PAGINATE_COUNT', None, _AS_INT) -# Enable installed plugins. Add the name of each plugin to the list. -PLUGINS = [] +# # 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 = { -} +# # 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 = { +# } # 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 = environ.get('PREFER_IPV4', 'False').lower() == 'true' +if 'PREFER_IPV4' in environ: + PREFER_IPV4 = _environ_get_and_map('PREFER_IPV4', None, _AS_BOOL) + +# The default value for the amperage field when creating new power feeds. +if 'POWERFEED_DEFAULT_AMPERAGE' in environ: + POWERFEED_DEFAULT_AMPERAGE = _environ_get_and_map('POWERFEED_DEFAULT_AMPERAGE', None, _AS_INT) + +# The default value (percentage) for the max_utilization field when creating new power feeds. +if 'POWERFEED_DEFAULT_MAX_UTILIZATION' in environ: + POWERFEED_DEFAULT_MAX_UTILIZATION = _environ_get_and_map('POWERFEED_DEFAULT_MAX_UTILIZATION', None, _AS_INT) + +# The default value for the voltage field when creating new power feeds. +if 'POWERFEED_DEFAULT_VOLTAGE' in environ: + POWERFEED_DEFAULT_VOLTAGE = _environ_get_and_map('POWERFEED_DEFAULT_VOLTAGE', None, _AS_INT) # 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 = int(environ.get('RACK_ELEVATION_DEFAULT_UNIT_HEIGHT', 22)) -RACK_ELEVATION_DEFAULT_UNIT_WIDTH = int(environ.get('RACK_ELEVATION_DEFAULT_UNIT_WIDTH', 220)) +if 'RACK_ELEVATION_DEFAULT_UNIT_HEIGHT' in environ: + RACK_ELEVATION_DEFAULT_UNIT_HEIGHT = _environ_get_and_map('RACK_ELEVATION_DEFAULT_UNIT_HEIGHT', None, _AS_INT) +if 'RACK_ELEVATION_DEFAULT_UNIT_WIDTH' in environ: + RACK_ELEVATION_DEFAULT_UNIT_WIDTH = _environ_get_and_map('RACK_ELEVATION_DEFAULT_UNIT_WIDTH', None, _AS_INT) # Remote authentication support -REMOTE_AUTH_ENABLED = environ.get('REMOTE_AUTH_ENABLED', 'False').lower() == 'true' +REMOTE_AUTH_ENABLED = _environ_get_and_map('REMOTE_AUTH_ENABLED', 'False', _AS_BOOL) REMOTE_AUTH_BACKEND = environ.get('REMOTE_AUTH_BACKEND', 'netbox.authentication.RemoteUserBackend') REMOTE_AUTH_HEADER = environ.get('REMOTE_AUTH_HEADER', 'HTTP_REMOTE_USER') -REMOTE_AUTH_AUTO_CREATE_USER = environ.get('REMOTE_AUTH_AUTO_CREATE_USER', 'True').lower() == 'true' -REMOTE_AUTH_DEFAULT_GROUPS = list(filter(None, environ.get('REMOTE_AUTH_DEFAULT_GROUPS', '').split(' '))) +REMOTE_AUTH_AUTO_CREATE_USER = _environ_get_and_map('REMOTE_AUTH_AUTO_CREATE_USER', 'True', _AS_BOOL) +REMOTE_AUTH_DEFAULT_GROUPS = _environ_get_and_map('REMOTE_AUTH_DEFAULT_GROUPS', '', _AS_LIST) +# REMOTE_AUTH_DEFAULT_PERMISSIONS = {} # 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. -# https://api.github.com/repos/netbox-community/netbox/releases RELEASE_CHECK_URL = environ.get('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 = environ.get('REPORTS_ROOT', '/etc/netbox/reports') # Maximum execution time for background tasks, in seconds. -RQ_DEFAULT_TIMEOUT = int(environ.get('RQ_DEFAULT_TIMEOUT', 300)) +RQ_DEFAULT_TIMEOUT = _environ_get_and_map('RQ_DEFAULT_TIMEOUT', 300, _AS_INT) # 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 = environ.get('SCRIPTS_ROOT', '/etc/netbox/scripts') +# The name to use for the csrf token cookie. +CSRF_COOKIE_NAME = environ.get('CSRF_COOKIE_NAME', 'csrftoken') + +# Cross-Site-Request-Forgery-Attack settings. If Netbox is sitting behind a reverse proxy, you might need to set the CSRF_TRUSTED_ORIGINS flag. +# Django 4.0 requires to specify the URL Scheme in this setting. An example environment variable could be specified like: +# CSRF_TRUSTED_ORIGINS=https://demo.netbox.dev http://demo.netbox.dev +CSRF_TRUSTED_ORIGINS = _environ_get_and_map('CSRF_TRUSTED_ORIGINS', '', _AS_LIST) + +# The name to use for the session cookie. +SESSION_COOKIE_NAME = environ.get('SESSION_COOKIE_NAME', 'sessionid') + # 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 = environ.get('SESSIONS_ROOT', None) +SESSION_FILE_PATH = environ.get('SESSION_FILE_PATH', environ.get('SESSIONS_ROOT', None)) # Time zone (default: UTC) TIME_ZONE = environ.get('TIME_ZONE', 'UTC') diff --git a/configuration/ldap/ldap_config.py b/configuration/ldap/ldap_config.py index 3071b45..7fb62d7 100644 --- a/configuration/ldap/ldap_config.py +++ b/configuration/ldap/ldap_config.py @@ -31,9 +31,12 @@ AUTH_LDAP_CONNECTION_OPTIONS = { ldap.OPT_REFERRALS: 0 } -# Set the DN and password for the NetBox service account. -AUTH_LDAP_BIND_DN = environ.get('AUTH_LDAP_BIND_DN', '') -AUTH_LDAP_BIND_PASSWORD = _read_secret('auth_ldap_bind_password', environ.get('AUTH_LDAP_BIND_PASSWORD', '')) +AUTH_LDAP_BIND_AS_AUTHENTICATING_USER = environ.get('AUTH_LDAP_BIND_AS_AUTHENTICATING_USER', 'False').lower() == 'true' + +# Set the DN and password for the NetBox service account if needed. +if not AUTH_LDAP_BIND_AS_AUTHENTICATING_USER: + AUTH_LDAP_BIND_DN = environ.get('AUTH_LDAP_BIND_DN', '') + AUTH_LDAP_BIND_PASSWORD = _read_secret('auth_ldap_bind_password', environ.get('AUTH_LDAP_BIND_PASSWORD', '')) # Set a string template that describes any user’s distinguished name based on the username. AUTH_LDAP_USER_DN_TEMPLATE = environ.get('AUTH_LDAP_USER_DN_TEMPLATE', None) diff --git a/docker-compose.yml b/docker-compose.yml index 029d87e..3d50c77 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,7 +1,7 @@ version: '3.4' services: netbox: &netbox - image: netboxcommunity/netbox:${VERSION-v3.3-2.2.0} + image: netboxcommunity/netbox:${VERSION-v3.3-2.3.0} depends_on: - postgres - redis @@ -55,6 +55,8 @@ services: - -c # this is to evaluate the $REDIS_PASSWORD from the env - redis-server --requirepass $$REDIS_PASSWORD ## $$ because of docker-compose env_file: env/redis-cache.env + volumes: + - netbox-redis-cache-data:/data volumes: netbox-media-files: @@ -63,3 +65,5 @@ volumes: driver: local netbox-redis-data: driver: local + netbox-redis-cache-data: + driver: local diff --git a/env/netbox.env b/env/netbox.env index b2f647e..a4a9d4a 100644 --- a/env/netbox.env +++ b/env/netbox.env @@ -16,12 +16,8 @@ EMAIL_USE_SSL=false EMAIL_USE_TLS=false GRAPHQL_ENABLED=true HOUSEKEEPING_INTERVAL=86400 -MAX_PAGE_SIZE=1000 MEDIA_ROOT=/opt/netbox/netbox/media METRICS_ENABLED=false -NAPALM_PASSWORD= -NAPALM_TIMEOUT=10 -NAPALM_USERNAME= REDIS_CACHE_DATABASE=1 REDIS_CACHE_HOST=redis-cache REDIS_CACHE_INSECURE_SKIP_TLS_VERIFY=false diff --git a/requirements-container.txt b/requirements-container.txt index 7aec758..f8b720e 100644 --- a/requirements-container.txt +++ b/requirements-container.txt @@ -1,5 +1,5 @@ django-auth-ldap==4.1.0 django-storages[azure,boto3,dropbox,google,libcloud,sftp]==1.13.1 napalm==4.0.0 -psycopg2==2.9.3 +psycopg2==2.9.4 social-auth-core[openidconnect]==4.3.0