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.

451 lines
11 KiB

  1. # -*- mode: shell-script -*-
  2. apache_proxy_dir () {
  3. DOMAIN=$(relation-get domain) || {
  4. err "You must specify a ${WHITE}domain$NORMAL option in relation."
  5. return 1
  6. }
  7. proxy=yes apache_vhost_create
  8. info "Added $DOMAIN as a proxy to $TARGET."
  9. }
  10. export -f apache_proxy_dir
  11. apache_publish_dir () {
  12. DOMAIN=$(relation-get domain) || {
  13. err "You must specify a ${WHITE}domain$NORMAL option in relation."
  14. return 1
  15. }
  16. DOCKER_SITE_PATH="/var/www/${DOMAIN}"
  17. LOCATION=$(relation-get location 2>/dev/null) ||
  18. LOCATION="$DATASTORE/$BASE_CHARM_NAME$DOCKER_SITE_PATH"
  19. apache_vhost_create || return 1
  20. info "Added $DOMAIN apache config."
  21. apache_code_dir || return 1
  22. apache_data_dirs
  23. }
  24. export -f apache_publish_dir
  25. apache_vhost_create () {
  26. export APACHE_CONFIG_LOCATION="$SERVICE_CONFIGSTORE/etc/apache2/sites-enabled"
  27. export PROTOCOLS=$(__vhost_cfg_normalize_protocol) || return 1
  28. apache_vhost_statement "$PROTOCOLS" |
  29. file_put "$APACHE_CONFIG_LOCATION/$prefix$DOMAIN.conf" || return 1
  30. __vhost_cfg_creds_enabled=$(relation-get creds 2>/dev/null) || true
  31. if [ "$__vhost_cfg_creds_enabled" ]; then
  32. apache_passwd_file
  33. fi
  34. if is_protocol_enabled https; then
  35. apache_ssl_files
  36. fi
  37. }
  38. is_protocol_enabled() {
  39. local protocol=$1
  40. [[ "$PROTOCOLS" == *",$protocol,"* ]]
  41. }
  42. export -f is_protocol_enabled
  43. __vhost_cfg_normalize_protocol() {
  44. local protocol
  45. if ! protocol=$(relation-get protocol 2>/dev/null); then
  46. protocol=auto
  47. else
  48. protocol=${protocol:-auto}
  49. fi
  50. case "$protocol" in
  51. auto)
  52. if __vhost_cfg_ssl="$(relation-get ssl 2>/dev/null)"; then
  53. protocol="https"
  54. export __vhost_cfg_ssl
  55. else
  56. protocol="http"
  57. fi
  58. ;;
  59. both)
  60. protocol="https,http"
  61. ;;
  62. ssl|https)
  63. protocol="https"
  64. ;;
  65. http)
  66. protocol="http"
  67. ;;
  68. *)
  69. err "Invalid value '$protocol' for ${WHITE}protocol$NORMAL option (use one of: http, https, both, auto)."
  70. return 1
  71. esac
  72. echo ",$protocol,"
  73. }
  74. apache_ssl_files() {
  75. local content
  76. ## XXXvlab: called twice... no better way to do this ?
  77. __vhost_ssl_statement > /dev/null
  78. dst="$CONFIGSTORE/$BASE_CHARM_NAME"
  79. volumes=""
  80. for label in cert key ca_cert; do
  81. content="$(eval echo "\"\$__vhost_cfg_ssl_$label\"")"
  82. if [ "$content" ]; then
  83. location="$(eval echo "\$__vhost_cfg_SSL_${label^^}_LOCATION")"
  84. echo "$content" | file_put "$dst$location"
  85. volumes="$volumes
  86. - $dst$location:$location:ro"
  87. fi
  88. done
  89. if [ "$volumes" ]; then
  90. config-add "\
  91. $MASTER_TARGET_CHARM_NAME:
  92. volumes:
  93. $volumes
  94. "
  95. fi
  96. }
  97. apache_passwd_file() {
  98. include parse || true
  99. ## XXXvlab: called twice... no better way to do this ?
  100. __vhost_creds_statement >/dev/null
  101. first=
  102. if ! [ -e "$CONFIGSTORE/$MASTER_TARGET_CHARM_NAME$password_file" ]; then
  103. debug "No file $CONFIGSTORE/$MASTER_TARGET_CHARM_NAME$password_file, creating password file." || true
  104. first=c
  105. fi
  106. while read-0 login password; do
  107. debug "htpasswd -b$first '${password_file}' '$login' '$password'"
  108. echo "htpasswd -b$first '${password_file}' '$login' '$password'"
  109. if [ "$first" ]; then
  110. first=
  111. fi
  112. done < <(echo "$__vhost_cfg_creds_enabled" | shyaml key-values-0 2>/dev/null) |
  113. docker run -i --entrypoint "/bin/bash" \
  114. -v "$APACHE_CONFIG_LOCATION:/etc/apache2/sites-enabled" \
  115. "$DOCKER_BASE_IMAGE" || return 1
  116. }
  117. ## Produce the full statements depending on relation-get informations
  118. apache_vhost_statement() {
  119. local vhost_statement
  120. export PROTOCOLS="$1"
  121. if is_protocol_enabled http; then
  122. __vhost_full_vhost_statement http
  123. fi
  124. if is_protocol_enabled https; then
  125. cat <<EOF
  126. <IfModule mod_ssl.c>
  127. $(__vhost_full_vhost_statement https | prefix " ")
  128. </IfModule>
  129. EOF
  130. fi
  131. }
  132. export -f apache_vhost_statement
  133. apache_code_dir() {
  134. local www_data_gid
  135. www_data_gid=$(cached_cmd_on_base_image apache 'id -g www-data') || {
  136. debug "Failed to query for www-data gid in ${DARKYELLOW}apache${NORMAL} base image."
  137. return 1
  138. }
  139. mkdir -p "$LOCATION" || return 1
  140. setfacl -R -m g:"$www_data_gid":rx "$LOCATION"
  141. info "Set permission for read and traversal on '$LOCATION'."
  142. config-add "
  143. $MASTER_BASE_CHARM_NAME:
  144. volumes:
  145. - $LOCATION:$DOCKER_SITE_PATH
  146. "
  147. }
  148. apache_data_dirs() {
  149. DATA_DIRS=$(relation-get data-dirs 2>/dev/null | shyaml get-values 2>/dev/null) || true
  150. if [ -z "$DATA_DIRS" ]; then
  151. return 0
  152. fi
  153. DST=$DATASTORE/$BASE_CHARM_NAME$DOCKER_SITE_PATH
  154. DATA=()
  155. while IFS="," read -ra ADDR; do
  156. for dir in "${ADDR[@]}"; do
  157. DATA+=($dir)
  158. done
  159. done <<< "$DATA_DIRS"
  160. www_data_gid=$(cached_cmd_on_base_image apache 'id -g www-data') || {
  161. debug "Failed to query for www-data gid in ${DARKYELLOW}apache${NORMAL} base image."
  162. return 1
  163. }
  164. info "www-data gid from ${DARKYELLOW}apache${NORMAL} is '$www_data_gid'"
  165. dirs=()
  166. for d in "${DATA[@]}"; do
  167. dirs+=("$DST/$d")
  168. done
  169. mkdir -p "${dirs[@]}"
  170. setfacl -R -m g:"$www_data_gid":rwx "${dirs[@]}"
  171. setfacl -R -d -m g:"$www_data_gid":rwx "${dirs[@]}"
  172. config-add "
  173. $MASTER_BASE_CHARM_NAME:
  174. volumes:
  175. $(
  176. for d in "${DATA[@]}"; do
  177. echo " - $DST/$d:$DOCKER_SITE_PATH/$d"
  178. done
  179. )"
  180. }
  181. deploy_files() {
  182. local src="$1" dst="$2"
  183. if ! [ -d "$dst" ]; then
  184. err "Destination '$dst' does not exist or is not a directory"
  185. return 1
  186. fi
  187. (
  188. cd "$dst" && info "In $dst:" &&
  189. get_file "$src" | tar xv
  190. )
  191. }
  192. export -f deploy_files
  193. apache_core_rules_add() {
  194. local conf="$1" dst="/etc/apache2/conf-enabled/$BASE_CHARM_NAME.conf"
  195. debug "Adding core rule."
  196. echo "$conf" | file_put "$CONFIGSTORE/$BASE_CHARM_NAME$dst"
  197. config-add "
  198. $MASTER_BASE_CHARM_NAME:
  199. volumes:
  200. - $CONFIGSTORE/$BASE_CHARM_NAME$dst:$dst:ro
  201. "
  202. }
  203. __vhost_ssl_statement() {
  204. local key cert ca_cert
  205. __vhost_cfg_ssl="$(relation-get ssl 2>/dev/null)"
  206. if __vhost_cfg_ssl_cert=$(echo "$__vhost_cfg_ssl" | shyaml get-value cert 2>/dev/null); then
  207. __vhost_cfg_SSL_CERT_LOCATION=/etc/ssl/certs/${DOMAIN}.pem
  208. fi
  209. if __vhost_cfg_ssl_key=$(echo "$__vhost_cfg_ssl" | shyaml get-value key 2>/dev/null); then
  210. __vhost_cfg_SSL_KEY_LOCATION=/etc/ssl/private/${DOMAIN}.key
  211. fi
  212. if __vhost_cfg_ssl_ca_cert=$(echo "$__vhost_cfg_ssl" | shyaml get-value ca-cert 2>/dev/null); then
  213. __vhost_cfg_SSL_CA_CERT_LOCATION=/etc/ssl/certs/${DOMAIN}-ca.pem
  214. fi
  215. if [ -z "$__vhost_cfg_SSL_CERT_LOCATION" ]; then
  216. __vhost_cfg_SSL_CERT_LOCATION=/etc/ssl/certs/ssl-cert-snakeoil.pem
  217. fi
  218. if [ -z "$__vhost_cfg_SSL_KEY_LOCATION" ]; then
  219. __vhost_cfg_SSL_KEY_LOCATION=/etc/ssl/private/ssl-cert-snakeoil.key
  220. fi
  221. cat <<EOF
  222. ##
  223. ## SSL Configuration
  224. ##
  225. SSLEngine On
  226. SSLCertificateFile $__vhost_cfg_SSL_CERT_LOCATION
  227. SSLCertificateKeyFile $__vhost_cfg_SSL_KEY_LOCATION
  228. $([ -z "$__vhost_cfg_SSL_CA_CERT_LOCATION" ] || echo "SSLCACertificateFile $__vhost_cfg_SSL_CA_CERT_LOCATION")
  229. SSLVerifyClient None
  230. EOF
  231. }
  232. __vhost_creds_statement() {
  233. if ! __vhost_cfg_creds_enabled=$(relation-get creds 2>/dev/null); then
  234. echo "Allow from all"
  235. return 0
  236. fi
  237. password_file=/etc/apache2/sites-enabled/${DOMAIN}.passwd
  238. cat <<EOF
  239. AuthType basic
  240. AuthName "private"
  241. AuthUserFile ${password_file}
  242. Require valid-user
  243. EOF
  244. }
  245. __vhost_head_statement() {
  246. local protocol="$1"
  247. SERVER_ALIAS=$(relation-get server-aliases 2>/dev/null) || true
  248. if [ "$protocol" == "https" ]; then
  249. prefix="s-"
  250. else
  251. prefix=
  252. fi
  253. cat <<EOF
  254. ServerAdmin ${ADMIN_MAIL:-contact@$DOMAIN}
  255. ServerName ${DOMAIN}
  256. $(
  257. while read-0 alias; do
  258. echo "ServerAlias $alias"
  259. done < <(echo "$SERVER_ALIAS" | shyaml get-values-0 2>/dev/null)
  260. )
  261. ServerSignature Off
  262. CustomLog /var/log/apache2/${prefix}${DOMAIN}_access.log combined
  263. ErrorLog /var/log/apache2/${prefix}${DOMAIN}_error.log
  264. ErrorLog syslog:local2
  265. EOF
  266. }
  267. __vhost_custom_rules() {
  268. local custom_rules
  269. if custom_rules=$(relation-get apache-custom-rules 2>/dev/null); then
  270. cat <<EOF
  271. ##
  272. ## Custom rules
  273. ##
  274. $custom_rules
  275. EOF
  276. fi
  277. }
  278. __vhost_content_statement() {
  279. if [ "$proxy" ]; then
  280. __vhost_proxy_statement "$@"
  281. else
  282. __vhost_publish_dir_statement "$@"
  283. fi
  284. }
  285. __vhost_proxy_statement() {
  286. local protocol="$1"
  287. TARGET=$(relation-get target 2>/dev/null) || true
  288. if [ -z "$TARGET" ]; then
  289. ## First exposed port:
  290. base_image=$(service_base_docker_image "$BASE_CHARM_NAME") || return 1
  291. first_exposed_port=$(image_exposed_ports_0 "$base_image" | tr '\0' '\n' | head -n 1 | cut -f 1 -d /) || return 1
  292. TARGET=$MASTER_BASE_CHARM_NAME:$first_exposed_port
  293. info "No target was specified, introspection found: $TARGET"
  294. fi
  295. cat <<EOF
  296. ##
  297. ## Proxy declaration towards $TARGET
  298. ##
  299. <IfModule mod_proxy.c>
  300. ProxyRequests Off
  301. <Proxy *>
  302. Order deny,allow
  303. Allow from all
  304. </Proxy>
  305. ProxyVia On
  306. ProxyPass / http://$TARGET/ retry=0
  307. <Location / >
  308. $(__vhost_creds_statement | prefix " ")
  309. ProxyPassReverse /
  310. </Location>
  311. $([ "$protocol" == "https" ] && echo " SSLProxyEngine On")
  312. </IfModule>
  313. RequestHeader set "X-Forwarded-Proto" "$protocol"
  314. ## Fix IE problem (httpapache proxy dav error 408/409)
  315. SetEnv proxy-nokeepalive 1
  316. EOF
  317. }
  318. __vhost_full_vhost_statement() {
  319. local protocol="$1"
  320. case "$protocol" in
  321. https)
  322. PORT=443
  323. ;;
  324. http)
  325. PORT=80
  326. ;;
  327. esac
  328. cat <<EOF
  329. <VirtualHost *:$PORT>
  330. $(__vhost_head_statement "$protocol" | prefix " ")
  331. $(__vhost_content_statement "$protocol" | prefix " ")
  332. ## Forbid any cache, this is only usefull on dev server.
  333. #Header set Cache-Control "no-cache"
  334. #Header set Access-Control-Allow-Origin "*"
  335. #Header set Access-Control-Allow-Methods "POST, GET, OPTIONS"
  336. #Header set Access-Control-Allow-Headers "origin, content-type, accept"
  337. $([ "$protocol" == "https" ] && __vhost_ssl_statement | prefix " ")
  338. $(__vhost_custom_rules | prefix " ")
  339. </VirtualHost>
  340. EOF
  341. }
  342. __vhost_publish_dir_statement() {
  343. cat <<EOF
  344. ##
  345. ## Publish directory $DOCKER_SITE_PATH
  346. ##
  347. DocumentRoot $DOCKER_SITE_PATH
  348. <Directory />
  349. Options FollowSymLinks
  350. AllowOverride None
  351. </Directory>
  352. <Directory $DOCKER_SITE_PATH>
  353. Options Indexes FollowSymLinks MultiViews
  354. AllowOverride all
  355. $(__vhost_creds_statement | prefix " ")
  356. </Directory>
  357. EOF
  358. }