From 076c61a4d8b6bd43b4e0eab0fcad9c44b4e9da94 Mon Sep 17 00:00:00 2001 From: fortuna Date: Thu, 15 Nov 2018 19:06:45 -0500 Subject: [PATCH 1/7] Restrict access to outline files --- .../install_scripts/install_server.sh | 73 +++++++++++-------- 1 file changed, 41 insertions(+), 32 deletions(-) diff --git a/src/server_manager/install_scripts/install_server.sh b/src/server_manager/install_scripts/install_server.sh index ed4dba63..2b3a24d6 100755 --- a/src/server_manager/install_scripts/install_server.sh +++ b/src/server_manager/install_scripts/install_server.sh @@ -35,6 +35,7 @@ set -euo pipefail readonly SENTRY_LOG_FILE=${SENTRY_LOG_FILE:-} +OUTLINE_USER=${OUTLINE_USER:-outline} function log_error() { local -r ERROR_TEXT="\033[0;31m" # red @@ -89,6 +90,20 @@ function log_for_sentry() { fi } +function create_outline_user() { + if id -u $OUTLINE_USER &> /dev/null; then + log_error "ALREADY EXISTS" + echo -n + if ! confirm "> Would you like to use user $OUTLINE_USER to run the Outline server? [Y/n] "; then + log_error "Please re-run the script with the OUTLINE_USER variable set to the user you would like to use" + exit 1 + fi + fi + if ! id -u $OUTLINE_USER &> /dev/null; then + useradd --system --user-group --groups docker $OUTLINE_USER + fi +} + # Check to see if docker is installed. function verify_docker_installed() { if command_exists docker; then @@ -109,7 +124,7 @@ function verify_docker_installed() { function verify_docker_running() { local readonly STDERR_OUTPUT - STDERR_OUTPUT=$($DOCKER_CMD info 2>&1 >/dev/null) + STDERR_OUTPUT=$(docker info 2>&1 >/dev/null) local readonly RET=$? if [[ $RET -eq 0 ]]; then return 0 @@ -119,15 +134,16 @@ function verify_docker_running() { } function verify_docker_permissions() { - if user_in_docker_group; then + readonly docker_user=$1 + if user_in_docker_group $docker_user; then return 0 fi log_error "FAILED" - local readonly PROMPT="> It seems like you may not have permission to run Docker. To solve this, we will attempt to add your user to the docker group by running 'sudo usermod -a -G docker $USER'. Would you like to proceed? [Y/n] " + local readonly PROMPT="> It seems like user $docker_user may not have permission to run Docker. To solve this, we will attempt to add your user to the docker group by running 'usermod -a -G docker $OUTLINE_USER'. Would you like to proceed? [Y/n] " if ! confirm "$PROMPT"; then exit 0 fi - if run_step "Adding $USER to docker group" add_user_to_docker_group; then + if run_step "Adding $docker_user to docker group" add_user_to_docker_group $docker_user; then echo -n "> Docker ready................................. " else log_error "FAILED" @@ -143,35 +159,21 @@ function user_in_docker_group() { # Assume root has docker access; test with $UID because it's one of the few # environment variables available in DigitalOcean's CloudInit environment. if [[ $UID -ne 0 ]]; then - groups $USER | grep -E '(^|\s)docker(\s|$)' > /dev/null 2>&1 + groups $1 | grep -E '(^|\s)docker(\s|$)' > /dev/null 2>&1 fi } function add_user_to_docker_group() { - sudo usermod -a -G docker $USER + usermod -a -G docker "$1" } function start_docker() { - sudo systemctl start docker.service > /dev/null 2>&1 - sudo systemctl enable docker.service > /dev/null 2>&1 + systemctl start docker.service > /dev/null 2>&1 + systemctl enable docker.service > /dev/null 2>&1 } -# If not running as root then run docker with sg to ensure the command has -# membership of the docker group, membership of which may only have been -# acquired during the session. Otherwise, don't do anything fancy with -# positional parameters because they're a nightmare to get right with -# subshells in both regular and the DigitalOcean CloudInit environment. -# See also comments for user_in_docker_group. -DOCKER_CMD=docker -if [[ $UID -ne 0 ]]; then - function safe_docker() { - sg docker -c "docker $*" - } - DOCKER_CMD=safe_docker -fi - function docker_container_exists() { - $DOCKER_CMD ps | grep $1 >/dev/null 2>&1 + docker ps | grep $1 >/dev/null 2>&1 } function remove_shadowbox_container() { @@ -183,7 +185,7 @@ function remove_watchtower_container() { } function remove_docker_container() { - $DOCKER_CMD rm -f $1 + docker rm -f $1 } function handle_docker_container_conflict() { @@ -229,7 +231,8 @@ function get_random_port { function create_persisted_state_dir() { readonly STATE_DIR="$SHADOWBOX_DIR/persisted-state" - mkdir -p "${STATE_DIR}" + mkdir -p --mode=770 "${STATE_DIR}" + chmod ug+s "${STATE_DIR}" } # Generate a secret key for access to the shadowbox API and store it in a tag. @@ -286,7 +289,7 @@ function start_shadowbox() { ) # By itself, local messes up the return code. local readonly STDERR_OUTPUT - STDERR_OUTPUT=$($DOCKER_CMD run -d "${docker_shadowbox_flags[@]}" ${SB_IMAGE} 2>&1 >/dev/null) + STDERR_OUTPUT=$(docker run -d "${docker_shadowbox_flags[@]}" ${SB_IMAGE} 2>&1 >/dev/null) local readonly RET=$? if [[ $RET -eq 0 ]]; then return 0 @@ -309,7 +312,7 @@ function start_watchtower() { docker_watchtower_flags+=(-v /var/run/docker.sock:/var/run/docker.sock) # By itself, local messes up the return code. local readonly STDERR_OUTPUT - STDERR_OUTPUT=$($DOCKER_CMD run -d "${docker_watchtower_flags[@]}" v2tec/watchtower --cleanup --tlsverify --interval $WATCHTOWER_REFRESH_SECONDS 2>&1 >/dev/null) + STDERR_OUTPUT=$(docker run -d "${docker_watchtower_flags[@]}" v2tec/watchtower --cleanup --tlsverify --interval $WATCHTOWER_REFRESH_SECONDS 2>&1 >/dev/null) local readonly RET=$? if [[ $RET -eq 0 ]]; then return 0 @@ -344,7 +347,7 @@ function add_api_url_to_config() { function check_firewall() { local readonly GET_ACCESS_KEYS=$(curl --insecure -s ${LOCAL_API_URL}/access-keys) - local readonly GET_ACCESS_KEY_PORT="$DOCKER_CMD exec shadowbox node -e 'console.log($GET_ACCESS_KEYS[\"accessKeys\"][0][\"port\"])'" + local readonly GET_ACCESS_KEY_PORT="docker exec shadowbox node -e 'console.log($GET_ACCESS_KEYS[\"accessKeys\"][0][\"port\"])'" local -r ACCESS_KEY_PORT=$($GET_ACCESS_KEY_PORT) if ! curl --max-time 5 --cacert "${SB_CERTIFICATE_FILE}" -s "${PUBLIC_API_URL}/access-keys" >/dev/null; then log_error "BLOCKED" @@ -376,13 +379,19 @@ blocks inbound connections, even though your machine seems to allow them. } install_shadowbox() { + # Make sure we don't leak readable files to other users. + umask 0007 + run_step "Verifying that Docker is installed" verify_docker_installed - run_step "Verifying Docker permissions" verify_docker_permissions + run_step "Creating outline user" create_outline_user + run_step "Verifying Docker permissions" verify_docker_permissions $USER run_step "Verifying that Docker daemon is running" verify_docker_running - log_for_sentry "Creating shadowbox directory" - export SHADOWBOX_DIR="${SHADOWBOX_DIR:-${HOME:-/root}/shadowbox}" - mkdir -p $SHADOWBOX_DIR + log_for_sentry "Creating Outline directory" + export SHADOWBOX_DIR="${SHADOWBOX_DIR:-/var/lib/outline}" + mkdir -p --mode=770 $SHADOWBOX_DIR + chmod ug+s $SHADOWBOX_DIR + chown :$OUTLINE_USER $SHADOWBOX_DIR log_for_sentry "Setting API port" readonly SB_API_PORT="${SB_API_PORT:-$(get_random_port)}" From fe510062817694af59e5e01f7313670f5b8b6edd Mon Sep 17 00:00:00 2001 From: fortuna Date: Thu, 15 Nov 2018 19:47:46 -0500 Subject: [PATCH 2/7] Remove outline user --- .../install_scripts/install_server.sh | 63 +++---------------- 1 file changed, 9 insertions(+), 54 deletions(-) diff --git a/src/server_manager/install_scripts/install_server.sh b/src/server_manager/install_scripts/install_server.sh index 2b3a24d6..2f1840da 100755 --- a/src/server_manager/install_scripts/install_server.sh +++ b/src/server_manager/install_scripts/install_server.sh @@ -35,7 +35,6 @@ set -euo pipefail readonly SENTRY_LOG_FILE=${SENTRY_LOG_FILE:-} -OUTLINE_USER=${OUTLINE_USER:-outline} function log_error() { local -r ERROR_TEXT="\033[0;31m" # red @@ -90,20 +89,6 @@ function log_for_sentry() { fi } -function create_outline_user() { - if id -u $OUTLINE_USER &> /dev/null; then - log_error "ALREADY EXISTS" - echo -n - if ! confirm "> Would you like to use user $OUTLINE_USER to run the Outline server? [Y/n] "; then - log_error "Please re-run the script with the OUTLINE_USER variable set to the user you would like to use" - exit 1 - fi - fi - if ! id -u $OUTLINE_USER &> /dev/null; then - useradd --system --user-group --groups docker $OUTLINE_USER - fi -} - # Check to see if docker is installed. function verify_docker_installed() { if command_exists docker; then @@ -133,40 +118,10 @@ function verify_docker_running() { fi } -function verify_docker_permissions() { - readonly docker_user=$1 - if user_in_docker_group $docker_user; then - return 0 - fi - log_error "FAILED" - local readonly PROMPT="> It seems like user $docker_user may not have permission to run Docker. To solve this, we will attempt to add your user to the docker group by running 'usermod -a -G docker $OUTLINE_USER'. Would you like to proceed? [Y/n] " - if ! confirm "$PROMPT"; then - exit 0 - fi - if run_step "Adding $docker_user to docker group" add_user_to_docker_group $docker_user; then - echo -n "> Docker ready................................. " - else - log_error "FAILED" - return 1 - fi -} - function install_docker() { curl -sS https://get.docker.com/ | sh > /dev/null 2>&1 } -function user_in_docker_group() { - # Assume root has docker access; test with $UID because it's one of the few - # environment variables available in DigitalOcean's CloudInit environment. - if [[ $UID -ne 0 ]]; then - groups $1 | grep -E '(^|\s)docker(\s|$)' > /dev/null 2>&1 - fi -} - -function add_user_to_docker_group() { - usermod -a -G docker "$1" -} - function start_docker() { systemctl start docker.service > /dev/null 2>&1 systemctl enable docker.service > /dev/null 2>&1 @@ -185,7 +140,7 @@ function remove_watchtower_container() { } function remove_docker_container() { - docker rm -f $1 + docker rm -f $1 > /dev/null } function handle_docker_container_conflict() { @@ -232,7 +187,7 @@ function get_random_port { function create_persisted_state_dir() { readonly STATE_DIR="$SHADOWBOX_DIR/persisted-state" mkdir -p --mode=770 "${STATE_DIR}" - chmod ug+s "${STATE_DIR}" + chmod g+s "${STATE_DIR}" } # Generate a secret key for access to the shadowbox API and store it in a tag. @@ -346,9 +301,12 @@ function add_api_url_to_config() { } function check_firewall() { - local readonly GET_ACCESS_KEYS=$(curl --insecure -s ${LOCAL_API_URL}/access-keys) - local readonly GET_ACCESS_KEY_PORT="docker exec shadowbox node -e 'console.log($GET_ACCESS_KEYS[\"accessKeys\"][0][\"port\"])'" - local -r ACCESS_KEY_PORT=$($GET_ACCESS_KEY_PORT) + local readonly ACCESS_KEY_PORT=$(curl --insecure -s ${LOCAL_API_URL}/access-keys | + docker exec -i shadowbox node -e ' + const fs = require("fs"); + const accessKeys = JSON.parse(fs.readFileSync(0, {encoding: "utf-8"})); + console.log(accessKeys["accessKeys"][0]["port"]); + ') if ! curl --max-time 5 --cacert "${SB_CERTIFICATE_FILE}" -s "${PUBLIC_API_URL}/access-keys" >/dev/null; then log_error "BLOCKED" FIREWALL_STATUS="\ @@ -383,15 +341,12 @@ install_shadowbox() { umask 0007 run_step "Verifying that Docker is installed" verify_docker_installed - run_step "Creating outline user" create_outline_user - run_step "Verifying Docker permissions" verify_docker_permissions $USER run_step "Verifying that Docker daemon is running" verify_docker_running log_for_sentry "Creating Outline directory" export SHADOWBOX_DIR="${SHADOWBOX_DIR:-/var/lib/outline}" mkdir -p --mode=770 $SHADOWBOX_DIR - chmod ug+s $SHADOWBOX_DIR - chown :$OUTLINE_USER $SHADOWBOX_DIR + chmod u+s $SHADOWBOX_DIR log_for_sentry "Setting API port" readonly SB_API_PORT="${SB_API_PORT:-$(get_random_port)}" From 69188e45817e61219ade97465de5c3899a79310e Mon Sep 17 00:00:00 2001 From: fortuna Date: Thu, 15 Nov 2018 19:53:43 -0500 Subject: [PATCH 3/7] Update documentation --- src/shadowbox/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/shadowbox/README.md b/src/shadowbox/README.md index 20d5676f..bff2d8f2 100644 --- a/src/shadowbox/README.md +++ b/src/shadowbox/README.md @@ -11,7 +11,7 @@ client apps. Shadowbox is also compatible with standard Shadowsocks clients. To install and run Shadowbox on your own server, run ``` -bash -c "$(wget -qO- https://raw.githubusercontent.com/Jigsaw-Code/outline-server/master/src/server_manager/install_scripts/install_server.sh)" +sudo bash -c "$(wget -qO- https://raw.githubusercontent.com/Jigsaw-Code/outline-server/master/src/server_manager/install_scripts/install_server.sh)" ``` Use `bash -x` instead at the end of the command if you need to debug the installation. From a62fcd29d0825640e7eabb9ba81ed1bc35de4687 Mon Sep 17 00:00:00 2001 From: fortuna Date: Thu, 15 Nov 2018 19:53:54 -0500 Subject: [PATCH 4/7] Update instructions --- .../ui_components/outline-manual-server-entry.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/server_manager/ui_components/outline-manual-server-entry.html b/src/server_manager/ui_components/outline-manual-server-entry.html index 434173e8..efe8a9f0 100644 --- a/src/server_manager/ui_components/outline-manual-server-entry.html +++ b/src/server_manager/ui_components/outline-manual-server-entry.html @@ -234,7 +234,7 @@
Log into your server, and run this command.
-
bash -c "$(wget -qO- https://raw.githubusercontent.com/Jigsaw-Code/outline-server/master/src/server_manager/install_scripts/install_server.sh)"
+
sudo bash -c "$(wget -qO- https://raw.githubusercontent.com/Jigsaw-Code/outline-server/master/src/server_manager/install_scripts/install_server.sh)"
From 6107e485ca8746b370617424c5a0f9fb20715871 Mon Sep 17 00:00:00 2001 From: fortuna Date: Thu, 15 Nov 2018 20:03:11 -0500 Subject: [PATCH 5/7] Fix formatting --- src/server_manager/install_scripts/install_server.sh | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/server_manager/install_scripts/install_server.sh b/src/server_manager/install_scripts/install_server.sh index 2f1840da..2c20b40f 100755 --- a/src/server_manager/install_scripts/install_server.sh +++ b/src/server_manager/install_scripts/install_server.sh @@ -159,7 +159,7 @@ function handle_docker_container_conflict() { return 0 fi if run_step "Removing $CONTAINER_NAME container" remove_"$CONTAINER_NAME"_container ; then - echo -n "> Restarting $CONTAINER_NAME ........................ " + echo -n "> Restarting $CONTAINER_NAME ....................... " start_"$CONTAINER_NAME" return $? fi @@ -312,8 +312,7 @@ function check_firewall() { FIREWALL_STATUS="\ You won’t be able to access it externally, despite your server being correctly set up, because there's a firewall (in this machine, your router or cloud -provider) that is preventing incoming connections to ports ${SB_API_PORT} and -${ACCESS_KEY_PORT}. +provider) that is preventing incoming connections to ports ${SB_API_PORT} and ${ACCESS_KEY_PORT}. - If you plan to have a single access key to access your server, opening those ports for TCP and UDP should suffice. From 7d54d56902345cbe28692657485cf7fcbfff140b Mon Sep 17 00:00:00 2001 From: fortuna Date: Thu, 15 Nov 2018 20:06:15 -0500 Subject: [PATCH 6/7] Restore dot --- 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 2c20b40f..182463fe 100755 --- a/src/server_manager/install_scripts/install_server.sh +++ b/src/server_manager/install_scripts/install_server.sh @@ -159,7 +159,7 @@ function handle_docker_container_conflict() { return 0 fi if run_step "Removing $CONTAINER_NAME container" remove_"$CONTAINER_NAME"_container ; then - echo -n "> Restarting $CONTAINER_NAME ....................... " + echo -n "> Restarting $CONTAINER_NAME ........................ " start_"$CONTAINER_NAME" return $? fi From 3cf728ecd0701d990b1c58d516e5b7775715d75f Mon Sep 17 00:00:00 2001 From: fortuna Date: Fri, 16 Nov 2018 11:48:16 -0500 Subject: [PATCH 7/7] Use /opt instead of /var/lib --- 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 182463fe..d8b04a60 100755 --- a/src/server_manager/install_scripts/install_server.sh +++ b/src/server_manager/install_scripts/install_server.sh @@ -343,7 +343,7 @@ install_shadowbox() { run_step "Verifying that Docker daemon is running" verify_docker_running log_for_sentry "Creating Outline directory" - export SHADOWBOX_DIR="${SHADOWBOX_DIR:-/var/lib/outline}" + export SHADOWBOX_DIR="${SHADOWBOX_DIR:-/opt/outline}" mkdir -p --mode=770 $SHADOWBOX_DIR chmod u+s $SHADOWBOX_DIR