291 lines
8.4 KiB

  1. # -*- mode: shell-script -*-
  2. ssl_get_plugin_fun() {
  3. local cfg="$1"
  4. type="$(echo "$cfg" | shyaml -y get-type 2>/dev/null)" || return 1
  5. case "$type" in
  6. bool)
  7. err "Option ${WHITE}ssl${NORMAL} value '$cfg' is not supported."
  8. return 1
  9. ;;
  10. str)
  11. keys=("$cfg")
  12. ;;
  13. struct)
  14. keys=( $(echo "$cfg" | shyaml keys 2>/dev/null) )
  15. ;;
  16. *)
  17. err "Invalid ${WHITE}ssl${NORMAL} value type '$type': please provide a string or a struct."
  18. return 1
  19. ;;
  20. esac
  21. for key in "${keys[@]}"; do
  22. target_relation="cert-provider"
  23. fun="ssl_plugin_${target_relation}"
  24. while read-0 relation_name target_service relation_config tech_dep; do
  25. [ "$relation_name" == "${target_relation}" ] || continue
  26. [ "$target_service" == "$key" ] || continue
  27. verb "Corresponding plugin ${DARKGREEN}found${NORMAL}" \
  28. "in ${DARKBLUE}$relation_name${NORMAL}/${DARKYELLOW}$key${NORMAL}"
  29. ssl_cfg=$(printf "%s" "$cfg" | shyaml get-value "$key" 2>/dev/null) || true
  30. merged_config=$(merge_yaml_str "$relation_config" "$ssl_cfg") || return 1
  31. printf "%s\0" "$fun" "$key" "$merged_config"
  32. return 0
  33. done < <(get_service_relations "$SERVICE_NAME") || return 1
  34. case "$key" in
  35. cert|ca-cert|key)
  36. :
  37. ;;
  38. *)
  39. err "Invalid key '$key' in ${WHITE}ssl${NORMAL}:" \
  40. "no corresponding services declared in ${DARKBLUE}${target_relation}$NORMAL"
  41. return 1
  42. ;;
  43. esac
  44. done
  45. }
  46. ssl_fallback_vars() {
  47. local cfg="$1" DOMAIN="$2" cert key ca_cert
  48. base_dir=/etc/ssl
  49. print "%s\0" \
  50. "$base_dir"/certs/"${DOMAIN}".pem \
  51. "$base_dir"/private/"${DOMAIN}".key \
  52. "$base_dir"/certs/"${DOMAIN}"-ca.pem
  53. print "%s\0" "$base_dir"/cert.pem "$base_dir"/privkey.pem "$base_dir"/chain.pem
  54. }
  55. ssl_fallback_prepare() {
  56. local cfg="$1" service="$2" l_cert l_key l_ca_cert
  57. shift 2
  58. read-0 l_cert l_key l_ca_cert < <(ssl_fallback_vars "$cfg" "$@")
  59. dst="$CONFIGSTORE/$BASE_SERVICE_NAME"
  60. volumes=""
  61. for label in cert key ca-cert; do
  62. if content=$(echo "$cfg" | shyaml get-value "$label" 2>/dev/null); then
  63. location="$(eval echo "\$l_${label//-/_}")"
  64. echo "$content" | file_put "$dst$location"
  65. config_hash=$(printf "%s\0" "$config_hash" "$label" "$content" | md5_compat)
  66. volumes="$volumes
  67. - $dst$location:$location:ro"
  68. fi
  69. done
  70. if [ "$volumes" ]; then
  71. init-config-add "\
  72. $MASTER_TARGET_SERVICE_NAME:
  73. volumes:
  74. $volumes
  75. "
  76. fi
  77. }
  78. ssl_plugin_cert-provider_vars() {
  79. local cfg="$1" DOMAIN="$2"
  80. base_dir="/etc/letsencrypt/live/$DOMAIN"
  81. print "%s\0" "$base_dir"/cert.pem "$base_dir"/privkey.pem "$base_dir"/chain.pem
  82. }
  83. ssl_plugin_cert-provider_prepare() {
  84. local cfg="$1" service="$2" options
  85. shift 2
  86. options=$(yaml_key_val_str "options" "$cfg") || return 1
  87. service_config=$(yaml_key_val_str "$service" "$options")
  88. compose --debug --add-compose-content "$service_config" run --rm --service-ports "$service" \
  89. crt create "$@" || {
  90. err "Failed to launch letsencrypt for certificate creation."
  91. return 1
  92. }
  93. init-config-add "\
  94. $MASTER_TARGET_SERVICE_NAME:
  95. volumes:
  96. - $DATASTORE/$service/etc/letsencrypt:/etc/letsencrypt:ro
  97. " || return 1
  98. }
  99. make_pasv_options() {
  100. local pasv="$1"
  101. if [[ "${pasv,,}" =~ ^(false|no|n|0|disable|disabled)$ ]]; then
  102. echo "pasv_enable=no"
  103. return 0
  104. fi
  105. ports="$(printf "%s" "$pasv" | shyaml get-value -q "ports")"
  106. if [ "$ports" ]; then
  107. if [[ "$ports" =~ ^[0-9]+-[0-9]+$ ]]; then
  108. port_min=${ports##*-}
  109. port_max=${ports%%-*}
  110. if [ "$port_min" -gt "$port_max" ]; then
  111. die "Invalid value for ${WHITE}ports${NORMAL}: first port in range has to be lesser than second."
  112. fi
  113. else
  114. die "Invalid value for ${WHITE}ports${NORMAL}: please specify a port range (ie: '123-345')."
  115. fi
  116. fi
  117. port_min=${port_min:-21100}
  118. port_max=${port_max:-21110}
  119. host_address="$(printf "%s" "$pasv" | shyaml get-value -q "address")"
  120. if [ -z "$host_address" ]; then
  121. if [[ "$SERVICE_NAME" =~ ^([a-z0-9]+(-[a-z0-9]+)*\.)+[a-z]{2,}$ ]]; then
  122. host_address="$SERVICE_NAME"
  123. fi
  124. fi
  125. if [ -z "$host_address" ]; then
  126. echo "## Could not find appropriate host return address"
  127. echo "pasv_enable=no"
  128. return 0
  129. fi
  130. if ! str_is_ipv4 "$host_address"; then
  131. host_address="$(get_ip_from_hostname "$host_address")" || {
  132. err "Can't resolve given hostname '$host_address'"
  133. return 1
  134. }
  135. else
  136. err "You should NOT provide a direct IP for ${WHITE}pasv.address${NORMAL}," \
  137. "try a resolvable address."
  138. return 1
  139. fi
  140. cat <<EOF
  141. pasv_address=$host_address
  142. pasv_enable=yes
  143. pasv_min_port=$port_min
  144. pasv_max_port=$port_max
  145. EOF
  146. init-config-add "
  147. $SERVICE_NAME:
  148. ports:
  149. - \"$port_min-$port_max:$port_min-$port_max\"
  150. "
  151. }
  152. make_ssl_options() {
  153. local ssl="$1"
  154. if [ -z "$ssl" ]; then
  155. target_relation="cert-provider"
  156. ssl=no
  157. while read-0 rn ts rc td; do
  158. [ "$rn" == "${target_relation}" ] || continue
  159. info "A ${DARKBLUE}$target_relation${NORMAL} ${DARKYELLOW}$ts${NORMAL} declared as 'ssl' value"
  160. ssl="$ts"
  161. break
  162. done < <(get_service_relations "$SERVICE_NAME")
  163. fi
  164. if [[ "${ssl,,}" =~ false|no|n|0|disable|disabled ]]; then
  165. echo "ssl_enable=no"
  166. return 0
  167. fi
  168. cat <<EOF
  169. ##
  170. ## SSL conf
  171. ##
  172. ssl_enable=yes
  173. ssl_tlsv1=yes
  174. ssl_sslv2=no
  175. ssl_sslv3=no
  176. ssl_ciphers=HIGH
  177. implicit_ssl=no
  178. EOF
  179. read-0 SSL_PLUGIN_FUN SSL_CFG_VALUE SSL_CFG_OPTIONS < <(ssl_get_plugin_fun "$ssl") || return 1
  180. read-0 cert key ca_cert < <("$SSL_PLUGIN_FUN"_vars "$SSL_CFG_OPTIONS" "$SSL_CFG_VALUE") || return 1
  181. "$SSL_PLUGIN_FUN"_prepare "$SSL_CFG_OPTIONS" "$SSL_CFG_VALUE" || return 1
  182. cat <<EOF
  183. rsa_cert_file=$cert
  184. rsa_private_key_file=$key
  185. ca_certs_file=$ca_cert
  186. EOF
  187. }
  188. make_build_script() {
  189. local users_def="$1" allow_writeable_chroot="$2"
  190. if [ -z "$users_def" ]; then
  191. return 0
  192. fi
  193. fixed_groups_code=""
  194. groups_code=""
  195. declare -A created_groups
  196. while read-0 user user_def; do
  197. first_group=
  198. groups=()
  199. first=1
  200. while read-0 group; do
  201. [ "${created_groups[$group]}" ] && continue
  202. if [[ "$group" == *":"* ]]; then
  203. gid=${group##*:}
  204. group=${group%%:*}
  205. fixed_groups_code+="addgroup -g \"$gid\" \"$group\""$'\n'
  206. else
  207. groups_code+="addgroup \"$group\""$'\n'
  208. fi
  209. created_groups[$group]=1
  210. echo "X" >&2
  211. if [ "$first" ]; then
  212. first_group="$group"
  213. first=
  214. else
  215. remaining_groups+=("$group")
  216. fi
  217. groups+=("$group")
  218. done < <(echo "$user_def" | shyaml get-values-0 groups 2>/dev/null)
  219. password=$(echo "$user_def" | shyaml get-value password 2>/dev/null) ||
  220. password=$(gen_password 14)
  221. uid=$(echo "$user_def" | shyaml get-value uid 2>/dev/null)
  222. useradd_options=(
  223. "-D" ## don't assign a password
  224. "-s" "/bin/false" ## default shell
  225. )
  226. if [ "$uid" ]; then
  227. useradd_options+=("-u" "$uid") ## force uid
  228. fi
  229. if [ "$first_group" ]; then
  230. useradd_options+=("-G" "$first_group") ## force main group
  231. fi
  232. code+="adduser ${useradd_options[*]} \"$user\""$'\n'
  233. code+="mkdir -p \"/home/$user\""$'\n'
  234. if [ "$allow_writeable_chroot" ]; then
  235. code+="chown $user \"/home/$user\""$'\n' ## sanitize
  236. else
  237. code+="chown root:root \"/home/$user\""$'\n' ## sanitize
  238. fi
  239. code+="chmod 755 \"/home/$user\""$'\n' ## sanitize
  240. code+="echo '$user:$password' | chpasswd"$'\n'
  241. for group in "${remaining_groups[@]}"; do
  242. code+="adduser \"$user\" \"$group\""$'\n'
  243. done
  244. done < <(echo "$users_def" | shyaml key-values-0)
  245. echo -n "$fixed_groups_code"
  246. echo -n "$groups_code"
  247. echo -n "$code"
  248. }