diff --git a/www/actions/load b/www/actions/load new file mode 100755 index 00000000..92afe4cd --- /dev/null +++ b/www/actions/load @@ -0,0 +1,61 @@ +#!/bin/bash + +## deploy-files will deploy data files to $DOMAIN data from given FILE/DIRECTORY/URL +## +## + +if [ -z "$SERVICE_DATASTORE" ]; then + echo "This script is meant to be run through 'compose' to work properly." >&2 + exit 1 +fi + +usage="$exname [-h|--help] SOURCE DBNAME" + +SRC= +DOMAIN= +while [ "$1" ]; do + case "$1" in + "--help"|"-h") + print_usage + exit 0 + ;; + *) + [ -z "$SRC" ] && { SRC=$1 ; shift ; continue ; } + [ -z "$DOMAIN" ] && { DOMAIN=$1 ; shift ; continue ; } + err "Unexpected argument '$1'." + exit 1 + ;; + esac + shift +done + +if [ -z "$SRC" ]; then + err "You must provide a source file as first argument." + print_usage + exit 1 +fi + +if [ -z "$DOMAIN" ]; then + err "You must provide a destination domain as second argument." + exit 1 +fi + + +. lib/common + +set -e + +DOCKER_SITE_PATH=/var/www/$DOMAIN +if [ "$RELATION_BASE_CHARM" ]; then ## In a relation, we should use it then + DST=$DATASTORE/$RELATION_BASE_CHARM$DOCKER_SITE_PATH +else + DST=${SERVICE_DATASTORE}$DOCKER_SITE_PATH +fi + +echo rm -rf "$DST" + +mkdir -p "$DST" + +deploy_files "$SRC" "$DST" + +info "Deployed files from '$SRC' to '$DST'." diff --git a/www/actions/relations/publish-dir/load-files b/www/actions/relations/publish-dir/load-files new file mode 100755 index 00000000..6d05d040 --- /dev/null +++ b/www/actions/relations/publish-dir/load-files @@ -0,0 +1,44 @@ +#!/bin/bash + +## Load action gets a first argument a FILE/DIRECTORY/URL holding the necessary files. +## +## + +if [ -z "$SERVICE_DATASTORE" ]; then + echo "This script is meant to be run through 'compose' to work properly." >&2 + exit 1 +fi + +usage="$exname [-h|--help] SOURCE" + +while [ "$1" ]; do + case "$1" in + "--help"|"-h") + print_usage + exit 0 + ;; + *) + [ -z "$SOURCE" ] && { SOURCE=$1 ; shift ; continue ; } + err "Unexpected argument '$1'." + exit 1 + ;; + esac + shift +done + +if [ -z "$SOURCE" ]; then + err "You must provide a source file as first argument." + print_usage + exit 1 +fi + +include parse +include pretty + +set -e + +DOMAIN=$(relation-get domain) + +run_service_action "$RELATION_TARGET_CHARM" load "$SOURCE" "$DOMAIN" "$@" + +info "Correctly deployed '$SOURCE' towards domain '$DOMAIN'" diff --git a/www/build/Dockerfile b/www/build/Dockerfile new file mode 100644 index 00000000..993a411f --- /dev/null +++ b/www/build/Dockerfile @@ -0,0 +1,27 @@ +FROM docker.0k.io/apache:carif + + +## Limesurvey +RUN apt-get update && \ + DEBIAN_FRONTEND=noninteractive apt-get install -y libfreetype6-dev libjpeg62-turbo-dev libmcrypt-dev libpng12-dev && \ + apt-get clean && \ + rm -rf /var/lib/apt/lists/* && \ + docker-php-ext-configure gd --with-freetype-dir=/usr/include/ --with-jpeg-dir=/usr/include/ && \ + docker-php-ext-install gd pdo_mysql mbstring + +## Formanoo_nfo +RUN apt-get update && \ + DEBIAN_FRONTEND=noninteractive apt-get install -y libpq-dev && \ + apt-get clean && \ + rm -rf /var/lib/apt/lists/* +RUN docker-php-ext-install pgsql pdo_pgsql + +RUN a2enmod headers proxy_http rewrite ssl + +## Can remove this when SSL certificate are all valid ones +RUN apt-get update && apt-get install -y --force-yes ssl-cert + +COPY entrypoint.sh /entrypoint.sh + +ENTRYPOINT [ "/entrypoint.sh" ] + diff --git a/www/build/entrypoint.sh b/www/build/entrypoint.sh new file mode 100755 index 00000000..418e4f4c --- /dev/null +++ b/www/build/entrypoint.sh @@ -0,0 +1,21 @@ +#!/bin/bash + +. /etc/apache2/envvars + +if [ "$SERVER_NAME" ]; then + FILE=/etc/apache2/apache2.conf + if grep -E "^ServerName\s+.*\$" "$FILE" > /dev/null 2>&1; then + echo "Updated IP." + sed -ri "s/^(ServerName)(\s+[^ ]*)\s*$/\1 $SERVER_NAME/g" "$FILE" + else + #echo "Added IP." + echo "ServerName $SERVER_NAME" >> "$FILE" + fi +fi + +## Using exec replaces the current bash process with the given one. +## this is necessary if we want that apache2 receives signals correctly. +exec apache2-foreground "$@" + + + diff --git a/www/build/etc/apache2/site-enabled/monitor.formanoo.org.passwd b/www/build/etc/apache2/site-enabled/monitor.formanoo.org.passwd new file mode 100644 index 00000000..6c537b27 --- /dev/null +++ b/www/build/etc/apache2/site-enabled/monitor.formanoo.org.passwd @@ -0,0 +1 @@ +admin:$apr1$DxN/jqDN$0hboJaVxmG5ETv8vNdDAN0 diff --git a/www/hooks/init b/www/hooks/init new file mode 100755 index 00000000..596fc15f --- /dev/null +++ b/www/hooks/init @@ -0,0 +1,50 @@ +#!/bin/bash + +## Init is run on host +## For now it is run every time the script is launched, but +## it should be launched only once after build. + +## Accessible variables are: +## - SERVICE_NAME Name of current service +## - DOCKER_BASE_IMAGE Base image from which this service might be built if any +## - SERVICE_DATASTORE Location on host of the DATASTORE of this service +## - SERVICE_CONFIGSTORE Location on host of the CONFIGSTORE of this service + + +APACHE_LOG_DIR=/var/log/apache2 + +set -u + +cat < + +RewriteEngine On + +RewriteCond %{HTTPS} off +RewriteRule ^(.*)$ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301] + +EOF + + +cat < + ServerAdmin webmaster@localhost + DocumentRoot /var/www/html + + + Options FollowSymLinks + AllowOverride None + + + + Options Indexes FollowSymLinks MultiViews + AllowOverride all + Order allow,deny + allow from all + + + ErrorLog ${APACHE_LOG_DIR}/error.log + CustomLog ${APACHE_LOG_DIR}/access.log combined + + +EOF diff --git a/www/hooks/log_rotate-relation-joined b/www/hooks/log_rotate-relation-joined new file mode 100755 index 00000000..7481de07 --- /dev/null +++ b/www/hooks/log_rotate-relation-joined @@ -0,0 +1,49 @@ +#!/bin/bash + +## Should be executable N time in a row with same result. + +. lib/common + +set -e + +LOGS=/var/log/apache2 + +## XXXvlab: hum it seems apache logging is run as root, so well... +# logs_creds=$(cached_cmd_on_base_image apache "stat -c '%u %g' '$LOGS'") || { +# debug "Failed to query for www-data gid in ${DARKYELLOW}apache${NORMAL} base image." +# return 1 +# } + +## XXXvlab: a lot of this intelligence should be moved away into ``logrotate`` charm +DST="$CONFIGSTORE/$TARGET_SERVICE_NAME/etc/logrotate.d/$SERVICE_NAME" +file_put "$DST" </dev/null | shyaml get-values 2>/dev/null) || true +LOCATION=$(relation-get location 2>/dev/null) || true +CREDS=$(relation-get creds 2>/dev/null) || true +SERVER_ALIAS=$(relation-get server-alias 2>/dev/null) || true + +export SERVER_ALIAS + +if SSL_CERT_LOCATION=$(relation-get ssl-cert-file 2>/dev/null); then + SSL_CERT=/etc/ssl/certs/${DOMAIN}.pem + config-add "\ +$MASTER_BASE_CHARM_NAME: + volumes: + - ${SSL_CERT_LOCATION}:${SSL_CERT} +" +fi + +if SSL_KEY_LOCATION=$(relation-get ssl-key-file 2>/dev/null); then + SSL_KEY=/etc/ssl/private/${DOMAIN}.key + config-add "\ +$MASTER_BASE_CHARM_NAME: + volumes: + - ${SSL_KEY_LOCATION}:${SSL_KEY} +" +fi + +if SSL_CA_CERT_LOCATION=$(relation-get ssl-ca-cert-file 2>/dev/null); then + SSL_CA_CERT=/etc/ssl/cert/${DOMAIN}-ca.pem + config-add "\ +$MASTER_BASE_CHARM_NAME: + volumes: + - ${SSL_CA_CERT_LOCATION}:${SSL_CA_CERT} +" +fi + +export CREDS +apache_ssl_add "$DOMAIN" + +if [ "$LOCATION" ]; then + if [ -d "$LOCATION" -a ! -d "$LOCATION/.git" ]; then + err "Hum, location '$LOCATION' does not seem to be a git directory." + exit 1 + fi + + if ! [ -d "$LOCATION/.git" ]; then + BRANCH=$(relation-get branch) + BRANCH=${BRANCH:-master} + + SOURCE=$(relation-get source) + parent="$(dirname "$LOCATION")" + ( + mkdir -p "$parent" && cd "$parent" + git clone -b "$BRANCH" "$SOURCE" "$(basename "$LOCATION")" + ) + fi + apache_code_dir "$DOMAIN" "$LOCATION" +else + mkdir -p "$SERVICE_DATASTORE/var/www/${DOMAIN}" || return 1 + config-add " +$MASTER_BASE_CHARM_NAME: + volumes: + - $DATASTORE/$BASE_CHARM_NAME/var/www/${DOMAIN}:/var/www/${DOMAIN} +" +fi + +if [ "$DATA_DIRS" ]; then + apache_data_dir "$DOMAIN" "$DATA_DIRS" +fi diff --git a/www/hooks/web_proxy-relation-joined b/www/hooks/web_proxy-relation-joined new file mode 100755 index 00000000..64248108 --- /dev/null +++ b/www/hooks/web_proxy-relation-joined @@ -0,0 +1,55 @@ +#!/bin/bash + +## Should be executable N time in a row with same result. + +. lib/common + +set -e + +DOMAIN=$(relation-get domain) +TARGET=$(relation-get target) +APACHE_CUSTOM_RULES=$(relation-get apache-custom-rules 2>/dev/null) || true +CREDS=$(relation-get creds 2>/dev/null) || true +SERVER_ALIAS=$(relation-get server-alias 2>/dev/null) || true + +export SERVER_ALIAS + + +if SSL_CERT_LOCATION=$(relation-get ssl-cert-file 2>/dev/null); then + SSL_CERT=/etc/ssl/certs/${DOMAIN}.pem + config-add "\ +$MASTER_TARGET_CHARM_NAME: + volumes: + - ${SSL_CERT_LOCATION}:${SSL_CERT} +" +fi + +if SSL_KEY_LOCATION=$(relation-get ssl-key-file 2>/dev/null); then + SSL_KEY=/etc/ssl/private/${DOMAIN}.key + config-add "\ +$MASTER_TARGET_CHARM_NAME: + volumes: + - ${SSL_KEY_LOCATION}:${SSL_KEY} +" +fi + +if SSL_CA_CERT_LOCATION=$(relation-get ssl-ca-cert-file 2>/dev/null); then + SSL_CA_CERT=/etc/ssl/cert/${DOMAIN}-ca.pem + config-add "\ +$MASTER_TARGET_CHARM_NAME: + volumes: + - ${SSL_CA_CERT_LOCATION}:${SSL_CA_CERT} +" +fi + +control=$(echo "$DOMAIN%$TARGET%$APACHE_CUSTOM_RULES%$CREDS%$SSL_CERT%$SSL_CA_CERT%$SSL_KEY" | md5_compat) + +[ "$control" == "$(relation-get control 2>/dev/null)" ] && exit 0 + +## XXXvlab: could probably figure target ourselves +apache_ssl_proxy_add "$DOMAIN" "$TARGET" "$APACHE_CUSTOM_RULES" "$CREDS" + +relation-set control "$control" + +info "Configured $DARKYELLOW$BASE_CHARM_NAME$NORMAL for proxy access." + diff --git a/www/lib/common b/www/lib/common new file mode 100644 index 00000000..a63af3a6 --- /dev/null +++ b/www/lib/common @@ -0,0 +1,277 @@ +# -*- mode: shell-script -*- + +export APACHE_CONFIG_LOCATION="$SERVICE_CONFIGSTORE/etc/apache2/sites-enabled" + +## XXXvlab: berk, sending conf via environment and args. +apache_ssl_proxy_config () { + local DOMAIN="$1" TARGET="$2" CUSTOM_RULES="$3" CREDS="$4" + ## target is meant to be a charm name + + PASSWORD_FILE=/etc/apache2/sites-enabled/${DOMAIN}.passwd + CRED_PART= + if [ "$CREDS" ]; then + CRED_PART=" + AuthType basic + AuthName "private" + AuthUserFile ${PASSWORD_FILE} + Require valid-user +" + rm -f "$SERVICE_CONFIGSTORE$PASSWORD_FILE" + include parse + first=c + while read-0 login password; do + debug "htpasswd -b$first ${PASSWORD_FILE} '$login' '$password'" + echo "htpasswd -b$first ${PASSWORD_FILE} '$login' '$password'" + [ "$first" ] && first= + done < <(echo "$CREDS" | shyaml key-values-0 2>/dev/null) | + docker run -i --entrypoint "/bin/bash" \ + -v "$APACHE_CONFIG_LOCATION:/etc/apache2/sites-enabled" \ + "$DOCKER_BASE_IMAGE" || return 1 + fi + + if [ -z "$SSL_CERT" ]; then + SSL_CERT=/etc/ssl/certs/ssl-cert-snakeoil.pem + fi + + if [ -z "$SSL_KEY" ]; then + SSL_KEY=/etc/ssl/private/ssl-cert-snakeoil.key + fi + + cat < + + + ServerAdmin ${ADMIN_MAIL:-contact@$DOMAIN} + ServerName ${DOMAIN} +$( + while read-0 alias; do + echo " ServerAlias $alias" + done < <(echo "$SERVER_ALIAS" | shyaml get-values-0 2>/dev/null) +) + ServerSignature Off + CustomLog /var/log/apache2/s-${DOMAIN}_access.log combined + ErrorLog /var/log/apache2/s-${DOMAIN}_error.log + ErrorLog syslog:local2 + + + ProxyRequests Off + + Order deny,allow + Allow from all + + ProxyVia On + ProxyPass / http://$TARGET/ retry=0 + + ${CRED_PART} + ProxyPassReverse / + + + + ## Forbid any cache, this is only usefull on dev server. + #Header set Cache-Control "no-cache" + #Header set Access-Control-Allow-Origin "*" + #Header set Access-Control-Allow-Methods "POST, GET, OPTIONS" + #Header set Access-Control-Allow-Headers "origin, content-type, accept" + + RequestHeader set "X-Forwarded-Proto" "https" + + ## Fix IE problem (httpapache proxy dav error 408/409) + SetEnv proxy-nokeepalive 1 + #ServerSignature On + SSLProxyEngine On + SSLEngine On + + ## Full stance + SSLCertificateFile $SSL_CERT + SSLCertificateKeyFile $SSL_KEY + $([ "$SSL_CA_CERT" ] && echo "SSLCACertificateFile $SSL_CA_CERT") + SSLVerifyClient None + + $CUSTOM_RULES + + + + +EOF + +} +export -f apache_ssl_proxy_config + + +apache_ssl_config() { + local DOMAIN=$1 + + + if [ -z "$SSL_CERT" ]; then + SSL_CERT=/etc/ssl/certs/ssl-cert-snakeoil.pem + fi + + if [ -z "$SSL_KEY" ]; then + SSL_KEY=/etc/ssl/private/ssl-cert-snakeoil.key + fi + + PASSWORD_FILE=/etc/apache2/sites-enabled/${DOMAIN}.passwd + CRED_PART= + if [ "$CREDS" ]; then + CRED_PART=" + AuthType basic + AuthName \"private\" + AuthUserFile ${PASSWORD_FILE} + Require valid-user +" + include parse || true + first= + if ! [ -e "$CONFIGSTORE/$MASTER_TARGET_CHARM_NAME$PASSWORD_FILE" ]; then + debug "No file $CONFIGSTORE/$MASTER_TARGET_CHARM_NAME$PASSWORD_FILE, creating password file." || true + first=c + fi + while read-0 login password; do + debug "htpasswd -b$first ${PASSWORD_FILE} '$login' '$password'" || true + echo "htpasswd -b$first ${PASSWORD_FILE} '$login' '$password'" + if [ "$first" ]; then + first= + fi + done < <(echo "$CREDS" | shyaml key-values-0 2>/dev/null) | + docker run -i --entrypoint "/bin/bash" \ + -v "$APACHE_CONFIG_LOCATION:/etc/apache2/sites-enabled" \ + "$DOCKER_BASE_IMAGE" || return 1 + else + CRED_PART="allow from all" + fi + + cat < + + + ServerAdmin ${ADMIN_MAIL:-contact@$DOMAIN} + ServerName ${DOMAIN} +$( + while read-0 alias; do + echo " ServerAlias $alias" + done < <(echo "$SERVER_ALIAS" | shyaml get-values-0 2>/dev/null) +) + + ServerSignature Off + CustomLog /var/log/apache2/s-${DOMAIN}_access.log combined + ErrorLog /var/log/apache2/s-${DOMAIN}_error.log + ErrorLog syslog:local2 + + DocumentRoot /var/www/${DOMAIN} + + + Options FollowSymLinks + AllowOverride None + + + + Options Indexes FollowSymLinks MultiViews + AllowOverride all + ${CRED_PART} + + + SSLEngine On + + ## Full stance + SSLCertificateFile $SSL_CERT + SSLCertificateKeyFile $SSL_KEY + $([ "$SSL_CA_CERT" ] && echo "SSLCACertificateFile $SSL_CA_CERT") + SSLVerifyClient None + + + + +EOF + +} +export -f apache_ssl_config + + +apache_ssl_add () { + local DOMAIN="$1" + DOCKER_SITE_PATH=/var/www/$DOMAIN + BASE=$DATASTORE/$BASE_CHARM_NAME + DST=$BASE$DOCKER_SITE_PATH + # [ -e "$APACHE_CONFIG_LOCATION/$DOMAIN.conf" ] && return 0 + mkdir -p "$APACHE_CONFIG_LOCATION" || return 1 + apache_ssl_config "$DOMAIN" > "$APACHE_CONFIG_LOCATION/$DOMAIN.conf" + www_data_gid=$(cached_cmd_on_base_image apache 'id -g www-data') || { + debug "Failed to query for www-data gid in ${DARKYELLOW}apache${NORMAL} base image." + return 1 + } + mkdir -p "$DST" + setfacl -R -m g:"$www_data_gid":rx "$DST" + info "Added $DOMAIN apache config." +} +export -f apache_ssl_add + + +apache_ssl_proxy_add () { + local DOMAIN="$1" TARGET="$2" CUSTOM_RULES="$3" CREDS="$4" + + mkdir -p "$APACHE_CONFIG_LOCATION" || return 1 + apache_ssl_proxy_config "$DOMAIN" "$TARGET" "$CUSTOM_RULES" "$CREDS" > "$APACHE_CONFIG_LOCATION/$DOMAIN.conf" || return 1 + info "Added $DOMAIN as a proxy to $TARGET." +} +export -f apache_ssl_proxy_add + + +apache_code_dir() { + local domain="$1" location="$2" + config-add " +$MASTER_BASE_CHARM_NAME: + volumes: + - $location:/var/www/$domain +" +} + +apache_data_dir() { + local DOMAIN=$1 DATA_COMMA_SEPARATED=$2 + + DOCKER_SITE_PATH=/var/www/$DOMAIN + BASE=$DATASTORE/$BASE_CHARM_NAME + DST=$BASE$DOCKER_SITE_PATH + DATA=() + while IFS="," read -ra ADDR; do + for dir in "${ADDR[@]}"; do + mkdir -p "$DST/$dir" + DATA+=($dir) + done + done <<< "$DATA_COMMA_SEPARATED" + + www_data_gid=$(cached_cmd_on_base_image apache 'id -g www-data') || { + debug "Failed to query for www-data gid in ${DARKYELLOW}apache${NORMAL} base image." + return 1 + } + info "www-data gid from ${DARKYELLOW}apache${NORMAL} is '$www_data_gid'" + + dirs=() + for d in "${DATA[@]}"; do + dirs+=("$DST/$d") + done + + chgrp "$www_data_gid" "${dirs[@]}" -R && chmod 775 "${dirs[@]}" -R + + config-add " +$MASTER_BASE_CHARM_NAME: + volumes: +$( + for d in "${DATA[@]}"; do + echo " - $DST/$d:$DOCKER_SITE_PATH/$d" + done +)" + +} + +deploy_files() { + local src="$1" dst="$2" + + if ! [ -d "$dst" ]; then + err "Destination '$dst' does not exist or is not a directory" + return 1 + fi + ( + cd "$dst" && info "In $dst:" && + get_file "$src" | tar xv + ) +} +export -f deploy_files diff --git a/www/metadata.yaml b/www/metadata.yaml index 5295c5b5..ece28a7c 100644 --- a/www/metadata.yaml +++ b/www/metadata.yaml @@ -1,13 +1,20 @@ -name: www -summary: "Apache" +description: "Apache Web Server" maintainer: "Valentin Lab " +## XXXvlab: currently only used when building LXC along with hooks/install +## XXXvlab: docker uses the 'build' directory or the 'image:' option here. inherit: base-0k -compatiblity: +compatiblity: ## 'hooks/install' script was run on a these images without issues - ubuntu/15.10 -description: | - Installs a HTTP Apache server. -config-resources: - - /etc/apache2 - - /etc/postfix +docker-compose: + ## XXXvlab: should move to global lxc/docker compatible option + ports: + - "0.0.0.0:80:80" + - "0.0.0.0:443:443" data-resources: - - /var/www + - /var/www/html + - /var/log/apache2 +config-resources: + - /etc/apache2/sites-enabled +provides: + web-proxy: + tech-dep: "reversed" diff --git a/www/revision b/www/revision deleted file mode 100644 index 573541ac..00000000 --- a/www/revision +++ /dev/null @@ -1 +0,0 @@ -0 diff --git a/www/shorewall b/www/shorewall index bafe2343..d0395384 100644 --- a/www/shorewall +++ b/www/shorewall @@ -1,3 +1,6 @@ +## XXXvlab: these should be set in metadata with a common option for +## docker and LXC/shorewall + DNAT net lan:%%NAME%%:80 tcp 80 DNAT net lan:%%NAME%%:443 tcp 443