#!/bin/bash # # Author: Georg Voell - georg.voell@standby.cloud # Version: @(#)bootstrap 3.2.1 21.09.2025 (c)2025 Standby.cloud # Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/ # #@ Attach this script to the instance (Advanced Options - Cloud Init Script File). #@ It downloads admin scripts and tools as soon as internet access is available. #@ Supported platforms: All Linux variants, but only tested with Oracle Linux. #@ Some commands were only executed if OS is "Oracle Linux" or "Red Hat Enterprise Linux" and version >= 7. #@ Works for Oracle Cloud Infrastructure (OCI) and Oracle Public Cloud (OPC or OCI-Classic) #@ An internet connection (e.g. via Internet or NAT Gateway) or at least a Service Gateway is needed. # # Exit codes: # 01: Unsupported platform. # 02: No root privileges (needed to proceed). # 03: No 'curl' in PATH. # 04: No internet connection. # # Update history: # # V 3.0.0 04.07.2020 New version # V 3.0.1 08.07.2020 Minor changes # V 3.0.2 10.02.2021 Check if we have internet access # V 3.2.0 29.08.2025 Revised version # V 3.2.1 21.09.2025 Optional parameter # # Usage: # # # Install for all users # curl -skL https://standby.cloud/download/latest/bootstrap | sudo bash # # # Install for one user # curl -skL https://standby.cloud/download/latest/bootstrap | bash # # # Install also software for bastion services # curl -skL https://standby.cloud/download/latest/bootstrap | sudo bash -s bastion # # # Only set password for standard users # curl -skL https://standby.cloud/download/latest/bootstrap | sudo bash -s password # # Set configuration variables TimeZone="Europe/Berlin" # Leave empty to use default timezone or change whatever is needed here Locale="LANG=C.utf8" # Leave empty to use default locale therwise change to e.g. "LANG=de_DE.utf8" or "LANG=en_US.UTF-8" Update="update" # Leave empty if you don't want to update OS otherwise use "update" or "update --security" Cockpit="true" # Set variable to true to enabcle cockpit otherwise set variable to false RotateLog="true" # Set variable to true to overwrite logfiles each time script is started otherwise set variable to false # Repository where we load our scripts from readonly storageurl="https://objectstorage.eu-frankfurt-1.oraclecloud.com/n/frcc4jd4wdkp/b/download/o" # Set some defaults exitcode=0 progstr="bootstrap" scriptsfile="install-scripts" toolsfile="install-tools" logfile="/var/log/${progstr}.log" jhuser="jumphost" jhhome=$(eval echo ~${jhuser}) # Get current user and os ME=`whoami` # Current user OS=`uname -s` # Infos about the host os (e.g. Darwin, SunOS, Linux) PARAM=`echo "$1" | tr '[:upper:]' '[:lower:]'` # First papameter # Set PATH PATH="/bin:/.local/bin:/usr/local/bin:$PATH" # Check for some tools curl=`which "curl" 2>/dev/null | sed 's|^no curl in .*||'` yum=`which "yum" 2>/dev/null | sed 's|^no yum in .*||'` rclone=`which "rclone" 2>/dev/null | sed 's|^no rclone in .*||'` systemctl=`which "systemctl" 2>/dev/null | sed 's|^no systemctl in .*||'` vncpasswd=`which "vncpasswd" 2>/dev/null | sed 's|^no vncpasswd in .*||'` setuptools=`which "setup-tools" 2>/dev/null | sed 's|^no setup-tools in .*||'` # Write string to log with current date and time function WriteLog { local param=${1} if [ "$logfile" != "" ]; then curdate=`date "+%Y-%m-%d %T"` printf "%s\t%s\n" "$curdate" "$param" >> "$logfile" fi } # Delete file function DeleteFile { local filename=${1} if [ "$filename" != "" ]; then if [ -f "$filename" ]; then rm -f "$filename" fi fi } # Delete tempfiles function Cleanup { DeleteFile "/tmp/$scriptsfile" DeleteFile "/tmp/$toolsfile" } # Get OS name and version from /etc/os-release. Examples: # OL6: NAME="Oracle Linux Server" / ID="rhel" / VERSION_ID="6.10" / ID="ol" # OL7: NAME="Oracle Linux Server" / VERSION_ID="7.9" / ID="ol" # OL8: NAME="Oracle Linux Server" / VERSION_ID="8.10" / ID="ol" # OL9: NAME="Oracle Linux Server" / VERSION_ID="9.6" / ID="ol" # OL10: NAME="Oracle Linux Server" / VERSION_ID="10.0" / ID="ol" # RHEL8: NAME="Red Hat Enterprise Linux" / VERSION_ID="8.6" / ID="rhel" function GetOSVersion { NAME="" ID="" VERSION_ID="" VERSION_MAIN="" VERSION_OK="false" if [ -r /etc/os-release ]; then source /etc/os-release if [ "$VERSION_ID" != "" ]; then VERSION_MAIN=`echo "$VERSION_ID" | cut -d'.' -f1` if [ "$VERSION_MAIN" != "" ]; then if [ "$ID" = "ol" -o "$ID" = "rhel" ]; then if [ $VERSION_MAIN -gt 6 ]; then VERSION_OK="true" fi fi fi fi fi } # localectl list-locales: List all available locales # cat /etc/locale.conf function SetLocale { local locale=${1} local localectl=`which "localectl" 2>/dev/null | sed 's|^no localectl in .*||'` local grepres="" local currlocale="" local currlang="" local version=8 if [ "$localectl" != "" -a "$locale" != "" ]; then # Check OS version if [ -f "/etc/os-release" ]; then version=`grep "^VERSION=" /etc/os-release | tr -d '"' | cut -d'=' -f2 | cut -d'.' -f1` fi # Load language packs if [ "$yum" != "" -a $version -ge 8 ]; then currlang="$LANG" LANG=C.utf8 $yum install -y langpacks-en glibc-all-langpacks LANG="$currlang" fi grepres=`$localectl status | tr -s ' '` currlocale=`echo "$grepres" | grep "^ System Locale: " | cut -d' ' -f4` if [ "$currlocale" = "" ]; then currlocale=`echo "$grepres" | grep "^System Locale: " | cut -d' ' -f3` fi if [ "$currlocale" != "$locale" ]; then $localectl set-locale "$locale" else echo "Locale already set to '$locale'." fi fi } # Set time zone for VM # timedatectl status: Show settings in human readyble format # timedatectl list-timezones: List all available timezones function SetTimeZone { local timezone=${1} local timedatectl=`which "timedatectl" 2>/dev/null | sed 's|^no timedatectl in .*||'` local currtimezone="" if [ "$timedatectl" != "" -a "$timezone" != "" ]; then # currtimezone=`$timedatectl show | grep "^Timezone=" | cut -d'=' -f2` # Does not work for OL7 currtimezone=`$timedatectl status | tr -s ' ' | grep "^ Time zone: " | cut -d' ' -f4` if [ "$currtimezone" != "$timezone" ]; then $timedatectl set-timezone "$timezone" if [ "$systemctl" != "" ]; then $systemctl restart crond.service fi else echo "Timezone already set to '$timezone'." fi fi } # Install and activate cockpit function EnableCockpit { local firewallcmd=`which "firewall-cmd" 2>/dev/null | sed 's|^no firewall-cmd in .*||'` local grepres="" if [ "$Cockpit" = "true" -a "$systemctl" != "" ]; then grepres=`$systemctl status cockpit.socket 2>/dev/null | grep "Active:" | tr -s ' ' | cut -d' ' -f3` if [ "$grepres" != "active" ]; then if [ "$yum" != "" ]; then $yum install -y cockpit fi $systemctl enable --now cockpit.socket 2>> $logfile if [ "$firewallcmd" != "" ]; then $firewallcmd --add-service=cockpit --permanent $firewallcmd --reload fi else echo "Cockpit already enabled." fi fi # Delete cockpit motd DeleteFile "/etc/motd.d/cockpit" } # Set password for a user / currently unused function SetPassword { local user=${1} local password=${2} local currstatus="" if [ "$user" != "" -a "$password" != "" ]; then currstatus=`passwd --status "$user" | cut -d' ' -f2` if [ "$currstatus" != "PS" -a "$currstatus" != "P" ]; then echo "$password" | passwd "$user" --stdin else echo "Password already set for user '$user'." fi fi } # Set passwords (read from file) for all specified users function SetAllPasswords { local password="" local passwdfile="${jhhome}/.config/tigervnc/passwd" local passwddir=`dirname $passwdfile` local user="" local stat=0 if [ -f "${HOME}/.newpasswd" ]; then echo "Setting password for user opc and root." password=`cat "${HOME}/.newpasswd"` for user in root opc; do echo "$password" | passwd "$user" --stdin stat=$? if [ $stat -ne 0 ]; then echo "Unable to set password for user '$user'." fi done # Rename passwd file mv -f "${HOME}/.newpasswd" "${HOME}/.passwd" fi # Set VNC password if [ "$vncpasswd" != "" -a -f "${HOME}/.newvncpasswd" ]; then # Could do better if we check the version of vncpasswd if [ "$VERSION_MAIN" != "" ]; then if [ $VERSION_MAIN -lt 8 ]; then passwdfile="${jhhome}/.vnc/passwd" passwddir=`dirname $passwdfile` fi fi echo "Setting vnc password for user '$jhuser'." password=`cat "${HOME}/.newvncpasswd"` echo "$password" | $vncpasswd -f > "$passwdfile" stat=$? if [ $stat -eq 0 ]; then mv -f "${HOME}/.newvncpasswd" "${HOME}/.vncpasswd" chown -R ${jhuser}:$jhuser "$passwddir" chmod 600 "$passwdfile" else echo "Unable to set vnc password for user '$jhuser''." fi fi } # Download file from standby.cloud function DownloadFile { local filename=${1} local beta=${2} local stage="latest" local grepres="" local code="" local stat=1 if [ "$filename" != "" ]; then if [ "$beta" = "true" ]; then stage="beta" fi $curl -skL "${storageurl}/${stage}/$filename" -o "/tmp/$filename" stat=$? if [ $stat -eq 0 -a -r "/tmp/$filename" ]; then grepres=`grep '^{"code":"' "/tmp/$filename"` if [ "$grepres" != "" ]; then code=`echo "$grepres" | cut -d'"' -f4` echo "Errorcode: '$code'" stat=1 else chmod 755 "/tmp/$filename" fi fi fi return $stat } # Check for internet connection function CheckInternet { local i=0 local max=120 local stat=1 while [ $stat -ne 0 -a $i -lt $max ]; do DownloadFile "$scriptsfile" "false" stat=$? # Increase counter let "i++" if [ $stat -ne 0 -a $i -lt $max ]; then # Pause 3 seconds sleep 3 fi done # Exit, if we can't download file if [ $stat -ne 0 ]; then exitcode=4 WriteLog "No internet connection - stat: '$stat'." Cleanup exit 4 fi } # Download and compile putty function CompilePutty { local curdir=`pwd` if [ "$yum" != "" -a ! -d "/usr/local/src/putty-0.83" ]; then # Install needed tools $yum install -y gcc cmake # Install putty cd /usr/local/src wget https://the.earth.li/~sgtatham/putty/latest/putty-0.83.tar.gz 2>>$logfile tar xvf putty-0.83.tar.gz rm -f putty-0.83.tar.gz cd putty* cmake . 2>>$logfile cmake --build . 2>>$logfile cd "$curdir" fi } # Download and compile python 3.9 function CompilePython39 { local curdir=`pwd` if [ "$yum" != "" -a ! -d "/usr/local/ssl" ]; then # Install needed tools $yum install -y gcc libffi-devel zlib-devel bzip2-devel openssl-devel ncurses-devel sqlite-devel \ readline-devel tk-devel gdbm-devel libpcap-devel xz-devel # Install openssl cd /usr/local/src wget https://www.openssl.org/source/openssl-1.0.2q.tar.gz 2>>$logfile tar xvf openssl-1.0.2q.tar.gz rm -f openssl-1.0.2q.tar.gz cd openssl* ./config 2>>$logfile make 2>>$logfile make install 2>>$logfile # Install Python 3.9 cd /usr/local/src wget https://www.python.org/ftp/python/3.9.19/Python-3.9.19.tgz 2>>$logfile tar xvf Python-3.9.19.tgz rm -f Python-3.9.19.tgz cd Python* mv -f Modules/Setup Modules/Setup.org cat Modules/Setup.org | sed 's|#SSL=/usr/local/ssl|SSL=/usr/local/ssl|' | sed 's|#_ssl _ssl.c|_ssl _ssl.c|' \ | sed 's|# -DUSE_SSL -I$(SSL)/include -I$(SSL)/include/openssl| -DUSE_SSL -I$(SSL)/include -I$(SSL)/include/openssl|' \ | sed 's|# -L$(SSL)/lib -lssl -lcrypto| -L$(SSL)/lib -lssl -lcrypto|' > Modules/Setup ./configure --enable-optimizations 2>>$logfile make install 2>>$logfile cd "$curdir" fi } # Configuration for tigervnc only works for OL8+ # For OL7: https://docs.oracle.com/en/operating-systems/oracle-linux/7/network/network-ConfiguringtheVNCService.html#ol7-vnc-config function InstallBastionAddons { # Create jumphost user user-management check $jhuser $jhuser -c "Jumphost User" -s "/bin/bash" >> $logfile # Create .ssh dir jhhome=$(eval echo ~${jhuser}) if [ ! -d "${jhhome}/.ssh" -o ! -d "${jhhome}/.config/tigervnc" ]; then mkdir -m 0700 -p "${jhhome}/.ssh" "${jhhome}/.config/tigervnc" chown -R ${jhuser}:$jhuser "${jhhome}/.ssh" "${jhhome}/.config" fi # Add jumphost public key to authorized_keys if [ ! -f "${jhhome}/.ssh/authorized_keys" ]; then echo "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQC7N8vGAD9qLrKqbkWBY81XnVszLH1DshOmRrBaCCpzWWW0E4nOCebDFPW4O0jUwtvYWfmxvxAaqFoRhvoIqCwyZcBK1fNGDVNeF9gmiWLThVAvs2CyVfvGFeIQXhdfyfJIxkX38Ft/zjg35Ksx1aM0Mvnzt9NMNoIjx3SGdAUAlnNXFuXAkYowzpF2MLfhGoWK473wmjs6zWbi0sl1+4o5jOxDNKS5vDPLmbRrogw68z94o24zeBFjThdICQujdnCtGmutr3Tw/DRaBg1roZuVv7lBzX43vo0Tt880kkDOmPpAiY4GSzIkW8rtEjvKKDME/5TBDMkHdgRqIq9wobIx7zcqg8O2Iji7Gsk5OVHneYqXhIohh0y19LpqJelkuAAjg+FOSGbEnB1Tm5MYZWmEuw+k6GWBQFv5i1nZFgw9KOaWP5MK1v6rrqntk91KTm5pWBs9Adrpfjv6CcxXMs1yjcFwGiHRO2OGb7KkTaIaHw6Nx3lXYLx40XetqWqnROU= $jhuser" > "${jhhome}/.ssh/authorized_keys" chown ${jhuser}:$jhuser "${jhhome}/.ssh/authorized_keys" chmod 600 "${jhhome}/.ssh/authorized_keys" fi # Copy bash profile from user opc cp -f "${opchome}/.bash_profile" "${jhhome}/" # Install server software and VNC server if [ "$yum" != "" ]; then $yum groupinstall -y "Server with GUI" >> $logfile $yum install -y tigervnc-server tigervnc-server-module >> $logfile # Configure VNC server echo ":1=$jhuser" >> /etc/tigervnc/vncserver.users echo "geometry=1280x1024" >> /etc/tigervnc/vncserver-config-defaults # Set VNC password vncpasswd=`which "vncpasswd" 2>/dev/null | sed 's|^no vncpasswd in .*||'` SetAllPasswords >> $logfile # Start VNC server if [ "$systemctl" != "" ]; then echo "Enabling VNC." $systemctl daemon-reload $systemctl enable --now vncserver@:1.service 2>> $logfile fi fi } # Get OS and version, if possible GetOSVersion if [ "$OS" != "Linux" ]; then echo "Unsupported platform '$OS'. Exiting." exitcode=1 else # Check if we have root privileges if [ "$ME" != "root" ]; then echo "Need to be 'root'. Exiting." exitcode=2 else # Check if we have curl installed if [ "$curl" = "" ]; then echo "No 'curl' in PATH. Exiting." exitcode=3 else logdir=`dirname "$logfile"` # Check if logdir exists - otherwise create it if [ ! -d "$logdir" ]; then mkdir -m 0755 -p "$logdir" fi # Create new empty log if RotateLog = true if [ "$RotateLog" = "true" ]; then printf "" > "$logfile" else touch "$logfile" fi WriteLog "Tool '$progstr' started." if [ "$NAME" != "" ]; then echo "OS: '$OS', Name: '$NAME', Version: '${ID}:$VERSION_ID'." >> $logfile else echo "OS: '$OS'." >> $logfile fi # Set password for opc, root and vnc user SetAllPasswords >> $logfile if [ "$PARAM" != "password" ]; then # Wait for internet connection CheckInternet # Only execute the following commands, if OS is 'ol' or 'rhel' and version >= 7 if [ "$VERSION_OK" = "true" ]; then # Set locale and timezone SetLocale "$Locale" >> $logfile SetTimeZone "$TimeZone" >> $logfile # Enable cockpit if needed EnableCockpit >> $logfile else echo "Skipping setting locale and timezone. Cockpit not enabled." >> $logfile fi # Get home dir of opc user opchome=$(eval echo ~opc) # ToDo: Does not work with Ubuntu # Install version specific software (before installing scripts and tools) if [ "$VERSION_MAIN" != "" ]; then case "$VERSION_MAIN" in 7) CompilePython39 >> $logfile if [ "$setuptools" = "" ]; then mv -f "${opchome}/.bash_profile" "${opchome}/.bash_profile.old" head -n 8 "${opchome}/.bash_profile.old" > "${opchome}/.bash_profile" chown opc:opc "${opchome}/.bash_profile" fi ;; 8) CompilePutty >> $logfile ;; esac fi # Install scripts (if not installed) if [ "$setuptools" = "" ]; then WriteLog "Installing admin scripts." if [ "$RotateLog" = "true" ]; then printf "" > /var/log/${toolsfile}.log fi "/tmp/$scriptsfile" >> $logfile DeleteFile "${opchome}/.bash_profile.old" if [ -f /etc/profile.d/alx.sh ]; then mkdir /etc/profile.d/unused mv -f /etc/profile.d/alx.sh /etc/profile.d/unused fi cp -f "${opchome}/.bash_profile" /root/ echo "get-platform --output line name,codename,version_id,machine,fqdn,ip_v4" >> "${opchome}/.bash_profile" else echo "Admin scripts already installed." >> $logfile fi # Install tools (if not installed) if [ "$rclone" = "" ]; then DownloadFile "$toolsfile" "false" stat=$? if [ $stat -eq 0 ]; then WriteLog "Installing tools." "/tmp/$toolsfile" >> $logfile fi else echo "Tools already installed. Update with 'setup-tools update' if needed." >> $logfile fi # Check if sudo is enabled for ocarun ocaruncmdfile="/etc/sudoers.d/101-oracle-cloud-agent-run-command" if [ ! -f "$ocaruncmdfile" ]; then echo 'ocarun ALL=NOPASSWD: /sbin/bootstrap, /sbin/cloud-agent' > $ocaruncmdfile chmod 440 $ocaruncmdfile fi # Update OS, if yum is available if [ "$yum" != "" -a "$Update" != "" ]; then $yum -y $Update >> $logfile fi # Install software for bastion services if [ "$PARAM" = "bastion" ]; then if [ "$vncpasswd" = "" ]; then # vncpasswd (coming with tigervnc) isn't installed yet - do it now InstallBastionAddons >> $logfile else echo "Bastion software already installed." >> $logfile fi fi fi fi fi fi # Cleanup and exit Cleanup exit $exitcode