You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

800 lines
23 KiB

  1. # -*- mode: shell-script -*-
  2. get_domain() {
  3. local cfg="$1" cache_file="$state_tmpdir/$FUNCNAME.cache.$(H "$SERVICE_NAME" "$MASTER_BASE_SERVICE_NAME" "$@")" \
  4. domain
  5. if [ -e "$cache_file" ]; then
  6. cat "$cache_file"
  7. return 0
  8. fi
  9. domain=$(e "$cfg" | cfg-get-value domain 2>/dev/null) || true
  10. if [ "$domain" ]; then
  11. echo "$domain" | tee "$cache_file"
  12. elif [[ "$BASE_SERVICE_NAME" =~ ^([a-z0-9]+(-[a-z0-9]+)*\.)+[a-z]{2,}$ ]]; then
  13. echo "$BASE_SERVICE_NAME" | tee "$cache_file"
  14. else
  15. err "You must specify a ${WHITE}domain$NORMAL option in relation. (${FUNCNAME[@]})"
  16. return 1
  17. fi
  18. }
  19. ##
  20. ## Master entrypoints
  21. ##
  22. apache_proxy_dir() {
  23. local cfg="$1" domain
  24. apache_vhost_create web_proxy "$cfg" || return 1
  25. }
  26. export -f apache_proxy_dir
  27. apache_publish_dir() {
  28. local cfg="$1" domain
  29. apache_vhost_create publish_dir "$cfg" || return 1
  30. apache_code_dir "$cfg" || return 1
  31. apache_data_dirs "$cfg"
  32. }
  33. export -f apache_publish_dir
  34. ##
  35. ## Simple functions
  36. ##
  37. apache_vhost_create() {
  38. local type="$1" cfg="$2" protocols="$3" dest="$4" custom_rules vhost_statement creds
  39. export APACHE_CONFIG_LOCATION="$SERVICE_CONFIGSTORE/etc/apache2/sites-enabled"
  40. if [ -z "$protocols" ]; then
  41. protocols=$(__vhost_cfg_normalize_protocol "$cfg") || return 1
  42. fi
  43. domain=$(get_domain "$cfg") && relation-set domain "$domain"
  44. if is_protocol_enabled https "$protocols"; then
  45. if [ -z "$domain" ]; then
  46. err "You must specify a domain for ssl to work."
  47. return 1
  48. fi
  49. read-0 ssl_plugin_fun ssl_cfg_value ssl_cfg_options < <(ssl_get_plugin_fun "$cfg") || return 1
  50. "$ssl_plugin_fun"_vars "$cfg" "$ssl_cfg_options" "$ssl_cfg_value" "$domain" || return 1
  51. redirect=$(e "$cfg" | cfg-get-value 'redirect-to-ssl' 2>/dev/null) || true
  52. if is_protocol_enabled http "$protocols"; then
  53. redirect=${redirect:-true}
  54. else
  55. redirect=false
  56. fi
  57. if [ "$redirect" == "true" ]; then
  58. custom_rules=$(_get_custom_rules "$cfg") || return 1
  59. if [[ "$custom_rules" != *"## Auto-redirection from http to https"* ]]; then
  60. redirect_rule="- |
  61. ## Auto-redirection from http to https
  62. RewriteEngine On
  63. RewriteCond %{HTTPS} off
  64. RewriteRule ^ https://%{HTTP_HOST}%{REQUEST_URI} [R=302,L,QSA]
  65. "
  66. relation-set apache-custom-rules "$redirect_rule
  67. $(if [ "$custom_rules" ]; then
  68. echo "- |"$'\n'"$(echo "$custom_rules" | prefix " ")"
  69. fi)"
  70. cfg=$(merge_yaml_str "$cfg" "$(yaml_key_val_str "apache-custom-rules" "$redirect_rule
  71. $(if [ "$custom_rules" ]; then
  72. echo "- |"$'\n'"$(echo "$custom_rules" | prefix " ")"
  73. fi)")")
  74. fi
  75. relation-set protocol https
  76. else
  77. ## Both services are available and different, don't do anything then ?
  78. #relation-set protocol https
  79. :
  80. fi
  81. else
  82. relation-set protocol http
  83. fi
  84. vhost_statement=$(apache_vhost_statement "$type" "$protocols" "$cfg" "$domain") || {
  85. err "Failed to get vhost statement for type $type on ${protocols:1:-1}"
  86. return 1
  87. }
  88. dest=${dest:-$domain}
  89. if [ -z "$dest" ]; then
  90. err "Please set either a domain or set a destination file."
  91. return 1
  92. fi
  93. echo "$vhost_statement" | file_put "$APACHE_CONFIG_LOCATION/$dest.conf" || return 1
  94. creds=$(e "$cfg" | cfg-get-value creds 2>/dev/null) || true
  95. if [ "$creds" ]; then
  96. apache_passwd_file "$cfg" "$dest"|| return 1
  97. fi
  98. if is_protocol_enabled https "$protocols"; then
  99. "$ssl_plugin_fun"_prepare "$cfg" "$ssl_cfg_options" "$ssl_cfg_value" || return 1
  100. fi
  101. }
  102. is_protocol_enabled() {
  103. local protocol="$1" protocols="$2"
  104. [[ "$protocols" == *",$protocol,"* ]]
  105. }
  106. export -f is_protocol_enabled
  107. _get_ssl_option_value() {
  108. local cfg="$1" cache_file="$state_tmpdir/$FUNCNAME.cache.$(H "$SERVICE_NAME" "$MASTER_BASE_SERVICE_NAME" "$@")" \
  109. target_relation rn ts rc td
  110. if [ -e "$cache_file" ]; then
  111. cat "$cache_file"
  112. return 0
  113. fi
  114. if ssl_cfg=$(e "$cfg" | cfg-get-value ssl 2>/dev/null); then
  115. if [[ "$ssl_cfg" =~ ^False|None$ ]]; then
  116. ssl_cfg=""
  117. fi
  118. echo "$ssl_cfg" | tee "$cache_file"
  119. return 0
  120. fi
  121. target_relation="cert-provider"
  122. while read-0 rn ts rc td; do
  123. [ "$rn" == "${target_relation}" ] || continue
  124. info "A cert-provider '$ts' declared as 'ssl' value"
  125. echo "$ts" | tee "$cache_file"
  126. return 0
  127. done < <(get_service_relations "$SERVICE_NAME")
  128. return 1
  129. }
  130. __vhost_cfg_normalize_protocol() {
  131. local cfg="$1" protocol ssl
  132. ## XXXvlab: can't cache if libcharm is not able to give me some checksums
  133. ## indeed, ``_get_ssl_option_value`` depends on relations calculations...
  134. # local cfg="$1" cache_file="$CACHEDIR/$FUNCNAME.cache.$(p0 "$@" | md5_compat)" \
  135. # protocol
  136. # if [ -e "$cache_file" ]; then
  137. # #debug "$FUNCNAME: STATIC cache hit $1"
  138. # cat "$cache_file" &&
  139. # touch "$cache_file" || return 1
  140. # return 0
  141. # fi
  142. if protocol=$(e "$cfg" | cfg-get-value protocol 2>/dev/null); then
  143. protocol=${protocol:-auto}
  144. else
  145. protocol=auto
  146. fi
  147. case "$protocol" in
  148. auto)
  149. ssl=$(_get_ssl_option_value "$cfg" 2>/dev/null)
  150. if [ "$ssl" ] ; then
  151. protocol="http,https"
  152. else
  153. protocol="http"
  154. fi
  155. ;;
  156. both)
  157. protocol="http,https"
  158. ;;
  159. ssl|https)
  160. protocol="https"
  161. ;;
  162. http)
  163. protocol="http"
  164. ;;
  165. *)
  166. err "Invalid value '$protocol' for ${WHITE}protocol$NORMAL option (use one of: http, https, both, auto)."
  167. return 1
  168. esac
  169. echo -n ",$protocol,"
  170. #| tee "$cache_file"
  171. }
  172. ## ssl_plugin_* and ssl_fallback should :
  173. ## - do anything to ensure that
  174. ## - issue config-add to add volumes if necessary
  175. ## - output 3 vars of where to find the 3 files from within the docker apache
  176. ssl_get_plugin_fun() {
  177. # from ssl conf, return the function that should manage SSL code creation
  178. local master_cfg="$1" cfg type keys
  179. cfg=$(_get_ssl_option_value "$master_cfg") || return 1
  180. local cache_file="$state_tmpdir/$FUNCNAME.cache.$(H "$SERVICE_NAME" "$cfg")"
  181. if [ -e "$cache_file" ]; then
  182. cat "$cache_file"
  183. return 0
  184. fi
  185. [ "$cfg" ] || {
  186. touch "$cache_file"
  187. return 0
  188. }
  189. type="$(echo "$cfg" | shyaml -y get-type 2>/dev/null)" || return 1
  190. if [[ "$type" == "bool" ]]; then
  191. printf "%s\0" "ssl_fallback" "" "$cfg" | tee "$cache_file"
  192. return 0
  193. fi
  194. if ! [[ "$type" == "str" || "$type" == "struct" ]]; then
  195. err "Invalid ${WHITE}ssl${NORMAL} value type '$type': please provide a string or a struct."
  196. return 1
  197. fi
  198. if [ -z "$NO_CERT_PROVIDER" ]; then
  199. if [[ "$type" == "str" ]]; then
  200. keys=("$cfg")
  201. else
  202. keys=($(echo "$cfg" | shyaml keys 2>/dev/null))
  203. fi
  204. for key in "${keys[@]}"; do
  205. target_relation="cert-provider"
  206. fun="ssl_plugin_${target_relation}"
  207. while read-0 relation_name target_service relation_config tech_dep; do
  208. [ "$relation_name" == "${target_relation}" ] || continue
  209. [ "$target_service" == "$key" ] || continue
  210. verb "Corresponding plugin ${DARKGREEN}found${NORMAL}" \
  211. "in ${DARKBLUE}$relation_name${NORMAL}/${DARKYELLOW}$key${NORMAL}"
  212. ssl_cfg=$(printf "%s" "$cfg" | shyaml get-value "$key" 2>/dev/null) || true
  213. merged_config=$(merge_yaml_str "$relation_config" "$ssl_cfg") || return 1
  214. printf "%s\0" "$fun" "$key" "$merged_config" | tee "$cache_file"
  215. return 0
  216. done < <(get_service_relations "$SERVICE_NAME") || return 1
  217. case "$key" in
  218. cert|ca-cert|key)
  219. :
  220. ;;
  221. *)
  222. err "Invalid key '$key' in ${WHITE}ssl${NORMAL}:" \
  223. "no corresponding services declared in ${DARKBLUE}${target_relation}$NORMAL"
  224. return 1
  225. ;;
  226. esac
  227. done
  228. fi
  229. ## No key of the struct seem to be declared cert-provider, so fallback
  230. printf "%s\0" "ssl_fallback" "" "$cfg" | tee "$cache_file"
  231. }
  232. ssl_fallback_vars() {
  233. local cfg="$1" ssl_cfg="$2" value="$3" domain="$4" cert key ca_cert domain
  234. if __vhost_cfg_ssl_cert=$(echo "$ssl_cfg" | shyaml get-value cert 2>/dev/null); then
  235. __vhost_cfg_SSL_CERT_LOCATION=/etc/ssl/certs/${domain}.pem
  236. fi
  237. if __vhost_cfg_ssl_key=$(echo "$ssl_cfg" | shyaml get-value key 2>/dev/null); then
  238. __vhost_cfg_SSL_KEY_LOCATION=/etc/ssl/private/${domain}.key
  239. fi
  240. if __vhost_cfg_ssl_ca_cert=$(echo "$ssl_cfg" | shyaml get-value ca-cert 2>/dev/null); then
  241. __vhost_cfg_SSL_CA_CERT_LOCATION=/etc/ssl/certs/${domain}-ca.pem
  242. fi
  243. }
  244. ssl_fallback_prepare() {
  245. local cfg="$1" cert key ca_cert
  246. dst="$CONFIGSTORE/$BASE_SERVICE_NAME"
  247. volumes=""
  248. for label in cert key ca_cert; do
  249. content="$(eval echo "\"\$__vhost_cfg_ssl_$label\"")"
  250. if [ "$content" ]; then
  251. location="$(eval echo "\$__vhost_cfg_SSL_${label^^}_LOCATION")"
  252. echo "$content" | file_put "$dst$location"
  253. config_hash=$(printf "%s\0" "$config_hash" "$label" "$content" | md5_compat)
  254. volumes="$volumes
  255. - $dst$location:$location:ro"
  256. fi
  257. done
  258. if [ "$volumes" ]; then
  259. config-add "\
  260. services:
  261. $MASTER_TARGET_SERVICE_NAME:
  262. volumes:
  263. $volumes
  264. "
  265. fi
  266. }
  267. ssl_plugin_cert-provider_vars() {
  268. local cfg="$1" ssl_cfg="$2" value="$3" domain="$4"
  269. __vhost_cfg_SSL_CERT_LOCATION=/etc/letsencrypt/live/${domain}/cert.pem
  270. __vhost_cfg_SSL_KEY_LOCATION=/etc/letsencrypt/live/${domain}/privkey.pem
  271. __vhost_cfg_SSL_CHAIN=/etc/letsencrypt/live/${domain}/chain.pem
  272. }
  273. ssl_plugin_cert-provider_prepare() {
  274. local cfg="$1" ssl_cfg="$2" service="$3" options domain server_aliases
  275. domain=$(get_domain "$cfg") || return 1
  276. options=$(yaml_key_val_str "options" "$ssl_cfg") || return 1
  277. service_config=$(yaml_key_val_str "$service" "$options")
  278. server_aliases=$(e "$cfg" | cfg-get-value server-aliases 2>/dev/null) || true
  279. [ "$server_aliases" == None ] && server_aliases=""
  280. if [ "$server_aliases" ]; then
  281. server_aliases=($(echo "$server_aliases" | shyaml get-values)) || return 1
  282. else
  283. server_aliases=()
  284. fi
  285. compose --debug --add-compose-content "$service_config" run --rm --service-ports "$service" \
  286. crt create "$domain" "${server_aliases[@]}" || {
  287. err "Failed to launch letsencrypt for certificate creation."
  288. return 1
  289. }
  290. config-add "\
  291. services:
  292. $MASTER_TARGET_SERVICE_NAME:
  293. volumes:
  294. - $DATASTORE/$service/etc/letsencrypt:/etc/letsencrypt:ro
  295. " || return 1
  296. }
  297. apache_passwd_file() {
  298. local cfg="$1" dest="$2" creds
  299. include parse || true
  300. ## XXXvlab: called twice... no better way to do this ?
  301. creds=$(e "$cfg" | cfg-get-value creds 2>/dev/null) || true
  302. password_path=$(password-path-get "$dest")
  303. first=
  304. if ! [ -e "$CONFIGSTORE/$MASTER_TARGET_SERVICE_NAME$password_path" ]; then
  305. debug "No file $CONFIGSTORE/$MASTER_TARGET_SERVICE_NAME$password_path, creating password file." || true
  306. first=c
  307. fi
  308. while read-0 login password; do
  309. debug "htpasswd -b$first '${password_path}' '$login' '$password'"
  310. echo "htpasswd -b$first '${password_path}' '$login' '$password'"
  311. if [ "$first" ]; then
  312. first=
  313. fi
  314. done < <(e "$creds" | shyaml key-values-0 2>/dev/null) |
  315. docker run -i --entrypoint "/bin/bash" \
  316. -v "$APACHE_CONFIG_LOCATION:/etc/apache2/sites-enabled" \
  317. "$DOCKER_BASE_IMAGE" || return 1
  318. }
  319. ## Produce the full statements depending on relation-get informations
  320. apache_vhost_statement() {
  321. local type="$1" protocols="$2" cfg="$3" domain="$4" \
  322. vhost_statement
  323. if is_protocol_enabled http "$protocols"; then
  324. __vhost_full_vhost_statement "$type" http "$cfg" "$domain" || return 1
  325. fi
  326. if is_protocol_enabled https "$protocols"; then
  327. read-0 ssl_plugin_fun ssl_cfg_value ssl_cfg_options < <(ssl_get_plugin_fun "$cfg") || return 1
  328. "$ssl_plugin_fun"_vars "$cfg" "$ssl_cfg_options" "$ssl_cfg_value" "$domain" || return 1
  329. vhost_statement=$(__vhost_full_vhost_statement "$type" https "$cfg" "$domain") || return 1
  330. cat <<EOF
  331. <IfModule mod_ssl.c>
  332. $(echo "$vhost_statement" | prefix " ")
  333. </IfModule>
  334. EOF
  335. fi
  336. }
  337. export -f apache_vhost_statement
  338. apache_code_dir() {
  339. local cfg="$1" www_data_gid local_path
  340. www_data_gid=$(cached_cmd_on_base_image apache 'id -g www-data') || {
  341. debug "Failed to query for www-data gid in ${DARKYELLOW}apache${NORMAL} base image."
  342. return 1
  343. }
  344. domain=$(get_domain "$cfg") || return 1
  345. local_path="/var/www/${domain}"
  346. host_path=$(e "$cfg" | cfg-get-value location 2>/dev/null) ||
  347. host_path="$DATASTORE/$BASE_SERVICE_NAME${local_path}"
  348. mkdir -p "$host_path" || return 1
  349. setfacl -R -m g:"$www_data_gid":rx "$host_path"
  350. info "Set permission for read and traversal on '$host_path'."
  351. config-add "
  352. $MASTER_BASE_SERVICE_NAME:
  353. volumes:
  354. - $host_path:$local_path
  355. "
  356. }
  357. apache_data_dirs() {
  358. local cfg="$1" data_dirs dst data dirs
  359. data_dirs=$(e "$cfg" | cfg-get-value data-dirs 2>/dev/null | shyaml get-values 2>/dev/null) || true
  360. if [ -z "$data_dirs" ]; then
  361. return 0
  362. fi
  363. domain=$(get_domain "$cfg") || return 1
  364. local_path="/var/www/${domain}"
  365. dst=$DATASTORE/$BASE_SERVICE_NAME$local_path
  366. data=()
  367. while IFS="," read -ra addr; do
  368. for dir in "${addr[@]}"; do
  369. data+=($dir)
  370. done
  371. done <<< "$data_dirs"
  372. www_data_gid=$(cached_cmd_on_base_image apache 'id -g www-data') || {
  373. debug "Failed to query for www-data gid in ${DARKYELLOW}apache${NORMAL} base image."
  374. return 1
  375. }
  376. info "www-data gid from ${DARKYELLOW}apache${NORMAL} is '$www_data_gid'"
  377. dirs=()
  378. for d in "${data[@]}"; do
  379. dirs+=("$dst/$d")
  380. done
  381. mkdir -p "${dirs[@]}"
  382. setfacl -R -m g:"$www_data_gid":rwx "${dirs[@]}"
  383. setfacl -R -d -m g:"$www_data_gid":rwx "${dirs[@]}"
  384. config-add "
  385. $MASTER_BASE_SERVICE_NAME:
  386. volumes:
  387. $(
  388. for d in "${data[@]}"; do
  389. echo " - $dst/$d:$local_path/$d"
  390. done
  391. )"
  392. }
  393. deploy_files() {
  394. local src="$1" dst="$2"
  395. if ! [ -d "$dst" ]; then
  396. err "Destination '$dst' does not exist or is not a directory"
  397. return 1
  398. fi
  399. (
  400. cd "$dst" && info "In $dst:" &&
  401. get_file "$src" | tar xv
  402. )
  403. }
  404. export -f deploy_files
  405. apache_core_rules_add() {
  406. local conf="$1" dst="/etc/apache2/conf-enabled/$BASE_SERVICE_NAME.conf"
  407. debug "Adding core rule."
  408. echo "$conf" | file_put "$CONFIGSTORE/$BASE_SERVICE_NAME$dst"
  409. config_hash=$(printf "%s\0" "$config_hash" "$conf" | md5_compat)
  410. config-add "
  411. $MASTER_BASE_SERVICE_NAME:
  412. volumes:
  413. - $CONFIGSTORE/$BASE_SERVICE_NAME$dst:$dst:ro
  414. "
  415. }
  416. __vhost_ssl_statement() {
  417. ## defaults
  418. __vhost_cfg_SSL_CERT_LOCATION=${__vhost_cfg_SSL_CERT_LOCATION:-/etc/ssl/certs/ssl-cert-snakeoil.pem}
  419. __vhost_cfg_SSL_KEY_LOCATION=${__vhost_cfg_SSL_KEY_LOCATION:-/etc/ssl/private/ssl-cert-snakeoil.key}
  420. cat <<EOF
  421. ##
  422. ## SSL Configuration
  423. ##
  424. SSLEngine On
  425. SSLCertificateFile $__vhost_cfg_SSL_CERT_LOCATION
  426. SSLCertificateKeyFile $__vhost_cfg_SSL_KEY_LOCATION
  427. $([ -z "$__vhost_cfg_SSL_CA_CERT_LOCATION" ] || echo "SSLCACertificateFile $__vhost_cfg_SSL_CA_CERT_LOCATION")
  428. $([ -z "$__vhost_cfg_SSL_CHAIN" ] || echo "SSLCertificateChainFile $__vhost_cfg_SSL_CHAIN")
  429. SSLVerifyClient None
  430. EOF
  431. }
  432. password-path-get() {
  433. local dest="$1"
  434. echo "/etc/apache2/sites-enabled/${dest}.passwd"
  435. }
  436. __vhost_creds_statement() {
  437. local cfg="$1" dest="$2" password_path
  438. password_path=$(password-path-get "$dest") || return 1
  439. if ! e "$cfg" | cfg-get-value creds >/dev/null 2>&1; then
  440. echo "Allow from all"
  441. return 0
  442. fi
  443. cat <<EOF
  444. AuthType basic
  445. AuthName "private"
  446. AuthUserFile ${password_path}
  447. Require valid-user
  448. EOF
  449. }
  450. __vhost_head_statement() {
  451. local cfg="$1" protocol="$2" domain="$3" server_aliases admin_mail prefix
  452. admin_mail=$(e "$1" | cfg-get-value "admin-mail" 2>/dev/null) || true
  453. if [ -z "$admin_mail" ]; then
  454. if [ -z "$domain" ]; then
  455. admin_mail=webmaster@localhost
  456. else
  457. admin_mail=${admin_mail:-contact@$domain}
  458. fi
  459. fi
  460. server_aliases=$(e "$cfg" | cfg-get-value server-aliases 2>/dev/null) || true
  461. [ "$server_aliases" == None ] && server_aliases=""
  462. if [ "$server_aliases" ]; then
  463. server_aliases=($(e "$server_aliases" | shyaml get-values)) || return 1
  464. if [ -z "$domain" ]; then
  465. err "You can't specify server aliases if you don't have a domain."
  466. return 1
  467. fi
  468. else
  469. server_aliases=()
  470. fi
  471. if [ "$protocol" == "https" ]; then
  472. prefix="s-"
  473. else
  474. prefix=
  475. fi
  476. if [ "$domain" ]; then
  477. log_prefix="${prefix}${domain}_"
  478. else
  479. log_prefix=""
  480. fi
  481. cat <<EOF
  482. $(
  483. echo "ServerAdmin ${admin_mail}"
  484. [ "$domain" ] && echo "ServerName ${domain}"
  485. for alias in "${server_aliases[@]}"; do
  486. [ "$alias" ] || continue
  487. echo "ServerAlias $alias"
  488. done
  489. )
  490. ServerSignature Off
  491. CustomLog /var/log/apache2/${log_prefix}access.log combined
  492. ErrorLog /var/log/apache2/${log_prefix}error.log
  493. ErrorLog syslog:local2
  494. EOF
  495. }
  496. _get_custom_rules() {
  497. local cfg="$1" custom_rules type elt value first
  498. custom_rules=$(e "$cfg" | cfg-get-value apache-custom-rules 2>/dev/null) || true
  499. if [ -z "$custom_rules" ]; then
  500. return 0
  501. fi
  502. type=$(echo "$custom_rules" | shyaml get-type)
  503. value=
  504. case "$type" in
  505. "sequence")
  506. first=1
  507. while read-0 elt; do
  508. elt="$(echo "$elt" | yaml_get_interpret)" || return 1
  509. [ "$elt" ] || continue
  510. if [ "$first" ]; then
  511. first=
  512. else
  513. value+=$'\n'$'\n'
  514. fi
  515. first=
  516. value+="$elt"
  517. done < <(echo "$custom_rules" | shyaml -y get-values-0)
  518. ;;
  519. "struct")
  520. while read-0 _key val; do
  521. value+=$'\n'"$(echo "$val" | yaml_get_interpret)" || return 1
  522. done < <(echo "$custom_rules" | shyaml -y key-values-0)
  523. ;;
  524. "str")
  525. value+=$(echo "$custom_rules")
  526. ;;
  527. *)
  528. value+=$(echo "$custom_rules")
  529. ;;
  530. esac
  531. printf "%s" "$value"
  532. }
  533. __vhost_custom_rules() {
  534. local cfg="$1" custom_rules
  535. custom_rules=$(_get_custom_rules "$cfg") || return 1
  536. if [ "$custom_rules" ]; then
  537. cat <<EOF
  538. ##
  539. ## Custom rules
  540. ##
  541. $custom_rules
  542. EOF
  543. fi
  544. }
  545. __vhost_content_statement() {
  546. local type="$1"
  547. shift
  548. case "$type" in
  549. "web_proxy")
  550. __vhost_proxy_statement "$@" || return 1
  551. ;;
  552. "publish_dir")
  553. __vhost_publish_dir_statement "$@" || return 1
  554. ;;
  555. esac
  556. }
  557. target-get() {
  558. local cfg="$1" target first_exposed_port base_image
  559. target=$(e "$cfg" | cfg-get-value target 2>/dev/null) || true
  560. if [ -z "$target" ]; then
  561. ## First exposed port:
  562. base_image=$(service_base_docker_image "$BASE_SERVICE_NAME") || return 1
  563. if ! docker_has_image "$base_image"; then
  564. docker pull "$base_image" >&2
  565. fi
  566. first_exposed_port=$(image_exposed_ports_0 "$base_image" | tr '\0' '\n' | head -n 1 | cut -f 1 -d /) || return 1
  567. if [ -z "$first_exposed_port" ]; then
  568. err "Failed to get first exposed port of image '$base_image'."
  569. return 1
  570. fi
  571. target=$MASTER_BASE_SERVICE_NAME:$first_exposed_port
  572. info "No target was specified, introspection found: $target"
  573. fi
  574. echo "$target"
  575. }
  576. __vhost_proxy_statement() {
  577. local protocol="$1" cfg="$2" dest="$3"
  578. target=$(target-get "$cfg") || return 1
  579. cat <<EOF
  580. ##
  581. ## Proxy declaration towards $target
  582. ##
  583. <IfModule mod_proxy.c>
  584. ProxyRequests Off
  585. <Proxy *>
  586. Order deny,allow
  587. Allow from all
  588. </Proxy>
  589. ProxyVia On
  590. ProxyPass / http://$target/ retry=0
  591. <Location / >
  592. $(__vhost_creds_statement "$cfg" "$dest" | prefix " ")
  593. ProxyPassReverse /
  594. </Location>
  595. $([ "$protocol" == "https" ] && echo " SSLProxyEngine On")
  596. </IfModule>
  597. RequestHeader set "X-Forwarded-Proto" "$protocol"
  598. ## Fix IE problem (httpapache proxy dav error 408/409)
  599. SetEnv proxy-nokeepalive 1
  600. EOF
  601. }
  602. __vhost_full_vhost_statement() {
  603. local type="$1" protocol="$2" cfg="$3" domain="$4" head_statement custom_rules content_statement
  604. head_statement=$(__vhost_head_statement "$cfg" "$protocol" "$domain") || return 1
  605. custom_rules=$(__vhost_custom_rules "$cfg") || return 1
  606. content_statement=$(__vhost_content_statement "$type" "$protocol" "$cfg" "${domain:-html}") || return 1
  607. case "$protocol" in
  608. https)
  609. PORT=443
  610. ;;
  611. http)
  612. PORT=80
  613. ;;
  614. esac
  615. cat <<EOF
  616. <VirtualHost *:$PORT>
  617. $(
  618. {
  619. echo "$head_statement"
  620. [ "$custom_rules" ] && echo "$custom_rules"
  621. echo "$content_statement"
  622. } | prefix " ")
  623. ## Forbid any cache, this is only usefull on dev server.
  624. #Header set Cache-Control "no-cache"
  625. #Header set Access-Control-Allow-Origin "*"
  626. #Header set Access-Control-Allow-Methods "POST, GET, OPTIONS"
  627. #Header set Access-Control-Allow-Headers "origin, content-type, accept"
  628. $([ "$protocol" == "https" ] && __vhost_ssl_statement | prefix " " && echo )
  629. </VirtualHost>
  630. EOF
  631. }
  632. __vhost_publish_dir_statement() {
  633. local protocol="$1" cfg="$2" dest="$3" dest
  634. local_path="/var/www/${dest}"
  635. cat <<EOF
  636. ##
  637. ## Publish directory $local_path
  638. ##
  639. DocumentRoot $local_path
  640. <Directory />
  641. Options FollowSymLinks
  642. AllowOverride None
  643. </Directory>
  644. <Directory $local_path>
  645. Options Indexes FollowSymLinks MultiViews
  646. AllowOverride all
  647. $(__vhost_creds_statement "$cfg" "$dest" | prefix " ")
  648. </Directory>
  649. EOF
  650. }
  651. apache_config_hash() {
  652. debug "Adding config hash to enable recreating upon config change."
  653. config_hash=$({
  654. printf "%s\0" "$config_hash"
  655. find "$SERVICE_CONFIGSTORE/etc/apache2/sites-enabled" \
  656. -name \*.conf -exec md5sum {} \;
  657. } | md5_compat) || exit 1
  658. init-config-add "
  659. $MASTER_BASE_SERVICE_NAME:
  660. labels:
  661. - compose.config_hash=$config_hash
  662. "
  663. }