Bitwarden's self-hosted release repository
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

315 lines
8.9 KiB

#!/usr/bin/env bash
set -e
cat << "EOF"
_ _ _ _
| |__ (_) |___ ____ _ _ __ __| | ___ _ __
| '_ \| | __\ \ /\ / / _` | '__/ _` |/ _ \ '_ \
| |_) | | |_ \ V V / (_| | | | (_| | __/ | | |
|_.__/|_|\__| \_/\_/ \__,_|_| \__,_|\___|_| |_|
EOF
cat << EOF
Open source password management solutions
Copyright 2015-$(date +'%Y'), Bitwarden, Inc.
https://bitwarden.com, https://github.com/bitwarden
===================================================
EOF
RED='\033[0;31m'
NC='\033[0m' # No Color
if [ "$EUID" -eq 0 ]; then
echo -e "${RED}WARNING: This script is running as the root user!"
echo -e "If you are running a standard deployment this script should be running as a dedicated Bitwarden User as per the documentation.${NC}"
read -p "Do you still want to continue? (y/n): " choice
# Check the user's choice
case "$choice" in
[Yy]|[Yy][Ee][Ss])
echo -e "Continuing...."
;;
*)
exit 1
;;
esac
fi
# Setup
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
SCRIPT_NAME=$(basename "$0")
SCRIPT_PATH="$DIR/$SCRIPT_NAME"
OUTPUT="$DIR/bwdata"
if [ $# -eq 2 ]
then
OUTPUT=$2
fi
if docker compose &> /dev/null; then
dccmd='docker compose'
elif command -v docker-compose &> /dev/null; then
dccmd='docker-compose'
echo "docker compose not found, falling back to docker-compose."
else
echo "Error: Neither 'docker compose' nor 'docker-compose' commands were found. Please install Docker Compose." >&2
exit 1
fi
SCRIPTS_DIR="$OUTPUT/scripts"
BITWARDEN_SCRIPT_URL="https://func.bitwarden.com/api/dl/?app=self-host&platform=linux"
RUN_SCRIPT_URL="https://func.bitwarden.com/api/dl/?app=self-host&platform=linux&variant=run"
# Please do not create pull requests modifying the version numbers.
COREVERSION="2025.12.0"
WEBVERSION="2025.12.0"
KEYCONNECTORVERSION="2025.11.0"
echo "bitwarden.sh version $COREVERSION"
docker --version
if [[ "$dccmd" == "docker compose" ]]; then
$dccmd version
else
$dccmd --version
fi
echo ""
# Functions
function downloadSelf() {
if curl -L -s -S -w "http_code %{http_code}" -o $SCRIPT_PATH.1 $BITWARDEN_SCRIPT_URL | grep -q "^http_code 20[0-9]"
then
mv -f $SCRIPT_PATH.1 $SCRIPT_PATH
chmod u+x $SCRIPT_PATH
else
exit_code=$?
rm -f $SCRIPT_PATH.1
exit $exit_code
fi
}
function downloadRunFile() {
if [ ! -d "$SCRIPTS_DIR" ]
then
mkdir $SCRIPTS_DIR
fi
local tmp_script=$(mktemp)
run_file_status_code=$(curl -s -S -L -w "%{http_code}" -o $tmp_script $RUN_SCRIPT_URL)
if echo "$run_file_status_code" | grep -q "^20[0-9]"
then
mv $tmp_script $SCRIPTS_DIR/run.sh
chmod u+x $SCRIPTS_DIR/run.sh
rm -f $SCRIPTS_DIR/install.sh
else
echo "Unable to download run script from $RUN_SCRIPT_URL. Received status code: $run_file_status_code"
echo "http response:"
cat $tmp_script
rm -f $tmp_script
exit 1
fi
}
function checkOutputDirExists() {
if [ ! -d "$OUTPUT" ]
then
echo "Cannot find a Bitwarden installation at $OUTPUT."
exit 1
fi
}
function checkOutputDirNotExists() {
if [ -d "$OUTPUT/docker" ]
then
echo "Looks like Bitwarden is already installed at $OUTPUT."
exit 1
fi
}
function compressLogs() {
LOG_DIR=${1#$(pwd)/}/logs
START_DATE=$2
END_DATE=$3
tempfile=$(mktemp)
function validateDateFormat() {
if ! [[ $1 =~ ^[0-9]{8}$ ]]; then
echo "Error: $2 date format is invalid. Please use YYYYMMDD."
exit 1
fi
}
function validateDateOrder() {
if [[ $(date -d "$1" +%s) > $(date -d "$2" +%s) ]]; then
echo "Error: start date ($1) must be earlier than end date ($2)."
exit 1
fi
}
# Validate start date format
if [ -n "$START_DATE" ]; then
validateDateFormat "$START_DATE" "start"
if [ -z "$END_DATE" ]; then
echo "Error: an end date is required when an start date is provided."
exit 1
fi
fi
# Validate end date format and order
if [ -n "$END_DATE" ]; then
validateDateFormat "$END_DATE" "end"
validateDateOrder "$START_DATE" "$END_DATE"
fi
if [ -n "$START_DATE" ] && [ -n "$END_DATE" ]; then
OUTPUT_FILE="bitwarden-logs-${START_DATE}-to-${END_DATE}.tar.gz"
if [[ "$START_DATE" == "$END_DATE" ]]; then
OUTPUT_FILE="bitwarden-logs-${START_DATE}.tar.gz"
fi
for d in $(seq $(date -d "$START_DATE" "+%Y%m%d") $(date -d "$END_DATE" "+%Y%m%d")); do
# Find and list files matching the date in the filename and modification time, append to tempfile
find $LOG_DIR \( -type f -name "*$d*.txt" -o -name "*.log" -newermt "$START_DATE" ! -newermt "$END_DATE" \) -exec bash -c 'echo "${1#./}" >> "$2"' _ {} "$tempfile" \;
done
echo "Compressing logs from $START_DATE to $END_DATE ..."
else
OUTPUT_FILE="bitwarden-logs-all.tar.gz"
find $LOG_DIR -type f -exec bash -c 'echo "${1#./}" >> "$2"' bash {} "$tempfile" \;
echo "Compressing all logs..."
fi
tar -czvf "$OUTPUT_FILE" -T "$tempfile"
echo "Logs compressed into $(pwd $OUTPUT_FILE)/$OUTPUT_FILE"
rm $tempfile
}
function shareConfig() {
TEMP_DIR=$(mktemp -d)
BASE_DIR=$(realpath "$OUTPUT")
BWDATA_PATH=$(basename "$BASE_DIR")
while IFS= read -r -d $'\0' file; do
rel_path="${file#$BASE_DIR/}"
target_path="$TEMP_DIR/$BWDATA_PATH/$rel_path"
mkdir -p "$(dirname "$target_path")"
cp "$file" "$target_path"
sed -i -e 's/\(globalSettings__duo__aKey=\).*/\1REDACTED/' \
-e 's/\(SA_PASSWORD=\).*/\1REDACTED/' \
-e 's/\(Password=\).*\(;.*\)/\1REDACTED\2/' \
-e 's/\(globalSettings__identityServer__certificatePassword=\).*/\1REDACTED/' \
-e 's/\(globalSettings__internalIdentityKey=\).*/\1REDACTED/' \
-e 's/\(globalSettings__oidcIdentityClientKey=\).*/\1REDACTED/' \
-e 's/\(globalSettings__mail__smtp__username=\).*/\1REDACTED/' \
-e 's/\(globalSettings__mail__smtp__password=\).*/\1REDACTED/' \
"$target_path"
done < <(find "$BASE_DIR" -type f \( -iname "*.conf" -o -iname "*.env" -o -iname "*.xml" -o -iname "*.yml" \) -print0)
OUTPUT_FILE="bitwarden-configs-redacted-$(date +%Y%m%d%H%M%S).tar.gz"
tar -czf "$OUTPUT_FILE" -C "$TEMP_DIR" .
rm -rf "$TEMP_DIR"
echo "The redacted configuration files have been compressed and saved as '$OUTPUT_FILE'."
echo "We have attempted to automatically mask sensitive values from your configuration files, however please ensure you check this before sharing."
echo "You may wish to remove these configuration files from the provided."
}
function listCommands() {
cat << EOT
Available commands:
install
start
restart
stop
update
updatedb
updaterun
updateself
updateconf
uninstall
renewcert
rebuild
compresslogs
shareconfig
help
See more at https://bitwarden.com/help/article/install-on-premise/#script-commands-reference
EOT
}
# Commands
case $1 in
"install")
checkOutputDirNotExists
mkdir -p $OUTPUT
downloadRunFile
$SCRIPTS_DIR/run.sh install $OUTPUT $COREVERSION $WEBVERSION $KEYCONNECTORVERSION
;;
"start" | "restart")
checkOutputDirExists
$SCRIPTS_DIR/run.sh restart $OUTPUT $COREVERSION $WEBVERSION $KEYCONNECTORVERSION
;;
"update")
checkOutputDirExists
downloadRunFile
$SCRIPTS_DIR/run.sh update $OUTPUT $COREVERSION $WEBVERSION $KEYCONNECTORVERSION
;;
"rebuild")
checkOutputDirExists
$SCRIPTS_DIR/run.sh rebuild $OUTPUT $COREVERSION $WEBVERSION $KEYCONNECTORVERSION
;;
"updateconf")
checkOutputDirExists
$SCRIPTS_DIR/run.sh updateconf $OUTPUT $COREVERSION $WEBVERSION $KEYCONNECTORVERSION
;;
"updatedb")
checkOutputDirExists
$SCRIPTS_DIR/run.sh updatedb $OUTPUT $COREVERSION $WEBVERSION $KEYCONNECTORVERSION
;;
"stop")
checkOutputDirExists
$SCRIPTS_DIR/run.sh stop $OUTPUT $COREVERSION $WEBVERSION $KEYCONNECTORVERSION
;;
"renewcert")
checkOutputDirExists
$SCRIPTS_DIR/run.sh renewcert $OUTPUT $COREVERSION $WEBVERSION $KEYCONNECTORVERSION
;;
"updaterun")
checkOutputDirExists
downloadRunFile
;;
"updateself")
downloadSelf && echo "Updated self." && exit
;;
"uninstall")
checkOutputDirExists
$SCRIPTS_DIR/run.sh uninstall $OUTPUT
;;
"compresslogs")
checkOutputDirExists
compressLogs $OUTPUT $2 $3
;;
"shareconfig")
checkOutputDirExists
shareConfig
;;
"help")
listCommands
;;
*)
echo "No command found."
echo
listCommands
esac