From 713a1eef79209de98e377a2f719461e36c4df6be Mon Sep 17 00:00:00 2001 From: ABelliqueux Date: Sun, 30 Oct 2022 14:46:08 +0100 Subject: [PATCH] Add dry run, sanitize bash syntax --- linux_server_deploy.sh | 307 ++++++++++++++++++++++++----------------- 1 file changed, 184 insertions(+), 123 deletions(-) diff --git a/linux_server_deploy.sh b/linux_server_deploy.sh index eece9bd..460b06d 100755 --- a/linux_server_deploy.sh +++ b/linux_server_deploy.sh @@ -1,47 +1,59 @@ #!/usr/bin/env bash -# +# pilpil client installer v0.1 # # https://sharats.me/posts/shell-script-best-practices/ set -o errexit set -o nounset set -o pipefail -if [[ "${TRACE-0}" == "1" ]]; then set -o xtrace; fi +if [[ "${TRACE-0}" == "1" ]]; then + set -o xtrace +fi + # Change to script dir cd "$(dirname "$0")" -# -if [[ "${1-}" =~ ^-*h(elp)?$ ]]; then - echo 'Usage: ./pilpil-server.sh path_to_device -This is an awesome bash script to make your life better. - -' - exit -fi -# Options -# Device block to write on # Colored output #~ set +x bold=$(tput bold) function red(){ echo -e "${bold}\x1B[31m$1 \x1B[0m" - if [ ! -z "${2}" ]; then + if [ ! -z "${2-}" ]; then echo -e "\x1B[31m $($2) \x1B[0m" fi } function green(){ echo -e "${bold}\x1B[32m$1 \x1B[0m" - if [ ! -z "${2}" ]; then + if [ ! -z "${2-}" ]; then echo -e "\x1B[32m $($2) \x1B[0m" fi } function yellow(){ echo -e "${bold}\x1B[33m$1 \x1B[0m" - if [ ! -z "${2}" ]; then + if [ ! -z "${2-}" ]; then echo -e "\x1B[33m $($2) \x1B[0m" fi } -SDCARD=$1 +# Display help if -h(elpf) used +if [[ "${1-}" =~ ^-*h(elp)?$ ]]; then + echo 'Usage: ./pilpil-server.sh path_to_device + +This script will setup as much pilpil-clients as needed. +' + exit +fi +# Set dry run if -d(ry) used +if [[ "${1-}" =~ ^-*d(ry)?$ ]]; then + DRY_RUN=1 + yellow "!!! Running in dry mode. No modifications will be made.\n" +fi +# Options +# Device block to write on +if [[ ! "$DRY_RUN" ]]; then + SDCARD="${1-}" +else + SDCARD="${2-}" +fi # TODO : accomodate for devices block with name mmcblk0p1|p2 if [[ "$SDCARD" == "" ]] || [[ ! -e "$SDCARD" ]] then @@ -50,14 +62,12 @@ then fi DD_BS="128K" DISK_IMAGE="$HOME/niels/imgs/2022-10-25-pilpil-WIP.img.xz" -if [[ ! -f "$DISK_IMAGE" ]] -then +if [[ ! -f "$DISK_IMAGE" ]]; then red "Disk image not found, aborting..." >&2 exit 0 fi CONFIG_DIR="$HOME/niels/pilpil-server" -if [[ ! -d "$CONFIG_DIR" ]] -then +if [[ ! -d "$CONFIG_DIR" ]]; then red "Config directory not found, aborting..." >&2 exit 0 fi @@ -67,8 +77,7 @@ BOOT_MOUNT="/run/media/$USER/boot" ROOTFS_MOUNT="/run/media/$USER/rootfs" #~ LOCAL_MEDIA_DIR="$HOME/Videos" LOCAL_MEDIA_DIR="$HOME/niels/medias" -if [[ ! -d "$LOCAL_MEDIA_DIR" ]] -then +if [[ ! -d "$LOCAL_MEDIA_DIR" ]]; then red "Medias directory not found, aborting..." >&2 exit 0 fi @@ -88,29 +97,42 @@ CHAN="802-11-wireless.channel 1" # # 0. Create AP connection # -# If connection exists, delete it -nmcli con delete $SSID -nmcli con add type wifi ifname $IFW con-name $SSID autoconnect yes ssid $SSID -nmcli con modify $SSID 802-11-wireless.mode ap 802-11-wireless.band $BAND $CHAN $HIDE ipv4.method shared -nmcli con modify $SSID wifi-sec.key-mgmt wpa-psk -nmcli con modify $SSID 802-11-wireless-security.proto rsn -nmcli con modify $SSID 802-11-wireless-security.pairwise ccmp -nmcli con modify $SSID wifi-sec.psk $PASSWD - +green " * Creating hotspot connection for NetworkManager \n" +if [[ ! "$DRY_RUN" ]]; then + # If connection exists, delete it + nmcli con delete $SSID + nmcli con add type wifi ifname $IFW con-name $SSID autoconnect yes ssid $SSID + nmcli con modify $SSID 802-11-wireless.mode ap 802-11-wireless.band "${BAND-}" "${CHAN-}" "${HIDE-}" ipv4.method shared + nmcli con modify $SSID wifi-sec.key-mgmt wpa-psk + nmcli con modify $SSID 802-11-wireless-security.proto rsn + nmcli con modify $SSID 802-11-wireless-security.pairwise ccmp + nmcli con modify $SSID wifi-sec.psk $PASSWD +fi # 0.a set IP range on server -echo -e "Setting IP range $IP_RANGE/24 in /etc/NetworkManager/system-connections/$SSID.nmconnection ... \n" -sudo sed -i "/method=shared/a address1=$IP_RANGE/24, $IP_RANGE" /etc/NetworkManager/system-connections/$SSID.nmconnection -# Remove existing leases -sudo rm /var/lib/NetworkManager/dnsmasq-$IFW.leases -sudo systemctl restart NetworkManager - -nmcli radio wifi on -nmcli con up $SSID - +green " * Setting IP range $IP_RANGE/24 in /etc/NetworkManager/system-connections/$SSID.nmconnection ... \n" +if [[ ! "$DRY_RUN" ]]; then + sudo sed -i "/method=shared/a address1=$IP_RANGE/24, $IP_RANGE" /etc/NetworkManager/system-connections/$SSID.nmconnection + # Remove existing leases + sudo rm /var/lib/NetworkManager/dnsmasq-$IFW.leases + # restart NM + sudo systemctl restart NetworkManager + # Turn hotspot on + nmcli radio wifi on + nmcli con up $SSID +fi # 0.b ask for number of clients # This will be used to determine static IP yellow "Nombre de clients à configurer : " read -n 4 CLIENT_NUMBER +# Check input +if [[ ! "${CLIENT_NUMBER}" =~ ^[0-9]+$ ]]; then + red "\nInput was not a number, aborting.\n" + exit 0 +fi +if [[ "${CLIENT_NUMBER}" -lt 1 ]]; then + red "\nInput was 0, nothing to do.\n" + exit 0 +fi green "Got $CLIENT_NUMBER...\n" # For some reason networkmanager finds it clever to offer only IPs in range 10-255 even when asked for a /24, /28, etc... # So IPs start at 10 @@ -118,36 +140,39 @@ green "Got $CLIENT_NUMBER...\n" #~ IP=$(echo $IP_RANGE | awk -F. '{print $4}') FIRST=1 IP=10 -echo -e "First IP is $(($RANGE_START)) ...\n" +#~ echo -e "First IP is $(($RANGE_START)) ...\n" # Remove IP's last byte IP_RANGE_3B=$(echo $IP_RANGE | awk -F. '{print $1"."$2"."$3"."}') +echo -e "First IP is $IP_RANGE_3B$IP ...\n" # Generate SSL cert with IPs in IP_RANGE IP_CNT=$IP IP_ARRAY=() -while [[ "$IP_CNT" -lt $(($CLIENT_NUMBER+$IP)) ]] + +while [[ "$IP_CNT" -lt $(("${CLIENT_NUMBER-}"+"${IP-}")) ]] do IP_ARRAY+=("IP:$IP_RANGE_3B$IP_CNT") ((IP_CNT++)) done # Convert array to string HOST_LIST="$(IFS=","; echo "${IP_ARRAY[*]}")" -yellow "Got host list : $HOST_LIST \n" +green "Got host list : ${HOST_LIST-} \n" # 5. Generate valid ssl cert/key for every IP in range # https://unix.stackexchange.com/questions/104171/create-ssl-certificate-non-interactively -yellow "Generating SSL crt/key for $HOST_LIST...\n" -openssl req -new -newkey rsa:4096 -days 1825 -nodes -x509 \ --subj "/C=/ST=Denial/L=/O=/CN=$IP_RANGE$FIRST" \ --addext "subjectAltName=$HOST_LIST" \ --keyout "$CONFIG_DIR/selfCA.key" -out "$CONFIG_DIR/selfCA.crt" -#sudo openssl req -x509 -nodes -days 3650 -newkey rsa:2048 -keyout "$ROOTFS_MOUNT/etc/ssl/private/nginx-selfsigned.key" -out "$ROOTFS_MOUNT/etc/ssl/certs/nginx-selfsigned.crt" - +green "Generating SSL crt/key for $HOST_LIST...\n" +if [[ ! "$DRY_RUN" ]]; then + openssl req -new -newkey rsa:4096 -days 1825 -nodes -x509 \ + -subj "/C=/ST=Denial/L=/O=/CN=$IP_RANGE$FIRST" \ + -addext "subjectAltName=$HOST_LIST" \ + -keyout "$CONFIG_DIR/selfCA.key" -out "$CONFIG_DIR/selfCA.crt" + #sudo openssl req -x509 -nodes -days 3650 -newkey rsa:2048 -keyout "$ROOTFS_MOUNT/etc/ssl/private/nginx-selfsigned.key" -out "$ROOTFS_MOUNT/etc/ssl/certs/nginx-selfsigned.crt" +fi # Proceed with each host for HOST in "${IP_ARRAY[@]}" do HOST=$(echo $HOST | awk -F: '{print $2}') HOST_NAME="pilpil-$(echo $HOST | awk -F. '{print $4}')" # 1. Copy img to sd - green "Operation 1/13 : Imaging $SDCARD with the file $DISK_IMAGE ...\n" + green "1/14 : Imaging $SDCARD with the file $DISK_IMAGE ...\n" red "Are you sure you want to ERASE THE CONTENT of $SDCARD ? Type uppercase 'yes' to confirm." read -n 4 GO_DD if [[ "$GO_DD" != "YES" ]] @@ -156,49 +181,70 @@ do break fi red "Received answer $GO_DD. Running dd on $SDCARD in 5 seconds." - sleep 5 + if [[ ! "$DRY_RUN" ]]; then + sleep 5 + fi GO_DD=0 # unmount / remount new filesystem - green "Unmounting $BOOT_MOUNT and $ROOTFS_MOUNT ...\n" - umount $BOOT_MOUNT - umount $ROOTFS_MOUNT - xzcat "$DISK_IMAGE" | sudo dd of=$SDCARD bs="$DD_BS" oflag=dsync status=progress && sync - green "Operation 3/13 : Remounting $BOOT_MOUNT and $ROOTFS_MOUNT ...\n" + green "2/14 : Unmounting $BOOT_MOUNT and $ROOTFS_MOUNT ...\n" + if [[ ! "$DRY_RUN" ]]; then + umount $BOOT_MOUNT + umount $ROOTFS_MOUNT + xzcat "$DISK_IMAGE" | sudo dd of=$SDCARD bs="$DD_BS" oflag=dsync status=progress && sync + fi + green "3/14 : Remounting $BOOT_MOUNT and $ROOTFS_MOUNT ...\n" echo "Remounting..." - sleep 1 - systemctl --user restart gvfs-udisks2-volume-monitor - sleep 3 - green "Operation 4/13 : Changing hostname to $HOST_NAME ...\n" - # Change hostname - echo "$HOST_NAME" | sudo tee "$ROOTFS_MOUNT/etc/hostname" - # Reflect that in /etc/hosts - sudo sed -i "$ d" "$ROOTFS_MOUNT/etc/hosts" - echo -e "127.0.1.1\t$HOST_NAME" | sudo tee -a "$ROOTFS_MOUNT/etc/hosts" + if [[ ! "$DRY_RUN" ]]; then + sleep 1 + systemctl --user restart gvfs-udisks2-volume-monitor + sleep 3 + fi + green "4/14 : Changing hostname to $HOST_NAME in $ROOTFS_MOUNT/etc/hostname and $ROOTFS_MOUNT/etc/hosts ...\n" + if [[ ! "$DRY_RUN" ]]; then + # Change hostname + echo "$HOST_NAME" | sudo tee "$ROOTFS_MOUNT/etc/hostname" + # Reflect that in /etc/hosts + sudo sed -i "$d" "$ROOTFS_MOUNT/etc/hosts" + echo -e "127.0.1.1\t$HOST_NAME" | sudo tee -a "$ROOTFS_MOUNT/etc/hosts" + fi ## Enable SSH - green "Operation 5/13 : Enabling SSH server on boot ...\n" - touch "$BOOT_MOUNT/ssh" - sync + green "5/14 : Enabling SSH server on boot : $BOOT_MOUNT/ssh ...\n" + if [[ ! "$DRY_RUN" ]]; then + touch "$BOOT_MOUNT/ssh" + sync + fi ## Generate SSH private/public key and install it - Disable passwd login - green "Operation 6/13 : Generating private/public SSH key as $HOME/.ssh/$HOST_NAME ...\n" - ssh-keygen -t ed25519 -f "$HOME/.ssh/$HOST_NAME" -N "" + green "6/14 : Generating private/public SSH key as $HOME/.ssh/$HOST_NAME ...\n" + if [[ ! "$DRY_RUN" ]]; then + ssh-keygen -t ed25519 -f "$HOME/.ssh/$HOST_NAME" -N "" + fi red "New SSH key pair generated as $HOME/.ssh/$HOST_NAME. Add to ~/.ssh/config ? (y/n)" read -n 2 ADD_SSH_CONF - if [[ "$ADD_SSH_CONF" == "y" ]] - then - # Add to ~/.ssh/config - echo -e "\nHost $HOST_NAME\n\tHostname $HOST\n\tIdentityFile ~/.ssh/$HOST_NAME\n\tUser $PI_USER" | tee -a "$HOME/.ssh/config" + if [[ "$ADD_SSH_CONF" == "y" ]];then + green "Adding $HOST_NAME with ip $HOST in $HOME/.ssh/config" + if [[ ! "$DRY_RUN" ]]; then + # Add to ~/.ssh/config + echo -e "\nHost $HOST_NAME\n\tHostname $HOST\n\tIdentityFile ~/.ssh/$HOST_NAME\n\tUser $PI_USER" | tee -a "$HOME/.ssh/config" + fi + else + yellow "\nAnswer was different from 'y', skipping..." fi # Copy public key to rpi - green "Operation 7/13 : Installing public SSH key $HOME/.ssh/$HOST.pub in $ROOTFS_MOUNT/home/$PI_USER/.ssh/authorized_keys...\n" - sudo cp "$HOME/.ssh/$HOST_NAME.pub" "$ROOTFS_MOUNT/home/$PI_USER/.ssh/authorized_keys" - sync + green "7/14 : Installing public SSH key $HOME/.ssh/$HOST.pub in $ROOTFS_MOUNT/home/$PI_USER/.ssh/authorized_keys...\n" + if [[ ! "$DRY_RUN" ]]; then + sudo cp "$HOME/.ssh/$HOST_NAME.pub" "$ROOTFS_MOUNT/home/$PI_USER/.ssh/authorized_keys" + sync + fi # Disable PW login - green "Operation 8/13 : Disabling SSH password based login in $ROOTFS_MOUNT/etc/ssh/sshd_config ...\n" - echo -e "PasswordAuthentication no\nChallengeResponseAuthentication no\nUsePAM no" | sudo tee -a "$ROOTFS_MOUNT/etc/ssh/sshd_config" - sync + green "8/14 : Disabling SSH password based login in $ROOTFS_MOUNT/etc/ssh/sshd_config ...\n" + if [[ ! "$DRY_RUN" ]]; then + echo -e "PasswordAuthentication no\nChallengeResponseAuthentication no\nUsePAM no" | sudo tee -a "$ROOTFS_MOUNT/etc/ssh/sshd_config" + sync + fi # 3. Configure wifi with static IP - green "Operation 9/13 : Configuring wireless connection to $SSID with pw $PASSWD : ...\n" - echo "ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=netdev + green "9/14 : Configuring wireless connection to $SSID with pw $PASSWD : ...\n" + if [[ ! "$DRY_RUN" ]]; then + echo "ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=netdev update_config=1 country=FR @@ -212,50 +258,65 @@ network={ # https://fr.wikipedia.org/wiki/Liste_des_canaux_Wi-Fi#Bande_2,4_GHz # scan_freq=2412 2437 2462 }" | sudo tee "$ROOTFS_MOUNT/etc/wpa_supplicant/wpa_supplicant.conf" - sync - # Request specific IP to dhcp server - green "Operation 10/13 : Setting static IP $HOST in $ROOTFS_MOUNT/etc/dhcpcd.conf...\n" - echo -e "interface wlan0\nrequest $HOST" | sudo tee -a "$ROOTFS_MOUNT/etc/dhcpcd.conf" - sync - # 5. Install previously generated SSL key/crt - #sudo openssl req -x509 -nodes -days 3650 -newkey rsa:2048 -keyout "$ROOTFS_MOUNT/etc/ssl/private/nginx-selfsigned.key" -out "$ROOTFS_MOUNT/etc/ssl/certs/nginx-selfsigned.crt" - green "Operation 11/13 : Installing public SSH key $CONFIG_DIR/selfCA.crt in $ROOTFS_MOUNT/etc/ssl/certs/ ...\n" - sudo cp "$CONFIG_DIR/selfCA.crt" "$ROOTFS_MOUNT/etc/ssl/certs/nginx-selfsigned.crt" - sudo cp "$CONFIG_DIR/selfCA.key" "$ROOTFS_MOUNT/etc/ssl/private/nginx-selfsigned.key" - sync - #~ Change VLC/pilpil http auth secret - sed -i "s:secret:$HTTP_SECRET:g" "$CONFIG_DIR/pilpil-server.toml" - sed -i "s:secret:$HTTP_SECRET:g" "$ROOTFS_MOUNT/home/pil/.config/systemd/user/vlc.service" - sed -i "s:secret:$HTTP_SECRET:g" "$ROOTFS_MOUNT/home/pil/pilpil-client/defaults.toml" - #~ # 6. Copy medias - green "Operation 12/13 : Syncing media folder $LOCAL_MEDIA_DIR/ with $REMOTE_MEDIA_DIR/ \n" - # Remove filler file - if [[ -f "$REMOTE_MEDIA_DIR/remove_me" ]] - then - sudo rm "$REMOTE_MEDIA_DIR/remove_me" - fi - # Get available space on rootfs - ROOTFS_AVAILABLE_SPACE=$(df -t ext4 -P $ROOTFS_MOUNT | tail -1 | awk '{print $4}') - # Get Media folder size - MEDIA_SIZE=$(du -c $LOCAL_MEDIA_DIR | tail -1 | awk '{print $1}') - # Only copy files if enough space available - if [[ "$MEDIA_SIZE" -lt "$ROOTFS_AVAILABLE_SPACE" ]] - then - USER_ID=$( cat "$ROOTFS_MOUNT/etc/passwd" | grep $PI_USER | awk -F: '{print $3}' ) - GROUP_ID=$( cat "$ROOTFS_MOUNT/etc/passwd" | grep $PI_USER | awk -F: '{print $4}' ) - sudo cp "$LOCAL_MEDIA_DIR/"* "$REMOTE_MEDIA_DIR/" - sudo chown -R $USER_ID:$GROUP_ID "$REMOTE_MEDIA_DIR" sync - else - red "Not enough space on $ROOTFS_MOUNT, skipping..." + fi + # Request specific IP to dhcp server + green "10/14 : Setting static IP $HOST in $ROOTFS_MOUNT/etc/dhcpcd.conf...\n" + if [[ ! "$DRY_RUN" ]]; then + echo -e "interface wlan0\nrequest $HOST" | sudo tee -a "$ROOTFS_MOUNT/etc/dhcpcd.conf" + sync + fi + # 5. Install previously generated SSL key/crt + green "11/14 : Installing SSL : $CONFIG_DIR/selfCA.crt and $CONFIG_DIR/selfCA.key in $ROOTFS_MOUNT/etc/ssl/certs/ and $ROOTFS_MOUNT/etc/ssl/private/nginx-selfsigned.key...\n" + if [[ ! "$DRY_RUN" ]]; then + sudo cp "$CONFIG_DIR/selfCA.crt" "$ROOTFS_MOUNT/etc/ssl/certs/nginx-selfsigned.crt" + sudo cp "$CONFIG_DIR/selfCA.key" "$ROOTFS_MOUNT/etc/ssl/private/nginx-selfsigned.key" + sync + fi + green "12/14 : Installing http auth secrets in $CONFIG_DIR/pilpil-server.toml, $ROOTFS_MOUNT/home/pil/.config/systemd/user/vlc.service and $ROOTFS_MOUNT/home/pil/pilpil-client/defaults.toml...\n" + if [[ ! "$DRY_RUN" ]]; then + #~ Change VLC/pilpil http auth secret + sed -i "s:secret:$HTTP_SECRET:g" "$CONFIG_DIR/pilpil-server.toml" + sed -i "s:secret:$HTTP_SECRET:g" "$ROOTFS_MOUNT/home/pil/.config/systemd/user/vlc.service" + sed -i "s:secret:$HTTP_SECRET:g" "$ROOTFS_MOUNT/home/pil/pilpil-client/defaults.toml" + fi + #~ # 6. Copy medias + green "13/14 : Syncing media folder $LOCAL_MEDIA_DIR/ with $REMOTE_MEDIA_DIR/ \n" + if [[ ! "$DRY_RUN" ]]; then + # Remove filler file + if [[ -f "$REMOTE_MEDIA_DIR/remove_me" ]]; then + sudo rm "$REMOTE_MEDIA_DIR/remove_me" + fi + fi + if [[ -d "$ROOTFS_MOUNT" ]]; then + # Get available space on rootfs + ROOTFS_AVAILABLE_SPACE=$(df -t ext4 -P "$ROOTFS_MOUNT" | tail -1 | awk '{print $4}') + yellow "Space available on rootfs : $ROOTFS_AVAILABLE_SPACE sectors" + # Get Media folder size + MEDIA_SIZE=$(du -c "$LOCAL_MEDIA_DIR" | tail -1 | awk '{print $1}') + yellow "Size of medias : $MEDIA_SIZE sectors" + fi + if [[ ! "$DRY_RUN" ]]; then + # Only copy files if enough space available + if [[ "$MEDIA_SIZE" -lt "$ROOTFS_AVAILABLE_SPACE" ]]; then + USER_ID=$( cat "$ROOTFS_MOUNT/etc/passwd" | grep $PI_USER | awk -F: '{print $3}' ) + GROUP_ID=$( cat "$ROOTFS_MOUNT/etc/passwd" | grep $PI_USER | awk -F: '{print $4}' ) + sudo cp "$LOCAL_MEDIA_DIR/"* "$REMOTE_MEDIA_DIR/" + sudo chown -R $USER_ID:$GROUP_ID "$REMOTE_MEDIA_DIR" + sync + else + red "Not enough space on $ROOTFS_MOUNT, skipping..." + fi fi # Unmount FS - green "Operation 13/13 : Unmounting filesystems" - umount $BOOT_MOUNT - umount $ROOTFS_MOUNT + green "14/14 : Unmounting filesystems" + if [[ ! "$DRY_RUN" ]]; then + umount "$BOOT_MOUNT" + umount "$ROOTFS_MOUNT" + fi yellow "Client $(($IP-9))/$CLIENT_NUMBER done." - if [[ "$IP" -le "$IP_CNT" ]] - then + #~ echo "$IP / $(($IP_CNT-1))" + if [[ "$IP" -lt "$(($IP_CNT-1))" ]]; then red "Please swap sd card in reader and enter uppercase 'yes' to proceed with next client or hit Ctrl-C:" read -n 4 GO_ON if [ "$GO_ON" != "YES" ]