From d1c6d54a2235c9655b7aa5a5139f777b75efc80f Mon Sep 17 00:00:00 2001 From: Kevin Baensch Date: Tue, 13 Dec 2022 17:04:48 +0100 Subject: [PATCH 01/24] [WIP] Docker Templater --- .gitignore | 4 +- script/docker-templater.sh | 122 +++++++++++++++++++++++++++++++++++++ templates/caddy.sh | 16 +++++ templates/hosts.sh | 40 ++++++++++++ templates/sqlproxy.sh | 6 ++ 5 files changed, 186 insertions(+), 2 deletions(-) create mode 100755 script/docker-templater.sh create mode 100644 templates/caddy.sh create mode 100644 templates/hosts.sh create mode 100644 templates/sqlproxy.sh diff --git a/.gitignore b/.gitignore index 55c916e..d483373 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,2 @@ -/etc/ssh/*key* -/etc/ssh/.ssh +etc/ssh/*key* +etc/ssh/.ssh/ diff --git a/script/docker-templater.sh b/script/docker-templater.sh new file mode 100755 index 0000000..a3081f8 --- /dev/null +++ b/script/docker-templater.sh @@ -0,0 +1,122 @@ +#!/usr/bin/env bash +# Export Variables so they can be picked up by envsubst +set -ae +TEMPLATE_SRC=$1; +DOCKER_DATA=$2; + +NETWORK_NAME="${NETWORK_NAME:-proxy}" + +# "NetworkSettings" + +# Import Template +# Required Variables: +# - TEMPLATE :: The template string which will be read by envsubst +# Optional Variables: +# - WRAP_START :: String to prepend in front of the mapped template result +# - WRAP_END :: String to append to the mapped template result +# - SEPARATOR :: String/Charater to separate TEMPLATE lines (DEFAULT: \n) +# - OUT :: if set the result will be written to $OUT instead of stdout +# Optional Functions: +# - label_hook :: modify/override label query results +# - template_hook :: hook to verify/modify template rsults (0 => OK; 1 => SKIP) + +if [ -f "${TEMPLATE_SRC}" ] +then + source "${TEMPLATE_SRC}" + if [ -z "${TEMPLATE}" ] + then + echo "[ERR]: Template \"${TEMPLATE_SRC}\" does define a template string." + exit 1 + fi +else + echo "[ERR]: No such File \"${TEMPLATE_SRC}\"."; + exit 1 +fi + +# Check Hooks +if type label_hook 2>/dev/null | grep -q "label_hook is a function" +then + RUN_LABEL_HOOK=true +else + RUN_LABEL_HOOK=false +fi + +if type template_hook 2>/dev/null | grep -q "template_hook is a function" +then + RUN_TEMPLATE_HOOK=true +else + RUN_TEMPLATE_HOOK=false +fi + +if [ -z "${SEPARATOR+.}" ] +then + SEPARATOR='\n' +fi + +for row in $(jq -r '.[] | @base64' <<< "${DOCKER_DATA}") +do + CONTAINER_DATA=$(base64 --decode <<< "${row}"); + # Set non Label values + if [ -z "${LOCAL_IP}" ] + then + LOCAL_IP=$(jq -r '.NetworkSettings.Networks | if has($ENV.NETWORK_NAME) then .[$ENV.NETWORK_NAME].IPAddress else first(.[].IPAddress) end' <<< "${CONTAINER_DATA}"); + fi + # export LOCAL_IP + + # Read Label values + CONTAINER_LABELS=$(jq '.Labels' <<< "${CONTAINER_DATA}"); + for var in $(envsubst -v "${TEMPLATE}") + do + if [ -z "${!var}" ] + then + VAR_LABEL=$(tr '[:upper:]_' '[:lower:].' <<< "${var}") + LABEL_VAL=$(jq --arg KEY "${VAR_LABEL}" -r 'if has($KEY) then .[$KEY] else "" end' <<< "${CONTAINER_LABELS}") + + if [ -n "${LABEL_VAL}" ] + then + declare "${var}"="${LABEL_VAL}"; + fi + fi + done + + if ${RUN_LABEL_HOOK} + then + label_hook + fi + + PARTIAL_RESULT="$(envsubst <<< "${TEMPLATE}")"; + # Verify template result if check_template is defined + if ${RUN_TEMPLATE_HOOK} + then + if template_hook + then + # FIX with printf + RESULT+="${PARTIAL_RESULT}" + fi + else + RESULT=$(printf '%s' "${RESULT}" "${RESULT:+$SEPARATOR}" "${PARTIAL_RESULT}") + fi + + # Unset label-environment variables for next container + for var in $(envsubst -v "${TEMPLATE}") + do + if [ "${var}" != "LOCAL_IP" ] + then + unset "${!var}" + fi + done +done + +RESULT="${WRAP_START}${RESULT}${WRAP_END}" + + +if [ -z "${OUT}" ] +then + printf "%b\n" "${RESULT}"; +else + if ! diff --brief "${OUT}" - > /dev/null 2>&1 <<< "${RESULT}" + then + printf "Template Task: '%s' has been written to: '%s'\n" "${TEMPLATE_SRC}" "${OUT}" + printf "%b\n" "${RESULT}" > "${OUT}"; + fi +fi diff --git a/templates/caddy.sh b/templates/caddy.sh new file mode 100644 index 0000000..7d5dc52 --- /dev/null +++ b/templates/caddy.sh @@ -0,0 +1,16 @@ +#!/usr/bin/env bash +set -e + +TEMPLATE='reverse_proxy $LOCAL_WEB_HOST ${LOCAL_IP}:$LOCAL_WEB_PORT' + +label_hook() { + LOCAL_WEB_PORT="${LOCAL_WEB_PORT:-80}" +} + +template_hook() { + if grep -q '^reverse_proxy [^\s] [:.0-9a-e]\+:\d+$' <<< "${PARTIAL_RESULT}" + then + return 0; + fi + return 1; +} diff --git a/templates/hosts.sh b/templates/hosts.sh new file mode 100644 index 0000000..2a04f5b --- /dev/null +++ b/templates/hosts.sh @@ -0,0 +1,40 @@ +#!/usr/bin/env bash +set -e +HOST_CONF_PATH="${HOST_CONF_PATH:-/tmp/hosts}" +RESOLVE_DOCKERHOST="${RESOLVE_DOCKERHOST:-false}" +DOCKER_HOSTNAME_VAR="${DOCKER_HOSTNAME_VAR:-LOCAL_WEB_HOST}" + +if [ -f "${HOST_CONF_PATH}" ] +then + WRAP_START=$(grep -ve "# Added by hostman$" "${HOST_CONF_PATH}") + WRAP_START+="\n" +else + printf "[WARN]: No such file or directory: %s\n" "${HOST_CONF_PATH}" + printf "Creating %s" "${HOST_CONF_PATH}" + touch "${HOST_CONF_PATH}" +fi + +if ! $RESOLVE_DOCKERHOST +then + TEMPLATE='127.0.0.1 ' +else + TEMPLATE='$LOCAL_IP ' +fi + +TEMPLATE="${TEMPLATE}\${${DOCKER_HOSTNAME_VAR}} # Added by hostman" + +# Allow overriding out for debugging and testing purposs +if [ -z "${HOST_CONF_OUT+.}" ] +then + OUT="${HOST_CONF_PATH}" +else + OUT="${HOST_CONF_OUT}" +fi + +check_template() { + if grep -q '^[:.0-9a-e]\+ [^\s]\+ # Added by hostman$' <<< "${PARTIAL_RESULT}" + then + return 0; + fi + return 1; +} diff --git a/templates/sqlproxy.sh b/templates/sqlproxy.sh new file mode 100644 index 0000000..614064a --- /dev/null +++ b/templates/sqlproxy.sh @@ -0,0 +1,6 @@ +#!/usr/bin/env bash +set -e +WRAP_START='[\n' +TEMPLATE=' { type: "${LOCAL_DB_TYPE}", user: "${LOCAL_DB_USER}", password: "${LOCAL_DB_PASSWORD}", host: "${LOCAL_DB_HOST}", port: "${LOCAL_DB_PORT}" }' +SEPARATOR=',\n' +WRAP_END='\n]' From ed719701538b6f1b1f9237c9cd5b2c76c0713628 Mon Sep 17 00:00:00 2001 From: Kevin Baensch Date: Fri, 16 Dec 2022 14:17:11 +0100 Subject: [PATCH 02/24] [WIP] Docker templater mostly working (to be squashed) --- docker-compose.yml | 14 +++++++--- script/docker-templater.sh | 20 ++++++-------- script/hostman.sh | 53 ++++++++++++++------------------------ script/sqlproxy.sh | 2 +- templates/caddy.sh | 21 +++++++++++++-- templates/hosts.sh | 6 ++--- templates/sqlproxy.sh | 17 +++++++++++- 7 files changed, 77 insertions(+), 56 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index b99048e..f16e64a 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -2,19 +2,25 @@ version: "3.4" services: hostman: - image: apteno/alpine-jq + image: julienlecomte/docker-make command: ["./hostman.sh", "&", "wait", "$!" ] volumes: - /var/run/docker.sock:/tmp/docker.sock:ro - - /etc/hosts:/tmp/hosts:rw + - /etc/hosts:/config/hosts:rw - ./script/hostman.sh:/hostman.sh:ro - nginx-proxy: - image: jwilder/nginx-proxy + - ./script/docker-templater.sh:/docker-templater.sh:ro + - ./templates:/templates + - ./config:/config + networks: + - proxy + proxy: + image: caddy:2.6.2-alpine ports: - "80:80" - "443:443" volumes: - /var/run/docker.sock:/tmp/docker.sock:ro + - ./config/Caddyfile:/etc/caddy/Caddyfile networks: - proxy diff --git a/script/docker-templater.sh b/script/docker-templater.sh index a3081f8..0fbf905 100755 --- a/script/docker-templater.sh +++ b/script/docker-templater.sh @@ -55,7 +55,7 @@ fi for row in $(jq -r '.[] | @base64' <<< "${DOCKER_DATA}") do - CONTAINER_DATA=$(base64 --decode <<< "${row}"); + CONTAINER_DATA=$(base64 -d <<< "${row}"); # Set non Label values if [ -z "${LOCAL_IP}" ] then @@ -91,7 +91,7 @@ do if template_hook then # FIX with printf - RESULT+="${PARTIAL_RESULT}" + RESULT=$(printf '%s' "${RESULT}" "${RESULT:+$SEPARATOR}" "${PARTIAL_RESULT}") fi else RESULT=$(printf '%s' "${RESULT}" "${RESULT:+$SEPARATOR}" "${PARTIAL_RESULT}") @@ -100,23 +100,19 @@ do # Unset label-environment variables for next container for var in $(envsubst -v "${TEMPLATE}") do - if [ "${var}" != "LOCAL_IP" ] - then - unset "${!var}" - fi + unset "${var}" done done -RESULT="${WRAP_START}${RESULT}${WRAP_END}" - +RESULT="$(printf "%b" "${WRAP_START}" "${RESULT}" "${WRAP_END}")" if [ -z "${OUT}" ] then - printf "%b\n" "${RESULT}"; + printf "%b" "${RESULT}"; else - if ! diff --brief "${OUT}" - > /dev/null 2>&1 <<< "${RESULT}" + if [ "$(cat "${OUT}")" != "${RESULT}" ] then - printf "Template Task: '%s' has been written to: '%s'\n" "${TEMPLATE_SRC}" "${OUT}" - printf "%b\n" "${RESULT}" > "${OUT}"; + printf "%s | Template Task: '%s' has been written to: '%s'\n" "$(date --iso-8601)" "${TEMPLATE_SRC}" "${OUT}" + printf "%b" "${RESULT}" > "${OUT}"; fi fi diff --git a/script/hostman.sh b/script/hostman.sh index f48a442..3c1340c 100755 --- a/script/hostman.sh +++ b/script/hostman.sh @@ -1,46 +1,33 @@ #!/usr/bin/env sh + # Configurable Variables -[ -z $DOCKER_SOCK_PATH ] && DOCKER_SOCK_PATH="/tmp/docker.sock" -[ -z $NETWORK_NAME ] && NETWORK_NAME="proxy" -[ -z $RESOLVE_DOCKERHOST ] && RESOLVE_DOCKERHOST=false -[ -z $HOST_CONF_PATH ] && HOST_CONF_PATH="/tmp/hosts" -[ -z $DOCKER_HOSTNAME_VAR ] && DOCKER_HOSTNAME_VAR="VIRTUAL_HOST" +DOCKER_SOCK_PATH="${DOCKER_SOCK_PATH:-/tmp/docker.sock}" +NETWORK_NAME="${NETWORK_NAME:-proxy}" +TEMPLATER_PATH="${TEMPLATER_PATH:-/docker-templater.sh}" +TEMPLATE_FOLDER_PATH="${TEMPLATE_FOLDER_PATH:-/templates}" query_docker () { curl --unix-socket $DOCKER_SOCK_PATH --silent -g http://v1.41/$1$2 } -get_host_list() { - PROXY_HOST_CONF="" - CONTAINER_LIST=$(query_docker "containers/json" "?filters={%22network%22:[%22${NETWORK_NAME}%22],%22status%22:[%22running%22]}" | jq -cr '.[].Id') - - for id in $CONTAINER_LIST - do - # Query individual container to access relevant data - CONTAINER_DATA=$(query_docker "containers/${id}/json") - if $RESOLVE_DOCKERHOST - then - HOST_IP=$(echo $CONTAINER_DATA | jq -cr '.NetworkSettings.Networks.proxy.IPAddress') - else - HOST_IP="127.0.0.1" - fi - # Filter Env for HOSTNAME, remove list parenthesis and split/only keep values - HOST_NAMES=$(echo $CONTAINER_DATA | jq -cr ".Config.Env[] | select(contains(\"$DOCKER_HOSTNAME_VAR=\")) | split(\"=\")[1]") - - for hostname in $HOST_NAMES +update_templates() { + CONTAINER_LIST=$(query_docker "containers/json" "?filters={%22network%22:[%22${NETWORK_NAME}%22],%22status%22:[%22running%22]}") + if [ "${CONTAINER_LIST}" != "${OLD_CONTAINER_LIST}" ] + then + for template in "${TEMPLATE_FOLDER_PATH}"/* do - PROXY_HOST_CONF="$PROXY_HOST_CONF\n$HOST_IP $hostname # Added by hostman" + if ! "${TEMPLATER_PATH}" "${template}" "${CONTAINER_LIST}" + then + echo "Error Processing Template: ${template}" + fi done - done - echo $PROXY_HOST_CONF + OLD_CONTAINER_LIST="${CONTAINER_LIST}" + fi } -update_host_list() { - FILTERED_HOSTS=$(grep -ve "# Added by hostman$" $HOST_CONF_PATH) - echo -e "$FILTERED_HOSTS$(get_host_list)" > $HOST_CONF_PATH -} +# Initial Generation +update_templates -update_host_list # cannot filter because reailine no longer recognized lines otherwise (check how IFS changes) query_docker "events" | while true do @@ -48,7 +35,7 @@ do # wait for related events to finish while [ $? -eq 0 ] do - read -t 5 -r; + read -t 8 -r done - update_host_list + update_templates done diff --git a/script/sqlproxy.sh b/script/sqlproxy.sh index 90be21b..63a55c2 100755 --- a/script/sqlproxy.sh +++ b/script/sqlproxy.sh @@ -5,4 +5,4 @@ chown -R sqlproxy:sqlproxy /etc/ssh/.ssh chmod 0700 /etc/ssh/.ssh chmod 0600 /etc/ssh/.ssh/authorized_keys -source ./hostman.sh +sleep infinity diff --git a/templates/caddy.sh b/templates/caddy.sh index 7d5dc52..1b9dc2d 100644 --- a/templates/caddy.sh +++ b/templates/caddy.sh @@ -1,16 +1,33 @@ #!/usr/bin/env bash set -e +DOCKER_SOCK_PATH="${DOCKER_SOCK_PATH:-/tmp/docker.sock}" +DOCKER_CADDY_NAME="${DOCKER_CADDY_NAME:-proxy}" +DOCKER_CADDY_PORT="${DOCKER_CADDY_PORT:-2020}" -TEMPLATE='reverse_proxy $LOCAL_WEB_HOST ${LOCAL_IP}:$LOCAL_WEB_PORT' +WRAP_START='{\n admin :2020\n}\n' +WRAP_END='\n' +TEMPLATE='${LOCAL_WEB_HOST} {\n reverse_proxy ${LOCAL_IP}:$LOCAL_WEB_PORT\n}' +SEPARATOR='\n' +OUT='/config/Caddyfile' label_hook() { LOCAL_WEB_PORT="${LOCAL_WEB_PORT:-80}" } template_hook() { - if grep -q '^reverse_proxy [^\s] [:.0-9a-e]\+:\d+$' <<< "${PARTIAL_RESULT}" + printf "%s" "${PARTIAL_RESULT}" + if grep '^[^ ]\+ {\\n reverse_proxy \(?::\|[.0-9a-e]\)\+\:[0-9]\+\\n}$' <<< "${PARTIAL_RESULT}" then return 0; fi return 1; } + +# finally_hook() { +# if curl --silent -g "${DOCKER_CADDY_NAME}:${DOCKER_CADDY_PORT}/load" +# then +# printf "Updated Caddy Config" +# else +# printf "Failed to update Caddy Config" +# fi +# } diff --git a/templates/hosts.sh b/templates/hosts.sh index 2a04f5b..810440c 100644 --- a/templates/hosts.sh +++ b/templates/hosts.sh @@ -1,6 +1,6 @@ #!/usr/bin/env bash set -e -HOST_CONF_PATH="${HOST_CONF_PATH:-/tmp/hosts}" +HOST_CONF_PATH="${HOST_CONF_PATH:-/config/hosts}" RESOLVE_DOCKERHOST="${RESOLVE_DOCKERHOST:-false}" DOCKER_HOSTNAME_VAR="${DOCKER_HOSTNAME_VAR:-LOCAL_WEB_HOST}" @@ -31,8 +31,8 @@ else OUT="${HOST_CONF_OUT}" fi -check_template() { - if grep -q '^[:.0-9a-e]\+ [^\s]\+ # Added by hostman$' <<< "${PARTIAL_RESULT}" +template_hook() { + if grep -q '^[:.0-9a-e]\+ [^ ]\+ # Added by hostman$' <<< "${PARTIAL_RESULT}" then return 0; fi diff --git a/templates/sqlproxy.sh b/templates/sqlproxy.sh index 614064a..5ed4ad3 100644 --- a/templates/sqlproxy.sh +++ b/templates/sqlproxy.sh @@ -1,6 +1,21 @@ #!/usr/bin/env bash +EXCLUDE_USERPASS="${EXCLUDE_USERPASS:-false}" set -e WRAP_START='[\n' -TEMPLATE=' { type: "${LOCAL_DB_TYPE}", user: "${LOCAL_DB_USER}", password: "${LOCAL_DB_PASSWORD}", host: "${LOCAL_DB_HOST}", port: "${LOCAL_DB_PORT}" }' +if ${EXCLUDE_USERPASS} +then + TEMPLATE=' { type: "${LOCAL_DB_TYPE}", host: "${LOCAL_DB_HOST}", port: "${LOCAL_DB_PORT}" }' +else + TEMPLATE=' { type: "${LOCAL_DB_TYPE}", user: "${LOCAL_DB_USER}", password: "${LOCAL_DB_PASSWORD}", host: "${LOCAL_DB_HOST}", port: "${LOCAL_DB_PORT}" }' +fi SEPARATOR=',\n' WRAP_END='\n]' +OUT="/config/sqlproxy.json" + +template_hook() { + if grep -q 'type: "(?mysql|psql)".*host: ".+", port: "\d+"' + then + return 0 + fi + return 1 +} From 3544247fc027246b196a8a6db0aa577d89802d19 Mon Sep 17 00:00:00 2001 From: Kevin Baensch Date: Sat, 17 Dec 2022 10:30:41 +0100 Subject: [PATCH 03/24] Initial working docker-templater+caddy version --- docker-compose.yml | 10 +++++----- script/docker-templater.sh | 27 +++++++++++++++++++++------ script/hostman.sh | 18 +++++++++++++++--- templates/caddy.sh | 23 +++++++++++------------ templates/sqlproxy.sh | 6 +++--- 5 files changed, 55 insertions(+), 29 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index f16e64a..9cd3bb8 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -6,11 +6,11 @@ services: command: ["./hostman.sh", "&", "wait", "$!" ] volumes: - /var/run/docker.sock:/tmp/docker.sock:ro - - /etc/hosts:/config/hosts:rw - ./script/hostman.sh:/hostman.sh:ro - ./script/docker-templater.sh:/docker-templater.sh:ro - - ./templates:/templates - - ./config:/config + - ./templates:/templates:ro + - ./config:/config:rw + - /etc/hosts:/config/hosts:rw networks: - proxy proxy: @@ -19,8 +19,8 @@ services: - "80:80" - "443:443" volumes: - - /var/run/docker.sock:/tmp/docker.sock:ro - - ./config/Caddyfile:/etc/caddy/Caddyfile + - ./config/Caddyfile:/etc/caddy/Caddyfile:ro + - ./caddy_data:/data/caddy networks: - proxy diff --git a/script/docker-templater.sh b/script/docker-templater.sh index 0fbf905..341b385 100755 --- a/script/docker-templater.sh +++ b/script/docker-templater.sh @@ -6,7 +6,9 @@ DOCKER_DATA=$2; NETWORK_NAME="${NETWORK_NAME:-proxy}" -# "NetworkSettings" +get_date() { + printf '%s' "$(date +'%Y.%m.%d_%H:%M:%S')" +} # Import Template # Required Variables: @@ -19,17 +21,18 @@ NETWORK_NAME="${NETWORK_NAME:-proxy}" # Optional Functions: # - label_hook :: modify/override label query results # - template_hook :: hook to verify/modify template rsults (0 => OK; 1 => SKIP) +# - finally_hook :: gets executed after (and also only if) the final result has been written to disk if [ -f "${TEMPLATE_SRC}" ] then source "${TEMPLATE_SRC}" if [ -z "${TEMPLATE}" ] then - echo "[ERR]: Template \"${TEMPLATE_SRC}\" does define a template string." + printf '%s | [ERR]: Template "%s" does define a template string.\n' "$(get_date)" "${TEMPLATE_SRC}" exit 1 fi else - echo "[ERR]: No such File \"${TEMPLATE_SRC}\"."; + printf '%s | [ERR]: No such File "%s".\n' "$(get_date)" "${TEMPLATE_SRC}"; exit 1 fi @@ -48,6 +51,14 @@ else RUN_TEMPLATE_HOOK=false fi +if type finally_hook 2>/dev/null | grep -q "finally_hook is a function" +then + RUN_FINALLY_HOOK=true +else + RUN_FINALLY_HOOK=false +fi + + if [ -z "${SEPARATOR+.}" ] then SEPARATOR='\n' @@ -104,15 +115,19 @@ do done done -RESULT="$(printf "%b" "${WRAP_START}" "${RESULT}" "${WRAP_END}")" +RESULT="$(printf "%s" "${WRAP_START}" "${RESULT}" "${WRAP_END}")" if [ -z "${OUT}" ] then printf "%b" "${RESULT}"; else - if [ "$(cat "${OUT}")" != "${RESULT}" ] + if [ "$(cat "${OUT}")" != "$(printf '%b'"${RESULT}")" ] then - printf "%s | Template Task: '%s' has been written to: '%s'\n" "$(date --iso-8601)" "${TEMPLATE_SRC}" "${OUT}" + printf "%s | Template Task: '%s' has been written to: '%s'\n" "$(get_date)" "${TEMPLATE_SRC}" "${OUT}" printf "%b" "${RESULT}" > "${OUT}"; + if ${RUN_FINALLY_HOOK} + then + finally_hook + fi fi fi diff --git a/script/hostman.sh b/script/hostman.sh index 3c1340c..1cd5f37 100755 --- a/script/hostman.sh +++ b/script/hostman.sh @@ -6,22 +6,31 @@ NETWORK_NAME="${NETWORK_NAME:-proxy}" TEMPLATER_PATH="${TEMPLATER_PATH:-/docker-templater.sh}" TEMPLATE_FOLDER_PATH="${TEMPLATE_FOLDER_PATH:-/templates}" +get_date() { + printf '%s' "$(date +'%Y.%m.%d_%H:%M:%S')" +} + query_docker () { curl --unix-socket $DOCKER_SOCK_PATH --silent -g http://v1.41/$1$2 } update_templates() { CONTAINER_LIST=$(query_docker "containers/json" "?filters={%22network%22:[%22${NETWORK_NAME}%22],%22status%22:[%22running%22]}") - if [ "${CONTAINER_LIST}" != "${OLD_CONTAINER_LIST}" ] + LABELS_NEW="$(echo "${CONTAINER_LIST}" | jq '.[].Labels | @base64' | sort)" + if [ "${LABELS_NEW}" != "${LABELS_OLD}" ] then + if [ -n "${LABELS_OLD}" ] + then + printf '%s | Container label list change detected.\n' "$(get_date)" + fi + LABELS_OLD="${LABELS_NEW}" for template in "${TEMPLATE_FOLDER_PATH}"/* do if ! "${TEMPLATER_PATH}" "${template}" "${CONTAINER_LIST}" then - echo "Error Processing Template: ${template}" + printf '%s | Error Processing Template: %s\n' "$(get_date)" "${template}" fi done - OLD_CONTAINER_LIST="${CONTAINER_LIST}" fi } @@ -31,11 +40,14 @@ update_templates # cannot filter because reailine no longer recognized lines otherwise (check how IFS changes) query_docker "events" | while true do + printf '%s | Listening for docker events\n' "$(get_date)" read -r; # wait for related events to finish + printf '%s | Waiting for event messages to settle.\n' "$(get_date)" while [ $? -eq 0 ] do read -t 8 -r done + printf '%s | Checking for changes in container label list.\n' "$(get_date)" update_templates done diff --git a/templates/caddy.sh b/templates/caddy.sh index 1b9dc2d..ea4c62a 100644 --- a/templates/caddy.sh +++ b/templates/caddy.sh @@ -4,9 +4,9 @@ DOCKER_SOCK_PATH="${DOCKER_SOCK_PATH:-/tmp/docker.sock}" DOCKER_CADDY_NAME="${DOCKER_CADDY_NAME:-proxy}" DOCKER_CADDY_PORT="${DOCKER_CADDY_PORT:-2020}" -WRAP_START='{\n admin :2020\n}\n' +WRAP_START='{\n\tadmin :2020\n}\n' WRAP_END='\n' -TEMPLATE='${LOCAL_WEB_HOST} {\n reverse_proxy ${LOCAL_IP}:$LOCAL_WEB_PORT\n}' +TEMPLATE='${LOCAL_WEB_HOST} {\n\treverse_proxy ${LOCAL_IP}:$LOCAL_WEB_PORT\n}' SEPARATOR='\n' OUT='/config/Caddyfile' @@ -15,19 +15,18 @@ label_hook() { } template_hook() { - printf "%s" "${PARTIAL_RESULT}" - if grep '^[^ ]\+ {\\n reverse_proxy \(?::\|[.0-9a-e]\)\+\:[0-9]\+\\n}$' <<< "${PARTIAL_RESULT}" + if grep -q '^[^ ]\+ {\\n\\treverse_proxy \(?::\|[.0-9a-e]\)\+\:[0-9]\+\\n}$' <<< "${PARTIAL_RESULT}" then return 0; fi return 1; } -# finally_hook() { -# if curl --silent -g "${DOCKER_CADDY_NAME}:${DOCKER_CADDY_PORT}/load" -# then -# printf "Updated Caddy Config" -# else -# printf "Failed to update Caddy Config" -# fi -# } +finally_hook() { + if curl --silent "${DOCKER_CADDY_NAME}:${DOCKER_CADDY_PORT}/load" -H "Content-Type: text/caddyfile" --data-binary "@${OUT}" + then + printf '%s | Updated Caddy Config\n' "$(get_date)" + else + printf '%s | Failed to update Caddy Config\n' "$(get_date)" + fi +} diff --git a/templates/sqlproxy.sh b/templates/sqlproxy.sh index 5ed4ad3..f94ba76 100644 --- a/templates/sqlproxy.sh +++ b/templates/sqlproxy.sh @@ -4,16 +4,16 @@ set -e WRAP_START='[\n' if ${EXCLUDE_USERPASS} then - TEMPLATE=' { type: "${LOCAL_DB_TYPE}", host: "${LOCAL_DB_HOST}", port: "${LOCAL_DB_PORT}" }' + TEMPLATE=' { "type": "${LOCAL_DB_TYPE}", "host": "${LOCAL_DB_HOST}", "port": "${LOCAL_DB_PORT}" }' else - TEMPLATE=' { type: "${LOCAL_DB_TYPE}", user: "${LOCAL_DB_USER}", password: "${LOCAL_DB_PASSWORD}", host: "${LOCAL_DB_HOST}", port: "${LOCAL_DB_PORT}" }' + TEMPLATE=' { "type": "${LOCAL_DB_TYPE}", "user": "${LOCAL_DB_USER}", "password": "${LOCAL_DB_PASSWORD}", "host": "${LOCAL_DB_HOST}", "port": "${LOCAL_DB_PORT}" }' fi SEPARATOR=',\n' WRAP_END='\n]' OUT="/config/sqlproxy.json" template_hook() { - if grep -q 'type: "(?mysql|psql)".*host: ".+", port: "\d+"' + if grep -q '"type": "(?mysql|psql)".*"host": ".+", port: "\d+"' <<< "${PARTIAL_RESULT}" then return 0 fi From 566c56c4230108a0c44a3532a7ffe93981e84300 Mon Sep 17 00:00:00 2001 From: Kevin Baensch Date: Wed, 21 Dec 2022 19:27:16 +0100 Subject: [PATCH 04/24] Add documentation for SSL cert configuration --- README.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/README.md b/README.md index 6b33221..a65be34 100644 --- a/README.md +++ b/README.md @@ -58,6 +58,13 @@ networks: external: true ``` +## SSL Zertifikat Konfiguration +Nach starten des docker containers ist das von caddy erstellte SSL Zertifikat in './caddy_data/pki/authorities/local/root.crt' gefunden werden. +Relevante Dokumentation: +- [MacOS](https://support.apple.com/guide/keychain-access/add-certificates-to-a-keychain-kyca2431/mac) +- [Linux](https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/7/html/security_guide/sec-shared-system-certificates) + + ## SQL Proxy ### Initiales Setup Öffne ein Terminal und führe die `sqlproxy_setup.sh` Datei aus. From cfb82fd78ab0e79c8b7ecb734c20fd3e5113e5df Mon Sep 17 00:00:00 2001 From: Kevin Baensch Date: Wed, 21 Dec 2022 19:30:38 +0100 Subject: [PATCH 05/24] [WIP] Configure SQL Proxy --- docker-compose-sqlproxy.yml | 3 +- script/myssh | 72 ++++++++++++++++++++++--------------- script/sqlproxy_cli.sh | 17 ++++++--- templates/sqlproxy.sh | 19 ++++++++-- 4 files changed, 74 insertions(+), 37 deletions(-) diff --git a/docker-compose-sqlproxy.yml b/docker-compose-sqlproxy.yml index 40c5db9..dc21c4c 100644 --- a/docker-compose-sqlproxy.yml +++ b/docker-compose-sqlproxy.yml @@ -10,10 +10,9 @@ services: - 3022:22 volumes: - ./etc/ssh:/etc/ssh/ - - /var/run/docker.sock:/tmp/docker.sock:ro - - ./script/hostman.sh:/hostman.sh:ro - ./script/sqlproxy.sh:/sqlproxy.sh:ro - ./script/sqlproxy_cli.sh:/sqlproxy_cli.sh:ro + - ./config:/config environment: DISABLE_KEYGEN: true DISABLE_CONFIG_GEN: true diff --git a/script/myssh b/script/myssh index d8b1c2b..1dcb181 100755 --- a/script/myssh +++ b/script/myssh @@ -4,9 +4,9 @@ if [ -z "$SQL_CLI_TEMPLATE" ] then if [ $(uname -s) = "Linux" ] then - SQL_CLI_TEMPLATE='mysql --protocol=TCP -u $MYSQL_USERNAME -p$MYSQL_PASSWORD -h localhost -P 3306' + SQL_CLI_TEMPLATE='mysql --protocol=TCP -u $MYSQL_USERNAME -p$MYSQL_PASSWORD -h localhost -P 6033' else - SQL_CLI_TEMPLATE='open \"mysql://$MYSQL_USERNAME:$MYSQL_PASSWORD@localhost:3306\" -a \"Sequel Ace\"' + SQL_CLI_TEMPLATE='open \"mysql://$MYSQL_USERNAME:$MYSQL_PASSWORD@localhost:6033\" -a \"Sequel Ace\"' fi else echo -e "Warning, custom client string:\n$SQL_CLI_TEMPLATE" @@ -18,7 +18,6 @@ else fi [ -z $SQL_PROXY_HOST ] && SQL_PROXY_HOST="localhost" -[ -z $SQL_PROXY_DB_PORT ] && SQL_PROXY_DB_PORT="3306" CONNECTION_CACHE="$HOME/.cache/sqlproxy_$SQL_PROXY_HOST" HELP="Usage: myssh [ls|connect]\n @@ -54,20 +53,41 @@ disconnect() { port_forward() { ACTIVE_HOST=$(cat $CONNECTION_CACHE) - if [ -z $ACTIVE_HOST ] || [ $ACTIVE_HOST != $1:$SQL_PROXY_DB_PORT ] + if [ -z $ACTIVE_HOST ] || [ $ACTIVE_HOST != $1:$2 ] then - echo "Reconnect" if [ ! -z $ACTIVE_HOST ] then - ssh -O cancel -L 3306:$ACTIVE_HOST -S $HOME/.ssh/controlmasters/%r@%h:%p $SQL_PROXY_HOST q + ssh -O cancel -L 6033:$ACTIVE_HOST -S $HOME/.ssh/controlmasters/%r@%h:%p ${SQL_PROXY_HOST} q fi - ssh -O forward -L 3306:$1:$SQL_PROXY_DB_PORT -S $HOME/.ssh/controlmasters/%r@%h:%p $SQL_PROXY_HOST + echo $ACTIVE_HOST + echo $1 $2 + ssh -O forward -L 6033:$1:$2 -S ${HOME}/.ssh/controlmasters/%r@%h:%p ${SQL_PROXY_HOST} fi - echo $1:$SQL_PROXY_DB_PORT > $CONNECTION_CACHE + echo $2:$3 > "${CONNECTION_CACHE}" } ls_hosts() { - ssh -S $HOME/.ssh/controlmasters/%r@%h:%p $SQL_PROXY_HOST ls + ssh -S ${HOME}/.ssh/controlmasters/%r@%h:%p "${SQL_PROXY_HOST}" ls +} + +get_host() { + if [ $1 == '' ] + then + printf 'Please specify the host to connect to.\nRun "myssh ls" to list all available hosts.\n' + else + ssh -S ${HOME}/.ssh/controlmasters/%r@%h:%p "${SQL_PROXY_HOST}" "get $1" + fi +} + +set_host_env() { + if [ $1 == 'mysql' | $1 == 'psql' ] + then + TARGET_HOST_TYPE=$1 + fi + if [ $2 != '' ] + then + + fi } run_client() { @@ -86,25 +106,21 @@ case $MAIN_OPTION in # check if host is valid TARGET_HOST=$1 shift - ls_hosts | grep -qe "^$TARGET_HOST$" - GREP_EXIT_CODE=$? - if [ $GREP_EXIT_CODE -eq 0 ] - then - while getopts "u:p:" o - do - case "$o" in - u) MYSQL_USERNAME="$OPTARG" ;; - p) MYSQL_PASSWORD="$OPTARG" ;; - esac - done - port_forward $TARGET_HOST - if [ ! -z $MYSQL_USERNAME ] && [ ! -z $MYSQL_PASSWORD ] - then - run_client - fi - else - echo "Invalid Hostname: $TARGET_HOST." - fi + + while getopts "u:p:" o + do + case "$o" in + u) MYSQL_USERNAME="$OPTARG" ;; + p) MYSQL_PASSWORD="$OPTARG" ;; + esac + done + TARGET_HOST_DATA="$(get_host "${TARGET_HOST}")" + port_forward $TARGET_HOST_DATA + # port_forward $TARGET_HOST + # if [ ! -z $MYSQL_USERNAME ] && [ ! -z $MYSQL_PASSWORD ] + # then + # run_client + # fi ;; disconnect) disconnect;; diff --git a/script/sqlproxy_cli.sh b/script/sqlproxy_cli.sh index 8d564eb..f25b35d 100755 --- a/script/sqlproxy_cli.sh +++ b/script/sqlproxy_cli.sh @@ -1,18 +1,27 @@ #!/usr/bin/env sh +DB_DATA_FILE="${DB_DATA_FILE:-/config/sqlproxy.json}" ls_hosts() { - # the containers version of grep does not support perl regex so "[^ ]*(?= # Added by hostman)" does not work - grep -e "# Added by hostman" /etc/hosts | grep -oe "^[^ ]* [^ ]*" | grep -oe "[^ ]*$" + jq -r '.[].host' < "${DB_DATA_FILE}" +} + +get_host() { + export HOST=$(echo "${SSH_ORIGINAL_COMMAND}" | cut -d ' ' -f2) + if [ "${HOST}" != 'get' ] + then + jq -r 'first(.[] | select(.host == $ENV.HOST)) | [ .type, .ip, .port, .user, .password ] | join(" ")' < "${DB_DATA_FILE}" + fi } idle() { - echo "Press CTRL C to quit this connection" + printf "Press CTRL C to quit this connection\n" sleep infinity } -case "$SSH_ORIGINAL_COMMAND" in +case "${SSH_ORIGINAL_COMMAND}" in "") idle;; ls) ls_hosts;; + get*) get_host;; q|quit) exit 0;; *) exit 1;; esac diff --git a/templates/sqlproxy.sh b/templates/sqlproxy.sh index f94ba76..f8613f9 100644 --- a/templates/sqlproxy.sh +++ b/templates/sqlproxy.sh @@ -4,16 +4,29 @@ set -e WRAP_START='[\n' if ${EXCLUDE_USERPASS} then - TEMPLATE=' { "type": "${LOCAL_DB_TYPE}", "host": "${LOCAL_DB_HOST}", "port": "${LOCAL_DB_PORT}" }' + TEMPLATE=' { "ip": "${LOCAL_IP}", "type": "${LOCAL_DB_TYPE}", "host": "${LOCAL_DB_HOST}", "port": "${LOCAL_DB_PORT}" }' else - TEMPLATE=' { "type": "${LOCAL_DB_TYPE}", "user": "${LOCAL_DB_USER}", "password": "${LOCAL_DB_PASSWORD}", "host": "${LOCAL_DB_HOST}", "port": "${LOCAL_DB_PORT}" }' + TEMPLATE=' { "ip": "${LOCAL_IP}", "type": "${LOCAL_DB_TYPE}", "user": "${LOCAL_DB_USER}", "password": "${LOCAL_DB_PASSWORD}", "host": "${LOCAL_DB_HOST}", "port": "${LOCAL_DB_PORT}" }' fi SEPARATOR=',\n' WRAP_END='\n]' OUT="/config/sqlproxy.json" +label_hook() { + if [ -z "{LOCAL_DB_PORT}" ] + then + if [ "${LOCAL_DB_TYPE}" = "mysql" ] + then + LOCAL_DB_PORT='3306' + elif [ "${LOCAL_DB_TYPE}" = "psql" ] + then + LOCAL_DB_PORT='5432' + fi + fi +} + template_hook() { - if grep -q '"type": "(?mysql|psql)".*"host": ".+", port: "\d+"' <<< "${PARTIAL_RESULT}" + if [ "$(jq '((.type == "mysql") or (.type == "psql")) and (.host != "")' <<< "${PARTIAL_RESULT}" 2> /dev/null)" = true ] then return 0 fi From 0f8b7b0530302f00ce54fa558a41127267c3bcb4 Mon Sep 17 00:00:00 2001 From: Kevin Baensch Date: Wed, 21 Dec 2022 19:43:49 +0100 Subject: [PATCH 06/24] Update README documentation to reference labels --- README.md | 30 +++++++++++++++++------------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index a65be34..6439ffe 100644 --- a/README.md +++ b/README.md @@ -6,10 +6,10 @@ docker compose up -d # Hinzufügen von Docker Containern Container werden automatisch vom reverse proxy/host manager aufgegriffen wenn sie: -1. Die Umgebungsvariable `VIRTUAL_HOST` gesetzt haben. +1. Das Label `local.web.host` gesetzt haben. 2. Im gleichen Docker-Netzwek (Default: `proxy`) sind. -Optional kann über die Umgebungsvariable `VIRTUAL_PORT` der gebundene Port gesetzt werden (Default: 80) +Optional kann über das Label `local.web.port` der gebundene Port gesetzt werden (Default: 80) ## Minimale Beispiel-Konfiguration In diesem Beispiel sind zwei Container, nur der `app` Kontainer ist teil des proxy Netzwerks, der `db` Container ist nicht im proxy Netzwerk. @@ -18,6 +18,14 @@ version: "3.4" services: db: image: mariadb:10.4 + labels: + # Required labels + local.db.type: "mysql" + local.db.host: "db_host" + # Optional labels + local.db.port: "3306" + local.db.user: "db_user" + local.db.password: "db_user_pass" environment: MYSQL_DATABASE: db_name MYSQL_USER: db_user @@ -31,15 +39,9 @@ services: image: some_base/image depends_on: - db - environment: - DB_TYPE: mysql - DB_HOST: db - DB_PORT: 3306 - DB_USER: db_user - DB_PASS: db_user_pass - DB_NAME: db_name - VIRTUAL_HOST: app_host - VIRTUAL_PORT: 3000 + labels: + local.web.host: "app_host" + local.web.port: "3000" restart: unless-stopped networks: - proxy @@ -81,9 +83,11 @@ docker compose -f docker-compose.yml -f docker-compose-sqlproxy.yml up -d ### Hinzufügen von DB Docker Containern Container werden automatisch vom reverse proxy/host manager aufgegriffen wenn sie: -1. Die Umgebungsvariable `DB_VHOST` gesetzt haben. +1. Die Umgebungsvariable `local.db.type` und `local.db.host` gesetzt haben. 2. Im gleichen Docker-Netzwek (Default: `proxy`) sind. +Optional kann über das Label `local.web.port` der gebundene Port gesetzt werden (Default mysql: 3306, Default psql: 5432) + ### myssh cli Der SQL Proxy Client hat folgende Optionen: ```bash @@ -97,7 +101,7 @@ disconnect: schließt die SSH Multiplex Session und damit auch alle aktuellen Ve - DEFAULT: `"localhost"` - DESCRIPTION: Setzt den Target Proxy Host - `SQL_PROXY_DB_PORT` - - DEFAULT: `"3306"` + - DEFAULT: sql: `"3306"`, psql: `"5432"` - DESCRIPTION: Setzt den DB Host Target Port - `SQL_CLI_TEMPLATE` - DEFAULT LINUX: `'mysql --protocol=TCP -u $MYSQL_USERNAME -p$MYSQL_PASSWORD -h localhost -P 3306'` From b215663184eb82ff626f157bc07ade7e5ab2224f Mon Sep 17 00:00:00 2001 From: Kevin Baensch Date: Thu, 22 Dec 2022 17:12:23 +0100 Subject: [PATCH 07/24] Semi working myssh script. --- script/myssh | 138 +++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 102 insertions(+), 36 deletions(-) diff --git a/script/myssh b/script/myssh index 1dcb181..9582dcc 100755 --- a/script/myssh +++ b/script/myssh @@ -1,24 +1,7 @@ #!/usr/bin/env bash - -if [ -z "$SQL_CLI_TEMPLATE" ] -then - if [ $(uname -s) = "Linux" ] - then - SQL_CLI_TEMPLATE='mysql --protocol=TCP -u $MYSQL_USERNAME -p$MYSQL_PASSWORD -h localhost -P 6033' - else - SQL_CLI_TEMPLATE='open \"mysql://$MYSQL_USERNAME:$MYSQL_PASSWORD@localhost:6033\" -a \"Sequel Ace\"' - fi -else - echo -e "Warning, custom client string:\n$SQL_CLI_TEMPLATE" - read -r -p "Continue [Y/n] " CONTINUE - if [[ ! $CONTINUE =~ "^[Yy]" ]] - then - exit - fi -fi - -[ -z $SQL_PROXY_HOST ] && SQL_PROXY_HOST="localhost" -CONNECTION_CACHE="$HOME/.cache/sqlproxy_$SQL_PROXY_HOST" +set -e +SQL_PROXY_HOST="${SQL_PROXY_HOST:-localhost}" +CONNECTION_CACHE="$HOME/.cache/sqlproxy_${SQL_PROXY_HOST}" HELP="Usage: myssh [ls|connect]\n SUBCOMMANDS:\n @@ -29,6 +12,26 @@ SUBCOMMANDS:\n SYNTAX connect host [-u user] [-p password] [-c client] " +get_template_string() { + if [ -z "$SQL_CLI_TEMPLATE" ] + then + if [ $(uname -s) = "Linux" ] + then + SQL_CLI_TEMPLATE='mysql --protocol=TCP -u $MYSQL_USERNAME -p$MYSQL_PASSWORD -h localhost -P 6033' + else + SQL_CLI_TEMPLATE='open \"mysql://$MYSQL_USERNAME:$MYSQL_PASSWORD@localhost:6033\" -a \"Sequel Ace\"' + fi + else + echo -e "Warning, custom client string:\n$SQL_CLI_TEMPLATE" + read -r -p "Continue [Y/n] " CONTINUE + if [[ ! $CONTINUE =~ "^[Yy]" ]] + then + exit + fi + fi + printf '%s' "${SQL_CLI_TEMPLATE}" +} + ssh_status() { ssh -O check -S $HOME/.ssh/controlmasters/%r@%h:%p $SQL_PROXY_HOST > /dev/null 2>&1 echo $? @@ -51,9 +54,13 @@ disconnect() { fi } +# Establishes a port forward to the target host +# args: +# $1 - target ip +# $2 - target port port_forward() { ACTIVE_HOST=$(cat $CONNECTION_CACHE) - if [ -z $ACTIVE_HOST ] || [ $ACTIVE_HOST != $1:$2 ] + if [ -z $ACTIVE_HOST ] || [ $ACTIVE_HOST != "$1:$2" ] then if [ ! -z $ACTIVE_HOST ] then @@ -63,7 +70,7 @@ port_forward() { echo $1 $2 ssh -O forward -L 6033:$1:$2 -S ${HOME}/.ssh/controlmasters/%r@%h:%p ${SQL_PROXY_HOST} fi - echo $2:$3 > "${CONNECTION_CACHE}" + echo "$1:$2" > "${CONNECTION_CACHE}" } ls_hosts() { @@ -71,27 +78,87 @@ ls_hosts() { } get_host() { - if [ $1 == '' ] + if [ $1 = '' ] then printf 'Please specify the host to connect to.\nRun "myssh ls" to list all available hosts.\n' + exit 1 else - ssh -S ${HOME}/.ssh/controlmasters/%r@%h:%p "${SQL_PROXY_HOST}" "get $1" + TARGET_HOST_DATA=$(ssh -S ${HOME}/.ssh/controlmasters/%r@%h:%p "${SQL_PROXY_HOST}" "get $1") + if [ "${TARGET_HOST_DATA}" = '' ] + then + printf 'No such host: "%s"\n' $1 + exit 1 + else + printf "%s" "${TARGET_HOST_DATA}" + fi fi } +# Checks and sets sql login variables +# args: +# $1 - sql type (mysql or psql) +# $2 - target ip +# $3 - target port +# $4 - username (optional) +# $5 - password (optional) set_host_env() { - if [ $1 == 'mysql' | $1 == 'psql' ] + if [ $1 = 'mysql' ] || [ $1 = 'psql' ] then TARGET_HOST_TYPE=$1 + else + printf 'Invalid Database type: "%s"\n' $1 + exit 1 fi if [ $2 != '' ] then - + TARGET_HOST_IP=$2 + else + printf 'No Host IP given.\n' + exit 1 fi + if echo $3 | grep -q '[0-9]\+' + then + TARGET_HOST_PORT=$3 + else + printf 'Invalid Host Port "%s" given.\n' "$3" + fi + if [ -z "${TARGET_HOST_USERNAME}" ] && [ -n $4 ] + then + TARGET_HOST_USERNAME=$4 + fi + if [ -z "${TARGET_HOST_PASSWORD}" ] && [ -n $5 ] + then + TARGET_HOST_PASSWORD=$5 + fi + } +# Runs the sql client for an active connection +# args: +# $1 - sql type (mysql or psql) +# $2 - username +# $3 - password run_client() { - $(eval echo $SQL_CLI_TEMPLATE) + if [ "$1" = 'mysql' ] + then + if which ace >/dev/null 2>&1 + then + ace + elif which mysql >/dev/null 2>&1 + then + mysql --protocol=TCP -u "$2" -p"$3" -h "${SQL_PROXY_HOST}" -P 6033 + else + SHOW_CLI_HELP=true + fi + elif [ "${$1}" = 'psql' ] + then + SHOW_CLI_HELP=true + fi + + if [ "${SHOW_CLI_HELP}" = true ] + then + printf 'No %s client binary found.\nYou can maually establish a connection using the following data.\nhost:\t%s\nport:\t6033\nuser:\t%s\npassword:\t%s\n' "$1" "${SQL_PROXY_HOST}" '3306' "$2" "$3" + fi } MAIN_OPTION=$1 @@ -110,17 +177,16 @@ case $MAIN_OPTION in while getopts "u:p:" o do case "$o" in - u) MYSQL_USERNAME="$OPTARG" ;; - p) MYSQL_PASSWORD="$OPTARG" ;; + u) TARGET_HOST_USERNAME="$OPTARG" ;; + p) TARGET_HOST_PASSWORD="$OPTARG" ;; esac done - TARGET_HOST_DATA="$(get_host "${TARGET_HOST}")" - port_forward $TARGET_HOST_DATA - # port_forward $TARGET_HOST - # if [ ! -z $MYSQL_USERNAME ] && [ ! -z $MYSQL_PASSWORD ] - # then - # run_client - # fi + set_host_env $(get_host "${TARGET_HOST}") + port_forward "${TARGET_HOST_IP}" "$TARGET_HOST_PORT" + if [ -n "${TARGET_HOST_USERNAME}" ] && [ -n "${TARGET_HOST_PASSWORD}" ] + then + run_client "${TARGET_HOST_TYPE}" "${TARGET_HOST_USERNAME}" "${TARGET_HOST_PASSWORD}" + fi ;; disconnect) disconnect;; From ded4b31dbb674a1896a760e30be56888f8036826 Mon Sep 17 00:00:00 2001 From: Kevin Baensch Date: Thu, 22 Dec 2022 17:16:05 +0100 Subject: [PATCH 08/24] Update setup script. --- setup.sh | 134 ++++++++++++++++++++++++++++++++++++++++++++++ sqlproxy_setup.sh | 65 ---------------------- 2 files changed, 134 insertions(+), 65 deletions(-) create mode 100755 setup.sh delete mode 100755 sqlproxy_setup.sh diff --git a/setup.sh b/setup.sh new file mode 100755 index 0000000..92e9113 --- /dev/null +++ b/setup.sh @@ -0,0 +1,134 @@ +#!/usr/bin/env bash +set -e +PROJECT_PATH=$(dirname $0) +WHOAMI="$(id -un)" + +if [ $(id -u) -eq 0 ] +then + printf 'Do not run this script as root.\n' + exit 1 +fi + +check() { + read -r -p "$1" ANSWER + if [[ "${ANSWER}" =~ ^[Yy] ]] + then + return 0 + fi + return 1 +} + +setup_base() { + mkdir -p "${PROJECT_PATH}/config" "${PROJECT_PATH}/caddy_data" + if [ "$(uname -s)" = 'Darwin' ] && [ ! -w '/etc/hosts' ] + then + printf 'On MacOS docker is run by your local user (not root).\nYour user has no write permission for "/etc/hosts".\nRunning: "sudo chown %s /etc/hosts"\n' "${WHOAMI}" + if check 'Continue? [Y/n] ' + then + sudo chown "${WHOAMI}" '/etc/hosts' + else + printf 'Setup Aborted!\n' + exit 1 + fi + fi +} + +setup_myssh() { + # Always copy newest version to bin + mkdir -p "${HOME}/bin" + cp "${PROJECT_PATH}/script/myssh" "${HOME}/bin/myssh" + + # Detect Shell Init Path + if [[ "${SHELL}" =~ bin/bash$ ]] + then + RC_FILE=".bashrc" + elif [[ "${SHELL}" =~ bin/zsh$ ]] + then + RC_FILE=".zshrc" + else + printf 'Unable to detect Shell Configuration.\nPlease add %s to your PATH variable.\n' "${HOME}/bin" + return 0 + fi + + touch "${HOME}/${RC_FILE}" + + if [ -f "${HOME}/${RC_FILE}" ] && [[ ! "${PATH}" =~ "${HOME}/bin" ]] && ! grep -qe '^PATH="${PATH}:${HOME}/bin"$' "${HOME}/${RC_FILE}" 2> /dev/null + then + printf 'PATH="${PATH}:${HOME}/bin"\n' >> "${HOME}/${RC_FILE}" + fi +} + +setup_sqlproxy() { + if [ ! -f "${PROJECT_PATH}/etc/ssh/ssh_host_ed25519_key" ] + then + printf "Generating sqlproxy SSHD keys\n" + ssh-keygen -f "${PROJECT_PATH}" -A + fi + + if check 'Auto generate client keys+config? [Y/n] ' + then + mkdir -p "${HOME}/.ssh" + read -r -p 'Key Name (default: sqlproxy): ' KEY_NAME + KEY_NAME="${KEY_NAME:-sqlproxy}" + + # Only add key if it does not already exist + if [ ! -f "${HOME}/.ssh/${KEY_NAME}" ] + then + ssh-keygen -t ed25519 -f "${HOME}/.ssh/${KEY_NAME}" -C "$(date +'%Y.%m.%d')_${WHOAMI}@${HOSTNAME}" + else + printf 'Key "%s" already exists. Using existing key.\n' "${HOME}/.ssh/${KEY_NAME}" + fi + + read -r -p 'Target Host (default: "localhost"): ' HOST_NAME + HOST_NAME="${HOST_NAME:-localhost}" + + # Check if there is an entry for $HOST_NAME in the users ssh config + if ! grep -qe "$(printf '^Host %s$' "${HOST_NAME}")" "${HOME}/.ssh/config" 2>/dev/null + then + printf '\nHost %s\n Port 3022\n User sqlproxy\n IdentityFile ~/.ssh/%s' "${HOST_NAME}" "${KEY_NAME}" >> "${HOME}/.ssh/config" + else + printf 'User ssh configuration located in "%s" already has a configuration for host "%s".\nMake sure your configuration matches the following:\n' "${HOME}/.ssh/config" "${HOST_NAME}" + printf '"""\nHost %s\n Port 3022\n User sqlproxy\n IdentityFile ~/.ssh/%s\n"""\n' "${HOST_NAME}" "${KEY_NAME}" + fi + + # Fix permssions if necessary + if [[ ! -w "${PROJECT_PATH}/etc/ssh/.ssh" ]] || [[ ! -w "${PROJECT_PATH}/etc/ssh/.ssh/authorized_keys" ]] + then + printf 'Missing file permissions for authorized key file\nrunning: "sudo chown -R %s %s"\n' "${WHOAMI}:${WHOAMI}" "${PROJECT_PATH}" + if check 'Continue? [Y/n] ' + then + sudo chown -R "${WHOAMI}:${WHOAMI}" "${PROJECT_PATH}" + else + printf 'Setup Aborted!\n' + exit 1 + fi + fi + + # Check if public key is already in the containers authorized_keys file + PUB_KEY="$(cat ${HOME}/.ssh/${KEY_NAME}.pub)" + if ! grep -qe "$(printf '%s$' "${PUB_KEY}")" "${PROJECT_PATH}/etc/ssh/.ssh/authorized_keys" + then + printf 'command="/sqlproxy_cli.sh" %s' "${PUB_KEY}" >> "${PROJECT_PATH}/etc/ssh/.ssh/authorized_keys" + fi + # Restart sshd if permissions were changed + if [ ! -z "${WHOAMI}" ] + then + printf 'Restarting sql proxy (if running) to fix permissions.\n' + docker compose --project-directory "${PROJECT_PATH}" -f "${PROJECT_PATH}/docker-compose.yml" -f "${PROJECT_PATH}/docker-compose-sqlproxy.yml" restart sshd + fi + else + printf 'Not generating client ssh key.\nPlease put your desired public keys into %s\nAlso add %s in front of your key\n' "${PROJECT_PATH}/etc/ssh/.ssh/authorized_keys" "'command=\"/sqlproxy_cli.sh\" '" + fi +} + +setup_base + +if check 'Install myssh binary? [Y/n] ' +then + setup_myssh +fi + +if check 'Configure sql proxy? [Y/n] ' +then + setup_sqlproxy +fi diff --git a/sqlproxy_setup.sh b/sqlproxy_setup.sh deleted file mode 100755 index 4224c0b..0000000 --- a/sqlproxy_setup.sh +++ /dev/null @@ -1,65 +0,0 @@ -#!/usr/bin/env bash -PROJECT_PATH=$(dirname $0) - -# Always copy newest version to bin -mkdir -p $HOME/bin -cp $PROJECT_PATH/script/myssh $HOME/bin/myssh - -# Detect Shell Init Path -if [[ $SHELL =~ bin/bash$ ]] -then - RC_FILE=.bashrc -elif [[ $SHELL =~ bin/zsh$ ]] -then - RC_FILE=.zshrc -fi - -grep -qe '^PATH=$PATH:$HOME/bin$' $HOME/$RC_FILE 2> /dev/null -if [ ! -z $HOME/$RC_FILE ] && [[ ! $PATH =~ $HOME/bin ]] && [ $? -ne 0 ] -then - echo -e 'PATH=$PATH:$HOME/bin' >> $HOME/$RC_FILE -fi - -if [ ! -f $PROJECT_PATH/etc/ssh/ssh_host_ed25519_key ] -then - echo "Generating sqlproxy SSHD keys" - ssh-keygen -f $PROJECT_PATH -A -fi - -read -r -p "Auto generate client keys+config? [Y/n] " GEN_KEYS -case $GEN_KEYS in - [yY]*) - mkdir -p $HOME/.ssh - read -r -p "Key Name (will not be overridden if it already exists in ~/.ssh): " KEY_NAME - # Only add key if it does not already exist - if [ ! -f $HOME/.ssh/$KEY_NAME.key ] - then - ssh-keygen -t ed25519 -f $HOME/.ssh/$KEY_NAME.key -C "$(date --iso-8601)_$(whoami)@$HOSTNAME" - fi - read -r -p "Target Host: " HOST_NAME - # Check if there is an entry for $HOST_NAME in the users ssh config - grep -qe "^Host $HOST_NAME$" $HOME/.ssh/config - if [ $? -ne 0 ] - then - echo -ne "\nHost $HOST_NAME\n Port 3022\n User sqlproxy\n IdentityFile ~/.ssh/$KEY_NAME.key" >> $HOME/.ssh/config - fi - # Fix permssions if necessary - if [[ ! -w $PROJECT_PATH/etc/ssh/.ssh ]] || [[ ! $PROJECT_PATH/etc/ssh/.ssh/authorized_keys ]] - then - WHOAMI=$(id -un) - echo -e "Missing file permissions for authorized key file\nrunning: 'sudo chown -R $WHOAMI:$WHOAMI $PROJECT_PATH'" - sudo chown -R $WHOAMI:$WHOAMI $PROJECT_PATH - fi - # Check if public key is already in the containers authorized_keys file - grep -qe "$(cat $HOME/.ssh/$KEY_NAME.key.pub)$" $PROJECT_PATH/etc/ssh/.ssh/authorized_keys - if [ $? -ne 0 ] - then - echo -e command=\"/sqlproxy_cli.sh\" $(cat $HOME/.ssh/$KEY_NAME.key.pub) >> $PROJECT_PATH/etc/ssh/.ssh/authorized_keys - fi - # Restart sshd if permissions were changed - if [ ! -z $WHOAMI ] - then - docker compose --project-directory $PROJECT_PATH -f $PROJECT_PATH/docker-compose.yml -f $PROJECT_PATH/docker-compose-sqlproxy.yml restart sshd - fi;; - *) echo -e "Not generating client ssh key.\nPlease put your desired public keys into $PROJECT_PATH/etc/ssh/.ssh/authorized_keys\nAlso add 'command=\"/sqlproxy_cli.sh\" ' in front of your key";; -esac From 87641a0714551a2b0dbd8d45ac57e87bc0868e9d Mon Sep 17 00:00:00 2001 From: Kevin Baensch Date: Fri, 23 Dec 2022 09:43:17 +0100 Subject: [PATCH 09/24] Merge compose configurations. --- docker-compose-sqlproxy.yml | 24 ------------------------ docker-compose.yml | 20 ++++++++++++++++++++ 2 files changed, 20 insertions(+), 24 deletions(-) delete mode 100644 docker-compose-sqlproxy.yml diff --git a/docker-compose-sqlproxy.yml b/docker-compose-sqlproxy.yml deleted file mode 100644 index dc21c4c..0000000 --- a/docker-compose-sqlproxy.yml +++ /dev/null @@ -1,24 +0,0 @@ -version: "3.4" - -services: - sshd: - build: - context: . - dockerfile: ./Dockerfile - command: ["./sqlproxy.sh", "&", "wait", "$!" ] - ports: - - 3022:22 - volumes: - - ./etc/ssh:/etc/ssh/ - - ./script/sqlproxy.sh:/sqlproxy.sh:ro - - ./script/sqlproxy_cli.sh:/sqlproxy_cli.sh:ro - - ./config:/config - environment: - DISABLE_KEYGEN: true - DISABLE_CONFIG_GEN: true - HOST_CONF_PATH: /etc/hosts - RESOLVE_DOCKERHOST: true - DOCKER_HOSTNAME_VAR: DB_VHOST - networks: - - proxy - restart: unless-stopped diff --git a/docker-compose.yml b/docker-compose.yml index 9cd3bb8..d3542bd 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -13,6 +13,7 @@ services: - /etc/hosts:/config/hosts:rw networks: - proxy + restart: unless-stopped proxy: image: caddy:2.6.2-alpine ports: @@ -23,6 +24,25 @@ services: - ./caddy_data:/data/caddy networks: - proxy + restart: unless-stopped + sshd: + build: + context: . + dockerfile: ./Dockerfile + command: ["./sqlproxy.sh", "&", "wait", "$!" ] + ports: + - 3022:22 + volumes: + - ./etc/ssh:/etc/ssh/ + - ./script/sqlproxy.sh:/sqlproxy.sh:ro + - ./script/sqlproxy_cli.sh:/sqlproxy_cli.sh:ro + - ./config:/config + environment: + DISABLE_KEYGEN: true + DISABLE_CONFIG_GEN: true + networks: + - proxy + restart: unless-stopped networks: proxy: From f35b108e172e5f5a690e8f1ea8ffc7acf2e2cc9d Mon Sep 17 00:00:00 2001 From: Kevin Baensch Date: Fri, 23 Dec 2022 09:54:21 +0100 Subject: [PATCH 10/24] Small fixes to setup script. --- .gitignore | 1 + etc/ssh/.ssh/authorized_keys | 0 setup.sh | 36 +++++++++++++----------------------- 3 files changed, 14 insertions(+), 23 deletions(-) delete mode 100644 etc/ssh/.ssh/authorized_keys diff --git a/.gitignore b/.gitignore index d483373..948284d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ etc/ssh/*key* etc/ssh/.ssh/ +config/ diff --git a/etc/ssh/.ssh/authorized_keys b/etc/ssh/.ssh/authorized_keys deleted file mode 100644 index e69de29..0000000 diff --git a/setup.sh b/setup.sh index 92e9113..217dd8d 100755 --- a/setup.sh +++ b/setup.sh @@ -19,16 +19,22 @@ check() { } setup_base() { - mkdir -p "${PROJECT_PATH}/config" "${PROJECT_PATH}/caddy_data" + printf 'Change ownership of "%s" to "%s"? (setup may fail otherwise)\n' "${PROJECT_PATH}" "${WHOAMI}" + printf 'running: "sudo chown -R %s %s"\n' "${WHOAMI}:${WHOAMI}" "${PROJECT_PATH}" + if check 'Continue? [Y/n] ' + then + sudo chown -R "${WHOAMI}:${WHOAMI}" "${PROJECT_PATH}" + fi + + + mkdir -p "${PROJECT_PATH}/config" "${PROJECT_PATH}/caddy_data" "${PROJECT_PATH}/etc/ssh/.ssh" + touch "${PROJECT_PATH}/config/Caddyfile" "${PROJECT_PATH}/etc/ssh/.ssh/authorized_keys" if [ "$(uname -s)" = 'Darwin' ] && [ ! -w '/etc/hosts' ] then printf 'On MacOS docker is run by your local user (not root).\nYour user has no write permission for "/etc/hosts".\nRunning: "sudo chown %s /etc/hosts"\n' "${WHOAMI}" if check 'Continue? [Y/n] ' then sudo chown "${WHOAMI}" '/etc/hosts' - else - printf 'Setup Aborted!\n' - exit 1 fi fi } @@ -91,31 +97,15 @@ setup_sqlproxy() { printf '"""\nHost %s\n Port 3022\n User sqlproxy\n IdentityFile ~/.ssh/%s\n"""\n' "${HOST_NAME}" "${KEY_NAME}" fi - # Fix permssions if necessary - if [[ ! -w "${PROJECT_PATH}/etc/ssh/.ssh" ]] || [[ ! -w "${PROJECT_PATH}/etc/ssh/.ssh/authorized_keys" ]] - then - printf 'Missing file permissions for authorized key file\nrunning: "sudo chown -R %s %s"\n' "${WHOAMI}:${WHOAMI}" "${PROJECT_PATH}" - if check 'Continue? [Y/n] ' - then - sudo chown -R "${WHOAMI}:${WHOAMI}" "${PROJECT_PATH}" - else - printf 'Setup Aborted!\n' - exit 1 - fi - fi - # Check if public key is already in the containers authorized_keys file PUB_KEY="$(cat ${HOME}/.ssh/${KEY_NAME}.pub)" if ! grep -qe "$(printf '%s$' "${PUB_KEY}")" "${PROJECT_PATH}/etc/ssh/.ssh/authorized_keys" then printf 'command="/sqlproxy_cli.sh" %s' "${PUB_KEY}" >> "${PROJECT_PATH}/etc/ssh/.ssh/authorized_keys" fi - # Restart sshd if permissions were changed - if [ ! -z "${WHOAMI}" ] - then - printf 'Restarting sql proxy (if running) to fix permissions.\n' - docker compose --project-directory "${PROJECT_PATH}" -f "${PROJECT_PATH}/docker-compose.yml" -f "${PROJECT_PATH}/docker-compose-sqlproxy.yml" restart sshd - fi + + printf 'Restarting sql proxy (if running) to fix permissions.\n' + docker compose --project-directory "${PROJECT_PATH}" -f "${PROJECT_PATH}/docker-compose.yml" restart sshd else printf 'Not generating client ssh key.\nPlease put your desired public keys into %s\nAlso add %s in front of your key\n' "${PROJECT_PATH}/etc/ssh/.ssh/authorized_keys" "'command=\"/sqlproxy_cli.sh\" '" fi From 2e824f92b34bba6e749fd9e38b881edd8e918b66 Mon Sep 17 00:00:00 2001 From: Kevin Baensch Date: Fri, 23 Dec 2022 11:14:55 +0100 Subject: [PATCH 11/24] Add fixes to sql proxy --- script/myssh | 51 +++++++++++++++++++++++++------------------ templates/sqlproxy.sh | 2 +- 2 files changed, 31 insertions(+), 22 deletions(-) diff --git a/script/myssh b/script/myssh index 9582dcc..fe52a16 100755 --- a/script/myssh +++ b/script/myssh @@ -88,8 +88,6 @@ get_host() { then printf 'No such host: "%s"\n' $1 exit 1 - else - printf "%s" "${TARGET_HOST_DATA}" fi fi } @@ -102,33 +100,34 @@ get_host() { # $4 - username (optional) # $5 - password (optional) set_host_env() { - if [ $1 = 'mysql' ] || [ $1 = 'psql' ] + if [ "$1" = 'mysql' ] || [ "$1" = 'psql' ] then - TARGET_HOST_TYPE=$1 + TARGET_HOST_TYPE="$1" else - printf 'Invalid Database type: "%s"\n' $1 + printf 'Invalid Database type: "%s"\n' "$1" exit 1 fi - if [ $2 != '' ] + if [[ "$2" =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ ]] then - TARGET_HOST_IP=$2 + TARGET_HOST_IP="$2" else - printf 'No Host IP given.\n' + printf 'Invalid Host IP "%s" given.\n' "$2" exit 1 fi - if echo $3 | grep -q '[0-9]\+' + if [[ "$3" =~ ^[0-9]+$ ]] then - TARGET_HOST_PORT=$3 + TARGET_HOST_PORT="$3" else printf 'Invalid Host Port "%s" given.\n' "$3" + exit 1 fi - if [ -z "${TARGET_HOST_USERNAME}" ] && [ -n $4 ] + if [ -z "${TARGET_HOST_USERNAME}" ] && [ -n "$4" ] then - TARGET_HOST_USERNAME=$4 + TARGET_HOST_USERNAME="$4" fi - if [ -z "${TARGET_HOST_PASSWORD}" ] && [ -n $5 ] + if [ -z "${TARGET_HOST_PASSWORD}" ] && [ -n "$5" ] then - TARGET_HOST_PASSWORD=$5 + TARGET_HOST_PASSWORD="$5" fi } @@ -161,18 +160,27 @@ run_client() { fi } -MAIN_OPTION=$1 -shift +MAIN_OPTION="$1" +if [ -n "$1" ] +then + shift +fi # ensure connection connect -case $MAIN_OPTION in +case "${MAIN_OPTION}" in ls) ls_hosts;; connect) # check if host is valid - TARGET_HOST=$1 - shift + TARGET_HOST="$1" + if [ -n "$1" ] + then + shift + else + printf 'No host specified.\n' + exit 1 + fi while getopts "u:p:" o do @@ -181,7 +189,8 @@ case $MAIN_OPTION in p) TARGET_HOST_PASSWORD="$OPTARG" ;; esac done - set_host_env $(get_host "${TARGET_HOST}") + get_host "${TARGET_HOST}" + set_host_env ${TARGET_HOST_DATA} port_forward "${TARGET_HOST_IP}" "$TARGET_HOST_PORT" if [ -n "${TARGET_HOST_USERNAME}" ] && [ -n "${TARGET_HOST_PASSWORD}" ] then @@ -191,5 +200,5 @@ case $MAIN_OPTION in disconnect) disconnect;; *) - echo -e $HELP;; + echo -e "${HELP}";; esac diff --git a/templates/sqlproxy.sh b/templates/sqlproxy.sh index f8613f9..384da0b 100644 --- a/templates/sqlproxy.sh +++ b/templates/sqlproxy.sh @@ -13,7 +13,7 @@ WRAP_END='\n]' OUT="/config/sqlproxy.json" label_hook() { - if [ -z "{LOCAL_DB_PORT}" ] + if [ -z "${LOCAL_DB_PORT}" ] then if [ "${LOCAL_DB_TYPE}" = "mysql" ] then From e30d950f88fc05b2102bfa7f6fd45c8e32a8327f Mon Sep 17 00:00:00 2001 From: Kevin Baensch Date: Sat, 24 Dec 2022 19:47:01 +0100 Subject: [PATCH 12/24] Ignore caddy_data folder. --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 948284d..435ba2f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ etc/ssh/*key* etc/ssh/.ssh/ config/ +caddy_data/ From 9083bfc5ad1c82bcd28b511f6b7b3ab162f7e415 Mon Sep 17 00:00:00 2001 From: Kevin Baensch Date: Sat, 24 Dec 2022 19:48:31 +0100 Subject: [PATCH 13/24] Remove debug message and wrap more variables. --- script/myssh | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/script/myssh b/script/myssh index fe52a16..dd7ef1a 100755 --- a/script/myssh +++ b/script/myssh @@ -60,14 +60,12 @@ disconnect() { # $2 - target port port_forward() { ACTIVE_HOST=$(cat $CONNECTION_CACHE) - if [ -z $ACTIVE_HOST ] || [ $ACTIVE_HOST != "$1:$2" ] + if [ -z "${ACTIVE_HOST}" ] || [ "${ACTIVE_HOST}" != "$1:$2" ] then - if [ ! -z $ACTIVE_HOST ] + if [ -n "${ACTIVE_HOST}" ] then - ssh -O cancel -L 6033:$ACTIVE_HOST -S $HOME/.ssh/controlmasters/%r@%h:%p ${SQL_PROXY_HOST} q + ssh -O cancel -L 6033:${ACTIVE_HOST} -S ${HOME}/.ssh/controlmasters/%r@%h:%p ${SQL_PROXY_HOST} q fi - echo $ACTIVE_HOST - echo $1 $2 ssh -O forward -L 6033:$1:$2 -S ${HOME}/.ssh/controlmasters/%r@%h:%p ${SQL_PROXY_HOST} fi echo "$1:$2" > "${CONNECTION_CACHE}" From 264cf17de58349257f7bbbe7cb1a9d35bd0de758 Mon Sep 17 00:00:00 2001 From: Kevin Baensch Date: Thu, 5 Jan 2023 09:48:19 +0100 Subject: [PATCH 14/24] Add postgres cli support. --- script/docker-templater.sh | 1 - script/myssh | 46 +++++++++++++++++++++----------------- templates/sqlproxy.sh | 4 ++-- 3 files changed, 28 insertions(+), 23 deletions(-) diff --git a/script/docker-templater.sh b/script/docker-templater.sh index 341b385..a2f0bde 100755 --- a/script/docker-templater.sh +++ b/script/docker-templater.sh @@ -72,7 +72,6 @@ do then LOCAL_IP=$(jq -r '.NetworkSettings.Networks | if has($ENV.NETWORK_NAME) then .[$ENV.NETWORK_NAME].IPAddress else first(.[].IPAddress) end' <<< "${CONTAINER_DATA}"); fi - # export LOCAL_IP # Read Label values CONTAINER_LABELS=$(jq '.Labels' <<< "${CONTAINER_DATA}"); diff --git a/script/myssh b/script/myssh index dd7ef1a..b656022 100755 --- a/script/myssh +++ b/script/myssh @@ -15,7 +15,7 @@ SYNTAX connect host [-u user] [-p password] [-c client] get_template_string() { if [ -z "$SQL_CLI_TEMPLATE" ] then - if [ $(uname -s) = "Linux" ] + if [ "$(uname -s)" = "Linux" ] then SQL_CLI_TEMPLATE='mysql --protocol=TCP -u $MYSQL_USERNAME -p$MYSQL_PASSWORD -h localhost -P 6033' else @@ -33,24 +33,23 @@ get_template_string() { } ssh_status() { - ssh -O check -S $HOME/.ssh/controlmasters/%r@%h:%p $SQL_PROXY_HOST > /dev/null 2>&1 - echo $? + ssh -O check -S "${HOME}/.ssh/controlmasters/%r@%h:%p" "${SQL_PROXY_HOST}" > /dev/null 2>&1 } connect() { - mkdir -p $HOME/.ssh/controlmasters + mkdir -p "${HOME}/.ssh/controlmasters" - if [ $(ssh_status) -ne 0 ] + if ! ssh_status then - echo "" > $CONNECTION_CACHE - ssh -o "ControlPersist=10m" -M -S $HOME/.ssh/controlmasters/%r@%h:%p $SQL_PROXY_HOST q + echo "" > "${CONNECTION_CACHE}" + ssh -o "ControlPersist=10m" -M -S "${HOME}/.ssh/controlmasters/%r@%h:%p" "${SQL_PROXY_HOST}" q fi } disconnect() { - if [ $(ssh_status) -eq 0 ] + if ssh_status then - ssh -O stop -S $HOME/.ssh/controlmasters/%r@%h:%p $SQL_PROXY_HOST q + ssh -O stop -S "${HOME}/.ssh/controlmasters/%r@%h:%p" "${SQL_PROXY_HOST}" q fi } @@ -59,32 +58,32 @@ disconnect() { # $1 - target ip # $2 - target port port_forward() { - ACTIVE_HOST=$(cat $CONNECTION_CACHE) + ACTIVE_HOST=$(cat "${CONNECTION_CACHE}") if [ -z "${ACTIVE_HOST}" ] || [ "${ACTIVE_HOST}" != "$1:$2" ] then if [ -n "${ACTIVE_HOST}" ] then - ssh -O cancel -L 6033:${ACTIVE_HOST} -S ${HOME}/.ssh/controlmasters/%r@%h:%p ${SQL_PROXY_HOST} q + ssh -O cancel -L "6033:${ACTIVE_HOST}" -S "${HOME}/.ssh/controlmasters/%r@%h:%p" "${SQL_PROXY_HOST}" q fi - ssh -O forward -L 6033:$1:$2 -S ${HOME}/.ssh/controlmasters/%r@%h:%p ${SQL_PROXY_HOST} + ssh -O forward -L "6033:$1:$2" -S "${HOME}/.ssh/controlmasters/%r@%h:%p" "${SQL_PROXY_HOST}" fi echo "$1:$2" > "${CONNECTION_CACHE}" } ls_hosts() { - ssh -S ${HOME}/.ssh/controlmasters/%r@%h:%p "${SQL_PROXY_HOST}" ls + ssh -S "${HOME}/.ssh/controlmasters/%r@%h:%p" "${SQL_PROXY_HOST}" ls } get_host() { - if [ $1 = '' ] + if [ "$1" = '' ] then printf 'Please specify the host to connect to.\nRun "myssh ls" to list all available hosts.\n' exit 1 else - TARGET_HOST_DATA=$(ssh -S ${HOME}/.ssh/controlmasters/%r@%h:%p "${SQL_PROXY_HOST}" "get $1") + TARGET_HOST_DATA=$(ssh -S "${HOME}/.ssh/controlmasters/%r@%h:%p" "${SQL_PROXY_HOST}" "get $1") if [ "${TARGET_HOST_DATA}" = '' ] then - printf 'No such host: "%s"\n' $1 + printf 'No such host: "%s"\n' "$1" exit 1 fi fi @@ -98,7 +97,7 @@ get_host() { # $4 - username (optional) # $5 - password (optional) set_host_env() { - if [ "$1" = 'mysql' ] || [ "$1" = 'psql' ] + if [ "$1" = 'mysql' ] || [ "$1" = 'postgres' ] then TARGET_HOST_TYPE="$1" else @@ -147,14 +146,20 @@ run_client() { else SHOW_CLI_HELP=true fi - elif [ "${$1}" = 'psql' ] + elif [ "$1" = 'postgres' ] then - SHOW_CLI_HELP=true + if which psql >/dev/null 2>&1 + then + psql "postgresql://$2:$3@${SQL_PROXY_HOST}:6033/postgres" + else + SHOW_CLI_HELP=true + fi fi if [ "${SHOW_CLI_HELP}" = true ] then - printf 'No %s client binary found.\nYou can maually establish a connection using the following data.\nhost:\t%s\nport:\t6033\nuser:\t%s\npassword:\t%s\n' "$1" "${SQL_PROXY_HOST}" '3306' "$2" "$3" + printf 'No %s client binary found.\nYou can maually establish a connection using the following data.\n' "$1" + printf 'host:\t%s\nport:\t%s\nuser:\t%s\npassword:\t%s\n' "${SQL_PROXY_HOST}" '6033' "$2" "$3" fi } @@ -188,6 +193,7 @@ case "${MAIN_OPTION}" in esac done get_host "${TARGET_HOST}" + # Do not quote this. set_host_env ${TARGET_HOST_DATA} port_forward "${TARGET_HOST_IP}" "$TARGET_HOST_PORT" if [ -n "${TARGET_HOST_USERNAME}" ] && [ -n "${TARGET_HOST_PASSWORD}" ] diff --git a/templates/sqlproxy.sh b/templates/sqlproxy.sh index 384da0b..c6781ad 100644 --- a/templates/sqlproxy.sh +++ b/templates/sqlproxy.sh @@ -18,7 +18,7 @@ label_hook() { if [ "${LOCAL_DB_TYPE}" = "mysql" ] then LOCAL_DB_PORT='3306' - elif [ "${LOCAL_DB_TYPE}" = "psql" ] + elif [ "${LOCAL_DB_TYPE}" = "postgres" ] then LOCAL_DB_PORT='5432' fi @@ -26,7 +26,7 @@ label_hook() { } template_hook() { - if [ "$(jq '((.type == "mysql") or (.type == "psql")) and (.host != "")' <<< "${PARTIAL_RESULT}" 2> /dev/null)" = true ] + if [ "$(jq '((.type == "mysql") or (.type == "postgres")) and (.host != "")' <<< "${PARTIAL_RESULT}" 2> /dev/null)" = true ] then return 0 fi From c7e39d61646bdee0928ac6a0c040ae7d5bd1e6c1 Mon Sep 17 00:00:00 2001 From: Kevin Baensch Date: Fri, 6 Jan 2023 08:34:35 +0100 Subject: [PATCH 15/24] Templater variables do not need to be overridable. --- script/hostman.sh | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/script/hostman.sh b/script/hostman.sh index 1cd5f37..971f593 100755 --- a/script/hostman.sh +++ b/script/hostman.sh @@ -3,8 +3,9 @@ # Configurable Variables DOCKER_SOCK_PATH="${DOCKER_SOCK_PATH:-/tmp/docker.sock}" NETWORK_NAME="${NETWORK_NAME:-proxy}" -TEMPLATER_PATH="${TEMPLATER_PATH:-/docker-templater.sh}" -TEMPLATE_FOLDER_PATH="${TEMPLATE_FOLDER_PATH:-/templates}" +# Path to Docker Templater and Templates +TEMPLATER_PATH="/docker-templater.sh" +TEMPLATE_FOLDER_PATH="/templates" get_date() { printf '%s' "$(date +'%Y.%m.%d_%H:%M:%S')" From 606f582dc003500cd4c33b6c67ac732f07b7ac45 Mon Sep 17 00:00:00 2001 From: Kevin Baensch Date: Fri, 6 Jan 2023 08:53:30 +0100 Subject: [PATCH 16/24] Remove unused environment variable. --- templates/caddy.sh | 1 - 1 file changed, 1 deletion(-) diff --git a/templates/caddy.sh b/templates/caddy.sh index ea4c62a..7333694 100644 --- a/templates/caddy.sh +++ b/templates/caddy.sh @@ -1,6 +1,5 @@ #!/usr/bin/env bash set -e -DOCKER_SOCK_PATH="${DOCKER_SOCK_PATH:-/tmp/docker.sock}" DOCKER_CADDY_NAME="${DOCKER_CADDY_NAME:-proxy}" DOCKER_CADDY_PORT="${DOCKER_CADDY_PORT:-2020}" From f0cbc32c365ea11ba7bf3997a455baea416e9932 Mon Sep 17 00:00:00 2001 From: Kevin Baensch Date: Fri, 6 Jan 2023 08:53:54 +0100 Subject: [PATCH 17/24] Update Documentation --- README.md | 130 +++++++++++++++++++++++++++++++----------------------- 1 file changed, 76 insertions(+), 54 deletions(-) diff --git a/README.md b/README.md index 6439ffe..f5786b2 100644 --- a/README.md +++ b/README.md @@ -1,27 +1,55 @@ -# Starten des Reverse-Proxies +# First Steps +## Initiales Setup +Öffne ein Terminal und führe die `setup.sh` Datei aus. +Das Script: +1. Behebt Berechtigungsprobleme und legt alle nötigen Ordner/Dateien an, die zum starten des Proxies benötigt werden. +2. Installiert den `myssh` befehl nach `$HOME/bin` und fügt diesen ggf zur PATH variable hinzu +3. Erstellt SSH Keys für den SSH Docker Container (wenn diese nicht bereits existieren) +4. Erstellt und Konfiguriert einen Client SSH Key, dessen public Key wird dem SSH Docker Container hinzugefügt + +## Starten des Reverse-Proxies Der Proxy kann über docker compose gestartet werden ```bash docker compose up -d ``` -# Hinzufügen von Docker Containern +# Proxy Konfiguration +## HTTP-Proxy +### Hinzufügen von Docker Containern (Hosts) Container werden automatisch vom reverse proxy/host manager aufgegriffen wenn sie: 1. Das Label `local.web.host` gesetzt haben. 2. Im gleichen Docker-Netzwek (Default: `proxy`) sind. Optional kann über das Label `local.web.port` der gebundene Port gesetzt werden (Default: 80) +### SSL Zertifikat Konfiguration +Nach starten des docker containers ist das von caddy erstellte SSL Zertifikat in './caddy_data/pki/authorities/local/root.crt' gefunden werden. +Relevante Dokumentation: +- [MacOS](https://support.apple.com/guide/keychain-access/add-certificates-to-a-keychain-kyca2431/mac) +- [Linux](https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/7/html/security_guide/sec-shared-system-certificates) + +## SQL Proxy +### Hinzufügen von DB Docker Containern +Container werden automatisch vom reverse proxy/host manager aufgegriffen wenn sie: +1. Die Umgebungsvariable `local.db.type` (`mysql` oder `postgres`) und `local.db.host` gesetzt haben. +2. Im gleichen Docker-Netzwek (Default: `proxy`) sind. + +Optional kann über das Label `local.web.port` der gebundene Port gesetzt werden (Default mysql: 3306, Default postgres: 5432) + ## Minimale Beispiel-Konfiguration -In diesem Beispiel sind zwei Container, nur der `app` Kontainer ist teil des proxy Netzwerks, der `db` Container ist nicht im proxy Netzwerk. +In diesem Beispiel sind zwei Container, beide sind Teil des `proxy` Netzwerks. +Der `app` Container hat das Label `local.web.host` und wird deshalb als HTTP Proxy Target registriert. +Der `db` Container hat die Label `local.db.type` und `local.db.host` und wird deshalt als SQL Proxy Target registriert. ```yaml version: "3.4" services: db: image: mariadb:10.4 labels: + # SQL-Proxy Configuration # Required labels local.db.type: "mysql" - local.db.host: "db_host" + local.db.host: "db.myapp.localhost" # Optional labels local.db.port: "3306" local.db.user: "db_user" @@ -34,16 +62,23 @@ services: restart: unless-stopped volumes: - db-data:/var/lib/mysql + networks: + - default + - proxy app: image: some_base/image depends_on: - db labels: - local.web.host: "app_host" + # HTTP-Proxy Configuration + # Required Label + local.web.host: "myapp.localhost" + # "Optional" Label to set the correct port local.web.port: "3000" restart: unless-stopped networks: + - default - proxy networks: @@ -60,35 +95,42 @@ networks: external: true ``` -## SSL Zertifikat Konfiguration -Nach starten des docker containers ist das von caddy erstellte SSL Zertifikat in './caddy_data/pki/authorities/local/root.crt' gefunden werden. -Relevante Dokumentation: -- [MacOS](https://support.apple.com/guide/keychain-access/add-certificates-to-a-keychain-kyca2431/mac) -- [Linux](https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/7/html/security_guide/sec-shared-system-certificates) +## Fortgeschrittene Optionen (für docker compose) +### Hostman Script Umgebugsvariablen +- `DOCKER_SOCK_PATH` + - DEFAULT: `"/tmp/docker.sock"` + - DESCRIPTION: Docker Socket Pfad +- `NETWORK_NAME` + - DEFAULT: `"proxy"` + - DESCRIPTION: Docker netzwerk in dem nach Containern gesucht wird + +### Template Spezifische Umgebungsvariablen +#### Caddy +- `DOCKER_CADDY_NAME` + - DEFAULT: `proxy` + - DESCRIPTION: Der Caddy Container-/Host-Name unter dem das Caddy Admin Interface erreichbar ist. +- `DOCKER_CADDY_PORT` + - DEFAULT: `2020` + - DESCRIPTION: Der Port des Caddy Admin Interfaces. + +#### Hosts +- `HOST_CONF_PATH` + - DEFAULT: `"/config/hosts"` + - DESCRIPTION: hosts Datei-Pfad +- `RESOLVE_DOCKERHOST` + - DEFAULT: `false` + - DESCRIPTION: Setzt ob IP Addressen in der hosts Datei auf die der Docker Container (true) oder 127.0.0.1 (false) aufgelöst werden. +- `DOCKER_HOSTNAME_VAR` + - DEFAULT: `"LOCAL_WEB_HOST"` (entspricht: `local.web.host`) + - DESCRIPTION: Docker Container Umgebungsvariable die den Hostnamen bestimmt (das auto generierte Öabel ist lower case und verwendet Punkte statt Unterstriche) + +#### SQLProxy +- `EXCLUDE_USERPASS` + - DEFAULT: `false` + - DESCRIPTION: Ob Nutzername und Passwort für die Authentifizierung ausgelassen werden soll. -## SQL Proxy -### Initiales Setup -Öffne ein Terminal und führe die `sqlproxy_setup.sh` Datei aus. -Das Script: -1. Installiert den `myssh` befehl nach `$HOME/bin` und fügt diesen ggf zur PATH variable hinzu -2. Erstellt SSH Keys für den SSH Docker Container (wenn diese nicht bereits existieren) -3. Erstellt und Konfiguriert einen Client SSH Key, dessen public Key wird dem SSH Docker Container hinzugefügt - -### Starten des Reverse-Proxies -Der SQL Proxy kann über docker compose gestartet werden -```bash -docker compose -f docker-compose.yml -f docker-compose-sqlproxy.yml up -d -``` - -### Hinzufügen von DB Docker Containern -Container werden automatisch vom reverse proxy/host manager aufgegriffen wenn sie: -1. Die Umgebungsvariable `local.db.type` und `local.db.host` gesetzt haben. -2. Im gleichen Docker-Netzwek (Default: `proxy`) sind. - -Optional kann über das Label `local.web.port` der gebundene Port gesetzt werden (Default mysql: 3306, Default psql: 5432) - -### myssh cli +# myssh cli Der SQL Proxy Client hat folgende Optionen: ```bash ls: Gibt eine Liste an verfügbaren DB Hosts zurück @@ -96,31 +138,11 @@ connect $DB_HOST [-u $USERNAME ] [-p $PASSWORD]: Erstellt einen Tunnel zum DB Ho disconnect: schließt die SSH Multiplex Session und damit auch alle aktuellen Verbindungen ``` -#### myssh Umgebungsvariablen +## myssh Umgebungsvariablen - `SQL_PROXY_HOST` - DEFAULT: `"localhost"` - DESCRIPTION: Setzt den Target Proxy Host -- `SQL_PROXY_DB_PORT` - - DEFAULT: sql: `"3306"`, psql: `"5432"` - - DESCRIPTION: Setzt den DB Host Target Port -- `SQL_CLI_TEMPLATE` +- (DEPRECATED) `SQL_CLI_TEMPLATE` - DEFAULT LINUX: `'mysql --protocol=TCP -u $MYSQL_USERNAME -p$MYSQL_PASSWORD -h localhost -P 3306'` - DEFAULT MACOS: `'open \"mysql://$MYSQL_USERNAME:$MYSQL_PASSWORD@localhost:3306\" -a \"Sequel Ace\"'` - DESCRIPTION: Setzt den auszuführenden Datenbank-Client Befehl - -## Hostman Umgebugsvariablen (für docker compose) -- `DOCKER_SOCK_PATH` - - DEFAULT: `"/tmp/docker.sock"` - - DESCRIPTION: Docker Socket Pfad -- `NETWORK_NAME` - - DEFAULT: `"proxy"` - - DESCRIPTION: Docker netzwerk in dem nach Containern gesucht wird -- `RESOLVE_DOCKERHOST` - - DEFAULT: `false` - - DESCRIPTION: Setzt ob IP Addressen in der hosts Datei auf die der Docker Container (true) oder 127.0.0.1 (false) aufgelöst werden. -- `HOST_CONF_PATH` - - DEFAULT: `"/tmp/hosts"` - - DESCRIPTION: hosts Datei-Pfad -- `DOCKER_HOSTNAME_VAR` - - DEFAULT: `"VIRTUAL_HOST"` - - DESCRIPTION: Docker Container Umgebungsvariable die den Hostnamen bestimmt From f77e32f7eedd0e8c3c7929fa4b5253a08037825b Mon Sep 17 00:00:00 2001 From: Kevin Baensch Date: Fri, 6 Jan 2023 09:19:59 +0100 Subject: [PATCH 18/24] Fix missing newline when adding new authorized ssh keys. --- setup.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.sh b/setup.sh index 217dd8d..6cadc98 100755 --- a/setup.sh +++ b/setup.sh @@ -101,7 +101,7 @@ setup_sqlproxy() { PUB_KEY="$(cat ${HOME}/.ssh/${KEY_NAME}.pub)" if ! grep -qe "$(printf '%s$' "${PUB_KEY}")" "${PROJECT_PATH}/etc/ssh/.ssh/authorized_keys" then - printf 'command="/sqlproxy_cli.sh" %s' "${PUB_KEY}" >> "${PROJECT_PATH}/etc/ssh/.ssh/authorized_keys" + printf 'command="/sqlproxy_cli.sh" %s\n' "${PUB_KEY}" >> "${PROJECT_PATH}/etc/ssh/.ssh/authorized_keys" fi printf 'Restarting sql proxy (if running) to fix permissions.\n' From f257be13782c9adc7ce2c0eba0611d56b5945f8f Mon Sep 17 00:00:00 2001 From: Kevin Baensch Date: Fri, 6 Jan 2023 09:32:29 +0100 Subject: [PATCH 19/24] Preventing overlap with existing ssh configuration hosts. --- script/myssh | 15 ++++++++------- setup.sh | 6 +++--- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/script/myssh b/script/myssh index b656022..1880766 100755 --- a/script/myssh +++ b/script/myssh @@ -1,6 +1,7 @@ #!/usr/bin/env bash set -e SQL_PROXY_HOST="${SQL_PROXY_HOST:-localhost}" +SSH_SQL_PROXY_HOST="sqlproxy.${SQL_PROXY_HOST}" CONNECTION_CACHE="$HOME/.cache/sqlproxy_${SQL_PROXY_HOST}" HELP="Usage: myssh [ls|connect]\n @@ -33,7 +34,7 @@ get_template_string() { } ssh_status() { - ssh -O check -S "${HOME}/.ssh/controlmasters/%r@%h:%p" "${SQL_PROXY_HOST}" > /dev/null 2>&1 + ssh -O check -S "${HOME}/.ssh/controlmasters/%r@%h:%p" "${SSH_SQL_PROXY_HOST}" > /dev/null 2>&1 } connect() { @@ -42,14 +43,14 @@ connect() { if ! ssh_status then echo "" > "${CONNECTION_CACHE}" - ssh -o "ControlPersist=10m" -M -S "${HOME}/.ssh/controlmasters/%r@%h:%p" "${SQL_PROXY_HOST}" q + ssh -o "ControlPersist=10m" -M -S "${HOME}/.ssh/controlmasters/%r@%h:%p" "${SSH_SQL_PROXY_HOST}" q fi } disconnect() { if ssh_status then - ssh -O stop -S "${HOME}/.ssh/controlmasters/%r@%h:%p" "${SQL_PROXY_HOST}" q + ssh -O stop -S "${HOME}/.ssh/controlmasters/%r@%h:%p" "${SSH_SQL_PROXY_HOST}" q fi } @@ -63,15 +64,15 @@ port_forward() { then if [ -n "${ACTIVE_HOST}" ] then - ssh -O cancel -L "6033:${ACTIVE_HOST}" -S "${HOME}/.ssh/controlmasters/%r@%h:%p" "${SQL_PROXY_HOST}" q + ssh -O cancel -L "6033:${ACTIVE_HOST}" -S "${HOME}/.ssh/controlmasters/%r@%h:%p" "${SSH_SQL_PROXY_HOST}" q fi - ssh -O forward -L "6033:$1:$2" -S "${HOME}/.ssh/controlmasters/%r@%h:%p" "${SQL_PROXY_HOST}" + ssh -O forward -L "6033:$1:$2" -S "${HOME}/.ssh/controlmasters/%r@%h:%p" "${SSH_SQL_PROXY_HOST}" fi echo "$1:$2" > "${CONNECTION_CACHE}" } ls_hosts() { - ssh -S "${HOME}/.ssh/controlmasters/%r@%h:%p" "${SQL_PROXY_HOST}" ls + ssh -S "${HOME}/.ssh/controlmasters/%r@%h:%p" "${SSH_SQL_PROXY_HOST}" ls } get_host() { @@ -80,7 +81,7 @@ get_host() { printf 'Please specify the host to connect to.\nRun "myssh ls" to list all available hosts.\n' exit 1 else - TARGET_HOST_DATA=$(ssh -S "${HOME}/.ssh/controlmasters/%r@%h:%p" "${SQL_PROXY_HOST}" "get $1") + TARGET_HOST_DATA=$(ssh -S "${HOME}/.ssh/controlmasters/%r@%h:%p" "${SSH_SQL_PROXY_HOST}" "get $1") if [ "${TARGET_HOST_DATA}" = '' ] then printf 'No such host: "%s"\n' "$1" diff --git a/setup.sh b/setup.sh index 6cadc98..cdb98f6 100755 --- a/setup.sh +++ b/setup.sh @@ -89,12 +89,12 @@ setup_sqlproxy() { HOST_NAME="${HOST_NAME:-localhost}" # Check if there is an entry for $HOST_NAME in the users ssh config - if ! grep -qe "$(printf '^Host %s$' "${HOST_NAME}")" "${HOME}/.ssh/config" 2>/dev/null + if ! grep -qe "$(printf '^Host %s$' "sqlproxy.${HOST_NAME}")" "${HOME}/.ssh/config" 2>/dev/null then - printf '\nHost %s\n Port 3022\n User sqlproxy\n IdentityFile ~/.ssh/%s' "${HOST_NAME}" "${KEY_NAME}" >> "${HOME}/.ssh/config" + printf '\nHost sqlproxy.%s\n HostName %s\n Port 3022\n User sqlproxy\n IdentityFile ~/.ssh/%s' "${HOST_NAME}" "${HOST_NAME}" "${KEY_NAME}" >> "${HOME}/.ssh/config" else printf 'User ssh configuration located in "%s" already has a configuration for host "%s".\nMake sure your configuration matches the following:\n' "${HOME}/.ssh/config" "${HOST_NAME}" - printf '"""\nHost %s\n Port 3022\n User sqlproxy\n IdentityFile ~/.ssh/%s\n"""\n' "${HOST_NAME}" "${KEY_NAME}" + printf '"""\nHost sqlproxy.%s\n HostName %s\n Port 3022\n User sqlproxy\n IdentityFile ~/.ssh/%s\n"""\n' "${HOST_NAME}" "${HOST_NAME}" "${KEY_NAME}" fi # Check if public key is already in the containers authorized_keys file From cb040df48b0c5121e229ca331263a7f4ac53ee42 Mon Sep 17 00:00:00 2001 From: Kevin Baensch Date: Fri, 6 Jan 2023 09:36:27 +0100 Subject: [PATCH 20/24] Always restart sshd after setup. --- setup.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/setup.sh b/setup.sh index cdb98f6..1e625d1 100755 --- a/setup.sh +++ b/setup.sh @@ -103,9 +103,6 @@ setup_sqlproxy() { then printf 'command="/sqlproxy_cli.sh" %s\n' "${PUB_KEY}" >> "${PROJECT_PATH}/etc/ssh/.ssh/authorized_keys" fi - - printf 'Restarting sql proxy (if running) to fix permissions.\n' - docker compose --project-directory "${PROJECT_PATH}" -f "${PROJECT_PATH}/docker-compose.yml" restart sshd else printf 'Not generating client ssh key.\nPlease put your desired public keys into %s\nAlso add %s in front of your key\n' "${PROJECT_PATH}/etc/ssh/.ssh/authorized_keys" "'command=\"/sqlproxy_cli.sh\" '" fi @@ -122,3 +119,6 @@ if check 'Configure sql proxy? [Y/n] ' then setup_sqlproxy fi + +printf 'Restarting sql proxy (if running) to fix permissions.\n' +docker compose --project-directory "${PROJECT_PATH}" -f "${PROJECT_PATH}/docker-compose.yml" restart sshd From 69419a8f9c591be49543f78bb5ca561f89ed17bd Mon Sep 17 00:00:00 2001 From: Kevin Baensch Date: Fri, 6 Jan 2023 14:54:36 +0100 Subject: [PATCH 21/24] Myssh: make ssh not slurp stdin. --- script/myssh | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/script/myssh b/script/myssh index 1880766..703a8fa 100755 --- a/script/myssh +++ b/script/myssh @@ -34,7 +34,7 @@ get_template_string() { } ssh_status() { - ssh -O check -S "${HOME}/.ssh/controlmasters/%r@%h:%p" "${SSH_SQL_PROXY_HOST}" > /dev/null 2>&1 + ssh -n -O check -S "${HOME}/.ssh/controlmasters/%r@%h:%p" "${SSH_SQL_PROXY_HOST}" > /dev/null 2>&1 } connect() { @@ -43,14 +43,14 @@ connect() { if ! ssh_status then echo "" > "${CONNECTION_CACHE}" - ssh -o "ControlPersist=10m" -M -S "${HOME}/.ssh/controlmasters/%r@%h:%p" "${SSH_SQL_PROXY_HOST}" q + ssh -n -o "ControlPersist=10m" -M -S "${HOME}/.ssh/controlmasters/%r@%h:%p" "${SSH_SQL_PROXY_HOST}" q fi } disconnect() { if ssh_status then - ssh -O stop -S "${HOME}/.ssh/controlmasters/%r@%h:%p" "${SSH_SQL_PROXY_HOST}" q + ssh -n -O stop -S "${HOME}/.ssh/controlmasters/%r@%h:%p" "${SSH_SQL_PROXY_HOST}" q fi } @@ -64,15 +64,15 @@ port_forward() { then if [ -n "${ACTIVE_HOST}" ] then - ssh -O cancel -L "6033:${ACTIVE_HOST}" -S "${HOME}/.ssh/controlmasters/%r@%h:%p" "${SSH_SQL_PROXY_HOST}" q + ssh -n -O cancel -L "6033:${ACTIVE_HOST}" -S "${HOME}/.ssh/controlmasters/%r@%h:%p" "${SSH_SQL_PROXY_HOST}" q fi - ssh -O forward -L "6033:$1:$2" -S "${HOME}/.ssh/controlmasters/%r@%h:%p" "${SSH_SQL_PROXY_HOST}" + ssh -n -O forward -L "6033:$1:$2" -S "${HOME}/.ssh/controlmasters/%r@%h:%p" "${SSH_SQL_PROXY_HOST}" fi echo "$1:$2" > "${CONNECTION_CACHE}" } ls_hosts() { - ssh -S "${HOME}/.ssh/controlmasters/%r@%h:%p" "${SSH_SQL_PROXY_HOST}" ls + ssh -n -S "${HOME}/.ssh/controlmasters/%r@%h:%p" "${SSH_SQL_PROXY_HOST}" ls } get_host() { @@ -81,7 +81,7 @@ get_host() { printf 'Please specify the host to connect to.\nRun "myssh ls" to list all available hosts.\n' exit 1 else - TARGET_HOST_DATA=$(ssh -S "${HOME}/.ssh/controlmasters/%r@%h:%p" "${SSH_SQL_PROXY_HOST}" "get $1") + TARGET_HOST_DATA=$(ssh -n -S "${HOME}/.ssh/controlmasters/%r@%h:%p" "${SSH_SQL_PROXY_HOST}" "get $1") if [ "${TARGET_HOST_DATA}" = '' ] then printf 'No such host: "%s"\n' "$1" From a24298219e70e311c32eadba7164727c4290bd78 Mon Sep 17 00:00:00 2001 From: Kevin Baensch Date: Mon, 9 Jan 2023 10:41:41 +0100 Subject: [PATCH 22/24] MacOS default group is not user name. --- setup.sh | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/setup.sh b/setup.sh index 1e625d1..b5d4e6f 100755 --- a/setup.sh +++ b/setup.sh @@ -2,6 +2,12 @@ set -e PROJECT_PATH=$(dirname $0) WHOAMI="$(id -un)" +if [ "$(uname -s)" = "Linux" ] +then + MYGROUP="${WHOAMI}" +else + MYGROUP="staff" +fi if [ $(id -u) -eq 0 ] then @@ -20,10 +26,10 @@ check() { setup_base() { printf 'Change ownership of "%s" to "%s"? (setup may fail otherwise)\n' "${PROJECT_PATH}" "${WHOAMI}" - printf 'running: "sudo chown -R %s %s"\n' "${WHOAMI}:${WHOAMI}" "${PROJECT_PATH}" + printf 'running: "sudo chown -R %s %s"\n' "${WHOAMI}:${MYGROUP}" "${PROJECT_PATH}" if check 'Continue? [Y/n] ' then - sudo chown -R "${WHOAMI}:${WHOAMI}" "${PROJECT_PATH}" + sudo chown -R "${WHOAMI}:${MYGROUP}" "${PROJECT_PATH}" fi From 58ce939c4055764cfd4ae30112c8e547c92a98ae Mon Sep 17 00:00:00 2001 From: Kevin Baensch Date: Wed, 18 Jan 2023 09:24:05 +0100 Subject: [PATCH 23/24] Fix Container Diffing --- script/hostman.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/script/hostman.sh b/script/hostman.sh index 971f593..f7dd1c8 100755 --- a/script/hostman.sh +++ b/script/hostman.sh @@ -17,7 +17,7 @@ query_docker () { update_templates() { CONTAINER_LIST=$(query_docker "containers/json" "?filters={%22network%22:[%22${NETWORK_NAME}%22],%22status%22:[%22running%22]}") - LABELS_NEW="$(echo "${CONTAINER_LIST}" | jq '.[].Labels | @base64' | sort)" + LABELS_NEW="$(echo "${CONTAINER_LIST}" | jq '.[].Labels | to_entries | map(select(.key | startswith("local.")) | .key + .value) | sort | .[] | @base64')" if [ "${LABELS_NEW}" != "${LABELS_OLD}" ] then if [ -n "${LABELS_OLD}" ] From f1c2a7dbb322a0e2de0cc689638e7d1076f534c4 Mon Sep 17 00:00:00 2001 From: Kevin Baensch Date: Wed, 18 Jan 2023 10:12:23 +0100 Subject: [PATCH 24/24] Update docker event query to filter events. --- script/hostman.sh | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/script/hostman.sh b/script/hostman.sh index f7dd1c8..0da4819 100755 --- a/script/hostman.sh +++ b/script/hostman.sh @@ -38,16 +38,16 @@ update_templates() { # Initial Generation update_templates -# cannot filter because reailine no longer recognized lines otherwise (check how IFS changes) -query_docker "events" | while true +LAST_CHECK="$(date +%s)" +while true do + EVENTS="" printf '%s | Listening for docker events\n' "$(get_date)" - read -r; - # wait for related events to finish - printf '%s | Waiting for event messages to settle.\n' "$(get_date)" - while [ $? -eq 0 ] + while [ -z "${EVENTS}" ] do - read -t 8 -r + NEXT_CHECK="$(($(date +%s) + 5))" + EVENTS=$(query_docker 'events' "?since=${LAST_CHECK}&until=${NEXT_CHECK}&filters={%22event%22:[%22die%22,%22kill%22,%22oom%22,%22pause%22,%22restart%22,%22start%22,%22stop%22,%22unpause%22,%22update%22]}") + LAST_CHECK="${NEXT_CHECK}" done printf '%s | Checking for changes in container label list.\n' "$(get_date)" update_templates