Commit d8b2c313 authored by Frank Greguska's avatar Frank Greguska
Browse files

docker version of the slcs

parent a250d785
Loading
Loading
Loading
Loading
+6 −0
Original line number Diff line number Diff line
@@ -91,6 +91,12 @@ WSGISocketPrefix run/wsgi
  # ProxyPass /las  ajp://localhost:8009/las
  # ProxyPassReverse /las   ajp://localhost:8009/las
  
  # proxy requests for slcs resources
  ProxyPass /slcs-admin http://slcs-server:5000/admin timeout=600
  ProxyPassReverse /slcs-admin http://slcs-server:5000/admin timeout=600
  ProxyPass /slcs http://slcs-nginx timeout=600
  ProxyPassReverse /slcs http://slcs-nginx timeout=600

  # note: WSGIPythonEggs must match location created by Dockerfile
  WSGIDaemonProcess cog-site python-path=/usr/local/cog/venv/lib/python2.7/site-packages:/usr/local/cog/cog-install python-eggs=/var/www/.python-eggs user=apache group=apache threads=25
  WSGIScriptAlias / /usr/local/cog/cog_install/apache/wsgi.py
+55 −0
Original line number Diff line number Diff line
--- 
networks: 
  dbnetwork: 
    external: 
      name: esgfdocker_dbnetwork
secrets: 
  slcsdb_passwd: 
    file: $ESGF_CONFIG/slcs/conf/db/slcsdb_passwd.txt
services: 
  slcs: 
    command: "-sn $ESGF_HOSTNAME -ds rootAdmin -sdn slcs -sdh slcs-postgres -sdu dbsuper -udn esgcet -udh postgres -udu dbsuper --static-url https://$ESGF_HOSTNAME/slcs/"
    container_name: slcs
    depends_on: 
      - slcs-postgres
    expose: 
      - "5000"
    image: esgfhub/slcs-server
    networks: 
      - default
      - dbnetwork
    ports: 
      - "5000:5000"
    volumes: 
      - "slcs-static-web:/var/www/static"
      - "$ESGF_CONFIG/slcs/conf/db/:/usr/local/esgf-slcs-server/conf/db"
      - "$ESGF_CONFIG/slcs/conf/ca/:/usr/local/esgf-slcs-server/conf/ca"
  slcs-nginx: 
    container_name: slcs-nginx
    depends_on: 
      - slcs
    expose: 
      - "443"
      - "80"
    image: nginx
    networks: 
      - default
      - dbnetwork
    volumes: 
      - "slcs-static-web:/usr/share/nginx/html:ro"
  slcs-postgres: 
    container_name: slcs-postgres
    environment: 
      - POSTGRES_PASSWORD=changeit
      - POSTGRES_USER=dbsuper
      - POSTGRES_DB=slcs
      - POSTGRES_INITDB_ARGS=--encoding=UTF-8
    expose: 
      - "5432"
    image: postgres
    networks: 
      - default
      - dbnetwork
version: "3.2"
volumes: 
  slcs-static-web: 
+218 −0
Original line number Diff line number Diff line
#!/usr/bin/python2.7
import argparse
import os
import subprocess
import shutil


