#!/usr/bin/env bash # LabCA: a private Certificate Authority for internal lab usage # (c) 2018-2024 Arjan Hakkesteegt # # Install with this command from a Linux machine (only tested with Debian and Ubuntu): # curl -sSL https://raw.githubusercontent.com/hakwerk/labca/master/install | bash set -eEo pipefail trap 'err_report $? $LINENO' INT TERM ERR err_report() { msg_fatal "return code $1 on line $2 in $(basename $0)" $1 } # # Variables / Constants # installMode=${installMode:-normal} baseDir=${baseDir:-/home/labca} logDir=${logDir:-"$baseDir/logs"} runId="`date +%y%m%d-%H%M%S`" installLog="$logDir/install-${runId}.log" logTimeFormat="+%Y-%m-%d %T.%3N" cloneDir=${cloneDir:-"$baseDir/labca"} adminDir=${adminDir:-"$baseDir/admin"} boulderDir=${boulderDir:-"$baseDir/boulder"} boulderLabCADir=${boulderLabCADir:-"${boulderDir}_labca"} dockerComposeVersion="v2.5.0" labcaUrl="https://github.com/hakwerk/labca/" boulderUrl="https://github.com/letsencrypt/boulder/" boulderTag="release-2024-01-08" # Feature flags flag_skip_redis=true # # Color configuration # COL_NC='\e[0m' # No Color COL_LIGHT_GREEN='\e[1;32m' COL_YELLOW='\e[1;33m' COL_LIGHT_RED='\e[1;31m' TICK="[${COL_LIGHT_GREEN}✓${COL_NC}]" CROSS="[${COL_LIGHT_RED}✗${COL_NC}]" WARN="[${COL_YELLOW}✠${COL_NC}]" INFO="[.]" DONE="${COL_LIGHT_GREEN} done!${COL_NC}" OVER="\\r\\033[K" # Dummy implementation in case utils.sh is not available (install via curl method) wait_down() { sleep 1 } wait_up() { sleep 5 } count() { echo 0 } dn=$(dirname $0) source "$dn/utils.sh" &>/dev/null || true cmdlineFqdn="" cmdlineBranch="" fullCmdline="" keepLocal=0 alphaTest=0 dcdowngraded=0 # # Helper functions for informing the user and logging to file # msg_info() { local msg="$1" echo -ne " ${INFO} ${msg}..." echo "[`date "${logTimeFormat}"`] [INFO ] ${msg}..." >> $installLog } msg_ok() { local msg="$1" echo -e "${OVER} ${TICK} ${msg}" echo "[`date "${logTimeFormat}"`] [OK ] ${msg}" >> $installLog } msg_warn() { local msg="$1" echo -e "${OVER} ${WARN} ${msg}" echo "[`date "${logTimeFormat}"`] [WARN] ${msg}" >> $installLog } msg_err() { local msg="$1" echo -e "${OVER} ${CROSS} ${msg}" echo "[`date "${logTimeFormat}"`] [ERROR] ${msg}" >> $installLog } msg_fatal() { local msg="$1" echo -e "\\n ${COL_LIGHT_RED}Error: ${msg}${COL_NC}\\n" echo "[`date "${logTimeFormat}"`] [FATAL] ${msg}" >> $installLog exit ${2:-1} } # # Log to /tmp first in case the labca user doesn't exist yet # start_temporary_log() { backupLog=$installLog installLog="/tmp/labca-install-$$.log" touch "$installLog" } end_temporary_log() { mv "$installLog" "$backupLog" installLog=$backupLog chown labca:labca "$installLog" } # Must run as root check_root() { if [ "$EUID" -eq 0 ]; then msg_ok "Running as root" else msg_err "Not running as root" local msg="Run using sudo" if command -v sudo &> /dev/null; then msg_ok "$msg" exec curl -sSL https://raw.githubusercontent.com/hakwerk/labca/master/install | sudo bash "$@" exit $? else msg_err "$msg" echo -e " ${COL_LIGHT_RED}Script should be run as the root user${COL_NC}" fi fi } # Create dedicated user labca_user() { adduser --gecos "LabCA,,," --disabled-login labca &>>$installLog && msg_ok "Created user 'labca'" || msg_ok "User 'labca' already exists" [ -d "$logDir" ] || mkdir "$logDir" chown -R labca:labca "$logDir" cd ~labca local gig=$(sudo -u labca -H git config --global core.excludesfile 2>/dev/null) if [ -z "$gig" ]; then sudo -u labca -H git config --global core.excludesfile /home/labca/.gitignore_global >/dev/null 2>&1 || msg_warn "WARNING: could not set core.excludesfile" gig=$(sudo -u labca -H git config --global core.excludesfile 2>/dev/null) fi gig=${gig/\~/\/home\/labca} gig=${gig:-/home/labca/.gitignore_global} [ -e "$gig" ] || sudo -u labca -H touch $gig sudo -u labca -H grep config_labca "$gig" >/dev/null 2>&1 || sudo -u labca -H echo "config_labca/" >> "$gig" } # # Get the latest code from the git repository # clone_repo() { local dir="$1" local url="$2" local branch="$3" local msg="Clone $url to $dir" msg_info "$msg" sudo -u labca -H git clone -q "$url" "$dir" &>>$installLog && msg_ok "$msg" || msg_fatal "Could not clone git repository" if [ "$branch" != "" ]; then cd "$dir" sudo -u labca -H git checkout $branch &>>$installLog cd - >/dev/null fi } pull_repo() { local dir="$1" local branch="$2" cd "$dir" &>>$installLog || msg_fatal "Could not switch to directory '$dir'" local msg="Update git repository in $dir" msg_info "$msg" sudo -u labca -H git stash --all --quiet &>>$installLog || true sudo -u labca -H git clean --quiet --force -d &>>$installLog || true sudo -u labca -H git pull --quiet &>>$installLog && msg_ok "$msg" || ( if [ "$dir" == "$GOPATH/src/github.com/letsencrypt/boulder" ]; then sudo -u labca -H git reset --hard $boulderTag &>>$installLog && msg_ok "$msg" || msg_fatal "Could not reset local repository" sudo -u labca -H git pull --quiet &>>$installLog && msg_ok "$msg" || msg_fatal "Could not update local repository (after reset)" else msg_fatal "Could not update local repository" fi ) if [ "$branch" != "" ]; then cd "$dir" sudo -u labca -H git checkout $branch &>>$installLog cd - >/dev/null fi } clone_or_pull() { local dir="$1" local url="$2" local branch="$3" local parentdir=$(dirname "$dir") local dirbase=$(basename "$dir") local found=0 for sd in $(git config --global --get-all safe.directory); do if [ "$sd" == "$dir" ]; then found=1 fi done if [ $found -eq 0 ]; then git config --global --add safe.directory $dir fi if [ -d "$dir" ]; then local rc=0 cd "$dir" sudo -u labca -H git status --short &> /dev/null || rc=$? if [ $rc -gt 0 ]; then cd "$parentdir" mv "$dirbase" "${dirbase}_${runId}" && msg_ok "Backup existing non-git directory '$dir'" clone_repo "$dir" "$url" "$branch" else pull_repo "$dir" "$branch" fi else clone_repo "$dir" "$url" "$branch" fi } # Checkout the latest release tag checkout_release() { local branch="$1" if [ "$branch" == "" ] || [ "$branch" == "master" ] || [ "$branch" == "main" ]; then cd "$cloneDir" if [ "$curChecksum" == "" ]; then curChecksum=$(md5sum $cloneDir/install 2>/dev/null | cut -d' ' -f1) fi TAG=$(sudo -u labca -H git describe --tags $(sudo -u labca -H git rev-list --tags --max-count=1)) sudo -u labca -H git reset --hard $TAG &>>$installLog fi } # Restart the script if it was updated itself restart_if_updated() { local gitRev=$(cd $cloneDir && sudo -u labca -H git describe --always --tags) echo "=== version $gitRev ($curChecksum) ===" >>$installLog if [ "$curChecksum" != "" ]; then local newChecksum=$(md5sum $cloneDir/install 2>/dev/null | cut -d' ' -f1) if [ "$curChecksum" != "$newChecksum" ]; then msg_info "Restarting updated version of install script" echo exec $cloneDir/install $fullCmdline exit $? fi fi } # Utility method to prompt the user for a config variable and export it prompt_and_export() { local varName="$1" local varDefault="$2" local promptMsg="$3" local answer read -p "$promptMsg [$varDefault] " answer >$installLog) || msg_fatal "Could not process commandline parameters" eval set -- "$parsed" while true; do case "$1" in -n|--name|--fqdn) cmdlineFqdn="$2" shift 2 msg_ok "option: using FQDN name '$cmdlineFqdn'" ;; -b|--branch) cmdlineBranch="$2" shift 2 msg_ok "option: using branch '$cmdlineBranch'" ;; -k|--keep) keepLocal=1 shift 1 msg_ok "option: keeping local version as is" ;; -t|--test) alphaTest=1 shift 1 msg_ok "option: INCLUDING ALPHA TEST STEPS" ;; --) shift break ;; *) msg_fatal "Should not have reached this" ;; esac done } # Utility method to check if value looks like a host + domain has_domain() { local dom="$1" if [[ "$dom" =~ ^\..*$ ]]; then false elif [[ "$dom" =~ ^.*\.$ ]]; then false elif [[ "$dom" =~ ^.*\..*$ ]]; then true else false fi } # Determine the remote address of this machine from (in order): commandline parameter, # existing configuration or full hostname. get_fqdn() { local cfgFile="$adminDir/data/config.json" local cfgFqdn=$(grep fqdn $cfgFile 2>/dev/null | grep -v LABCA_FQDN | cut -d ":" -f 2- | tr -d " \"," || echo "") LABCA_FQDN=${cfgFqdn:-$(hostname -f)} while [ "$cfgFqdn" == "" ]; do if [ "$cmdlineFqdn" != "" ]; then export LABCA_FQDN="$cmdlineFqdn" else prompt_and_export LABCA_FQDN "$LABCA_FQDN" "FQDN (Fully Qualified Domain Name) for this PKI host (users will use this in their browsers and clients)?" fi if has_domain $LABCA_FQDN; then cfgFqdn="ok" else msg_err "FQDN must include a hostname AND a domain!" cmdlineFqdn="" fi done if ! has_domain $LABCA_FQDN; then msg_fatal "FQDN must include a hostname AND a domain!" fi msg_ok "Determine web address" } # Utility method to replace all instances of given variables in a file replace_all() { local filename="$1" local var for var in ${@:2}; do sed -i -e "s|$var|${!var}|g" $filename done } # Copy and configure the admin tree from the local repository copy_admin() { local rc=0 local msg="Setup admin application" msg_info "$msg" [ -d "$adminDir" ] || mkdir "$adminDir" cd "$adminDir" git config --global --add safe.directory "$adminDir" git status --short &> /dev/null || rc=$? if [ $rc -gt 0 ]; then git init &>>$installLog fi git add --all &>/dev/null || true git commit --all --quiet -m "LabCA before update $runId" &>>$installLog && { msg_ok "Commit existing modifications of $adminDir"; msg_info "$msg"; } || true cp -rp $cloneDir/gui/* "./" &>>$installLog || msg_fatal "Cannot copy the admin files to $adminDir" msg_ok "$msg" msg="Configure the admin application" msg_info "$msg" [ -e "$adminDir/data/config.json" ] || echo -e "{\n \"config\": {\n \"complete\": false\n },\n \"labca\": {\n \"fqdn\": \"$LABCA_FQDN\"\n },\n \"version\": \"\"\n}" > "$adminDir/data/config.json" replace_all $adminDir/data/openssl.cnf LABCA_FQDN replace_all $adminDir/data/issuer/openssl.cnf LABCA_FQDN cd "$cloneDir" sudo -u labca -H git config --global --add safe.directory "$cloneDir" version=$(sudo -u labca -H git describe --always --tags HEAD 2>/dev/null) cd "$adminDir" grep \"version\" data/config.json &>/dev/null || sed -i -e 's/^}$/,\n "version": ""\n}/' data/config.json sed -i -e "s/\"version\": \".*\"/\"version\": \"$version\"/" data/config.json [ ! -e bin/labca-gui ] || mv bin/labca-gui bin/labca-gui_prev chown -R labca:labca $baseDir chown root:root "$cloneDir/cron_d" [ -e /etc/cron.d/labca ] && rm /etc/cron.d/labca || true [ -e /etc/logrotate.d/labca ] && rm /etc/logrotate.d/labca || true git add --all &>/dev/null || true git commit --all --quiet -m "LabCA after update $runId" &>>$installLog || true msg_ok "$msg" } # Update any outdated packages update_upgrade() { msg_info "Making sure all software is up-to-date" apt update &>>$installLog apt upgrade -y &>>$installLog apt autoremove -y &>>$installLog msg_ok "Software is up-to-date" } # # Install extra packages that we rely upon # install_pkg() { local package="$1" msg_info "Install package '$package'" err="" apt install -y "$package" &>>$installLog || err="err" if [ "$err" == "err" ]; then if [ "$package" == "python" ]; then package="python3" apt install -y "$package" &>>$installLog || msg_fatal "Could not install package '$package'" else msg_fatal "Could not install package '$package'" fi fi msg_ok "Package '$package' is installed" } install_extra() { local packages=(apt-transport-https ca-certificates curl gnupg net-tools tzdata ucspi-tcp zip unzip python lsb-release) for package in "${packages[@]}"; do install_pkg "$package" done distrib=$(lsb_release -is | tr '[:upper:]' '[:lower:]') install -m 0755 -d /etc/apt/keyrings [ ! -e /etc/apt/keyrings/docker.gpg ] || mv /etc/apt/keyrings/docker.gpg /etc/apt/keyrings/docker.gpg_PREV curl -fsSL https://download.docker.com/linux/${distrib}/gpg 2>>$installLog | gpg --dearmor -o /etc/apt/keyrings/docker.gpg &>>$installLog || msg_fatal "Could not download docker repository key" chmod a+r /etc/apt/keyrings/docker.gpg echo \ "deb [arch="$(dpkg --print-architecture)" signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/${distrib} \ "$(. /etc/os-release && echo "$VERSION_CODENAME")" stable" | \ tee /etc/apt/sources.list.d/docker.list > /dev/null &>>$installLog apt-get update &>>$installLog local packages=(docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin) for package in "${packages[@]}"; do install_pkg "$package" done dcver=$(docker compose version | grep v2.19 | wc -l) if [ "$dcver" != "0" ]; then dc18=$(apt list docker-compose-plugin -a 2>/dev/null | grep 2.18 | cut -d ' ' -f 2) apt install -y --allow-downgrades docker-compose-plugin=${dc18} &>>$installLog dcdowngraded=1 msg_warn "docker-compose-plugin was downgraded to v2.18 due to a known bug with v2.19.x" fi # Make sure the labca user has docker permissions usermod -aG docker labca } # Configure the static web pages (for end users) static_web() { local rc=0 local msg="Static web pages" msg_info "$msg" if [ -d /etc/nginx ] && [ ! -d /etc/nginx.backup ]; then # Migrate cert from host nginx to dockerized nginx [ -d /home/labca/nginx_data/ssl ] || mkdir -p /home/labca/nginx_data/ssl [ -d /etc/nginx/ssl/ ] && mv /etc/nginx/ssl/* /home/labca/nginx_data/ssl/ || true mv /etc/nginx /etc/nginx.backup fi [ -d /home/labca/nginx_data/conf.d ] || mkdir -p /home/labca/nginx_data/conf.d [ -d /home/labca/nginx_data/ssl ] || mkdir -p /home/labca/nginx_data/ssl cp $cloneDir/nginx.conf /home/labca/nginx_data/conf.d/labca.conf cp $cloneDir/proxy.inc /home/labca/nginx_data/conf.d/proxy.inc if [ -f "$boulderLabCADir/setup_complete" ]; then perl -i -p0e 's/\n # BEGIN temporary redirect\n location = \/ \{\n return 302 \/admin\/;\n }\n # END temporary redirect\n//igs' /home/labca/nginx_data/conf.d/labca.conf fi [ -d /home/labca/nginx_data/static ] || mkdir /home/labca/nginx_data/static cd /home/labca/nginx_data/static git config --global --add safe.directory /home/labca/nginx_data/static git status --short &> /dev/null || rc=$? if [ $rc -gt 0 ]; then git init &>>$installLog fi git add --all &>/dev/null || true git commit --all --quiet -m "LabCA before update $runId" &>>$installLog && { msg_ok "Commit existing modifications of $adminDir"; msg_info "$msg"; } || true mkdir -p .well-known/acme-challenge find .well-known/acme-challenge/ -type f -mtime +10 -exec rm {} \; # Clean up files older than 10 days mkdir -p crl [ -e cert ] || ln -s certs cert cp -rp $cloneDir/gui/static/* . [ -e $adminDir/data/root-ca.crl ] && cp $adminDir/data/root-ca.crl crl/ || true [ -e $adminDir/data/root-ca.pem ] && cp $adminDir/data/root-ca.pem certs/ || true [ -e $adminDir/data/root-ca.pem ] && ln -sf root-ca.pem certs/test-root.pem || true [ -e $adminDir/data/root-ca.der ] && cp $adminDir/data/root-ca.der certs/ || true [ -e $adminDir/data/issuer/ca-int.pem ] && cp $adminDir/data/issuer/ca-int.pem certs/ || true [ -e $adminDir/data/issuer/ca-int.pem ] && ln -sf ca-int.pem certs/test-ca.pem || true [ -e $adminDir/data/issuer/ca-int.der ] && cp $adminDir/data/issuer/ca-int.der certs/ || true local have_config=$(grep restarted $adminDir/data/config.json | grep true) if [ "$have_config" != "" ]; then $adminDir/apply-nginx fi git add --all &>/dev/null || true git commit --all --quiet -m "LabCA after update $runId" &>>$installLog || true msg_ok "$msg" } # Create a temporary self-signed certificate if there is no certificate yet selfsigned_cert() { if [ -e /home/labca/nginx_data/ssl/labca_cert.pem ]; then msg_ok "Certificate is present" else local msg="Create self-signed certificate" msg_info "$msg" mkdir -p /home/labca/nginx_data/ssl cd /home/labca/nginx_data/ssl openssl req -x509 -nodes -sha256 -newkey rsa:2048 -keyout labca_key.pem -out labca_cert.pem -days 7 \ -subj "/O=LabCA/CN=$LABCA_FQDN" -reqexts SAN -extensions SAN \ -config <(cat /etc/ssl/openssl.cnf <(printf "\n[SAN]\nbasicConstraints=CA:FALSE\nnsCertType=server\nsubjectAltName=DNS:$LABCA_FQDN")) &>>$installLog msg_ok "$msg" fi } # Clone or update the boulder code (Let's Encrypt (tm) implementation of ACME protocol) get_boulder() { export GOPATH="/home/labca/gopath" sudo -u labca -H mkdir -p "$GOPATH/src/github.com/letsencrypt" [ -h "$boulderDir" ] || sudo -u labca -H ln -s "$GOPATH/src/github.com/letsencrypt/boulder" "$boulderDir" if [ -e "$boulderDir" ]; then cd "$boulderDir" chown -R labca:labca . rm -rf bin/orphan-finder bin/validate sudo -u labca -H git reset --hard HEAD^1 &>>$installLog fi clone_or_pull "$GOPATH/src/github.com/letsencrypt/boulder" "$boulderUrl" cd "$boulderDir" chown -R labca:labca . sudo -u labca -H git reset --hard $boulderTag &>>$installLog sudo -u labca -H git prune &>>$installLog if [ -e "sa/_db-next/migrations/20190221140139_AddAuthz2.sql" ]; then sudo -u labca -H cp sa/_db-next/migrations/20190221140139_AddAuthz2.sql sa/_db/migrations/ fi if [ -e "sa/_db-next/migrations/20190524120239_AddAuthz2ExpiresIndex.sql" ]; then sudo -u labca -H cp sa/_db-next/migrations/20190524120239_AddAuthz2ExpiresIndex.sql sa/_db/migrations/ fi msg_ok "Boulder checkout '$boulderTag'" } # Configure boulder based on their test subdirectory config_boulder() { local msg="Setup boulder configuration folder" msg_info "$msg" [ -d "$boulderLabCADir" ] || mkdir -p "$boulderLabCADir" cd "$boulderLabCADir" local rc=0 git config --global --add safe.directory "$boulderLabCADir" git status --short &> /dev/null || rc=$? if [ $rc -gt 0 ]; then git init &>>$installLog fi [ -d ".backup" ] || mkdir -p ".backup" git add --all &>/dev/null || true [ "$installMode" == "normal" ] && git commit --all --quiet -m "LabCA before update $runId" &>>$installLog && { msg_ok "Commit existing modifications of $boulderLabCADir"; msg_info "$msg"; } || true [ ! -e "$boulderLabCADir/secrets/smtp_password" ] || mv "$boulderLabCADir/secrets/smtp_password" "$boulderLabCADir/secrets/smtp_password_PRESERVE" cp -r "$boulderDir/test" -T "$boulderLabCADir" &>>$installLog [ ! -e "$boulderLabCADir/secrets/smtp_password_PRESERVE" ] || mv "$boulderLabCADir/secrets/smtp_password_PRESERVE" "$boulderLabCADir/secrets/smtp_password" [ "$installMode" == "normal" ] && chown -R labca:labca "$boulderLabCADir" || /bin/true rm -rf authz-filler challtestsrv gsb-test-srv msg_ok "$msg" msg="Configure the boulder application" msg_info "$msg" cd "$boulderDir" if [ "$installMode" == "normal" ]; then $cloneDir/patch.sh "sudo -u labca -H" &>>$installLog sed -i -e "s/LABCA_FQDN: .*/LABCA_FQDN: $LABCA_FQDN/" docker-compose.yml else $cloneDir/patch.sh &>>$installLog fi git config --global --add safe.directory /home/labca/boulder_labca cp docker-compose.yml "$boulderLabCADir/.backup/" cp cmd/shell.go "$boulderLabCADir/.backup/" cp core/interfaces.go "$boulderLabCADir/.backup/" cp policy/pa.go "$boulderLabCADir/.backup/" cp ra/ra.go "$boulderLabCADir/.backup/" cp mail/mailer.go "$boulderLabCADir/.backup/" cp cmd/expiration-mailer/main.go "$boulderLabCADir/.backup/" cp cmd/notify-mailer/main.go "$boulderLabCADir/.backup/" cp cmd/contact-auditor/main.go "$boulderLabCADir/.backup/" cp cmd/bad-key-revoker/main.go "$boulderLabCADir/.backup/" cp cmd/cert-checker/main.go "$boulderLabCADir/.backup/" cp cmd/log-validator/main.go "$boulderLabCADir/.backup/" cp cmd/boulder/main.go "$boulderLabCADir/.backup/" cp ratelimit/rate-limits.go "$boulderLabCADir/.backup/" cp errors/errors.go "$boulderLabCADir/.backup/" cp log/log.go "$boulderLabCADir/.backup/" cp sa/db/boulder_sa/20230419000000_CombinedSchema.sql "$boulderLabCADir/.backup/" cp Makefile "$boulderLabCADir/.backup/" if [ "$installMode" == "normal" ]; then $cloneDir/patch-cfg.sh "sudo -u labca -H" "$boulderLabCADir" &>>$installLog else $cloneDir/patch-cfg.sh " " "$boulderLabCADir" &>>$installLog fi mkdir -p $baseDir/backup if [ ! -z "$(docker ps | grep boulder-bmysql-1)" ]; then docker exec boulder-bmysql-1 mysqldump --databases boulder_sa_integration >$baseDir/backup/dbdata-old-${runId}.sql docker exec boulder-bmysql-1 mysql boulder_sa_integration -e 'delete from gorp_migrations;' msg_ok "Saved pre-update database to dbdata-old-${runId}.sql" msg_info "$msg" fi if [ ! -z "$(docker ps | grep labca-bmysql-1)" ]; then docker exec labca-bmysql-1 mysqldump --databases boulder_sa_integration >$baseDir/backup/dbdata-${runId}.sql docker exec labca-bmysql-1 mysql boulder_sa_integration -e 'delete from gorp_migrations;' msg_ok "Saved pre-update database to dbdata-${runId}.sql" msg_info "$msg" fi # housekeeping for file in `ls -1t $baseDir/backup/dbdata-*.sql 2>&1 | tail -n +3 || true`; do rm $file done if [ "$installMode" == "normal" ]; then cd "$boulderLabCADir" sed -i -e "s|https://boulder.service.consul:4431/terms/v7|https://$LABCA_FQDN/terms/v1|" config/wfe2.json sed -i -e "s|boulder.service.consul:4000|$LABCA_FQDN|g" config/wfe2.json sed -i -e "s|http://127.0.0.1:4002/|http://$LABCA_FQDN/ocsp/|g" config/ca-a.json sed -i -e "s|http://127.0.0.1:4002/|http://$LABCA_FQDN/ocsp/|g" config/ca-b.json sed -i -e "s|http://example.com/cps|http://$LABCA_FQDN/cps/|g" config/ca-a.json sed -i -e "s|http://example.com/cps|http://$LABCA_FQDN/cps/|g" config/ca-b.json sed -i -e "s|http://example.com/crl|http://$LABCA_FQDN/crl/|g" config/ca-a.json sed -i -e "s|http://example.com/crl|http://$LABCA_FQDN/crl/|g" config/ca-b.json sed -i -e "s|boulder.service.consul:4000|$LABCA_FQDN|g" config/va.json sed -i -e "s|boulder.service.consul:4001|$LABCA_FQDN|g" config/va.json sed -i -e "s|boulder.service.consul:4000|$LABCA_FQDN|g" config/va-remote-a.json sed -i -e "s|boulder.service.consul:4001|$LABCA_FQDN|g" config/va-remote-a.json sed -i -e "s|boulder.service.consul:4000|$LABCA_FQDN|g" config/va-remote-b.json sed -i -e "s|boulder.service.consul:4001|$LABCA_FQDN|g" config/va-remote-b.json cd "$boulderDir" fi ([ -e mock-vendor.go ] && rm mock-vendor.go) || /bin/true ([ -e test-tools.go ] && rm test-tools.go) || /bin/true if [ "$installMode" == "normal" ]; then local have_config=$(grep restarted $adminDir/data/config.json 2>/dev/null | grep true) if [ "$have_config" != "" ]; then cd "$boulderLabCADir" $adminDir/apply-boulder &>>$installLog cd "$boulderDir" else chown -R labca:labca "$boulderLabCADir" || /bin/true fi fi git add --all &>/dev/null || true [ "$installMode" == "normal" ] && git commit --all --quiet -m "LabCA after update $runId" &>>$installLog || true msg_ok "$msg" } # Cleanup any now obsolete files cleanup() { local msg="Cleaning up obsolete files" msg_info "$msg" if [ -d /var/www/html ]; then rm -f /var/www/html/css/skeleton.css rm -f /var/www/html/css/skeleton-tabs.css rm -f /var/www/html/css/normalize.css rm -f /var/www/html/css/font.css rm -f /var/www/html/img/favicon.ico rm -f /var/www/html/js/jquery-3.3.1.min.js rm -f /var/www/html/js/skeleton-tabs.js fi rm -f $adminDir/templates/cert.tmpl rm -f $adminDir/templates/error.tmpl rm -f $adminDir/templates/final.tmpl rm -f $adminDir/templates/footer.tmpl rm -f $adminDir/templates/header.tmpl rm -f $adminDir/templates/index.tmpl rm -f $adminDir/templates/login.tmpl rm -f $adminDir/templates/polling.tmpl rm -f $adminDir/templates/register.tmpl rm -f $adminDir/templates/setup.tmpl rm -f $adminDir/templates/wrapup.tmpl # Remove host nginx if installed, as we are now using the docker container systemctl stop nginx &>>$installLog || true systemctl disable nginx &>>$installLog || true apt remove -y nginx &>>$installLog msg_ok "$msg" } # Startup all the components startup() { local msg="Restart docker containers and service" cd "$boulderDir" cnt=$(docker compose ps | wc -l) if [ "$cnt" -le "2" ]; then msg="Download docker images and build containers" fi msg_info "$msg (this will take a while!!)" docker compose pull -q &>>$installLog export BOULDER_TOOLS_TAG=$(grep go1. .github/workflows/boulder-ci.yml | head -1 | sed -e "s/\s*- //") docker pull -q letsencrypt/boulder-tools:$BOULDER_TOOLS_TAG &>>$installLog # Cleanup any remaining containers with old names docker compose -p boulder stop &>>$installLog || true docker compose -p boulder rm -f &>>$installLog || true for ct in boulder_bhsm_1 boulder_bredis_1 boulder_bredis_2 boulder_bredis_3 boulder_bredis_4 boulder_bredis_5 boulder_bredis_6; do [ -z "$(docker ps | grep $ct)" ] || docker stop $ct &>>$installLog done for ct in boulder_bhsm_1 boulder_bredis_1 boulder_bredis_2 boulder_bredis_3 boulder_bredis_4 boulder_bredis_5 boulder_bredis_6; do [ -z "$(docker ps -a | grep -e "$ct\$")" ] || docker rm -f $ct &>>$installLog done docker network rm -f boulder_bluenet boulder_consulnet boulder_rednet &>>$installLog || true docker stop boulder-labca-1 >&/dev/null || true docker rm -f boulder-labca-1 >&/dev/null || true docker stop labca-labca-1 >&/dev/null || true docker rm -f labca-labca-1 >&/dev/null || true cnt=$(count $PS_CONTROL || echo "0") haserr=$(docker compose logs | grep "cannot assign requested address" | wc -l) docker compose stop &>>$installLog || true wait_down $PS_NGINX &>>$installLog || true wait_down $PS_MYSQL &>>$installLog || true wait_down $PS_CONSUL &>>$installLog || true wait_down $PS_LABCA &>>$installLog || true wait_down $PS_CONTROL &>>$installLog || true wait_down $PS_BOULDER &>>$installLog || true if [ $dcdowngraded -eq 1 ] || [ $haserr -ne 0 ]; then docker compose rm -f &>>$installLog || true fi local rc=0 service labca status &> /dev/null || rc=$? if [ $rc -eq 0 ]; then service labca stop &>>$installLog || true update-rc.d labca disable &>>$installLog || true [ -e "/etc/init.d/labca" ] && rm /etc/init.d/labca || true fi [ -d /home/labca/control_logs ] || mkdir -p /home/labca/control_logs # Restore MySQL data when moving from boulder-bmysql-1 to labca-bmysql-1 if [ -z "$(docker volume ls | grep labca_dbdata)" ] && [ ! -z "$(docker volume ls | grep boulder_dbdata)" ]; then docker volume create labca_dbdata &>>$installLog dimg=$(grep mariadb: docker-compose.yml | sed -e "s/\s*image:\s*//") docker run --rm -v boulder_dbdata:/old -v labca_dbdata:/new $dimg bash -c "cp -R /old/* /new/" &>>$installLog fi COMPOSE_HTTP_TIMEOUT=180 docker compose up -d &>>$installLog wait_up $PS_NGINX &>>$installLog || true wait_up $PS_MYSQL &>>$installLog || true wait_up $PS_CONSUL 2 &>>$installLog || true wait_up $PS_LABCA &>>$installLog || true wait_up $PS_CONTROL &>>$installLog || true docker exec -i labca-bmysql-1 mysql_upgrade &>>$installLog [ -f "$boulderLabCADir/setup_complete" ] && wait_up $PS_BOULDER $PS_BOULDER_COUNT &>>$installLog || true msg_ok "$msg" } # If the nginx certificate is self-signed then show extra text first_time() { local certFile="/home/labca/nginx_data/ssl/labca_cert.pem" [ -e "$certFile" ] || msg_fatal "The SSL certificate $certFile does not exist" local subject=$(openssl x509 -noout -in "$certFile" -subject_hash) local issuer=$(openssl x509 -noout -in "$certFile" -issuer_hash) if [ "$subject" == "$issuer" ]; then echo echo ======== echo echo "Congratulations! LabCA is now installed and should be available at https://$LABCA_FQDN" echo "Please go there now to finish the setup. Note that a TEMPORARY (7 days) self-signed certificate" echo "is used; as part of the setup verification a new certificate will be issued." echo fi } check_dockeronly() { set +e wd=$(which docker) set -e if [ "$wd" != "" ]; then let num=$(docker volume ls | grep labca_ | grep -v labca_dbdata | wc -l) if [ $num -gt 0 ]; then scriptname=$(basename $0) echo "You can not run the $scriptname script when using dockeronly mode!" exit 1 fi fi } # # The actual main function to tie it all together # main() { local curdir="$PWD" echo check_dockeronly start_temporary_log check_root install_pkg "git" install_pkg "sudo" install_pkg "patch" labca_user end_temporary_log this=$0 [ -e $this ] || this="$curdir/$0" curChecksum=$(md5sum $this 2>/dev/null | cut -d' ' -f1) [ ! -e "$cloneDir/cron_d" ] || chown labca:labca "$cloneDir/cron_d" parse_cmdline "$@" if [ $keepLocal -eq 0 ]; then clone_or_pull "$cloneDir" "$labcaUrl" "$cmdlineBranch" checkout_release "$cmdlineBranch" restart_if_updated fi if [ $alphaTest -eq 1 ]; then install_extra cd $(dirname $this) local msg="TEST: build labca-gui binary" msg_info "$msg" # this will ultimately NOT be done on the target machine! build/build.sh &>>$installLog || msg_fatal "Could not build docker images!" msg_ok "$msg" msg="TEST build local docker image" msg_info "$msg" build/tag_and_upload.sh &>>$installLog || msg_fatal "Could not tag (and upload) docker images!" msg_ok "$msg" msg_ok "That's it for now!" exit 0 fi get_fqdn copy_admin update_upgrade install_extra static_web selfsigned_cert get_boulder config_boulder cleanup startup echo -e "$DONE" echo first_time cd "$curdir" } [ "$installMode" == "normal" ] && main "$@" || /bin/true