diff --git a/app/config/app.php b/app/config/app.php index 3692e7cc..9caafa64 100644 --- a/app/config/app.php +++ b/app/config/app.php @@ -344,41 +344,72 @@ /** * Configures logging options */ - 'Log' => [ - 'debug' => [ - 'className' => 'Cake\Log\Engine\FileLog', - 'path' => LOGS, - 'file' => 'debug', - 'url' => env('LOG_DEBUG_URL', null), - 'scopes' => false, - 'levels' => ['notice', 'info', 'debug'], - ], - 'error' => [ - 'className' => 'Cake\Log\Engine\FileLog', - 'path' => LOGS, - 'file' => 'error', - 'url' => env('LOG_ERROR_URL', null), - 'scopes' => false, - 'levels' => ['warning', 'error', 'critical', 'alert', 'emergency'], - ], - // To enable this dedicated query log, you need set your datasource's log flag to true - 'queries' => [ - 'className' => 'Cake\Log\Engine\FileLog', - 'path' => LOGS, - 'file' => 'queries', - 'url' => env('LOG_QUERIES_URL', null), - 'scopes' => ['queriesLog'], - ], - // We define a trace level for what is really debugging, except debug level - // will write to stdout instead of the log when debug=true - 'trace' => [ - 'className' => 'Cake\Log\Engine\FileLog', - 'path' => LOGS, - 'file' => 'trace', - 'url' => env('LOG_TRACE_URL', null), - 'scopes' => ['trace'], - ] - ], + 'Log' => null !== env('COMANAGE_REGISTRY_CONTAINER', null) + // Configuration for container deployments + ? [ + 'debug' => [ + 'className' => 'Cake\Log\Engine\ConsoleLog', + 'stream' => 'php://stdout', + 'outputAs' => 0, + 'scopes' => false, + 'levels' => ['notice', 'info', 'debug'], + ], + 'error' => [ + 'className' => 'Cake\Log\Engine\ConsoleLog', + 'stream' => 'php://stderr', + 'outputAs' => 0, + 'scopes' => false, + 'levels' => ['warning', 'error', 'critical', 'alert', 'emergency'], + ], + 'queries' => [ + 'className' => 'Cake\Log\Engine\ConsoleLog', + 'stream' => 'php://stdout', + 'outputAs' => 0, + 'scopes' => ['queriesLog'] + ], + 'trace' => [ + 'className' => 'Cake\Log\Engine\ConsoleLog', + 'stream' => 'php://stdout', + 'outputAs' => 0, + 'scopes' => ['trace'], + ] + ] + // Configuration for tranditional deployments + : [ + 'debug' => [ + 'className' => 'Cake\Log\Engine\FileLog', + 'path' => LOGS, + 'file' => 'debug', + 'url' => env('LOG_DEBUG_URL', null), + 'scopes' => false, + 'levels' => ['notice', 'info', 'debug'], + ], + 'error' => [ + 'className' => 'Cake\Log\Engine\FileLog', + 'path' => LOGS, + 'file' => 'error', + 'url' => env('LOG_ERROR_URL', null), + 'scopes' => false, + 'levels' => ['warning', 'error', 'critical', 'alert', 'emergency'], + ], + // To enable this dedicated query log, you need set your datasource's log flag to true + 'queries' => [ + 'className' => 'Cake\Log\Engine\FileLog', + 'path' => LOGS, + 'file' => 'queries', + 'url' => env('LOG_QUERIES_URL', null), + 'scopes' => ['queriesLog'], + ], + // We define a trace level for what is really debugging, except debug level + // will write to stdout instead of the log when debug=true + 'trace' => [ + 'className' => 'Cake\Log\Engine\FileLog', + 'path' => LOGS, + 'file' => 'trace', + 'url' => env('LOG_TRACE_URL', null), + 'scopes' => ['trace'], + ] + ], /** * Session configuration. diff --git a/app/src/Command/DatabaseTestCommand.php b/app/src/Command/DatabaseTestCommand.php new file mode 100644 index 00000000..b158e085 --- /dev/null +++ b/app/src/Command/DatabaseTestCommand.php @@ -0,0 +1,65 @@ +abort('Unable to connect to database', 1); + } + + if(!empty($db)) { + $io->out('Connected to database'); + } + } +} diff --git a/app/src/Command/SetupCommand.php b/app/src/Command/SetupCommand.php index 4b00f291..588df0aa 100644 --- a/app/src/Command/SetupCommand.php +++ b/app/src/Command/SetupCommand.php @@ -72,7 +72,7 @@ public function execute(Arguments $args, ConsoleIo $io) { // Check if the security salt file already exists, and if so abort. - $securitySaltFile = LOCAL . DS . "Config" . DS . "security.salt"; + $securitySaltFile = LOCAL . DS . "config" . DS . "security.salt"; if(file_exists($securitySaltFile)) { $io->out(__d('command', 'se.already')); @@ -130,7 +130,7 @@ public function execute(Arguments $args, ConsoleIo $io) { if(file_put_contents($securitySaltFile, $salt)===false) { $err = error_get_last(); - throw new \RuntimeException($err[message]); + throw new \RuntimeException($err['message']); } // We set 444 to prevent accidental changing of the salt, but also so the @@ -138,4 +138,4 @@ public function execute(Arguments $args, ConsoleIo $io) { // We assume we're not installed on a shared, semi-public server. chmod($securitySaltFile, 0444); } -} \ No newline at end of file +} diff --git a/container/build.sh b/container/build.sh new file mode 100755 index 00000000..f3f4ce27 --- /dev/null +++ b/container/build.sh @@ -0,0 +1,386 @@ +#!/bin/bash +# +# Build containers for COmanage Registry and associated tools. +# +# Portions licensed to the University Corporation for Advanced Internet +# Development, Inc. ("UCAID") under one or more contributor license agreements. +# See the NOTICE file distributed with this work for additional information +# regarding copyright ownership. +# +# UCAID licenses this file to you under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with the +# License. You may obtain a copy of the License at: +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +########################################################################### +# Build the Registry base image. +# Globals: +# None +# Arguments: +# Base image version, an integer. +# Build flags, other flags for docker build. +# Outputs: +# None +########################################################################### +function build_base() { + local image_version + local tag + local version + + image_version="$1" + if [[ -z "${image_version}" ]]; then + err "ERROR: image version cannot be empty" + return 1 + fi + + version="$(version_from_repository)" + + tag="comanage-registry-base:${version}-${image_version}" + + docker build \ + "${@:2}" \ + --tag "${tag}" \ + --build-arg COMANAGE_REGISTRY_VERSION="${version}" \ + --file container/registry/base/Dockerfile \ + . +} + +########################################################################### +# Build the Registry basic auth image. +# Globals: +# None +# Arguments: +# Base image version, an integer. +# Image version, an integer. +# Build flags, other flags for docker build. +# Outputs: +# None +########################################################################### +function build_basic_auth() { + local base_image_version + local image_version + local tag + local version + + base_image_version="$1" + if [[ -z "${base_image_version}" ]]; then + err "ERROR: base image version cannot be empty" + return 1 + fi + + image_version="$2" + if [[ -z "${image_version}" ]]; then + err "ERROR: image version cannot be empty" + return 1 + fi + + version="$(version_from_repository)" + + tag="comanage-registry:${version}-basic-auth-${image_version}" + + docker build \ + "${@:3}" \ + --tag "${tag}" \ + --build-arg \ + COMANAGE_REGISTRY_VERSION="${version}" \ + --build-arg \ + COMANAGE_REGISTRY_BASE_IMAGE_VERSION="${base_image_version}" \ + --file container/registry/basic-auth/Dockerfile \ + . +} + +########################################################################### +# Build the Registry mod_auth_openidc image. +# Globals: +# None +# Arguments: +# Base image version, an integer. +# Image version, an integer. +# Build flags, other flags for docker build. +# Outputs: +# None +########################################################################### +function build_mod_auth_openidc() { + local base_image_version + local image_version + local tag + local version + + base_image_version="$1" + if [[ -z "${base_image_version}" ]]; then + err "ERROR: base image version cannot be empty" + return 1 + fi + + image_version="$2" + if [[ -z "${image_version}" ]]; then + err "ERROR: image version cannot be empty" + return 1 + fi + + version="$(version_from_repository)" + + tag="comanage-registry:${version}-mod_auth_openidc-${image_version}" + + docker build \ + "${@:3}" \ + --tag "${tag}" \ + --build-arg \ + COMANAGE_REGISTRY_VERSION="${version}" \ + --build-arg \ + COMANAGE_REGISTRY_BASE_IMAGE_VERSION="${base_image_version}" \ + --file container/registry/mod_auth_openidc/Dockerfile \ + . +} + +########################################################################### +# Build the Shibboleth SP base image. +# Globals: +# None +# Arguments: +# Shibboleth SP version. +# Image version, an integer. +# Build flags, other flags for docker build. +# Outputs: +# None +########################################################################### +function build_shibboleth_sp_base() { + local image_version + local tag + local version + + version="$1" + if [[ -z "${version}" ]]; then + err "ERROR: Shibboleth SP version cannot be empty" + return 1 + fi + + image_version="$2" + if [[ -z "${image_version}" ]]; then + err "ERROR: image version cannot be empty" + return 1 + fi + + tag="comanage-registry-shibboleth-sp-base:${version}-${image_version}" + + docker build \ + "${@:3}" \ + --tag "${tag}" \ + --build-arg \ + SHIBBOLETH_SP_VERSION="${version}" \ + --file container/shibboleth-sp-base/Dockerfile \ + . +} + +########################################################################### +# Build the Shibboleth SP with supervisor image. +# Globals: +# None +# Arguments: +# Base image version, an integer. +# Shibboleth SP version. +# Shibboleth SP base image version, an integer. +# Image version, an integer. +# Build flags, other flags for docker build. +# Outputs: +# None +########################################################################### +function build_shibboleth_sp_supervisor() { + local base_image_version + local image_version + local shibboleth_sp_base_image_version + local shibboleth_sp_version + local tag + local version + + base_image_version="$1" + if [[ -z "${base_image_version}" ]]; then + err "ERROR: base image version cannot be empty" + return 1 + fi + + shibboleth_sp_version="$2" + if [[ -z "${shibboleth_sp_version}" ]]; then + err "ERROR: Shibboleth SP version cannot be empty" + return 1 + fi + + shibboleth_sp_base_image_version="$3" + if [[ -z "${shibboleth_sp_base_image_version}" ]]; then + err "ERROR: Shibboleth SP base image version cannot be empty" + return 1 + fi + + image_version="$4" + if [[ -z "${image_version}" ]]; then + err "ERROR: image version cannot be empty" + return 1 + fi + + version="$(version_from_repository)" + + tag="comanage-registry:${version}-shibboleth-sp-supervisor-${image_version}" + + docker build \ + "${@:5}" \ + --tag "${tag}" \ + --build-arg \ + COMANAGE_REGISTRY_VERSION="${version}" \ + --build-arg \ + COMANAGE_REGISTRY_BASE_IMAGE_VERSION="${base_image_version}" \ + --build-arg \ + COMANAGE_REGISTRY_SHIBBOLETH_SP_VERSION="${shibboleth_sp_version}" \ + --build-arg \ + COMANAGE_REGISTRY_SHIBBOLETH_SP_BASE_IMAGE_VERSION="${shibboleth_sp_base_image_version}" \ + --file container/registry/shibboleth-sp-supervisor/Dockerfile \ + . + +} + +########################################################################### +# Echo errors to stderr with timestamp. +# Globals: +# None +# Arguments: +# None +# Outputs: +# Writes errors to stderr. +########################################################################### +function err() { + echo "[$(date +'%Y-%m-%dT%H:%M:%S%z')]: $*" >&2 +} + +########################################################################### +# Echo usage message to stdout. +# Globals: +# None +# Arguments: +# Array of all input parameters +# Outputs: +# Writes usage message to stdout. +########################################################################### +function usage() { + local usage + + read -d '' usage < /dev/null 2>&1 + if (( $? == 0 )); then + branch="$(git rev-parse --abbrev-ref HEAD)" + if [[ "${branch}" == "main" ]]; then + version="$(git describe --tags --abbrev=0)" + else + version="${branch}-$(git rev-parse --short HEAD)" + fi + else + version="$(git rev-parse --short HEAD)" + fi + + echo "${version}" +} + +########################################################################### +# Parse command line and execute as specified. +# Globals: +# SHIBBOLETH_SP_VERSION, string +# Arguments: +# Array of all input parameters +# Outputs: +# None +########################################################################### +function main() { + local build_target + local gnu_getopt_out + local tag_suffix + + gnu_getopt_out=$(/usr/bin/getopt \ + --options ht: \ + --longoptions help,tag_suffix: \ + --name 'build.sh' -- "${@:1:3}") + + if [[ $? != 0 ]]; then + err "ERROR: unable to parse command line" + exit 1 + fi + + eval set -- "${gnu_getopt_out} ${@:4}" + + while true; do + case "$1" in + -h | --help ) usage $@; exit ;; + -t | --tag_suffix ) tag_suffix="$2"; shift 2 ;; + -- ) shift; break ;; + * ) break ;; + esac + done + + build_target="$1" + + case "${build_target}" in + all ) + build_base 1 "${@:2}" + build_basic_auth 1 "${tag_suffix}" "${@:2}" + build_shibboleth_sp_base "${SHIBBOLETH_SP_VERSION}" 1 "${@:2}" + build_shibboleth_sp_supervisor 1 "${SHIBBOLETH_SP_VERSION}" 1 "${tag_suffix}" "${@:2}" + build_mod_auth_openidc 1 "${tag_suffix}" "${@:2}" + ;; + basic_auth ) + build_base 1 "${@:2}" + build_basic_auth 1 "${tag_suffix}" "${@:2}" + ;; + mod_auth_openidc ) + build_base 1 "${@:2}" + build_mod_auth_openidc 1 "${tag_suffix}" "${@:2}" + ;; + shibboleth ) + build_base 1 "${@:2}" + build_shibboleth_sp_base "${SHIBBOLETH_SP_VERSION}" 1 "${@:2}" + build_shibboleth_sp_supervisor 1 "${SHIBBOLETH_SP_VERSION}" 1 "${tag_suffix}" "${@:2}" + ;; + *) + err "ERROR: Unrecognized image type" + echo + usage + ;; + esac +} + +# Globals +SHIBBOLETH_SP_VERSION=3.3.0 + +main "$@" diff --git a/container/registry/base/Dockerfile b/container/registry/base/Dockerfile new file mode 100644 index 00000000..e9e136e2 --- /dev/null +++ b/container/registry/base/Dockerfile @@ -0,0 +1,123 @@ +# COmanage Registry Dockerfile +# +# Portions licensed to the University Corporation for Advanced Internet +# Development, Inc. ("UCAID") under one or more contributor license agreements. +# See the NOTICE file distributed with this work for additional information +# regarding copyright ownership. +# +# UCAID licenses this file to you under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with the +# License. You may obtain a copy of the License at: +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +FROM php:8.1.4-apache-bullseye + +# Official PHP image with Apache HTTPD includes +# --with-openssl +# --with-mbstring +# but xls, pdo, pdo_mysql, pdo_pgsql, mysqli, pgsql, +# and ldap extensions must be built. +RUN apt-get update && apt-get install -y \ + libldap-2.4-2 \ + libldap2-dev \ + libmariadb-dev \ + libpq-dev \ + libxml2 \ + libxslt1-dev \ + libxslt1.1 \ + ssl-cert \ + wget \ + zlib1g \ + libpcre3-dev \ + && docker-php-ext-configure pgsql -with-pgsql=/usr/local/pgsql \ + && docker-php-ext-configure ldap --with-libdir=lib/x86_64-linux-gnu \ + && docker-php-ext-configure mysqli \ + && docker-php-ext-install intl xsl pdo pdo_mysql pdo_pgsql mysqli pgsql ldap \ +# Do we need SimpleXML PHP extension? +# See https://book.cakephp.org/4/en/installation.html + && docker-php-source delete \ + && apt-get purge -y \ + libldap2-dev \ + libmysqlclient-dev \ + libpq-dev \ + && apt-get clean + +# Signal the PHP code to configure itself for container deployment. +ENV COMANAGE_REGISTRY_CONTAINER 1 + +ARG COMANAGE_REGISTRY_VERSION +ENV COMANAGE_REGISTRY_VERSION ${COMANAGE_REGISTRY_VERSION:-develop} +LABEL comanage_registry_version=${COMANAGE_REGISTRY_VERSION} + +ARG COMANAGE_REGISTRY_DIR +ENV COMANAGE_REGISTRY_DIR ${COMANAGE_REGISTRY_DIR:-/srv/comanage-registry} +LABEL comanage_registry_dir=${COMANAGE_REGISTRY_DIR} + +WORKDIR $COMANAGE_REGISTRY_DIR + +COPY app ${COMANAGE_REGISTRY_DIR}/app/ +COPY LICENSE ${COMANAGE_REGISTRY_DIR}/ +COPY NOTICE ${COMANAGE_REGISTRY_DIR}/ + +RUN mkdir -p ${COMANAGE_REGISTRY_DIR}/local/tmp/sessions \ + && mkdir -p ${COMANAGE_REGISTRY_DIR}/local/tmp/cache/persistent \ + && mkdir -p ${COMANAGE_REGISTRY_DIR}/local/tmp/cache/views \ + && mkdir -p ${COMANAGE_REGISTRY_DIR}/local/tmp/cache/models \ + && mkdir -p ${COMANAGE_REGISTRY_DIR}/local/logs \ + && mkdir -p ${COMANAGE_REGISTRY_DIR}/local/config \ + && chown -R www-data:www-data ${COMANAGE_REGISTRY_DIR}/local/logs \ + && chown -R www-data:www-data ${COMANAGE_REGISTRY_DIR}/local/tmp \ + && cd /var/www/html \ + && ln -s ${COMANAGE_REGISTRY_DIR}/app/webroot registry + +RUN a2enmod headers \ + && a2enmod ssl \ + && a2enmod rewrite \ + && a2dissite 000-default.conf \ + && a2disconf other-vhosts-access-log \ + && cd /etc/apache2 \ + && ln -s /etc/ssl/certs/ssl-cert-snakeoil.pem cert.pem \ + && ln -s /etc/ssl/private/ssl-cert-snakeoil.key privkey.pem + +# Do we need to run +# +# php composer.phar dumpautoload -o +# +# here, along with +# +# bin/cake plugin assets symlink +# +# ? +# +# See https://book.cakephp.org/4/en/deployment.html + +COPY container/registry/base/apache-* /etc/apache2/ +COPY container/registry/base/comanage_utils.sh /usr/local/lib/ +COPY container/registry/base/comanage_shibboleth_sp_utils.sh /usr/local/lib/ +COPY container/registry/base/docker-comanage-entrypoint /usr/local/bin/ + +EXPOSE 80 443 + +# Allow values for first administrator bootstrapped into the +# platform to be specified at image build time, in addition to +# being injected at run time through the entrypoint script. +ARG COMANAGE_REGISTRY_ADMIN_GIVEN_NAME +ARG COMANAGE_REGISTRY_ADMIN_FAMILY_NAME +ARG COMANAGE_REGISTRY_ADMIN_USERNAME +ARG COMANAGE_REGISTRY_ENABLE_POOLING + +# Set simple defaults for first administrator bootstrapped into the +# platform to make simple evaluation of the platform easier. +ENV COMANAGE_REGISTRY_ADMIN_GIVEN_NAME ${COMANAGE_REGISTRY_ADMIN_GIVEN_NAME:-Registry} +ENV COMANAGE_REGISTRY_ADMIN_FAMILY_NAME ${COMANAGE_REGISTRY_ADMIN_FAMILY_NAME:-Admin} +ENV COMANAGE_REGISTRY_ADMIN_USERNAME ${COMANAGE_REGISTRY_ADMIN_USERNAME:-admin} + +ENTRYPOINT ["docker-comanage-entrypoint"] + +CMD ["apache2-foreground"] diff --git a/container/registry/base/apache-include-directory-registry b/container/registry/base/apache-include-directory-registry new file mode 100644 index 00000000..b76eee48 --- /dev/null +++ b/container/registry/base/apache-include-directory-registry @@ -0,0 +1,6 @@ + +Options Indexes FollowSymLinks +DirectoryIndex index.php +AllowOverride All +Require all granted + diff --git a/container/registry/base/apache-include-virtual-host-port443-base b/container/registry/base/apache-include-virtual-host-port443-base new file mode 100644 index 00000000..999d08f2 --- /dev/null +++ b/container/registry/base/apache-include-virtual-host-port443-base @@ -0,0 +1,14 @@ +ServerName https://${COMANAGE_REGISTRY_VIRTUAL_HOST_FQDN}:443 + +DocumentRoot /var/www/html + +RedirectMatch ^/$ /registry/ + +LogLevel warn + +Header always set Strict-Transport-Security "max-age=63072000; includeSubDomains" + +SSLEngine on +SSLProtocol all -SSLv2 -SSLv3 +SSLCipherSuite EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH +SSLHonorCipherOrder on diff --git a/container/registry/base/apache-include-virtual-host-port80-redirect b/container/registry/base/apache-include-virtual-host-port80-redirect new file mode 100644 index 00000000..0e61457d --- /dev/null +++ b/container/registry/base/apache-include-virtual-host-port80-redirect @@ -0,0 +1,6 @@ + +ServerName http://${COMANAGE_REGISTRY_VIRTUAL_HOST_FQDN}:80 +RewriteEngine On +RewriteCond %{HTTPS} off +RewriteRule ^ https://%{HTTP_HOST}:443%{REQUEST_URI} [R=302,L,QSA] + diff --git a/container/registry/base/comanage_shibboleth_sp_utils.sh b/container/registry/base/comanage_shibboleth_sp_utils.sh new file mode 100755 index 00000000..dd9cacf0 --- /dev/null +++ b/container/registry/base/comanage_shibboleth_sp_utils.sh @@ -0,0 +1,317 @@ +#!/bin/bash + +# COmanage Registry Shibboleth SP Dockerfile entrypoint +# +# Portions licensed to the University Corporation for Advanced Internet +# Development, Inc. ("UCAID") under one or more contributor license agreements. +# See the NOTICE file distributed with this work for additional information +# regarding copyright ownership. +# +# UCAID licenses this file to you under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with the +# License. You may obtain a copy of the License at: +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +if [ -n "$COMANAGE_DEBUG" ] +then + OUTPUT=/dev/stdout +else + OUTPUT=/dev/null +fi + +########################################## +# Consume injected environment variables +# Globals: +# See function +# Arguments: +# None +# Returns: +# None +########################################## +function comanage_shibboleth_sp_utils::consume_injected_environment() { + + echo "Examining environment variables for Shibboleth SP..." > "$OUTPUT" + + # Configuration details that may be injected through environment + # variables or the contents of files. + # + # SHIBBOLETH_SP_METADATA_PROVIDER_XML may also be injected in the + # same way but because of the presence of special characters in the + # XML it is handled differently. + + injectable_config_vars=( + SHIBBOLETH_SP_ENTITY_ID + SHIBBOLETH_SP_CERT + SHIBBOLETH_SP_PRIVKEY + SHIBBOLETH_SP_SIGNING_CERT + SHIBBOLETH_SP_SIGNING_PRIVKEY + SHIBBOLETH_SP_ENCRYPT_CERT + SHIBBOLETH_SP_ENCRYPT_PRIVKEY + SHIBBOLETH_SP_SAMLDS_URL + ) + + # If the file associated with a configuration variable is present then + # read the value from it into the appropriate variable. So for example + # if the variable COMANAGE_REGISTRY_DATASOURCE_FILE exists and its + # value points to a file on the file system then read the contents + # of that file into the variable COMANAGE_REGISTRY_DATASOURCE. + + for config_var in "${injectable_config_vars[@]}" + do + eval file_name=\$"${config_var}_FILE"; + + if [ -e "$file_name" ]; then + payload=`cat $file_name` + declare "${config_var}"="${payload}" + fi + done + + echo "Done examining environment variables" > "$OUTPUT" +} + +########################################## +# Prepare shibboleth2.xml configuration file +# Globals: +# OUTPUT +# SHIBBOLETH_SP_ENTITY_ID +# SHIBBOLETH_SP_SAMLDS_URL +# SHIBBOLETH_SP_METADATA_PROVIDER_XML_FILE +# Arguments: +# None +# Returns: +# None +########################################## +function comanage_shibboleth_sp_utils::prepare_shibboleth2xml() { + + local shib_file + local xml_content_file + local sed_script_file + + # If no shibboleth2.xml file is present then create one using + # injected information or defaults that are not particularly + # useful in a federated context but will allow shibd to start. + shib_file='/etc/shibboleth/shibboleth2.xml' + + if [[ ! -e "${shib_file}" ]]; then + cp "${shib_file}.template" "${shib_file}" > "${OUTPUT}" 2>&1 + sed -i -e s@%%SHIBBOLETH_SP_ENTITY_ID%%@"${SHIBBOLETH_SP_ENTITY_ID:-https://comanage.registry/shibboleth}"@ "${shib_file}" > "${OUTPUT}" 2>&1 + sed -i -e s@%%SHIBBOLETH_SP_SAMLDS_URL%%@"${SHIBBOLETH_SP_SAMLDS_URL:-https://localhost/registry/pages/eds/index}"@ "${shib_file}" > "${OUTPUT}" 2>&1 + + # The metadata provider injected input most likely contains special characters + # so use a sed script instead of simple substitution on the command line. + + if [[ -n "${SHIBBOLETH_SP_METADATA_PROVIDER_XML_FILE}" ]]; then + xml_content_file="${SHIBBOLETH_SP_METADATA_PROVIDER_XML_FILE}" + else + xml_content_file=`/bin/mktemp` + echo "${SHIBBOLETH_SP_METADATA_PROVIDER_XML:-}" > "${xml_content_file}" + fi + + sed_script_file=`/bin/mktemp` + cat > ${sed_script_file}< "${OUTPUT}" 2>&1 + + chmod 0644 "${shib_file}" > "${OUTPUT}" 2>&1 + + rm -f "${xml_content_file}" > "${OUTPUT}" 2>&1 + rm -f "${sed_script_file}" > "${OUTPUT}" 2>&1 + + fi +} + +########################################## +# Prepare SAML certs and keys +# Globals: +# SHIBBOLETH_SP_CERT +# SHIBBOLETH_SP_PRIVKEY +# SHIBBOLETH_SP_SIGNING_CERT +# SHIBBOLETH_SP_SIGNING_PRIVKEY +# SHIBBOLETH_SP_ENCRYPT_CERT +# SHIBBOLETH_SP_ENCRYPT_PRIVKEY +# Arguments: +# None +# Returns: +# None +########################################## +function comanage_shibboleth_sp_utils::prepare_saml_cert_key() { + + local saml_file + local owner + + if [[ -e '/etc/debian_version' ]]; then + owner='_shibd' + elif [[ -e '/etc/centos-release' ]]; then + owner='shibd' + fi + + # If defined use configured location of Shibboleth SP SAML certificate and key. + saml_file='/etc/shibboleth/sp-cert.pem' + if [[ -n "${SHIBBOLETH_SP_CERT}" ]]; then + cp "${SHIBBOLETH_SP_CERT}" "${saml_file}" + chown "${owner}" "${saml_file}" + chmod 0644 "${saml_file}" + fi + + saml_file='/etc/shibboleth/sp-key.pem' + if [[ -n "${SHIBBOLETH_SP_PRIVKEY}" ]]; then + cp "${SHIBBOLETH_SP_PRIVKEY}" "${saml_file}" + chown "${owner}" "${saml_file}" + chmod 0600 "${saml_file}" + fi + + saml_file='/etc/shibboleth/sp-signing-cert.pem' + if [[ -n "${SHIBBOLETH_SP_SIGNING_CERT}" ]]; then + cp "${SHIBBOLETH_SP_SIGNING_CERT}" "${saml_file}" + chown "${owner}" "${saml_file}" + chmod 0644 "${saml_file}" + fi + + saml_file='/etc/shibboleth/sp-signing-key.pem' + if [[ -n "${SHIBBOLETH_SP_SIGNING_PRIVKEY}" ]]; then + cp "${SHIBBOLETH_SP_SIGNING_PRIVKEY}" "${saml_file}" + chown "${owner}" "${saml_file}" + chmod 0600 "${saml_file}" + fi + + saml_file='/etc/shibboleth/sp-encrypt-cert.pem' + if [[ -n "${SHIBBOLETH_SP_ENCRYPT_CERT}" ]]; then + cp "${SHIBBOLETH_SP_ENCRYPT_CERT}" "${saml_file}" + chown "${owner}" "${saml_file}" + chmod 0644 "${saml_file}" + fi + + saml_file='/etc/shibboleth/sp-encrypt-key.pem' + if [[ -n "${SHIBBOLETH_SP_ENCRYPT_PRIVKEY}" ]]; then + cp "${SHIBBOLETH_SP_ENCRYPT_PRIVKEY}" "${saml_file}" + chown "${owner}" "${saml_file}" + chmod 0600 "${saml_file}" + fi +} + +########################################## +# Manage UID and GID on files +# Globals: +# None +# Arguments: +# None +# Returns: +# None +########################################## +function comanage_shibboleth_sp_utils::manage_uid_gid() { + + local owner + local ownership + local not_readable + + # A deployer may make their own mapping between the shibd username + # and the UID, and between the shibd group and GID, so before starting + # make sure files have the correct ownership and group. + + not_readable='/tmp/shibd-not-readable' + + if [[ -e '/etc/debian_version' ]]; then + owner='_shibd' + ownership="${owner}:${owner}" + + chown "${ownership}" /etc/shibboleth/sp-cert.pem > /dev/null 2>&1 + chown "${ownership}" /etc/shibboleth/sp-key.pem > /dev/null 2>&1 + + chown "${ownership}" /etc/shibboleth/sp-signing-cert.pem > /dev/null 2>&1 + chown "${ownership}" /etc/shibboleth/sp-signing-key.pem > /dev/null 2>&1 + + chown "${ownership}" /etc/shibboleth/sp-encrypt-cert.pem > /dev/null 2>&1 + chown "${ownership}" /etc/shibboleth/sp-encrypt-key.pem > /dev/null 2>&1 + + chown "${ownership}" /opt/shibboleth-sp/var > /dev/null 2>&1 + chown "${ownership}" /opt/shibboleth-sp/var > /dev/null 2>&1 + chown "${ownership}" /opt/shibboleth-sp/var/run > /dev/null 2>&1 + chown "${ownership}" /opt/shibboleth-sp/var/run/shibboleth > /dev/null 2>&1 + chown "${ownership}" /opt/shibboleth-sp/var/run/shibboleth/shibd.sock > /dev/null 2>&1 + chown "${ownership}" /opt/shibboleth-sp/var/log > /dev/null 2>&1 + chown "${ownership}" /opt/shibboleth-sp/var/log/shibboleth > /dev/null 2>&1 + chown "${ownership}" /opt/shibboleth-sp/var/log/shibboleth/transaction.log > /dev/null 2>&1 + chown "${ownership}" /opt/shibboleth-sp/var/log/shibboleth/signature.log > /dev/null 2>&1 + chown "${ownership}" /opt/shibboleth-sp/var/log/shibboleth/shibd_warn.log > /dev/null 2>&1 + chown "${ownership}" /opt/shibboleth-sp/var/log/shibboleth/shibd.log > /dev/null 2>&1 + chown "${ownership}" /opt/shibboleth-sp/var/log/shibboleth-www > /dev/null 2>&1 + chown "${ownership}" /opt/shibboleth-sp/var/cache > /dev/null 2>&1 + chown "${ownership}" /opt/shibboleth-sp/var/cache/shibboleth > /dev/null 2>&1 + elif [[ -e '/etc/centos-release' ]]; then + owner='shibd' + ownership="${owner}:${owner}" + + chown "${ownership}" /etc/shibboleth/sp-cert.pem > /dev/null 2>&1 + chown "${ownership}" /etc/shibboleth/sp-key.pem > /dev/null 2>&1 + + chown "${ownership}" /etc/shibboleth/sp-signing-cert.pem > /dev/null 2>&1 + chown "${ownership}" /etc/shibboleth/sp-signing-key.pem > /dev/null 2>&1 + + chown "${ownership}" /etc/shibboleth/sp-encrypt-cert.pem > /dev/null 2>&1 + chown "${ownership}" /etc/shibboleth/sp-encrypt-key.pem > /dev/null 2>&1 + fi + + # Warn about any files the shibd user cannot read. + sudo -u "${owner}" find /etc/shibboleth ! -readable > "${not_readable}" 2>/dev/null + if [[ -s "${not_readable}" ]]; then + echo "WARNING: the following files are not readable by ${owner}" + cat "${not_readable}" + echo "" + fi + + rm -f "${not_readable}" > /dev/null 2>&1 +} + +########################################## +# Exec to start and become Shibboleth SP shibd +# Globals: +# None +# Arguments: +# Command and arguments to exec +# Returns: +# Does not return +########################################## +function comanage_shibboleth_sp_utils::exec_shibboleth_sp_daemon() { + + local user + local group + local shibd_daemon + local config + local pidfile + + comanage_shibboleth_sp_utils::consume_injected_environment + + comanage_shibboleth_sp_utils::prepare_shibboleth2xml + + comanage_shibboleth_sp_utils::prepare_saml_cert_key + + comanage_shibboleth_sp_utils::manage_uid_gid + + config='/etc/shibboleth/shibboleth2.xml' + pidfile='/var/run/shibboleth/shibd.pid' + + if [[ -e '/etc/debian_version' ]]; then + user='_shibd' + group='_shibd' + shibd_daemon='/opt/shibboleth-sp/sbin/shibd' + elif [[ -e '/etc/centos-release' ]]; then + user='shibd' + group='shibd' + shibd_daemon='/usr/sbin/shibd' + export LD_LIBRARY_PATH=/opt/shibboleth/lib64 + fi + + exec "${shibd_daemon}" -f -u "${user}" -g "${group}" -c "${config}" -p "${pidfile}" -F +} diff --git a/container/registry/base/comanage_utils.sh b/container/registry/base/comanage_utils.sh new file mode 100644 index 00000000..58f345b9 --- /dev/null +++ b/container/registry/base/comanage_utils.sh @@ -0,0 +1,799 @@ +#!/bin/bash + +# COmanage Registry bash shell utilities +# +# Portions licensed to the University Corporation for Advanced Internet +# Development, Inc. ("UCAID") under one or more contributor license agreements. +# See the NOTICE file distributed with this work for additional information +# regarding copyright ownership. +# +# UCAID licenses this file to you under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with the +# License. You may obtain a copy of the License at: +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +if [ -n "$COMANAGE_REGISTRY_DEBUG" ] +then + OUTPUT=/dev/stdout +else + OUTPUT=/dev/null +fi + +########################################## +# Configure CakePHP cache disable +# Globals: +# COMANAGE_REGISTRY_DIR +# OUTPUT +# Arguments: +# None +# Returns: +# None +########################################## +function comanage_utils::configure_cache_disable() { + sed -i -e '/Cache.disable/ s+/++g' "$COMANAGE_REGISTRY_DIR/app/Config/core.php" +} + +########################################## +# Configure CakePHP debug level +# Globals: +# COMANAGE_REGISTRY_DIR +# OUTPUT +# Arguments: +# None +# Returns: +# None +########################################## +function comanage_utils::configure_cakephp_debug() { + sed -i -e '/Configure::write(.debug/ s/0/2/' "$COMANAGE_REGISTRY_DIR/app/Config/core.php" +} + +########################################## +# Configure CakePHP DebugKit +# Globals: +# COMANAGE_REGISTRY_DIR +# OUTPUT +# Arguments: +# None +# Returns: +# None +########################################## +function comanage_utils::configure_cakephp_debugkit() { + sed -i -e '/\$components = array/ s/$/'"'"'DebugKit.Toolbar'"'"',/' "$COMANAGE_REGISTRY_DIR/app/Controller/AppController.php" +} + +########################################## +# Consume injected environment variables +# Globals: +# See function +# Arguments: +# None +# Returns: +# None +########################################## +function comanage_utils::consume_injected_environment() { + + echo "Examining environment variables..." > "$OUTPUT" + + # Configuration details that may be injected through environment + # variables or the contents of files. + local injectable_config_vars + + injectable_config_vars=( + COMANAGE_REGISTRY_ADMIN_GIVEN_NAME + COMANAGE_REGISTRY_ADMIN_FAMILY_NAME + COMANAGE_REGISTRY_ADMIN_USERNAME + COMANAGE_REGISTRY_CRONTAB + COMANAGE_REGISTRY_DATASOURCE + COMANAGE_REGISTRY_DATABASE + COMANAGE_REGISTRY_DATABASE_HOST + COMANAGE_REGISTRY_DATABASE_PORT + COMANAGE_REGISTRY_DATABASE_USER + COMANAGE_REGISTRY_DATABASE_USER_PASSWORD + COMANAGE_REGISTRY_EMAIL_FROM + COMANAGE_REGISTRY_EMAIL_TRANSPORT + COMANAGE_REGISTRY_EMAIL_HOST + COMANAGE_REGISTRY_EMAIL_PORT + COMANAGE_REGISTRY_EMAIL_ACCOUNT + COMANAGE_REGISTRY_EMAIL_ACCOUNT_PASSWORD + COMANAGE_REGISTRY_NO_DATABASE_CONFIG + COMANAGE_REGISTRY_NO_EMAIL_CONFIG + COMANAGE_REGISTRY_SECURITY_SALT + COMANAGE_REGISTRY_SECURITY_SEED + COMANAGE_REGISTRY_VIRTUAL_HOST_FQDN + HTTPS_CERT_FILE + HTTPS_PRIVKEY_FILE + ) + + # If the file associated with a configuration variable is present then + # read the value from it into the appropriate variable. So for example + # if the variable COMANAGE_REGISTRY_DATASOURCE_FILE exists and its + # value points to a file on the file system then read the contents + # of that file into the variable COMANAGE_REGISTRY_DATASOURCE. + + local config_var + for config_var in "${injectable_config_vars[@]}" + do + local file_name + eval file_name=\$"${config_var}_FILE"; + + if [[ -e "$file_name" ]]; then + declare -g "${config_var}"=`cat $file_name` + echo "Set ${config_var} to be contents of ${file_name}" > "$OUTPUT" + fi + done + + echo "Done examining environment variables" > "$OUTPUT" +} + +########################################## +# Deploy crontab file +# Globals: +# COMANAGE_REGISTRY_DIR +# COMANAGE_REGISTRY_CRONTAB +# OUTPUT +# Arguments: +# None +# Returns: +# None +########################################## +function comanage_utils::deploy_crontab() { + + local crontab + + if [[ -n "$COMANAGE_REGISTRY_CRONTAB" ]]; then + crontab="$COMANAGE_REGISTRY_CRONTAB" + else + crontab="$COMANAGE_REGISTRY_DIR/local/crontab" + fi + + if [[ -f "$crontab" ]]; then + echo "Deploying crontab $crontab..." > "$OUTPUT" 2>&1 + /usr/bin/crontab -u www-data $crontab > "$OUTPUT" 2>&1 + fi +} + +########################################## +# Enable non-core plugins +# Globals: +# COMANAGE_REGISTRY_DIR +# COMANAGE_REGISTRY_ENABLE_PLUGIN +# OUTPUT +# Arguments: +# None +# Returns: +# None +########################################## +function comanage_utils::enable_plugins() { + + # Enable any supported non-core plugins if requested. + if [[ -n "$COMANAGE_REGISTRY_ENABLE_PLUGIN" ]]; then + # Clear the caches. + comanage_utils::registry_clear_cache + + local plugins + local plugin + plugins=(`echo "$COMANAGE_REGISTRY_ENABLE_PLUGIN" | sed -e 's@,@ @g'`) > "$OUTPUT" 2>&1 + pushd "$COMANAGE_REGISTRY_DIR/local/Plugin" > "$OUTPUT" 2>&1 + for plugin in "${plugins[@]}"; + do + echo "Enabling available plugin $plugin..." > "$OUTPUT" 2>&1 + ln -s "../../app/AvailablePlugin/$plugin" "$plugin" > "$OUTPUT" 2>&1 + done + + popd > "$OUTPUT" 2>&1 + pushd "$COMANAGE_REGISTRY_DIR/app" > "$OUTPUT" 2>&1 + ./Console/cake database > "$OUTPUT" 2>&1 + popd > "$OUTPUT" 2>&1 + + # Clear the caches again. + comanage_utils::registry_clear_cache + fi +} + +########################################## +# Exec to start and become Apache HTTP Server +# Globals: +# COMANAGE_REGISTRY_NO_DATABASE_CONFIG +# COMANAGE_REGISTRY_NO_EMAIL_CONFIG +# Arguments: +# Command and arguments to exec +# Returns: +# Does not return +########################################## +function comanage_utils::exec_apache_http_server() { + + comanage_utils::consume_injected_environment + + comanage_utils::prepare_local_directory + + if [[ -z ${COMANAGE_REGISTRY_NO_DATABASE_CONFIG} ]]; then + comanage_utils::prepare_database_config + fi + + #if [[ -z ${COMANAGE_REGISTRY_NO_EMAIL_CONFIG} ]]; then + # comanage_utils::prepare_email_config + #fi + + comanage_utils::prepare_https_cert_key + + comanage_utils::prepare_server_name + + comanage_utils::wait_database_connectivity + + comanage_utils::registry_setup + + #comanage_utils::registry_upgrade + + #comanage_utils::enable_plugins + + #comanage_utils::registry_clear_cache + + comanage_utils::tmp_ownership + + # first arg is `-f` or `--some-option` + if [ "${1#-}" != "$1" ]; then + set -- apache2-foreground "$@" + fi + + exec "$@" +} + +########################################## +# Exec to start and become cron +# Globals: +# None +# Arguments: +# Command and arguments to exec +# Returns: +# Does not return +########################################## +function comanage_utils::exec_cron() { + + comanage_utils::consume_injected_environment + + comanage_utils::prepare_local_directory + + if [[ -z ${COMANAGE_REGISTRY_NO_DATABASE_CONFIG} ]]; then + comanage_utils::prepare_database_config + fi + + comanage_utils::wait_database_connectivity + + comanage_utils::registry_clear_cache + + comanage_utils::configure_cache_disable + + comanage_utils::tmp_ownership + + comanage_utils::deploy_crontab + + comanage_utils::start_syslogd + + exec "$@" +} + +########################################## +# Prepare database configuration +# Globals: +# COMANAGE_REGISTRY_DATABASE +# COMANAGE_REGISTRY_DATABASE_HOST +# COMANAGE_REGISTRY_DATABASE_PORT +# COMANAGE_REGISTRY_DATABASE_USER +# COMANAGE_REGISTRY_DATABASE_USER_PASSWORD +# COMANAGE_REGISTRY_DATASOURCE +# COMANAGE_REGISTRY_DIR +# OUTPUT +# Arguments: +# None +# Returns: +# None +########################################## +function comanage_utils::prepare_database_config() { + + # If the COmanage Registry database configuration file does not exist + # then try to create it from injected information with reasonable defaults + # that aid simple evaluation deployments. + local database_config + database_config="$COMANAGE_REGISTRY_DIR/local/config/database.php" + + # File already exists so return. + if [[ -e "$database_config" ]]; then + return + fi + + # File does not exist so create it. + local php_string + + read -r -d '' php_string <<'EOF' + [ + 'default' => [ + 'className' => 'Cake\Database\Connection', +EOF + + php_string+=$'\n ' + php_string+="'driver' => '${COMANAGE_REGISTRY_DATABASE_DRIVER:-Cake\Database\Driver\Postgres}'," + + php_string+=$'\n ' + php_string+="'persistent' => '${COMANAGE_REGISTRY_DATABASE_PERSISTENT:-false}'," + + php_string+=$'\n ' + php_string+="'host' => '${COMANAGE_REGISTRY_DATABASE_HOST:-registry-database}'," + + php_string+=$'\n ' + php_string+="'username' => '${COMANAGE_REGISTRY_DATABASE_USER:-registry_user}'," + + php_string+=$'\n ' + php_string+="'password' => '${COMANAGE_REGISTRY_DATABASE_USER_PASSWORD:-password}'," + + php_string+=$'\n ' + php_string+="'database' => '${COMANAGE_REGISTRY_DATABASE:-registry}'," + + # The value of port is an integer. + if [[ -n "${COMANAGE_REGISTRY_DATABASE_PORT}" ]]; then + php_string+=$'\n ' + php_string+="'port' => ${COMANAGE_REGISTRY_DATABASE_PORT}," + fi + + php_string+=$'\n ' + php_string+="'encoding' => '${COMANAGE_REGISTRY_DATABASE_ENCODING:-utf8}'," + + php_string+=$'\n ' + php_string+="'timezone' => '${COMANAGE_REGISTRY_DATABASE_ENCODING:-UTC}'," + + # Only used when the database driver is Postgres. + if [[ ((${COMANAGE_REGISTRY_DATABASE_DRIVER} == 'Cake\Database\Driver\Postgres') || + -z ${COMANAGE_REGISTRY_DATABASE_DRIVER}) && + -n ${COMANAGE_REGISTRY_DATABASE_SCHEMA} ]]; then + php_string+=$'\n ' + php_string+="'schema' => '${COMANAGE_REGISTRY_DATABASE_SCHEMA}'," + fi + + if [[ -n ${COMANAGE_REGISTRY_DATABASE_UNIX_SOCKET} ]]; then + php_string+=$'\n ' + php_string+="'unix_socket' => '${COMANAGE_REGISTRY_DATABASE_UNIX_SOCKET}'," + fi + + # Only used when the database driver is MySQL. + if [[ (${COMANAGE_REGISTRY_DATABASE_DRIVER} == 'Cake\Database\Driver\Mysql') && + -n ${COMANAGE_REGISTRY_DATABASE_SSL_KEY} ]]; then + php_string+=$'\n ' + php_string+="'ssl_key' => '${COMANAGE_REGISTRY_DATABASE_SSL_KEY}'," + fi + + # Only used when the database driver is MySQL. + if [[ (${COMANAGE_REGISTRY_DATABASE_DRIVER} == 'Cake\Database\Driver\Mysql') && + -n ${COMANAGE_REGISTRY_DATABASE_SSL_CERT} ]]; then + php_string+=$'\n ' + php_string+="'ssl_cert' => '${COMANAGE_REGISTRY_DATABASE_SSL_CERT}'," + fi + + # Only used when the database driver is MySQL. + if [[ (${COMANAGE_REGISTRY_DATABASE_DRIVER} == 'Cake\Database\Driver\Mysql') && + -n ${COMANAGE_REGISTRY_DATABASE_SSL_CA} ]]; then + php_string+=$'\n ' + php_string+="'ssl_ca' => '${COMANAGE_REGISTRY_DATABASE_SSL_CA}'," + fi + + php_string+=$'\n ' + php_string+="'log' => '${COMANAGE_REGISTRY_DATABASE_LOG:-false}'," + + php_string+=$'\n ' + php_string+="'quoteIdentifiers' => '${COMANAGE_REGISTRY_DATABASE_QUOTE_IDENTIFIERS:-false}'," + + php_string+=$'\n ' + php_string+="'cacheMetadata' => '${COMANAGE_REGISTRY_DATABASE_CACHE_METADATA:-true}'," + + php_string+=$'\n ]\n ]\n];\n'; + + printf "%s" "$php_string" > $database_config + + echo "Wrote new database configuration file ${database_config}" > "$OUTPUT" +} + +########################################## +# Prepare email configuration +# Globals: +# COMANAGE_REGISTRY_EMAIL_ACCOUNT +# COMANAGE_REGISTRY_EMAIL_ACCOUNT_PASSWORD +# COMANAGE_REGISTRY_EMAIL_FROM +# COMANAGE_REGISTRY_EMAIL_HOST +# COMANAGE_REGISTRY_EMAIL_PORT +# COMANAGE_REGISTRY_EMAIL_TRANSPORT +# COMANAGE_REGISTRY_DIR +# Arguments: +# None +# Returns: +# None +########################################## +function comanage_utils::prepare_email_config() { + + # If the COmanage Registry email configuration file does not exist + # then try to create it from injected information with reasonable defaults + # that aid simple evaluation deployments. + local email_config + email_config="$COMANAGE_REGISTRY_DIR/local/config/email.php" + + # File already exists so return. + if [[ -e ${email_config} ]]; then + return + fi + + # File does not exist so create it. + local php_string + + read -r -d '' php_string <<'EOF' + array('${COMANAGE_REGISTRY_EMAIL_FROM_EMAIL}' => '${COMANAGE_REGISTRY_EMAIL_FROM_NAME}')," + elif [[ -n "${COMANAGE_REGISTRY_EMAIL_FROM_EMAIL}" && -z "${COMANAGE_REGISTRY_EMAIL_FROM_NAME}" ]]; then + php_string+="'from' => '${COMANAGE_REGISTRY_EMAIL_FROM_EMAIL}'," + elif [[ -n "${COMANAGE_REGISTRY_EMAIL_FROM}" && -z "${COMANAGE_REGISTRY_EMAIL_FROM_EMAIL}" ]]; then + php_string+="'from' => '${COMANAGE_REGISTRY_EMAIL_FROM}'," + elif [[ -z "${COMANAGE_REGISTRY_EMAIL_FROM}" ]]; then + php_string+="'from' => 'you@localhost'," + fi + + if [[ -n "${COMANAGE_REGISTRY_EMAIL_TRANSPORT}" ]]; then + php_string+=$'\n\t\t' + php_string+="'transport' => '${COMANAGE_REGISTRY_EMAIL_TRANSPORT}'," + fi + + if [[ -n "${COMANAGE_REGISTRY_EMAIL_HOST}" ]]; then + php_string+=$'\n\t\t' + php_string+="'host' => '${COMANAGE_REGISTRY_EMAIL_HOST}'," + fi + + # The value of port is an integer. + if [[ -n "${COMANAGE_REGISTRY_EMAIL_PORT}" ]]; then + php_string+=$'\n\t\t' + php_string+="'port' => ${COMANAGE_REGISTRY_EMAIL_PORT}," + fi + + if [[ -n "${COMANAGE_REGISTRY_EMAIL_ACCOUNT}" ]]; then + php_string+=$'\n\t\t' + php_string+="'username' => '${COMANAGE_REGISTRY_EMAIL_ACCOUNT}'," + fi + + if [[ -n "${COMANAGE_REGISTRY_EMAIL_ACCOUNT_PASSWORD}" ]]; then + php_string+=$'\n\t\t' + php_string+="'password' => '${COMANAGE_REGISTRY_EMAIL_ACCOUNT_PASSWORD}'," + fi + + php_string+=$'\n\t\t);\n\n}\n'; + + printf "%s" "$php_string" > $email_config +} + +########################################## +# Prepare cert and key for HTTPS +# Globals: +# HTTPS_CERT_FILE +# HTTPS_PRIVKEY_FILE +# Arguments: +# None +# Returns: +# None +########################################## +function comanage_utils::prepare_https_cert_key() { + + local cert_path + local privkey_path + local web_user + + cert_path='/etc/apache2/cert.pem' + privkey_path='/etc/apache2/privkey.pem' + web_user='www-data' + + # If defined use configured location of Apache HTTP Server + # HTTPS certificate and key files. The certificate file may also + # include intermediate CA certificates, sorted from leaf to root. + if [[ -n "${HTTPS_CERT_FILE}" ]]; then + rm -f "${cert_path}" + cp "${HTTPS_CERT_FILE}" "${cert_path}" + chown "${web_user}" "${cert_path}" + chmod 0644 "${cert_path}" + echo "Copied HTTPS certificate file ${HTTPS_CERT_FILE} to ${cert_path}" > "$OUTPUT" + echo "Set ownership of ${cert_path} to ${web_user}" > "$OUTPUT" + fi + + if [[ -n "${HTTPS_PRIVKEY_FILE}" ]]; then + rm -f "${privkey_path}" + cp "${HTTPS_PRIVKEY_FILE}" "${privkey_path}" + chown "${web_user}" "${privkey_path}" + chmod 0600 "${privkey_path}" + echo "Copied HTTPS private key file ${HTTPS_PRIVKEY_FILE} to ${privkey_path}" > "$OUTPUT" + echo "Set ownership of ${privkey_path} to ${web_user}" > "$OUTPUT" + fi +} + +########################################## +# Prepare local directory structure +# Globals: +# COMANAGE_REGISTRY_DIR +# Arguments: +# None +# Returns: +# None +########################################## +function comanage_utils::prepare_local_directory() { + + # Make sure the directory structure we need is available + # in the data volume for $COMANAGE_REGISTRY_DIR/local/config + local directories + + declare -a directories=("config") + + local dir + local full_path + for dir in "${directories[@]}" + do + full_path="${COMANAGE_REGISTRY_DIR}/local/${dir}" + if [[ ! -d "${full_path}" ]]; then + mkdir -p "${full_path}" > "$OUTPUT" 2>&1 + echo "Created directory ${full_path}" + fi + done +} + +########################################## +# Prepare web server name +# Globals: +# SERVER_NAME +# COMANAGE_REGISTRY_VIRTUAL_HOST_FQDN +# Arguments: +# None +# Returns: +# None +########################################## +function comanage_utils::prepare_server_name() { + + # If COMANAGE_REGISTRY_VIRTUAL_HOST_FQDN has not been injected try to determine + # it from the HTTPS_CERT_FILE. + if [[ -z "$COMANAGE_REGISTRY_VIRTUAL_HOST_FQDN" ]]; then + COMANAGE_REGISTRY_VIRTUAL_HOST_FQDN=$(openssl x509 -in /etc/apache2/cert.pem -text -noout | + sed -n '/X509v3 Subject Alternative Name:/ {n;p}' | + sed -E 's/.*DNS:(.*)\s*$/\1/') + if [[ -n "$COMANAGE_REGISTRY_VIRTUAL_HOST_FQDN" ]]; then + echo "Set COMANAGE_REGISTRY_VIRTUAL_HOST_FQDN=${COMANAGE_REGISTRY_VIRTUAL_HOST_FQDN} using Subject Alternative Name from x509 certificate" > "$OUTPUT" + else + COMANAGE_REGISTRY_VIRTUAL_HOST_FQDN=$(openssl x509 -in /etc/apache2/cert.pem -subject -noout | + sed -E 's/subject=.*CN=(.*)\s*/\1/') + if [[ -n "$COMANAGE_REGISTRY_VIRTUAL_HOST_FQDN" ]]; then + echo "Set COMANAGE_REGISTRY_VIRTUAL_HOST_FQDN=${COMANAGE_REGISTRY_VIRTUAL_HOST_FQDN} using CN from x509 certificate" > "$OUTPUT" + fi + fi + fi + + # Configure Apache HTTP Server with the server name. + # This configures the server name for the default Debian + # Apache HTTP Server configuration but not the server name used + # by any virtual hosts. + cat > /etc/apache2/conf-available/server-name.conf < "$OUTPUT" 2>&1 + + # Export the server name so that it may be used by + # Apache HTTP Server virtual host configurations. + export COMANAGE_REGISTRY_VIRTUAL_HOST_FQDN +} + +########################################## +# Clear CakePHP cache files +# Globals: +# None +# Arguments: +# None +# Returns: +# None +########################################## +function comanage_utils::registry_clear_cache() { + + local cache_dir + cache_dir="$COMANAGE_REGISTRY_DIR/app/tmp/cache" + + if [[ -d $cache_dir ]]; then + find $cache_dir -type f -exec rm -f {} \; + echo "Cleared COmanage Registry CakePHP cache files in ${cache_dir}" > "$OUTPUT" + fi + +} + +########################################## +# Run COmanage Registry setup shell command +# Globals: +# COMANAGE_REGISTRY_ADMIN_GIVEN_NAME +# COMANAGE_REGISTRY_ADMIN_FAMILY_NAME +# COMANAGE_REGISTRY_ADMIN_USERNAME +# COMANAGE_REGISTRY_DIR +# COMANAGE_REGISTRY_ENABLE_POOLING +# COMANAGE_REGISTRY_SECURITY_SALT +# COMANAGE_REGISTRY_SECURITY_SEED +# OUTPUT +# Arguments: +# None +# Returns: +# None +########################################## +function comanage_utils::registry_setup() { + + local salt_file="${COMANAGE_REGISTRY_DIR}/local/config/security.salt" + local seed_file="${COMANAGE_REGISTRY_DIR}/local/config/security.seed" + + if [[ -e ${salt_file} ]]; then + return + fi + + pushd "$COMANAGE_REGISTRY_DIR/app" > "$OUTPUT" 2>&1 + + echo "COmanage Registry setup has not been done previously" > "$OUTPUT" + + rm -f "$COMANAGE_REGISTRY_DIR/local/Config/security.salt" > "$OUTPUT" 2>&1 + rm -f "$COMANAGE_REGISTRY_DIR/local/Config/security.seed" > "$OUTPUT" 2>&1 + + echo "Running ./bin/cake database..." > "$OUTPUT" + ./bin/cake database > "$OUTPUT" 2>&1 + + echo "Running ./bin/cake setup..." > "$OUTPUT" + ./bin/cake setup \ + --admin-username "${COMANAGE_REGISTRY_ADMIN_USERNAME}" \ + # --admin-given-name "${COMANAGE_REGISTRY_ADMIN_GIVEN_NAME}" \ + # --admin-family-name "${COMANAGE_REGISTRY_ADMIN_FAMILY_NAME}" + + #echo "Set admin given name ${COMANAGE_REGISTRY_ADMIN_GIVEN_NAME}" > "$OUTPUT" + #echo "Set admin family name ${COMANAGE_REGISTRY_ADMIN_FAMILY_NAME}" > "$OUTPUT" + echo "Set admin username ${COMANAGE_REGISTRY_ADMIN_USERNAME}" > "$OUTPUT" + + # If salt and security were injected then delete those created + # by the SetupCommand and write the injected values. + if [[ -n ${COMANAGE_REGISTRY_SECURITY_SALT} ]]; then + echo ${COMANAGE_REGISTRY_SECURITY_SALT} > ${salt_file} + echo "Used injected value to write security salt file ${salt_file}" > "$OUTPUT" + chown www-data:www-data ${salt_file} + echo "Set ownership to www-data for ${salt_file}" > "$OUTPUT" + chmod 0600 ${salt_file} + echo "Set mode to 0600 for ${salt_file}" > "$OUTPUT" + fi + + if [[ -n ${COMANAGE_REGISTRY_SECURITY_SEED} ]]; then + echo ${COMANAGE_REGISTRY_SECURITY_SEED} > ${seed_file} + echo "Used injected value to write security seed file ${seed_file}" > "$OUTPUT" + chown www-data:www-data ${seed_file} + echo "Set ownership to www-data for ${seed_file}" > "$OUTPUT" + chmod 0600 ${seed_file} + echo "Set mode to 0600 for ${seed_file}" > "$OUTPUT" + fi +} + +########################################## +# Run COmanage Registry upgradeVersion shell command +# Globals: +# COMANAGE_REGISTRY_DATABASE_SCHEMA_FORCE +# COMANAGE_REGISTRY_DIR +# OUTPUT +# Arguments: +# None +# Returns: +# None +########################################## +function comanage_utils::registry_upgrade() { + + # We always run upgradeVersion since it will not make any changes + # if the current and target versions are the same or if + # an upgrade from the current to the target version is not allowed. + + # First clear the caches. + comanage_utils::registry_clear_cache + + pushd "$COMANAGE_REGISTRY_DIR/app" > "$OUTPUT" 2>&1 + echo "Running ./Console/cake upgradeVersion..." > "$OUTPUT" + ./Console/cake upgradeVersion > "$OUTPUT" 2>&1 + echo "Done running ./Console/cake upgradeVersion" > "$OUTPUT" + echo "You may ignore errors reported above if the Current and Target versions are the same" > "$OUTPUT" + popd > "$OUTPUT" 2>&1 + + # Force a datbase update if requested. This is helpful when deploying + # a new version of the code that does not result in a change in the + # version number and so upgradeVersion does not fire. An example + # of this scenario is when new code is introduced in the develop + # branch but before a release happens. + if [ -n "$COMANAGE_REGISTRY_DATABASE_SCHEMA_FORCE" ]; then + echo "Forcing a database schema update..." > "$OUTPUT" + pushd "$COMANAGE_REGISTRY_DIR/app" > "$OUTPUT" 2>&1 + ./Console/cake database > "$OUTPUT" 2>&1 + echo "Done forcing database schema update" > "$OUTPUT" + popd > "$OUTPUT" 2>&1 + fi + + # Clear the caches again. + comanage_utils::registry_clear_cache +} + +########################################## +# Start syslogd from busybox for use with cron +# Globals: +# None +# Arguments: +# None +# Returns: +# None +########################################## +function comanage_utils::start_syslogd() { + + /sbin/syslogd -O /proc/1/fd/1 -S + +} + +########################################## +# Set tmp directory file ownership +# Globals: +# COMANAGE_REGISTRY_DIR +# Arguments: +# None +# Returns: +# None +########################################## +function comanage_utils::tmp_ownership() { + + # Ensure that the web server user owns the tmp directory + # and all children. + local tmp_dir + local ownership + + ownership='www-data:www-data' + + tmp_dir="${COMANAGE_REGISTRY_DIR}/local/tmp" + + chown -R "${ownership}" "${tmp_dir}" + + echo "Recursively set ownership of ${tmp_dir} to ${ownership}" > "$OUTPUT" +} + +########################################## +# Wait until able to connect to database +# Globals: +# COMANAGE_REGISTRY_DIR +# OUTPUT +# Arguments: +# None +# Returns: +# None +########################################## +function comanage_utils::wait_database_connectivity() { + + pushd "$COMANAGE_REGISTRY_DIR/app" > "$OUTPUT" 2>&1 + + # Loop until we are able to open a connection to the database. + echo "Testing database availability..." > "$OUTPUT" + until ./bin/cake databaseTest > "$OUTPUT" 2>&1; do + >&2 echo "Database is unavailable - sleeping" + sleep 1 + done + + rm -f "$database_test_script" + + echo "Database is available" > "$OUTPUT" + + popd > "$OUTPUT" 2>&1 +} diff --git a/container/registry/base/docker-comanage-entrypoint b/container/registry/base/docker-comanage-entrypoint new file mode 100755 index 00000000..3ffc2331 --- /dev/null +++ b/container/registry/base/docker-comanage-entrypoint @@ -0,0 +1,24 @@ +#!/bin/bash + +# COmanage Registry Dockerfile entrypoint +# +# Portions licensed to the University Corporation for Advanced Internet +# Development, Inc. ("UCAID") under one or more contributor license agreements. +# See the NOTICE file distributed with this work for additional information +# regarding copyright ownership. +# +# UCAID licenses this file to you under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with the +# License. You may obtain a copy of the License at: +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +source /usr/local/lib/comanage_utils.sh + +comanage_utils::exec_apache_http_server "$@" diff --git a/container/registry/basic-auth/000-comanage.conf b/container/registry/basic-auth/000-comanage.conf new file mode 100644 index 00000000..1973469e --- /dev/null +++ b/container/registry/basic-auth/000-comanage.conf @@ -0,0 +1,42 @@ +# COmanage Registry Apache HTTP Server configuration +# +# Portions licensed to the University Corporation for Advanced Internet +# Development, Inc. ("UCAID") under one or more contributor license agreements. +# See the NOTICE file distributed with this work for additional information +# regarding copyright ownership. +# +# UCAID licenses this file to you under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with the +# License. You may obtain a copy of the License at: +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +Include apache-include-virtual-host-port80-redirect + + + +Include apache-include-virtual-host-port443-base + +SSLCertificateFile /etc/apache2/cert.pem +SSLCertificateKeyFile /etc/apache2/privkey.pem + +ErrorLog ${APACHE_LOG_DIR}/error.log +CustomLog ${APACHE_LOG_DIR}/access.log combined + +Include apache-include-directory-registry + + +AuthType Basic +AuthName "COmanage Registry Login" +AuthBasicProvider file +AuthUserFile "/etc/apache2/basic-auth" +Require valid-user + + + diff --git a/container/registry/basic-auth/Dockerfile b/container/registry/basic-auth/Dockerfile new file mode 100644 index 00000000..b8cd785f --- /dev/null +++ b/container/registry/basic-auth/Dockerfile @@ -0,0 +1,30 @@ +# COmanage Registry Dockerfile template +# +# Portions licensed to the University Corporation for Advanced Internet +# Development, Inc. ("UCAID") under one or more contributor license agreements. +# See the NOTICE file distributed with this work for additional information +# regarding copyright ownership. +# +# UCAID licenses this file to you under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with the +# License. You may obtain a copy of the License at: +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +ARG COMANAGE_REGISTRY_VERSION=develop +ARG COMANAGE_REGISTRY_BASE_IMAGE_VERSION=1 +FROM comanage-registry-base:${COMANAGE_REGISTRY_VERSION}-${COMANAGE_REGISTRY_BASE_IMAGE_VERSION} + +ARG COMANAGE_REGISTRY_BASE_IMAGE_VERSION +ENV COMANAGE_REGISTRY_BASE_IMAGE_VERSION ${COMANAGE_REGISTRY_BASE_IMAGE_VERSION} +LABEL comanage_registry_base_image_version=${COMANAGE_REGISTRY_BASE_IMAGE_VERSION} + +COPY container/registry/basic-auth/basic-auth /etc/apache2/ +COPY container/registry/basic-auth/000-comanage.conf /etc/apache2/sites-available/ + +RUN a2ensite 000-comanage diff --git a/container/registry/basic-auth/basic-auth b/container/registry/basic-auth/basic-auth new file mode 100644 index 00000000..250651ea --- /dev/null +++ b/container/registry/basic-auth/basic-auth @@ -0,0 +1 @@ +registry.admin:$apr1$qqrvav7G$nSHYErU4ljDPmO1wNBG6e0