def parse_args():
    parser = argparse.ArgumentParser(description='Start an ESGF SLCS instance.',
                                     formatter_class=argparse.ArgumentDefaultsHelpFormatter)

    parser.add_argument('-sn', '--server-name',
                        help='The Fully Qualified Domain Name of the SLCS server.',
                        required=True,
                        metavar='my-node.esgf.org')

    parser.add_argument('-ds', '--django-superuser',
                        help='The user in UserDB that should be given django superuser permissions.',
                        required=True,
                        metavar='another')

    parser.add_argument('-su', '--static-url',
                        help='The URL used when retrieving static files.',
                        required=True,
                        metavar='http://my-cdn.esgf.org/slcs-static/')

    slcs_db_group = parser.add_argument_group(title='SLCS Database Settings',
                                              description='Settings used for connecting to the SLCS database.')
    slcs_db_group.add_argument('-sdn', '--slcs-database-name',
                               help='The SLCS database name.',
                               required=True,
                               metavar='esgf_slcs_server')
    slcs_db_group.add_argument('-sdu', '--slcs-database-user',
                               help='The SLCS database user.',
                               required=True,
                               metavar='db_user')
    slcs_db_group.add_argument('-sdh', '--slcs-database-host',
                               help='The SLCS database host.',
                               required=True,
                               metavar='slcsdb.esgf.org')

    slcs_db_group.add_argument('-sdp', '--slcs-database-port',
                               help='The SLCS database port.',
                               required=False,
                               default=5432,
                               metavar=5432)
    slcs_db_group.add_argument('-sde', '--slcs-database-engine',
                               help='The engine to use for the SLCS database.',
                               required=False,
                               choices=['django.db.backends.postgresql', 'django.db.backends.mysql',
                                        'django.db.backends.sqlite3', 'django.db.backends.oracle'],
                               default='django.db.backends.postgresql',
                               metavar='django.db.backends.postgresql')

    user_db_group = parser.add_argument_group(title='User Database Settings',
                                              description='Settings used for connecting to the User database.')

    user_db_group.add_argument('-udn', '--user-database-name',
                               help='The User database name.',
                               required=True,
                               metavar='esgf_slcs_server')
    user_db_group.add_argument('-udu', '--user-database-user',
                               help='The User database user.',
                               required=True,
                               metavar='db_user')
    user_db_group.add_argument('-udh', '--user-database-host',
                               help='The User database host.',
                               required=True,
                               metavar='slcsdb.esgf.org')
    user_db_group.add_argument('-udt', '--user-database-table',
                               help='The name of the User table',
                               required=False,
                               default='user',
                               metavar='user')
    user_db_group.add_argument('-uds', '--user-database-schema',
                               help='The name of the schema that contains the User table',
                               required=False,
                               default='esgf_security',
                               metavar='esgf_security')
    user_db_group.add_argument('-udp', '--user-database-port',
                               help='The User database port.',
                               required=False,
                               default=5432,
                               metavar=5432)
    user_db_group.add_argument('-ude', '--user-database-engine',
                               help='The engine to use for the User database.',
                               required=False,
                               choices=['django.db.backends.postgresql', 'django.db.backends.mysql',
                                        'django.db.backends.sqlite3', 'django.db.backends.oracle'],
                               default='django.db.backends.postgresql',
                               metavar='django.db.backends.postgresql')

    parser.add_argument('-se', '--server-email',
                        help='The email address used when sending emails. Defaults to no-reply@[--server-name].',
                        required=False,
                        metavar='no-reply@my-node.esgf.org')

    parser.add_argument('-ccf', '--cacert-chain-filepaths',
                        help='List of PEM-encoded certificate files corresponding to CA trustroot '
                             'files to be returned in the certificate issuing response. '
                             'These are concatenated with the new issued certificate. '
                             'This setting is optional and may be useful where the client''s trust roots do not '
                             'contain the complete chain of trust from the newly issued cert and a root certificate. '
                             'This option does not apply if the CA for this service is itself a root CA.',
                        required=False,
                        nargs='+',
                        metavar='/usr/local/esgf-slcs-server/conf/ca/08bd99c7.0')

    parser.add_argument('-dd', '--django-debug',
                        help='Enable Django debug mode.',
                        required=False,
                        action='store_true')

    parser.add_argument('-da', '--django-admin',
                        help='https://docs.djangoproject.com/en/1.10/ref/settings/#admins',
                        nargs='+',
                        required=False,
                        default=[],
                        metavar=('John Doe,john@example.com', 'Mary,mary@example.com'))

    args = parser.parse_args()

    if args.server_email is None:
        args.server_email = "no-reply@{0}".format(args.server_name)

    return args


def replace_in_file(file_path, replacements):
    lines = []
    with open(file_path) as infile:
        for line in infile:
            for src, target in replacements.iteritems():
                line = line.replace(src, target)
            lines.append(line)
    with open(file_path, 'w') as outfile:
        for line in lines:
            outfile.write(line)


the_args = parse_args()

os.environ['SLCS_SERVER_NAME'] = the_args.server_name
os.environ['SLCS_STATIC_URL'] = the_args.static_url

os.environ['SLCS_DB_SLCS_ENGINE'] = the_args.slcs_database_engine
os.environ['SLCS_DB_SLCS_NAME'] = the_args.slcs_database_name
os.environ['SLCS_DB_SLCS_HOST'] = the_args.slcs_database_host
os.environ['SLCS_DB_SLCS_PORT'] = str(the_args.slcs_database_port)
os.environ['SLCS_DB_SLCS_USER'] = the_args.slcs_database_user

os.environ['SLCS_DB_USER_ENGINE'] = the_args.user_database_engine
os.environ['SLCS_DB_USER_NAME'] = the_args.user_database_name
os.environ['SLCS_DB_USER_HOST'] = the_args.user_database_host
os.environ['SLCS_DB_USER_PORT'] = str(the_args.user_database_port)
os.environ['SLCS_DB_USER_USER'] = the_args.user_database_user
os.environ['SLCS_DB_USER_TABLE'] = the_args.user_database_table
os.environ['SLCS_DB_USER_SCHEMA'] = the_args.user_database_schema

