Skip to content

Installation with Docker

Mathieu Rampant edited this page Aug 7, 2020 · 42 revisions

Installing and running NEMO with Docker is a quick and easy way to run a production version of NEMO. The benefit of using Docker is that you can skip installing the Python interpreter, package dependencies, and setting environment variables. These things are all included with the image. The NEMO Docker image is hosted on Docker Hub, and you can download it using this command:

docker pull nanofab/nemo

NEMO requires certain runtime information to be available before running the container.

Below is a template for NEMO settings that would be suitable for production. The settings must be customized appropriately for your organization. This is the single most important file for NEMO to run properly, and you should take your time to ensure it is correct. Furthermore, it's probably the most likely place where things can go wrong when configured improperly. So grab a coffee, take your time, and be thorough when crafting this file for your organization. In order to make things easier, several methods are described below to test your configuration and ensure it's working properly.

The settings reference particular locations on disk that must exist, and external services that must be available for NEMO to work properly. A single, consolidated directory that contains all NEMO runtime information is recommended. Here is the suggested directory structure and contents:

nemo/
|
|--- logs/                        # Optional: store all log files. (Recommended approach: don't store logs locally... instead, send them to a central logging server via syslog so your disk never overflows)
|--- media/                       # Images and files uploaded to NEMO are stored here
|--- secrets/                     # Contains all passwords, certificates, and keys that NEMO uses
|    |--- nemo.example.org.key    # Private TLS key used for encryption
|    |--- nemo.example.org.crt    # Public TLS certificate, signed by a certificate authority
|    |--- Other certificates      # Other certificates, such as public TLS certs for email or LDAPS authentication
|--- static/                      # JavaScript, images, and CSS
|--- settings.py                  # NEMO settings file
|--- sqlite.db                    # SQLite database - this is automatically created by NEMO (see deployment instructions)

settings.py template for production deployment of NEMO

# -------------------- Django settings for NEMO --------------------
# Customize these to suit your needs. Documentation can be found at:
# https://docs.djangoproject.com/en/1.11/ref/settings/

# Core settings
# DANGER: SETTING "DEBUG = True" ON A PRODUCTION SYSTEM IS EXTREMELY DANGEROUS.
# ONLY SET "DEBUG = True" FOR DEVELOPMENT AND TESTING!!!
DEBUG = False
AUTH_USER_MODEL = 'NEMO.User'
WSGI_APPLICATION = 'NEMO.wsgi.application'
ROOT_URLCONF = 'NEMO.urls'

# Information security
SESSION_COOKIE_AGE = 2419200  # 2419200 seconds == 4 weeks
SESSION_EXPIRE_AT_BROWSER_CLOSE = True
SESSION_COOKIE_SECURE = True
CSRF_COOKIE_SECURE = True
CSRF_COOKIE_AGE = None
CSRF_USE_SESSIONS = False
X_FRAME_OPTIONS = 'DENY'
SECURE_BROWSER_XSS_FILTER = True
SECURE_CONTENT_TYPE_NOSNIFF = True
SECURE_HSTS_INCLUDE_SUBDOMAINS = True
SECURE_HSTS_SECONDS = 15768000
SECURE_SSL_REDIRECT = True

# Authentication
LOGIN_URL = 'login'
LOGIN_REDIRECT_URL = 'login'

# Date and time formats
DATETIME_FORMAT = "l, F jS, Y @ g:i A"
DATE_FORMAT = "m/d/Y"
TIME_FORMAT = "g:i A"
DATETIME_INPUT_FORMATS = ['%m/%d/%Y %I:%M %p']
DATE_INPUT_FORMATS = ['%m/%d/%Y']
TIME_INPUT_FORMATS = ['%I:%M %p']

USE_I18N = False
USE_L10N = False
USE_TZ = True

INSTALLED_APPS = [
	'django.contrib.auth',
	'django.contrib.contenttypes',
	'django.contrib.sessions',
	'django.contrib.messages',
	'django.contrib.staticfiles',
	'django.contrib.admin',
	'django.contrib.humanize',
	'NEMO',
	'rest_framework',
	'django_filters',
]

