From 6e5e9549705b2c0966e4fb1dd14006fff5d20f7f Mon Sep 17 00:00:00 2001 From: Valentin Lab Date: Tue, 30 Jan 2024 21:46:19 +0100 Subject: [PATCH] new: [apache] improve domain specification and provide service to domain mapping facility --- apache/README.org | 188 ++++++++++++++ apache/README.rst | 29 --- apache/hooks/publish_dir-relation-joined | 4 +- apache/hooks/web_proxy-relation-joined | 4 +- apache/lib/common | 308 +++++++++++++++------- apache/test/get_domains | 316 +++++++++++++++++++++++ apache/test/vhost | 11 +- apache/test/vhost_cert_provider | 2 +- 8 files changed, 733 insertions(+), 129 deletions(-) create mode 100644 apache/README.org delete mode 100644 apache/README.rst create mode 100644 apache/test/get_domains diff --git a/apache/README.org b/apache/README.org new file mode 100644 index 0000000..bf096ff --- /dev/null +++ b/apache/README.org @@ -0,0 +1,188 @@ + + +* Usage + +Other services will often require a service managed with this charm to +act as a HTTP/HTTPS front-end. It can provide certificates with HTTPS. + + +** Domain assignment + +Services using relation =web-proxy= or =publish-dir= will be required +to be assigned a domain name for the virtual host that will be +created. + +*** Domain sources + +This domain name can be set (in order of priority), the first source +giving a name will be taken. + +- *Relation's options* (=web-proxy= or =publish-dir=) + Using =domain= option, and optionally the deprecated + =server-aliases= for additional names. + + #+begin_src yaml + myservice: + # ... + relations: + web-proxy: + apache: + domain: mydomain.org + #server-aliases: + # - www.mydomain.org + # - pro.mydomain.org + #+end_src +- *Apache service's options*, using a =service-domain-name= mapping: + + #+begin_src yaml + myservice: + # ... + apache: + options: + service-domain-map: + # ... + myservice: + - mydomain.org + - www.mydomain.org + - pro.mydomain.org + # ... + #+end_src + +- *the service name* itself if is a domain name: + + #+begin_src yaml + www.mydomain.org: + # ... + #+end_src + + Please note that this is not recommended, and will be deprecated. + +*** Domain and alternate domains + +Every source (except the one coming out from the domain name), can use +several ways to provide *more than one domain name*. + +Please remember: +- At least one domain name needs to be provided +- and the first domain can't use wildcards and will be considered the main domain name. + +If other domains are specified, they will be used as aliases, and +wildcard (using ~*~) is supported. + +Additionally, bash braces expansion and regex matching are +available. Space separated YAML string or YAML sequences are +supported, also as mix of both. + +As examples, notice the following are equivalent and will serve +=myservice= on the exact same set of domain names: + +#+begin_src yaml +myservice: + relations: + web-proxy: + domain: + ## A yaml list + - myservice.home.org + - mydomain.org + - www.mydomain.org + - pro.mydomain.org + - *.myservice.hop.org +#+end_src + + +#+begin_src yaml +myservice: + # ... no domain set in relation +apache: + options: + service-domain-map: + ## A yaml list as a mapping value + myservice: + - myservice.home.org + - {,www.,pro.}mydomain.org ## bash braces expansion used + - *.myservice.hop.org +#+end_src + +#+begin_src yaml +myservice: + # ... +apache: + options: + service-domain-map: + ## space separated YAML string and bash braces expansion + myservice: myservice.home.org {,www.,pro.}mydomain.org *.myservice.hop.org +#+end_src + +#+begin_src yaml +myservice: + # ... +apache: + options: + service-domain-map: + ## Leveraging bash braces expansion and regex replacement + .*: {$0.home,{,www.,pro.}mydomain,*.$0.hop}.org +#+end_src + +** Domain mapping + +You can automatically assign a domain to services in relation +=web-proxy= or =publish-dir= with services managed by this charm using +the =service-domain-name= option. For instance: + +#+begin_src yaml +apache: + options: + service-domain-map: + .*: $0.mydomain.org +#+end_src + +Where ~mydomain.org~ stands for the domain where most of your services +will be served. You can override this behavior for some services: +- by adding a matching rule *before* the given rule. +- by specifying a =domain= in the relation's options. + +first rule matching will end the mapping: + +#+begin_src yaml +apache: + options: + service-domain-map: + foo: www.mydomain.org + bar: beta.myotherdomain.com +#+end_src + +Allows to distribute services to domains quite freely. + + +* SSH Tunnel + +On the server side, you can configure your compose file:: + +#+begin_src yaml +apache: + options: + ssh-tunnel: + domain: ssh.domain.com ## required + #ssh: ... ## required, but automatically setup if you + ## provide a ``cert-provider`` to ``apache``. +#+end_src + + +On the client side you should add this to your ``~/.ssh/config``:: + +#+begin_src conf-space +Host ssh.domain.com + Port 443 + ProxyCommand proxytunnel -q -E -p ssh.domain.com:443 -d ssh.domain.com:22 + DynamicForward 1080 + ServerAliveInterval 60 +#+end_src + +If it doesn't work, you can do some checks thanks to this command:: + +#+begin_example +$ proxytunnel -E -p ssh.domain.com:443 -d ssh.domain.com:22 -v \ + -H "User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Win32)\n" +#+end_example + + diff --git a/apache/README.rst b/apache/README.rst deleted file mode 100644 index 7eedb25..0000000 --- a/apache/README.rst +++ /dev/null @@ -1,29 +0,0 @@ - - -SSH Tunnel ----------- - -On the server side, you can configure your compose file:: - - apache: - options: - ssh-tunnel: - domain: ssh.domain.com ## required - #ssh: ... ## required, but automatically setup if you - ## provide a ``cert-provider`` to ``apache``. - - -On the client side you should add this to your ``~/.ssh/config``:: - - Host ssh.domain.com - Port 443 - ProxyCommand proxytunnel -q -E -p ssh.domain.com:443 -d ssh.domain.com:22 - DynamicForward 1080 - ServerAliveInterval 60 - -If it doesn't work, you can do some checks thanks to this command:: - - $ proxytunnel -E -p ssh.domain.com:443 -d ssh.domain.com:22 -v \ - -H "User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Win32)\n" - - diff --git a/apache/hooks/publish_dir-relation-joined b/apache/hooks/publish_dir-relation-joined index 1d0202a..a8d587f 100755 --- a/apache/hooks/publish_dir-relation-joined +++ b/apache/hooks/publish_dir-relation-joined @@ -6,8 +6,10 @@ set -e +service_def=$(get_compose_service_def "$SERVICE_NAME") + cfg=$(relation-get) -apache_publish_dir "$cfg" +apache_publish_dir "$cfg" "$service_def" APACHE_CORE_RULES=$(relation-get apache-core-rules 2>/dev/null) || true if [ "$APACHE_CORE_RULES" ]; then diff --git a/apache/hooks/web_proxy-relation-joined b/apache/hooks/web_proxy-relation-joined index fc90dc5..ce93586 100755 --- a/apache/hooks/web_proxy-relation-joined +++ b/apache/hooks/web_proxy-relation-joined @@ -5,8 +5,10 @@ set -e . lib/common +service_def=$(get_compose_service_def "$SERVICE_NAME") + cfg=$(relation-get) -apache_proxy_dir "$cfg" +apache_proxy_dir "$cfg" "$service_def" APACHE_CORE_RULES=$(relation-get apache-core-rules 2>/dev/null) || true if [ "$APACHE_CORE_RULES" ]; then diff --git a/apache/lib/common b/apache/lib/common index a612d05..78a775a 100644 --- a/apache/lib/common +++ b/apache/lib/common @@ -1,22 +1,163 @@ # -*- mode: shell-script -*- +replace_by_rematch_pattern() { + local input="$1" output="" char next_char i + # Loop through each character in the string + for (( i=0; i<${#input}; i++ )); do + char="${input:$i:1}" + + # If a dollar sign is found + if [[ "$char" == '$' ]]; then + next_char="${input:$((i+1)):1}" + # Check if next character is a digit + if [[ "$next_char" =~ [0-9] ]]; then + # Replace $N with ${rematch[N]} + output+='${rematch['"$next_char"']}' + ((i++)) # Skip next character as it's already processed + continue + fi + fi -get_domain() { - local cfg="$1" cache_file="$state_tmpdir/$FUNCNAME.cache.$(H "$SERVICE_NAME" "$MASTER_BASE_SERVICE_NAME" "$@")" \ - domain + output+="$char" + done + + echo "$output" +} +export -f replace_by_rematch_pattern + + + +get_domains() { + local cfg="$1" service_cfg="$2" cache_file="$state_tmpdir/$FUNCNAME.cache.$(H "$SERVICE_NAME" "$MASTER_BASE_SERVICE_NAME" "$@")" \ + domains new_domains maps m regex rematch type value key domain if [ -e "$cache_file" ]; then cat "$cache_file" return 0 fi - domain=$(e "$cfg" | cfg-get-value domain 2>/dev/null) || true - if [ "$domain" ]; then - echo "$domain" | tee "$cache_file" - elif [[ "$BASE_SERVICE_NAME" =~ ^([a-z0-9]+(-[a-z0-9]+)*\.)+[a-z]{2,}$ ]]; then - echo "$BASE_SERVICE_NAME" | tee "$cache_file" - else - err "You must specify a ${WHITE}domain$NORMAL option. (${FUNCNAME[*]})" + domains=() + for key in domain server-aliases; do + type=$(e "$cfg" | shyaml -q get-type "$key" 2>/dev/null) || true + value=$(e "$cfg" | shyaml -q get-value -y "$key" 2>/dev/null) || true + while true; do + case "$type" in + sequence) + while read-0 domain; do + if [[ "$domain" != "None" ]]; then + if [ "${#domains[@]}" == 0 ] && [ "$key" == "server-aliases" ]; then + err "You can't specify server aliases if you don't have a domain." + return 1 + fi + + domains+=("$domain") + fi + done < <(e "$value" | shyaml get-values-0) + ;; + str) + if ! domain=$(e "$value" | shyaml get-value 2>/dev/null); then + err "Failed to get domain value from config." + return 1 + fi + [[ "$domain" == "" ]] && break + if [ "${#domains[@]}" == 0 ] && [ "$key" == "server-aliases" ]; then + err "You can't specify server aliases if you don't have a domain." + return 1 + fi + + domains+=("$domain") + ;; + NoneType|"") + : + ;; + \!*) + value=$(e "$value" | cfg-get-value "$key" 2>/dev/null) || true + type=$(e "$value" | shyaml -q get-type) || true + continue + ;; + esac + break + done + ## check and expand domain + new_domains=() + for domain in "${domains[@]}"; do + if ! [[ "$domain" =~ ^[a-z0-9\{\}*\ \,.-]+$ ]]; then + err "Invalid domain value '$domain' expression in ${WHITE}$key$NORMAL option." + return 1 + fi + new_domains+=($(eval echo "${domain//\*/\\*}")) + done + domains=("${new_domains[@]}") + done + ## Fill with service-domain-map + if [ "${#domains[@]}" == 0 ] && + service_domain_map=$(e "$service_cfg" | cfg-get-value 'options.service-domain-map' 2>/dev/null) && + [ -n "$service_domain_map" ]; then + while read-0 regex map; do + if [[ "$BASE_SERVICE_NAME" =~ ^$regex$ ]]; then + rematch=("${BASH_REMATCH[@]}") + maps=() + type=$(e "$map" | shyaml -q get-type 2>/dev/null) || true + value=$(e "$map" | shyaml -q get-value -y 2>/dev/null) || true + case "$type" in + sequence) + while read-0 m; do + if [[ "$m" != "None" ]] && [ -n "$m" ]; then + maps+=("$m") + fi + done < <(e "$value" | shyaml get-values-0) + ;; + str) + if ! m=$(e "$value" | shyaml get-value 2>/dev/null); then + err "Failed to get mapping value from config." + return 1 + fi + [ -z "$m" ] && continue + maps+=("$m") + ;; + NoneType|"") + : + ;; + esac + for map in "${maps[@]}"; do + if ! [[ "$map" =~ ^([a-z0-9*\{\}\ \,.-]|\$[0-9])+$ ]]; then + err "Invalid mapping value '$map' in ${WHITE}service-domain-map$NORMAL option." + return 1 + fi + + map="${map//\*/\\*}" ## protect star from eval + map=$(replace_by_rematch_pattern "$map") + + domains+=($(set -- "${BASH_REMATCH[@]}"; eval echo "${map//\*/\\*}")) + done + break + fi + done < <(e "$service_domain_map" | yq e -0 'to_entries | .[] | [.key, .value] |.[]') + fi + if [ "${#domains[@]}" == 0 ] && + [[ "$BASE_SERVICE_NAME" =~ ^([a-z0-9]+(-[a-z0-9]+)*\.)+[a-z]{2,}$ ]]; then + domains+=("$BASE_SERVICE_NAME") + fi + new_domains=() + ## remove duplicates + for domain in "${domains[@]}"; do + if ! [[ " ${new_domains[*]} " == *" $domain "* ]]; then + new_domains+=("$domain") + fi + done + domains=("${new_domains[@]}") + if [ "${#domains[@]}" == 0 ]; then + err "No domain name set for your service ${YELLOW}$BASE_SERVICE_NAME${NORMAL}." + echo " You can specify a ${WHITE}domain$NORMAL option in the" \ + "relation with ${YELLOW}$TARGET_SERVICE_NAME${NORMAL}." >&2 + echo " Or you can specify a service to domain mapping in" \ + "${WHITE}service-domain-map${NORMAL} option of ${YELLOW}$TARGET_SERVICE_NAME${NORMAL}." >&2 return 1 fi + ## check that first domain should not have a wildcard + if [[ "${domains[0]}" == *"*"* ]]; then + err "First domain name '${domains[0]}' can't contain a wildcard." + return 1 + fi + echo "${domains[@]}" | tee "$cache_file" } @@ -25,17 +166,17 @@ get_domain() { ## apache_proxy_dir() { - local cfg="$1" domain - apache_vhost_create web_proxy "$cfg" || return 1 + local cfg="$1" service_cfg="$2" + apache_vhost_create web_proxy "$cfg" "$service_cfg" || return 1 } export -f apache_proxy_dir apache_publish_dir() { - local cfg="$1" domain - apache_vhost_create publish_dir "$cfg" || return 1 - apache_code_dir "$cfg" || return 1 - apache_data_dirs "$cfg" + local cfg="$1" service_cfg="$2" + apache_vhost_create publish_dir "$cfg" "$service_cfg" || return 1 + apache_code_dir "$cfg" "$service_cfg" || return 1 + apache_data_dirs "$cfg" "$service_cfg" } export -f apache_publish_dir @@ -51,7 +192,7 @@ apache_ssh_tunnel() { err "${WHITE}ssl${NORMAL} must be valued in ${WHITE}ssh-tunnel${NORMAL} config." return 1 fi - apache_vhost_create ssh_tunnel "$cfg" ",https," "000-$domain" || return 1 + apache_vhost_create ssh_tunnel "$cfg" "" ",https," "000-$domain" || return 1 } export -f apache_publish_dir @@ -62,7 +203,7 @@ export -f apache_publish_dir apache_vhost_create() { - local type="$1" cfg="$2" protocols="$3" dest="$4" custom_rules vhost_statement creds \ + local type="$1" cfg="$2" service_cfg="$3" protocols="$4" dest="$5" custom_rules vhost_statement creds \ redirect domain ssl_plugin_fun ssl_cfg_value ssl_cfg_options export APACHE_CONFIG_LOCATION="$SERVICE_CONFIGSTORE/etc/apache2/sites-enabled" @@ -73,21 +214,21 @@ apache_vhost_create() { protocols=$(__vhost_cfg_normalize_protocol "$cfg") || return 1 fi - domain=$(get_domain "$cfg") && { - [ "$RELATION_DATA_FILE" ] && relation-set domain "$domain" + domains=($(get_domains "$cfg" "$service_cfg")) && { + [ "$RELATION_DATA_FILE" ] && relation-set domain "${domains[0]}" } - + echo "Domains: ${domains[*]}" >&2 if is_protocol_enabled https "$protocols"; then [ "$RELATION_DATA_FILE" ] && { - relation-set url "https://$domain" + relation-set url "https://${domains[0]}" } - if [ -z "$domain" ]; then + if [ "${#domains[@]}" == 0 ]; then err "You must specify a domain for ssl to work." return 1 fi read-0 ssl_plugin_fun ssl_cfg_value ssl_cfg_options < <(ssl_get_plugin_fun "$cfg") || return 1 - "$ssl_plugin_fun"_vars "$cfg" "$ssl_cfg_options" "$ssl_cfg_value" "$domain" || return 1 + "$ssl_plugin_fun"_vars "$cfg" "$ssl_cfg_options" "$ssl_cfg_value" "${domains[*]}" || return 1 redirect=$(e "$cfg" | cfg-get-value 'redirect-to-ssl' 2>/dev/null) || true if is_protocol_enabled http "$protocols"; then redirect=${redirect:-true} @@ -116,15 +257,15 @@ $(if [ "$custom_rules" ]; then fi else [ "$RELATION_DATA_FILE" ] && { - relation-set url "http://$domain" + relation-set url "http://${domains[0]}" } fi - vhost_statement=$(apache_vhost_statement "$type" "$protocols" "$cfg" "$domain") || { + vhost_statement=$(apache_vhost_statement "$type" "$protocols" "$cfg" "${domains[*]}") || { err "Failed to get vhost statement for type $type on ${protocols:1:-1}" return 1 } - dest=${dest:-$domain} + dest=${dest:-${domains[0]}} if [ -z "$dest" ]; then err "Please set either a domain or set a destination file." return 1 @@ -137,7 +278,7 @@ $(if [ "$custom_rules" ]; then fi if is_protocol_enabled https "$protocols"; then - "$ssl_plugin_fun"_prepare "$cfg" "$ssl_cfg_options" "$ssl_cfg_value" || return 1 + "$ssl_plugin_fun"_prepare "$cfg" "$ssl_cfg_options" "$ssl_cfg_value" "${domains[*]}" || return 1 fi } @@ -296,18 +437,18 @@ ssl_get_plugin_fun() { ssl_fallback_vars() { - local cfg="$1" ssl_cfg="$2" value="$3" domain="$4" cert key ca_cert domain - + local cfg="$1" ssl_cfg="$2" value="$3" domains="$4" cert key ca_cert + domains=($domains) if __vhost_cfg_ssl_cert=$(echo "$ssl_cfg" | shyaml get-value cert 2>/dev/null); then - __vhost_cfg_SSL_CERT_LOCATION=/etc/ssl/certs/${domain}.pem + __vhost_cfg_SSL_CERT_LOCATION=/etc/ssl/certs/${domains[0]}.pem fi if __vhost_cfg_ssl_key=$(echo "$ssl_cfg" | shyaml get-value key 2>/dev/null); then - __vhost_cfg_SSL_KEY_LOCATION=/etc/ssl/private/${domain}.key + __vhost_cfg_SSL_KEY_LOCATION=/etc/ssl/private/${domains[0]}.key fi if __vhost_cfg_ssl_ca_cert=$(echo "$ssl_cfg" | shyaml get-value ca-cert 2>/dev/null); then - __vhost_cfg_SSL_CA_CERT_LOCATION=/etc/ssl/certs/${domain}-ca.pem + __vhost_cfg_SSL_CA_CERT_LOCATION=/etc/ssl/certs/${domains[0]}-ca.pem fi } @@ -338,29 +479,20 @@ $volumes } ssl_plugin_cert-provider_vars() { - local cfg="$1" ssl_cfg="$2" value="$3" domain="$4" - - __vhost_cfg_SSL_CERT_LOCATION=/etc/letsencrypt/live/${domain}/cert.pem - __vhost_cfg_SSL_KEY_LOCATION=/etc/letsencrypt/live/${domain}/privkey.pem - __vhost_cfg_SSL_CHAIN=/etc/letsencrypt/live/${domain}/chain.pem + local cfg="$1" ssl_cfg="$2" value="$3" domains="$4" + domains=($domains) + __vhost_cfg_SSL_CERT_LOCATION=/etc/letsencrypt/live/${domains[0]}/cert.pem + __vhost_cfg_SSL_KEY_LOCATION=/etc/letsencrypt/live/${domains[0]}/privkey.pem + __vhost_cfg_SSL_CHAIN=/etc/letsencrypt/live/${domains[0]}/chain.pem } ssl_plugin_cert-provider_prepare() { - local cfg="$1" ssl_cfg="$2" service="$3" options domain server_aliases - - domain=$(get_domain "$cfg") || return 1 - + local cfg="$1" ssl_cfg="$2" service="$3" domains="$4" options server_aliases + domains=($domains) options=$(yaml_key_val_str "options" "$ssl_cfg") || return 1 service_config=$(yaml_key_val_str "$service" "$options") - server_aliases=$(e "$cfg" | cfg-get-value server-aliases 2>/dev/null) || true - [ "$server_aliases" == None ] && server_aliases="" - if [ "$server_aliases" ]; then - server_aliases=($(echo "$server_aliases" | shyaml get-values)) || return 1 - else - server_aliases=() - fi compose --debug --add-compose-content "$service_config" crt "$service" \ - create "$domain" "${server_aliases[@]}" || { + create "${domains[@]}" || { err "Failed to launch letsencrypt for certificate creation." return 1 } @@ -399,16 +531,16 @@ apache_passwd_file() { ## Produce the full statements depending on relation-get informations apache_vhost_statement() { - local type="$1" protocols="$2" cfg="$3" domain="$4" \ + local type="$1" protocols="$2" cfg="$3" domains="$4" \ vhost_statement if is_protocol_enabled http "$protocols"; then - __vhost_full_vhost_statement "$type" http "$cfg" "$domain" || return 1 + __vhost_full_vhost_statement "$type" http "$cfg" "$domains" || return 1 fi if is_protocol_enabled https "$protocols"; then read-0 ssl_plugin_fun ssl_cfg_value ssl_cfg_options < <(ssl_get_plugin_fun "$cfg") || return 1 - "$ssl_plugin_fun"_vars "$cfg" "$ssl_cfg_options" "$ssl_cfg_value" "$domain" || return 1 - vhost_statement=$(__vhost_full_vhost_statement "$type" https "$cfg" "$domain") || return 1 + "$ssl_plugin_fun"_vars "$cfg" "$ssl_cfg_options" "$ssl_cfg_value" "$domains" || return 1 + vhost_statement=$(__vhost_full_vhost_statement "$type" https "$cfg" "$domains") || return 1 cat < @@ -421,13 +553,13 @@ export -f apache_vhost_statement apache_code_dir() { - local cfg="$1" www_data_gid local_path + local cfg="$1" service_cfg="$2" www_data_gid local_path www_data_gid=$(cached_cmd_on_base_image "$TARGET_SERVICE_NAME" 'id -g www-data') || { debug "Failed to query for www-data gid in ${DARKYELLOW}$TARGET_SERVICE_NAME${NORMAL} base image." return 1 } - domain=$(get_domain "$cfg") || return 1 - local_path="/var/www/${domain}" + domains=($(get_domains "$cfg" "$service_cfg")) || return 1 + local_path="/var/www/${domains[0]}" host_path=$(e "$cfg" | cfg-get-value location 2>/dev/null) || host_path="$DATASTORE/$BASE_SERVICE_NAME${local_path}" @@ -455,13 +587,13 @@ $SERVICE_NAME: } apache_data_dirs() { - local cfg="$1" data_dirs dst data fdir to_create + local cfg="$1" service_cfg="$2" data_dirs dst data fdir to_create data_dirs=$(e "$cfg" | cfg-get-value data-dirs 2>/dev/null | shyaml get-values 2>/dev/null) || true if [ -z "$data_dirs" ]; then return 0 fi - domain=$(get_domain "$cfg") || return 1 - local_path="/var/www/${domain}" + domains=($(get_domains "$cfg" "$service_cfg")) || return 1 + local_path="/var/www/${domains[0]}" dst=$DATASTORE/$BASE_SERVICE_NAME$local_path data=() while IFS="," read -ra addr; do @@ -577,26 +709,15 @@ EOF __vhost_head_statement() { - local cfg="$1" protocol="$2" domain="$3" server_aliases admin_mail prefix + local cfg="$1" protocol="$2" domains="$3" server_aliases admin_mail prefix + domains=($domains) admin_mail=$(e "$1" | cfg-get-value "admin-mail" 2>/dev/null) || true if [ -z "$admin_mail" ]; then - if [ -z "$domain" ]; then + if [ "${#domains[@]}" == 0 ]; then admin_mail=webmaster@localhost else - admin_mail=${admin_mail:-contact@$domain} - fi - fi - - server_aliases=$(e "$cfg" | cfg-get-value server-aliases 2>/dev/null) || true - [ "$server_aliases" == None ] && server_aliases="" - if [ "$server_aliases" ]; then - server_aliases=($(e "$server_aliases" | shyaml get-values)) || return 1 - if [ -z "$domain" ]; then - err "You can't specify server aliases if you don't have a domain." - return 1 + admin_mail=${admin_mail:-contact@${domains[0]}} fi - else - server_aliases=() fi if [ "$protocol" == "https" ]; then @@ -605,8 +726,8 @@ __vhost_head_statement() { prefix= fi - if [ "$domain" ]; then - log_prefix="${prefix}${domain}_" + if [ "${#domains[@]}" != 0 ]; then + log_prefix="${prefix}${domains[0]}_" else log_prefix="" fi @@ -614,8 +735,8 @@ __vhost_head_statement() { cat < + Order deny,allow $(__vhost_creds_statement "$cfg" "$dest" | prefix " ") diff --git a/apache/test/get_domains b/apache/test/get_domains new file mode 100644 index 0000000..ca037a0 --- /dev/null +++ b/apache/test/get_domains @@ -0,0 +1,316 @@ +#!/bin/bash + +exname=$(basename $0) + +compose_core=$(which compose-core) || { + echo "Requires compose-core executable to be in \$PATH." >&2 + exit 1 +} + +fetch-def() { + local path="$1" fname="$2" + ( . "$path" 1>&2 || { + echo "Failed to load '$path'." >&2 + exit 1 + } + declare -f "$fname" + ) +} + +prefix_cmd=" +. /etc/shlib + +include common +include parse + +. ../lib/common + +$(fetch-def "$compose_core" yaml_get_values) +$(fetch-def "$compose_core" yaml_get_interpret) + +" || { + echo "Couldn't build prefix cmd" >&2 + exit 1 +} + +# mock +cfg-get-value() { + local key="$1" + shyaml get-value "$key" 2>/dev/null +} +export -f cfg-get-value + +yaml_get_interpret() { + shyaml get-value +} +export -f yaml_get_interpret + + +export state_tmpdir=$(mktemp -d -t tmp.XXXXXXXXXX) +trap "rm -rf \"$state_tmpdir\"" EXIT + +## +## Tests +## + +try " +get_domains ' +'" +is errlvl 1 +is err reg 'Error: .*domain option.*' +is out '' + + +try " +get_domains ' +domain: toto +'" +noerror +is out 'toto +' + + +try " +get_domains ' +domain: toto titi +'" +noerror +is out 'toto titi +' + +try " +get_domains ' +domain: +- toto +'" +noerror +is out 'toto +' + + +try " +get_domains ' +server-aliases: +'" +is errlvl 1 +is err part 'Error: ' +is err part 'No domain name set' + +try " +get_domains ' +domain: +server-aliases: +'" +is errlvl 1 +is err part 'Error: ' +is err part 'No domain name set' + + +try " +get_domains ' +domain: +server-aliases: +- toto +'" +is errlvl 1 +is err part 'Error: ' +is err part "You can't specify server aliases if you don't have a domain" + +try " +get_domains ' +domain: foo +server-aliases: +- bar +'" +noerror +is out 'foo bar +' + + +try " +get_domains ' +domain: foo +server-aliases: bar +'" +noerror +is out 'foo bar +' + + +try " +get_domains ' +domain: +- foo +server-aliases: bar +'" +noerror +is out 'foo bar +' + +try " +get_domains ' +domain: +- foo{1,2} bar +server-aliases: wiz +'" +noerror +is out 'foo1 foo2 bar wiz +' + +try " +get_domains ' +domain: +- foo{1,2} bar +server-aliases: foo1 +'" +noerror +is out 'foo1 foo2 bar +' + +try " +get_domains ' +domain: +- foo{1,2} bar +- \"*.zoo\" +server-aliases: foo1 +'" +noerror +is out 'foo1 foo2 bar *.zoo +' + +try " +get_domains ' +domain: foo+ bar +'" +is errlvl 1 +is err part 'Error: ' +is err part 'Invalid domain value' + + +try " +get_domains ' +domain: +' ' +options.service-domain-map: +'" "empty service-domain-map" +is errlvl 1 +is err part 'Error: ' +is err part 'No domain name set' +is err part 'service-domain-map' + + +try " +BASE_SERVICE_NAME=foo +get_domains ' +domain: +' ' +options: + service-domain-map: + wiz: bar +'" "no map matching in service-domain-map" +is errlvl 1 +is err part 'Error: ' +is err part 'No domain name set' +is err part 'service-domain-map' + + +try " +export BASE_SERVICE_NAME=wiz +get_domains ' +domain: +' ' +options: + service-domain-map: + wiz: bar +'" "matching map in service-domain-map" +noerror +is out 'bar +' + + +try " +export BASE_SERVICE_NAME=wiz +get_domains ' +domain: +' ' +options: + service-domain-map: + wiz?: bar + wiz: bar2 +'" "only first matching map in service-domain-map" +noerror +is out 'bar +' + + +try " +export BASE_SERVICE_NAME=wiz +get_domains ' +domain: +' ' +options: + service-domain-map: + \"[w]i?zz?\": bar +'" "map are regex in service-domain-map" +noerror +is out 'bar +' + + +try " +export BASE_SERVICE_NAME=wiz +get_domains ' +domain: +' ' +options: + service-domain-map: + (w)i(z): bar\$1\$2 +'" "regex capture in service-domain-map" +noerror +is out 'barwz +' + + +try " +export BASE_SERVICE_NAME=wiz +get_domains ' +domain: +' ' +options: + service-domain-map: + .*: \$0.shrubbery +'" "regex capture 2 in service-domain-map" +noerror +is out 'wiz.shrubbery +' + + +try " +export BASE_SERVICE_NAME=wiz +get_domains ' +domain: +' ' +options: + service-domain-map: + .*: \$x +'" "refuse other variables in service-domain-map" +is errlvl 1 +is err part 'Error: ' +is err part 'Invalid mapping value' + + +try " +export BASE_SERVICE_NAME=wiz +get_domains ' +domain: +' ' +options: + service-domain-map: + .*: + - \$0.example.com + - my-\$0.domain.org + +'" "list is possible as value of service-domain-map" +noerror +is out 'wiz.example.com my-wiz.domain.org +' diff --git a/apache/test/vhost b/apache/test/vhost index 88be607..2fdd81b 100644 --- a/apache/test/vhost +++ b/apache/test/vhost @@ -107,20 +107,13 @@ is out ' ## try " -apache_vhost_statement publish_dir ,http, ' -server-aliases: -- toto -' www.example.com" +apache_vhost_statement publish_dir ,http, '' 'www.example.com toto'" noerror is out reg 'ServerAlias toto' try " -apache_vhost_statement publish_dir ,http, ' -server-aliases: -- toto -- titi -' www.example.com" +apache_vhost_statement publish_dir ,http, '' 'www.example.com toto titi'" noerror is out reg 'ServerAlias toto' is out reg 'ServerAlias titi' diff --git a/apache/test/vhost_cert_provider b/apache/test/vhost_cert_provider index 6754dba..b10b4e9 100644 --- a/apache/test/vhost_cert_provider +++ b/apache/test/vhost_cert_provider @@ -182,7 +182,7 @@ ssl: foo: | a b -' +' '' " "unknown cert key" is errlvl 1 is err reg 'Error: .*cert-provider.*'