os.environ['SLCS_DJANGO_DEBUG_MODE'] = str(the_args.django_debug)
os.environ['SLCS_SERVER_EMAIL'] = the_args.server_email
os.environ['SLCS_ADMINS'] = ';'.join(the_args.django_admin)
os.environ['SLCS_SERVER_ROOT'] = "https://{0}".format(the_args.server_name)

APPLICATION_HOME = os.environ['APPLICATION_HOME']

db_conf_dir = os.path.join(os.environ['APPLICATION_HOME'], 'conf', 'db')
assert os.path.isfile(os.path.join(db_conf_dir, 'slcsdb_passwd.txt')), \
    "{0} must be mounted as a volume and contain slcsdb_passwd.txt, userdb_passwd.txt, " \
    "and django_superuser_passwd.txt".format(db_conf_dir)
assert os.path.isfile(os.path.join(db_conf_dir, 'userdb_passwd.txt')), \
    "{0} must be mounted as a volume and contain slcsdb_passwd.txt, userdb_passwd.txt, " \
    "and django_superuser_passwd.txt".format(db_conf_dir)
assert os.path.isfile(os.path.join(db_conf_dir, 'django_superuser_passwd.txt')), \
    "{0} must be mounted as a volume and contain slcsdb_passwd.txt, userdb_passwd.txt, " \
    "and django_superuser_passwd.txt".format(db_conf_dir)

with open(os.path.join(db_conf_dir, 'django_superuser_passwd.txt')) as pass_file:
    django_su_password = pass_file.read()

ca_conf_dir = os.path.join(APPLICATION_HOME, 'conf', 'ca')
assert os.path.isfile(os.path.join(ca_conf_dir, 'onlineca.crt')), \
    "{0} must be mounted as a volume and contain onlineca.crt and onlineca.key and a directory called trustroots".format(
        ca_conf_dir)
assert os.path.isfile(os.path.join(ca_conf_dir, 'onlineca.key')), \
    "{0} must be mounted as a volume and contain onlineca.crt and onlineca.key and a directory called trustroots".format(
        ca_conf_dir)
assert os.path.isdir(os.path.join(ca_conf_dir, 'trustroots')), \
    "{0} must be mounted as a volume and contain onlineca.crt and onlineca.key and a directory called trustroots".format(
        ca_conf_dir)

app_conf_dir = os.environ['APPLICATION_CONF_DIR']
online_ca_ini = os.path.join(app_conf_dir, 'onlineca.ini')

cacert_chain_filepaths = "onlineca.server.cacert_chain_filepaths: {0}".format(
    ','.join(the_args.cacert_chain_filepaths)) if the_args.cacert_chain_filepaths else "#"
replace_in_file(online_ca_ini, {
    "{{ onlineca_cacert_chain_filepaths }}": cacert_chain_filepaths,
    "{{ application_home }}": APPLICATION_HOME})

subprocess.check_call(["/usr/bin/python2.7", "{0}/manage.py".format(os.environ['CODE_LOCATION']), "makemigrations"])
subprocess.check_call(["/usr/bin/python2.7", "{0}/manage.py".format(os.environ['CODE_LOCATION']), "migrate"])

try:
    subprocess.check_output(
        ["/usr/bin/python2.7", "{0}/manage.py".format(os.environ['CODE_LOCATION']), "createsuperuser", "--noinput",
         "--username={0}".format(the_args.django_superuser), "--email=notused@notused.com"], stderr=subprocess.STDOUT)
except subprocess.CalledProcessError as cpe:
    if "duplicate key value violates unique constraint" not in cpe.output:
        raise IOError("Error creating django superuser\n{0}".format(cpe.output))

subprocess.check_call(
    ["/usr/bin/python2.7", "{0}/manage.py".format(os.environ['CODE_LOCATION']), "collectstatic", "--noinput",
     "--clear"])

subprocess.call(['chmod', '-R', '777', os.environ['SLCS_STATIC_ROOT']])

subprocess.check_call(["waitress-serve", "--listen=*:5000", "esgf_slcs_server.wsgi:application"])
+64 −0
Original line number Diff line number Diff line
#
# PasteDeploy configuration for the Online CA WSGI application
#
[app:main]
paste.app_factory = contrail.security.onlineca.server.wsgi.app:OnlineCaApp.app_factory

# Set an alternative prefix for settings - default is 'onlineca.server.'
#prefix: online-ca.

