Skip to content

Installation guide with CentOS 7

Dylan Klomparens edited this page Jan 26, 2018 · 30 revisions

This guide is intended for setting up a production instance of NEMO. See the desktop development guide if you just want to try out NEMO and explore its features.

This guide assumes you have the CentOS 7 operating system installed, and nothing else.

Recommended environment

The following components are recommended for running NEMO, and the installation process for each one will be explained.

  • CentOS 7 (operating system)
  • Python 3.6
  • Gunicorn (Python WSGI server)
  • Nginx (static content server and reverse proxy)
  • SQLite (database)

There is nothing to prevent you from using NEMO in a different environment, however, the recommended components are tested and known to work well.

Install the Python 3.6 interpreter

Download and install the following packages in order to compile Python. Also create a nemo user which will run the NEMO application and web server.

Execute these commands with root privileges:

yum --assumeyes install gcc wget sqlite-devel openssl-devel git
useradd --comment "NEMO" nemo
su nemo

Execute these commands as the nemo user (unprivileged):

To compile and install Python...

cd /home/nemo
wget https://www.python.org/ftp/python/3.6.4/Python-3.6.4.tgz
tar xf Python-3.6.4.tgz
cd Python-3.6.4/
./configure --prefix=/home/nemo/python
make
make install
cd /home/nemo
rm -rf Python-3.6.4 Python-3.6.4.tgz

Python is now installed in the /home/nemo/python/ directory.

Configure environment variables

You'll need to set some environment variables to specify the location of the Python interpreter, and where NEMO should read its settings from.

All Django projects (including NEMO) read in their settings from one important file: settings.py. The Django documentation states you should specify the settings location using the environment variable DJANGO_SETTINGS_MODULE. The value of DJANGO_SETTINGS_MODULE should be in Python path syntax.

Additionally, the settings file should be on the Python import search path. You will probably also need to set PYTHONPATH to the directory that contains the settings file. For example, if the file settings.py resides in the directory /home/nemo/, you would set PYTHONPATH=/home/nemo and DJANGO_SETTINGS_MODULE=settings.

Edit /home/nemo/.bashrc to set environment variables to point to the correct Python interpreter and settings by adding:

PATH=/home/nemo/python/bin:$PATH
PYTHONPATH=/home/nemo
DJANGO_SETTINGS_MODULE="settings"
export PATH PYTHONPATH DJANGO_SETTINGS_MODULE

Refresh the environment variables by closing the bash terminal and reopening it as the nemo user. Ensure the proper Python interpreter is available in your PATH environment variable using which python3... the first line of output should be /home/nemo/python/bin.

Install NEMO

pip3 install git+https://github.com/usnistgov/NEMO.git

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 strcuture 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
|--- nginx/                       # Reverse proxy and static content server
|    |--- configuration           # Configuration file for Nginx
|    |--- nginx                   # Nginx binary executable
|--- python/                      # Python 3.6+ interpreter with NEMO package installed
|--- secrets/                     # Contains all passwords, certificates, and keys that NEMO uses
|    |--- django_secret_key.txt   # Used by Django for hashing and signing
|    |--- database_password.txt   # To connect to the database
|    |--- 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)

Testing email settings

Much of NEMO's functionality relies on sending emails, so it is important to ensure it interfaces correctly with your organization's email server. The Django documentation contains a reference of all email related settings that can be configured for NEMO. All the relevant settings begin with EMAIL_. The most important ones to configure are, EMAIL_HOST, EMAIL_PORT, EMAIL_USE_TLS, and EMAIL_SSL_CERTFILE.

We highly recommend using TLS when sending email. This prevents unauthorized parties from intercepting, reading, or modifying emails.

When you're ready to test your settings, you can use manage.py to send a test email. Configure your settings.py first. Then, from the command line, source any environment variables you might need, and invoke manage.py:

python manage.py sendtestemail you@example.org

If you get an error, or do not receive the test email, you can ask questions on the NEMO email list. If you encounter a problem that you think is particularly common, please help us by improving the documentation on this wiki.

Allowed hosts

In a production deployment of NEMO, the site should be served from a domain name such as nemo.example.org. ALLOWED_HOSTS controls where NEMO site content can come from.

TO DO: discuss email, LDAP servers, allowed hosts, database (with dbshell), external certs

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.auth.middleware.RemoteUserMiddleware',
	'django.contrib.messages.middleware.MessageMiddleware',
	'django.middleware.clickjacking.XFrameOptionsMiddleware',
	'django.middleware.common.BrokenLinkEmailsMiddleware',
	'NEMO.middleware.DeviceDetectionMiddleware',
]

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.device',  # Informs the templating engine whether the template is being rendered for a desktop or mobile device.
				'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.contrib.messages.context_processors.messages',
			],
		},
	},
]


def get_file_contents(path):
	with open(path) as f:
		return f.read().strip()


# -------------------- 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 <nemo.admin@example.org>'

ADMINS = [
	('System administrator', 'sysadmin@example.org'),
]
MANAGERS = ADMINS

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

TIME_ZONE = 'America/New_York'

DATABASES = {
	'default': {
		'OPTIONS': {
			'threaded': True,
		},
		'ENGINE': 'django.db.backends.postgresql',
		'NAME': 'NEMO',
		'USER': 'NEMO',
		'PASSWORD': get_file_contents('/path/to/secrets/database_password.txt'),
		'HOST': 'database.example.org',
		'PORT': '5432',
		'CONN_MAX_AGE': 60,  # seconds
	}
}

STATIC_ROOT = '/path/to/static/'
STATIC_URL = '/static/'
MEDIA_ROOT = '/path/to/media/'
MEDIA_URL = '/media/'

# Make this unique, and don't share it with anybody.
SECRET_KEY = get_file_contents('/path/to/secrets/django_secret_key.txt')

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': '/path/to/logs/django_error.log'
		},
		'security_file': {
			'level': 'INFO',
			'class': 'logging.FileHandler',
			'filename': '/path/to/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

# 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': '/path/to/secrets/root.crt',
	},
	{
		'url': 'ldap.example.org',
		'domain': 'EXAMPLE',
		'certificate': '/path/to/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'],
}