From 57861df5ffdcadc8cd10d1b434bf070ed1c69b77 Mon Sep 17 00:00:00 2001 From: Vinicius Fortuna Date: Thu, 24 Nov 2022 02:26:07 +0000 Subject: [PATCH 1/4] Centralize list of server files --- .../install_scripts/install_server.sh | 72 +++++++++++-------- 1 file changed, 41 insertions(+), 31 deletions(-) diff --git a/src/server_manager/install_scripts/install_server.sh b/src/server_manager/install_scripts/install_server.sh index ff181885b..baa483491 100755 --- a/src/server_manager/install_scripts/install_server.sh +++ b/src/server_manager/install_scripts/install_server.sh @@ -51,8 +51,6 @@ Usage: install_server.sh [--hostname ] [--api-port ] [--keys-por EOF } -readonly SENTRY_LOG_FILE=${SENTRY_LOG_FILE:-} - # I/O conventions for this script: # - Ordinary status messages are printed to STDOUT # - STDERR is only used in the event of a fatal error @@ -123,6 +121,34 @@ function log_for_sentry() { echo "$@" >> "${FULL_LOG}" } +function setup_file_paths() { + # The base directory for all the files. + local -r OUTLINE_DIR="${SHADOWBOX_DIR:-/opt/outline}" + log_for_sentry "Creating Outline directory" + mkdir -p "${OUTLINE_DIR}" + chmod u+s,ug+rwx,o-rwx "${OUTLINE_DIR}" + + # Holds the information on how to access the management API. + declare -rg ACCESS_CONFIG_FILE="${ACCESS_CONFIG:-${BASE_DIR}/access.txt}" + + # Directory for data to be peristed across container restarts. Mounted on `docker run`. + declare -rg STATE_DIR="${OUTLINE_DIR}/persisted-state" + mkdir -p "${STATE_DIR}" + chmod g+s,ug+rwx,o-rwx "${STATE_DIR}" + + # The TLS certificate for the management API. + local -r CERTIFICATE_NAME="shadowbox-selfsigned" + declare -rg CERTIFICATE_FILE="${STATE_DIR}/${CERTIFICATE_NAME}.crt" + declare -rg PRIVATE_KEY_FILE="${STATE_DIR}/${CERTIFICATE_NAME}.key" + + # The server-level configuration (doesn't include the access keys). + declare -rg SERVER_CONFIG_FILE="${STATE_DIR}/shadowbox_server_config.json" + # NOTE: The access keys are in "${STATE_DIR}/shadowbox_config.json", a different file. + + # Used to report install failures to Sentry. Usually "${SHADOWBOX_DIR}/sentry-log-file.txt" + declare -rg SENTRY_LOG_FILE=${SENTRY_LOG_FILE:-} +} + # Check to see if docker is installed. function verify_docker_installed() { if command_exists docker; then @@ -233,12 +259,6 @@ function get_random_port { echo "${num}"; } -function create_persisted_state_dir() { - readonly STATE_DIR="${SHADOWBOX_DIR}/persisted-state" - mkdir -p "${STATE_DIR}" - chmod ug+rwx,g+s,o-rwx "${STATE_DIR}" -} - # Generate a secret key for access to the Management API and store it in a tag. # 16 bytes = 128 bits of entropy should be plenty for this use. function safe_base64() { @@ -259,13 +279,10 @@ function generate_secret_key() { function generate_certificate() { # Generate self-signed cert and store it in the persistent state directory. - local -r CERTIFICATE_NAME="${STATE_DIR}/shadowbox-selfsigned" - readonly SB_CERTIFICATE_FILE="${CERTIFICATE_NAME}.crt" - readonly SB_PRIVATE_KEY_FILE="${CERTIFICATE_NAME}.key" declare -a openssl_req_flags=( -x509 -nodes -days 36500 -newkey rsa:4096 -subj "/CN=${PUBLIC_HOSTNAME}" - -keyout "${SB_PRIVATE_KEY_FILE}" -out "${SB_CERTIFICATE_FILE}" + -keyout "${PRIVATE_KEY_FILE}" -out "${CERTIFICATE_FILE}" ) openssl req "${openssl_req_flags[@]}" >&2 } @@ -275,7 +292,7 @@ function generate_certificate_fingerprint() { # (Electron uses SHA-256 fingerprints: https://github.com/electron/electron/blob/9624bc140353b3771bd07c55371f6db65fd1b67e/atom/common/native_mate_converters/net_converter.cc#L60) # Example format: "SHA256 Fingerprint=BD:DB:C9:A4:39:5C:B3:4E:6E:CF:18:43:61:9F:07:A2:09:07:37:35:63:67" local CERT_OPENSSL_FINGERPRINT - CERT_OPENSSL_FINGERPRINT="$(openssl x509 -in "${SB_CERTIFICATE_FILE}" -noout -sha256 -fingerprint)" || return + CERT_OPENSSL_FINGERPRINT="$(openssl x509 -in "${CERTIFICATE_FILE}" -noout -sha256 -fingerprint)" || return # Example format: "BDDBC9A4395CB34E6ECF1843619F07A2090737356367" local CERT_HEX_FINGERPRINT CERT_HEX_FINGERPRINT="$(echo "${CERT_OPENSSL_FINGERPRINT#*=}" | tr -d :)" || return @@ -295,12 +312,10 @@ function write_config() { fi # printf is needed to escape the hostname. config+=("$(printf '"hostname": "%q"' "${PUBLIC_HOSTNAME}")") - echo "{$(join , "${config[@]}")}" > "${STATE_DIR}/shadowbox_server_config.json" + echo "{$(join , "${config[@]}")}" > "${SERVER_CONFIG_FILE}" } function start_shadowbox() { - # TODO(fortuna): Write API_PORT to config file, - # rather than pass in the environment. local -ar docker_shadowbox_flags=( --name "${CONTAINER_NAME}" --restart always --net host --label 'com.centurylinklabs.watchtower.enable=true' @@ -308,8 +323,8 @@ function start_shadowbox() { -e "SB_STATE_DIR=${STATE_DIR}" -e "SB_API_PORT=${API_PORT}" -e "SB_API_PREFIX=${SB_API_PREFIX}" - -e "SB_CERTIFICATE_FILE=${SB_CERTIFICATE_FILE}" - -e "SB_PRIVATE_KEY_FILE=${SB_PRIVATE_KEY_FILE}" + -e "SB_CERTIFICATE_FILE=${CERTIFICATE_FILE}" + -e "SB_PRIVATE_KEY_FILE=${PRIVATE_KEY_FILE}" -e "SB_METRICS_URL=${SB_METRICS_URL:-}" -e "SB_DEFAULT_SERVER_NAME=${SB_DEFAULT_SERVER_NAME:-}" ) @@ -360,7 +375,7 @@ function create_first_user() { } function output_config() { - echo "$@" >> "${ACCESS_CONFIG}" + echo "$@" >> "${ACCESS_CONFIG_FILE}" } function add_api_url_to_config() { @@ -424,23 +439,19 @@ install_shadowbox() { # Make sure we don't leak readable files to other users. umask 0007 + run_step "Setting up Outline directories" setup_file_paths + export CONTAINER_NAME="${CONTAINER_NAME:-shadowbox}" run_step "Verifying that Docker is installed" verify_docker_installed run_step "Verifying that Docker daemon is running" verify_docker_running - log_for_sentry "Creating Outline directory" - export SHADOWBOX_DIR="${SHADOWBOX_DIR:-/opt/outline}" - mkdir -p "${SHADOWBOX_DIR}" - chmod u+s,ug+rwx,o-rwx "${SHADOWBOX_DIR}" - log_for_sentry "Setting API port" API_PORT="${FLAGS_API_PORT}" if (( API_PORT == 0 )); then API_PORT=${SB_API_PORT:-$(get_random_port)} fi readonly API_PORT - readonly ACCESS_CONFIG="${ACCESS_CONFIG:-${SHADOWBOX_DIR}/access.txt}" readonly SB_IMAGE="${SB_IMAGE:-quay.io/outline/shadowbox:stable}" PUBLIC_HOSTNAME="${FLAGS_HOSTNAME:-${SB_PUBLIC_IP:-}}" @@ -449,16 +460,15 @@ install_shadowbox() { fi readonly PUBLIC_HOSTNAME - # If $ACCESS_CONFIG is already populated, make a backup before clearing it. - log_for_sentry "Initializing ACCESS_CONFIG" - if [[ -s "${ACCESS_CONFIG}" ]]; then + # If $ACCESS_CONFIG_FILE is already populated, make a backup before clearing it. + log_for_sentry "Initializing ACCESS_CONFIG_FILE" + if [[ -s "${ACCESS_CONFIG_FILE}" ]]; then # Note we can't do "mv" here as do_install_server.sh may already be tailing # this file. - cp "${ACCESS_CONFIG}" "${ACCESS_CONFIG}.bak" && true > "${ACCESS_CONFIG}" + cp "${ACCESS_CONFIG_FILE}" "${ACCESS_CONFIG_FILE}.bak" && true > "${ACCESS_CONFIG_FILE}" fi # Make a directory for persistent state - run_step "Creating persistent state dir" create_persisted_state_dir run_step "Generating secret key" generate_secret_key run_step "Generating TLS certificate" generate_certificate run_step "Generating SHA-256 certificate fingerprint" generate_certificate_fingerprint @@ -485,7 +495,7 @@ install_shadowbox() { # e.g. if ACCESS_CONFIG contains the line "certSha256:1234", # calling $(get_field_value certSha256) will echo 1234. function get_field_value { - grep "$1" "${ACCESS_CONFIG}" | sed "s/$1://" + grep "$1" "${ACCESS_CONFIG_FILE}" | sed "s/$1://" } # Output JSON. This relies on apiUrl and certSha256 (hex characters) requiring From ebac78962ea21c4d5287dd04af750c7c6cf1b9d7 Mon Sep 17 00:00:00 2001 From: Vinicius Fortuna Date: Thu, 24 Nov 2022 02:52:24 +0000 Subject: [PATCH 2/4] Fix Sentry call --- src/server_manager/install_scripts/install_server.sh | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/server_manager/install_scripts/install_server.sh b/src/server_manager/install_scripts/install_server.sh index baa483491..b4adeb148 100755 --- a/src/server_manager/install_scripts/install_server.sh +++ b/src/server_manager/install_scripts/install_server.sh @@ -115,7 +115,8 @@ function command_exists { } function log_for_sentry() { - if [[ -n "${SENTRY_LOG_FILE}" ]]; then + # Used to report install failures to Sentry. Usually "${SHADOWBOX_DIR}/sentry-log-file.txt" + if [[ -n "${SENTRY_LOG_FILE:-}" ]]; then echo "[$(date "+%Y-%m-%d@%H:%M:%S")] install_server.sh" "$@" >> "${SENTRY_LOG_FILE}" fi echo "$@" >> "${FULL_LOG}" @@ -144,9 +145,6 @@ function setup_file_paths() { # The server-level configuration (doesn't include the access keys). declare -rg SERVER_CONFIG_FILE="${STATE_DIR}/shadowbox_server_config.json" # NOTE: The access keys are in "${STATE_DIR}/shadowbox_config.json", a different file. - - # Used to report install failures to Sentry. Usually "${SHADOWBOX_DIR}/sentry-log-file.txt" - declare -rg SENTRY_LOG_FILE=${SENTRY_LOG_FILE:-} } # Check to see if docker is installed. From 4763950a91d3172308b7028ff02c091c7ffc054a Mon Sep 17 00:00:00 2001 From: Vinicius Fortuna Date: Thu, 24 Nov 2022 02:59:03 +0000 Subject: [PATCH 3/4] Fix BASE_DIR --- src/server_manager/install_scripts/install_server.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/server_manager/install_scripts/install_server.sh b/src/server_manager/install_scripts/install_server.sh index b4adeb148..04e111651 100755 --- a/src/server_manager/install_scripts/install_server.sh +++ b/src/server_manager/install_scripts/install_server.sh @@ -130,7 +130,7 @@ function setup_file_paths() { chmod u+s,ug+rwx,o-rwx "${OUTLINE_DIR}" # Holds the information on how to access the management API. - declare -rg ACCESS_CONFIG_FILE="${ACCESS_CONFIG:-${BASE_DIR}/access.txt}" + declare -rg ACCESS_CONFIG_FILE="${ACCESS_CONFIG:-${OUTLINE_DIR}/access.txt}" # Directory for data to be peristed across container restarts. Mounted on `docker run`. declare -rg STATE_DIR="${OUTLINE_DIR}/persisted-state" From cdaf6098494687b871b5f1c098b6861f1e75c6f5 Mon Sep 17 00:00:00 2001 From: Vinicius Fortuna Date: Mon, 28 Nov 2022 04:45:31 +0000 Subject: [PATCH 4/4] Fix CERTIFICATE_FILE --- src/server_manager/install_scripts/install_server.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/server_manager/install_scripts/install_server.sh b/src/server_manager/install_scripts/install_server.sh index 04e111651..64138db48 100755 --- a/src/server_manager/install_scripts/install_server.sh +++ b/src/server_manager/install_scripts/install_server.sh @@ -390,7 +390,7 @@ function check_firewall() { console.log(accessKeys["accessKeys"][0]["port"]); ') || return readonly ACCESS_KEY_PORT - if ! fetch --max-time 5 --cacert "${SB_CERTIFICATE_FILE}" "${PUBLIC_API_URL}/access-keys" >/dev/null; then + if ! fetch --max-time 5 --cacert "${CERTIFICATE_FILE}" "${PUBLIC_API_URL}/access-keys" >/dev/null; then log_error "BLOCKED" FIREWALL_STATUS="\ You won’t be able to access it externally, despite your server being correctly