# Path to CA Python class constructor or factory function
onlineca.server.ca_class_factory_path: contrail.security.onlineca.server.impl:CertificateAuthorityImpl

# onlineca.server.ca_class.* values correspond to settings for the CA class.
# The CA class is embedded into the online CA via an interface.  It is used to
# issue certificates. The settings below apply to the
# ca.impl.CertificateAuthority class

# CA certificate file location
onlineca.server.ca_class.cert_filepath: {{ application_home }}/conf/ca/onlineca.crt

# CA certificate private key file location
onlineca.server.ca_class.key_filepath: {{ application_home }}/conf/ca/onlineca.key

# Password for CA private key file.  Omit this option if no password is set
#onlineca.server.ca_class.key_passwd:

# Lifetime of certificates issued from this CA (in seconds) - set here to 8
# hours, if not set, defaults to *3 years*!
onlineca.server.ca_class.not_after_time_nsecs: 28800

# Default number of bits for key pair.  Certificate signing requests submitted
# with public keys with less than this minimum will be rejected.  The default is
# 2048
onlineca.server.min_key_nbits: 2048

# Template to set the form for the certificate subject name for output
# certificates.  This can reference any key name set in environ.
onlineca.server.cert_subject_name_template: "/DC=esgf/CN=$OPENID"

# URI path for certificate issuing endpoint. e.g. if online ca app is mounted
# at https://myonlineca.somewhere.ac.uk/onlineca and issue_cert_uripath is
# /issue_cert/, then the full path would be,
#
# https://myonlineca.somewhere.ac.uk/onlineca/issue_cert/
#
# Default path is /certificate/
#onlineca.server.issue_cert_uripath: /issue-cert/

# URI path for trustroots retrieval endpoint.  The default path is
# /trustroots/
#onlineca.server.trustroots_uripath: /trusted-cas/

# Directory where trustroots should be read from and returned via the
# trustroots call.  It is vital that this is path is carefully selected and
# checked.
onlineca.server.trustroots_dir: {{ application_home }}/conf/ca/trustroots

# Comma delimited list of PEM-encoded certificate files corresponding to
# CA trustroot files to be returned in the certificate issuing response.  These
# are concatenated with the new issued certificate.   This setting is optional
# and may be useful where the client's trust roots do not contain the complete
# chain of trust from the newly issued cert and a root certificate.  This
# option does not apply if the CA for this service is itself a root CA.
{{ onlineca_cacert_chain_filepaths }}
+206 −0
Original line number Diff line number Diff line
# -*- coding: utf-8 -*-
"""
Django settings for the ESGF SLCS Server.
"""

import os
from django.core.urlresolvers import reverse_lazy

# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
APPLICATION_HOME = os.environ['SLCS_APPLICATION_HOME']

ALLOWED_HOSTS = ['localhost', '127.0.0.1', os.environ['SLCS_SERVER_NAME']]

DEBUG = "true" == str(os.getenv('SLCS_DJANGO_DEBUG_MODE')).lower()

# Security settings
if not DEBUG:
    SECURE_CONTENT_TYPE_NOSNIFF = True
    SECURE_BROWSER_XSS_FILTER = True
    SESSION_COOKIE_SECURE = True
    CSRF_COOKIE_SECURE = True
    CSRF_COOKIE_HTTPONLY = True
    X_FRAME_OPTIONS = 'DENY'

# Read the secret key from a file
SECRET_KEY_FILE = '{0}/conf/app/secret_key.txt'.format(APPLICATION_HOME)
with open(SECRET_KEY_FILE) as f:
    SECRET_KEY = f.read().strip()

# Logging settings
DEFAULT_FROM_EMAIL = SERVER_EMAIL = os.environ['SLCS_SERVER_EMAIL']


if not DEBUG:
    if 'SLCS_ADMINS' in os.environ.keys() and '' != os.environ['SLCS_ADMINS']:
        ADMINS = [ (name_email.split(',')[0], name_email.split(',')[1]) for name_email in os.environ['SLCS_ADMINS'].split(';')]

    LOGGING_CONFIG = None
    LOGGING = {
        'version' : 1,
        'disable_existing_loggers' : False,
        'formatters' : {
            'generic' : {
                'format' : '[%(levelname)s] [%(asctime)s] [%(name)s:%(lineno)s] [%(threadName)s] %(message)s',
            },
        },
        'handlers' : {
            'stdout' : {
                'class' : 'logging.StreamHandler',
                'formatter' : 'generic',
            },
            'mail_admins' : {
                'class' : 'django.utils.log.AdminEmailHandler',
                'formatter' : 'generic',
                'level' : 'ERROR',
            },
        },
        'loggers' : {
            '' : {
                'handlers' : ['stdout', 'mail_admins'],
                'level' : 'INFO',
                'propogate' : True,
            },
        },
    }
    import logging.config
    logging.config.dictConfig(LOGGING)