MIDDLEWARE = [
	'django.middleware.security.SecurityMiddleware',
	'django.middleware.common.CommonMiddleware',
	'django.contrib.sessions.middleware.SessionMiddleware',
	'django.middleware.csrf.CsrfViewMiddleware',
	'django.contrib.auth.middleware.AuthenticationMiddleware',
	'django.contrib.messages.middleware.MessageMiddleware',
	'django.middleware.clickjacking.XFrameOptionsMiddleware',
	'django.middleware.common.BrokenLinkEmailsMiddleware',
	'NEMO.middleware.DeviceDetectionMiddleware',
	#'NEMO.middleware.HTTPHeaderAuthenticationMiddleware', needed for Nginx Authentication
]

TEMPLATES = [
	{
		'BACKEND': 'django.template.backends.django.DjangoTemplates',
		'APP_DIRS': True,
		'OPTIONS': {
			'context_processors': [
				'NEMO.context_processors.hide_logout_button',  # Add a 'request context processor' in order to figure out whether to display the logout button. If the site is configured to use the LDAP authentication backend then we want to provide a logoff button (in the menu bar). Otherwise the Kerberos authentication backend is used and no logoff button is necessary.
				'NEMO.context_processors.base_context',
				'django.contrib.auth.context_processors.auth',
				'django.template.context_processors.debug',
				'django.template.context_processors.media',
				'django.template.context_processors.static',
				'django.template.context_processors.tz',
				'django.template.context_processors.request',
				'django.contrib.messages.context_processors.messages',
			],
		},
	},
]

# -------------------- Third party Django addons for NEMO --------------------
# These are third party capabilities that NEMO employs. They are documented on
# the respective project sites. Only customize these if you know what you're doing.

# Django REST framework:
# http://www.django-rest-framework.org/
REST_FRAMEWORK = {
	'DEFAULT_PERMISSION_CLASSES': ('NEMO.permissions.BillingAPI',),
	'DEFAULT_FILTER_BACKENDS': ('django_filters.rest_framework.DjangoFilterBackend',),
	'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',
	'PAGE_SIZE': 1000,
}


# ------------ Organization specific settings (officially supported by Django) ------------
# Customize these to suit your needs. Documentation can be found at:
# https://docs.djangoproject.com/en/1.11/ref/settings/


ALLOWED_HOSTS = [
	'nemo.example.org',
]

SERVER_EMAIL = 'NEMO Server Administrator <[email protected]>'

ADMINS = [
	('System administrator', '[email protected]'),
]
MANAGERS = ADMINS

EMAIL_HOST = 'mail.example.org'
EMAIL_PORT = 25

TIME_ZONE = 'America/New_York'

DATABASES = {
	'default': {
		'ENGINE': 'django.db.backends.sqlite3',
		'NAME': 'sqlite.db',
	}
}

STATIC_ROOT = '/nemo/static/'
STATIC_URL = '/static/'
MEDIA_ROOT = '/nemo/media/'
MEDIA_URL = '/media/'

# Make this unique, and don't share it with anybody.
SECRET_KEY = ''  # Generate this for yourself. You can use `nemo generate_secret_key` to help

LOGGING = {
	'version': 1,
	'disable_existing_loggers': False,
	'handlers': {
		'mail_admins': {
			'level': 'INFO',
			'class': 'django.utils.log.AdminEmailHandler'
		},
		'error_file': {
			'level': 'WARNING',
			'class': 'logging.FileHandler',
			'filename': '/nemo/logs/django_error.log'
		},
		'security_file': {
			'level': 'INFO',
			'class': 'logging.FileHandler',
			'filename': '/nemo/logs/django_security.log'
		},
	},
	'loggers': {
		'django.request': {
			'handlers': ['mail_admins', 'error_file'],
			'level': 'WARNING',
			'propagate': True,
		},
		'django.security': {
			'handlers': ['mail_admins', 'security_file'],
			'level': 'WARNING',
			'propagate': True,
		},
	}
}


# ------------ Organization specific settings (NEMO specific; NOT supported by Django) ------------
# Customize these to suit your needs

# When true, all available URLs and NEMO functionality is enabled.
# When false, conditional URLs are removed to reduce the attack surface of NEMO.
# Reduced functionality for NEMO is desirable for the public facing version
# of the site in order to mitigate security risks.
ALLOW_CONDITIONAL_URLS = True

