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.

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