# Application definition

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'oauth2_provider',
    'bootstrap3',
]

MIDDLEWARE_CLASSES = [
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.auth.middleware.SessionAuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
]

ROOT_URLCONF = 'esgf_slcs_server.urls'

TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [os.path.join(BASE_DIR, 'esgf_slcs_server', 'templates')],
        'APP_DIRS': True,
        'OPTIONS': {
            'context_processors': [
                'django.template.context_processors.debug',
                'django.template.context_processors.request',
                'django.contrib.auth.context_processors.auth',
                'django.contrib.messages.context_processors.messages',
            ],
        },
    },
]

WSGI_APPLICATION = 'django_wsgi.handler.APPLICATION'


DATABASES = {
    'default': {
        'ENGINE' : os.environ['SLCS_DB_SLCS_ENGINE'],
        'NAME' : os.environ['SLCS_DB_SLCS_NAME'],
        'HOST' : os.environ['SLCS_DB_SLCS_HOST'],
        'PORT' : os.environ['SLCS_DB_SLCS_PORT'],
        'USER' : os.environ['SLCS_DB_SLCS_USER'],
    },
    'userdb' : {
        'ENGINE' : os.environ['SLCS_DB_USER_ENGINE'],
        'NAME' : os.environ['SLCS_DB_USER_NAME'],
        'HOST' : os.environ['SLCS_DB_USER_HOST'],
        'PORT' : os.environ['SLCS_DB_USER_PORT'],
        'USER' : os.environ['SLCS_DB_USER_USER'],
    },
}

ESGF_SLCSDB_PASSWD_FILE = '{0}/conf/db/slcsdb_passwd.txt'.format(APPLICATION_HOME)
with open(ESGF_SLCSDB_PASSWD_FILE) as f:
    DATABASES['default']['PASSWORD'] = f.read().strip()

ESGF_USERDB_PASSWD_FILE = '{0}/conf/db/userdb_passwd.txt'.format(APPLICATION_HOME)
with open(ESGF_USERDB_PASSWD_FILE) as f:
    DATABASES['userdb']['PASSWORD'] = f.read().strip()
ESGF_USERDB_USER_TABLE = os.environ['SLCS_DB_USER_TABLE']
ESGF_USERDB_USER_SCHEMA = os.environ['SLCS_DB_USER_SCHEMA']


AUTH_PASSWORD_VALIDATORS = [
    {
        'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
    },
    {
        'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
    },
    {
        'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
    },
    {
        'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
    },
]

AUTHENTICATION_BACKENDS = [
    'oauth2_provider.backends.OAuth2Backend',
    'esgf_auth.backend.EsgfUserBackend'
]

LANGUAGE_CODE = 'en-us'

TIME_ZONE = 'UTC'

USE_I18N = True

USE_L10N = True

USE_TZ = True


STATIC_URL = os.environ['SLCS_STATIC_URL']
STATICFILES_DIRS = [os.path.join(BASE_DIR, 'esgf_slcs_server', 'static')]
STATIC_ROOT = os.environ['SLCS_STATIC_ROOT']


# URLs for login/logout
LOGIN_URL = 'accounts_login'
LOGOUT_URL = 'accounts_logout'
LOGIN_REDIRECT_URL = 'home'

BOOTSTRAP3 = {
    'success_css_class': '',
}


# OAuth provider configuration
CERTIFICATE_SCOPE = '{0}/oauth/certificate/'.format(os.environ['SLCS_SERVER_ROOT'])
OAUTH2_PROVIDER = {
    'SCOPES' : {
        CERTIFICATE_SCOPE : 'Obtain short-lived certificate for user',
    },
    'DEFAULT_SCOPES' : [CERTIFICATE_SCOPE],
}

BASIC_AUTH_REALM = 'esgf.llnl.gov'


# Configuration for the Online CA WSGI application
# This is instantiated using the PasteDeploy app_factory, so the configuration below
# corresponds to the global and local configs expected by that function
ONLINECA_PASTEDEPLOY_CONF = 'config:{0}/conf/app/onlineca.ini'.format(APPLICATION_HOME)
ONLINECA_DJANGO_USER_TO_OPENID = 'esgf_auth.openid.django_user_to_openid'