# When true, interlock function will be enabled and request will be made to lock/unlock interlocks.
# When false, the feature will be disabled
INTERLOCKS_ENABLED = True

# There are two options to authenticate users:
#   1) A decoupled "REMOTE_USER" method (such as Kerberos authentication from a reverse proxy)
#   2) LDAP authentication from NEMO itself
AUTHENTICATION_BACKENDS = ['NEMO.views.authentication.LDAPAuthenticationBackend']

# Specify your list of LDAP authentication servers only if you choose to use LDAP authentication
LDAP_SERVERS = [
	{
		'url': 'ldap.another.org',
		'domain': 'ANOTHER',
		'certificate': '/nemo/secrets/root.crt',
	},
	{
		'url': 'ldap.example.org',
		'domain': 'EXAMPLE',
		'certificate': '/nemo/secrets/root.crt',
	},
]

# NEMO can integrate with a custom Identity Service to manage user accounts on
# related computer systems, which streamlines user onboarding and offboarding.
IDENTITY_SERVICE = {
	'available': False,
	'url': 'https://identity.example.org/',
	'domains': ['EXAMPLE', 'ANOTHER'],
}

Collect static files

NEMO comes with static files, such as JavaScript and CSS, to improve the look and feel of the website. There are also static files included with the Django web framework, used in the /admin/ section of the site. These static files are dispersed throughout the source tree, and can be collected into a single place by specifying STATIC_ROOT in settings.py. You will need to specify a location that is exposed as a Docker volume, so that the files can be served by a reverse proxy and static content server. The recommended default location is STATIC_ROOT = '/nemo/static/'. Then collect the files using this command:

docker run --volume /home/user/nemo:/nemo nanofab/nemo django-admin collectstatic

Create the database

Now that NEMO has been installed and configured, you'll need to create the database. An SQLite database will be suitable for most organizations. If your organization has hundreds of concurrent NEMO users then consider using PostgreSQL. The procedure to construct the database is the same for SQLite and Postgres:

docker run --volume /home/user/nemo:/nemo nanofab/nemo bash -c "django-admin makemigrations NEMO && django-admin migrate"

If you are using SQLite, the database file should now exist. (Recall that the location of the database file is specified in settings.py.)

Create a super user

You will need to log in to NEMO in order to access and manage it. Create a "super user" with this command:

docker run --interactive --tty --volume /home/user/nemo:/nemo nanofab/nemo django-admin createsuperuser

You will be prompted for a username, first name, last name, email address, and password. Enter the appropriate information. Note, that even though you enter a password, NEMO is designed to not store passwords in the database, therefore the password you enter is discarded. It will not work when you try to log in. NEMO relies exclusively on external authentication sources (such as LDAP or Kerberos) for authentication. Usernames are stored in NEMO, and these are authenticated against the external authentication source(s). So, your NEMO username must match the username of the external authentication source.

Running NEMO in Docker

You can run the NEMO Docker container now that the NEMO runtime information exists:

docker run --detach --publish 8000:8000 --volume /home/user/nemo:/nemo nanofab/nemo

The --volume option mounts your NEMO runtime directory /home/user/nemo/ to the container at /nemo/; customize this path to suit your needs. Port 8000 is published to the host machine, and you can use a reverse proxy to expose NEMO on HTTPS port 443.

NGINX Reverse proxy

For Reverse proxy NGINX can be used.

SSL certificate can also be used at server level instead of NEMO level. Here is an exemple of a nemo.conf file to put in /etc/nginx/sites-enabled/

### Redirection from http to https
server {
    listen 80;
    listen [::]:80;
    server_name myserver.example.com;
    return 301 https://$host$request_uri;
}
#  https server
server {
        listen 443 ssl http2;
        ssl on;

        ssl_certificate        /etc/nginx/ssl/server_certificate.pem;
        ssl_certificate_key     /etc/nginx/ssl/server_certificate.key;
        ssl_protocols       TLSv1.2;


        charset UTF-8;

        server_name myserver.example.com;

        # NEMO reverse proxy for port 8000
        location / {
                proxy_pass http://127.0.0.1:8000;
		proxy_buffering off;
		proxy_set_header X-Real-IP $remote_addr;
                }


    # Static files

	location /static/ { alias /nemo/static/; }

    location /favicon.ico { alias /nemo/static/favicon.ico; }

}