Centralize list of server files

This commit is contained in:
Vinicius Fortuna 2022-11-24 02:26:07 +00:00
parent 3698eb712e
commit 57861df5ff

View file

@ -51,8 +51,6 @@ Usage: install_server.sh [--hostname <hostname>] [--api-port <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