2022-10-28 19:02:46 +02:00
#!/usr/bin/env bash
2022-10-30 14:46:08 +01:00
# pilpil client installer v0.1
2022-10-28 19:02:46 +02:00
#
# https://sharats.me/posts/shell-script-best-practices/
set -o errexit
set -o nounset
set -o pipefail
2022-10-30 14:46:08 +01:00
if [ [ " ${ TRACE -0 } " = = "1" ] ] ; then
set -o xtrace
fi
2022-10-28 19:02:46 +02:00
# Change to script dir
cd " $( dirname " $0 " ) "
2022-10-19 14:54:58 +02:00
# Colored output
#~ set +x
bold = $( tput bold)
function red( ) {
echo -e " ${ bold } \x1B[31m $1 \x1B[0m "
2022-10-30 14:46:08 +01:00
if [ ! -z " ${ 2 - } " ] ; then
2022-10-19 14:54:58 +02:00
echo -e " \x1B[31m $( $2 ) \x1B[0m "
fi
}
function green( ) {
echo -e " ${ bold } \x1B[32m $1 \x1B[0m "
2022-10-30 14:46:08 +01:00
if [ ! -z " ${ 2 - } " ] ; then
2022-10-19 14:54:58 +02:00
echo -e " \x1B[32m $( $2 ) \x1B[0m "
fi
}
function yellow( ) {
echo -e " ${ bold } \x1B[33m $1 \x1B[0m "
2022-10-30 14:46:08 +01:00
if [ ! -z " ${ 2 - } " ] ; then
2022-10-19 14:54:58 +02:00
echo -e " \x1B[33m $( $2 ) \x1B[0m "
fi
}
2022-10-30 14:46:08 +01:00
# 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
2022-10-22 15:04:00 +02:00
# TODO : accomodate for devices block with name mmcblk0p1|p2
2022-10-28 19:02:46 +02:00
if [ [ " $SDCARD " = = "" ] ] || [ [ ! -e " $SDCARD " ] ]
2022-10-18 17:18:55 +02:00
then
2022-10-28 19:02:46 +02:00
red "Please specify an existing device block for your sd-card, e.g: '/dev/sda'." >& 2
2022-10-18 17:18:55 +02:00
exit 0
fi
DD_BS = "128K"
2022-10-25 18:43:20 +02:00
DISK_IMAGE = " $HOME /niels/imgs/2022-10-25-pilpil-WIP.img.xz "
2022-10-30 14:46:08 +01:00
if [ [ ! -f " $DISK_IMAGE " ] ] ; then
2022-10-28 19:02:46 +02:00
red "Disk image not found, aborting..." >& 2
2022-10-22 15:04:00 +02:00
exit 0
fi
2022-10-22 14:22:38 +02:00
CONFIG_DIR = " $HOME /niels/pilpil-server "
2022-10-30 14:46:08 +01:00
if [ [ ! -d " $CONFIG_DIR " ] ] ; then
2022-10-28 19:02:46 +02:00
red "Config directory not found, aborting..." >& 2
2022-10-22 15:04:00 +02:00
exit 0
fi
2022-10-28 19:02:46 +02:00
HTTP_SECRET = $( openssl rand -base64 12)
2022-10-19 18:39:23 +02:00
PI_USER = "pil"
2022-10-18 17:18:55 +02:00
BOOT_MOUNT = " /run/media/ $USER /boot "
ROOTFS_MOUNT = " /run/media/ $USER /rootfs "
#~ LOCAL_MEDIA_DIR="$HOME/Videos"
LOCAL_MEDIA_DIR = " $HOME /niels/medias "
2022-10-30 14:46:08 +01:00
if [ [ ! -d " $LOCAL_MEDIA_DIR " ] ] ; then
2022-10-28 19:02:46 +02:00
red "Medias directory not found, aborting..." >& 2
2022-10-22 15:04:00 +02:00
exit 0
fi
2022-10-19 18:39:23 +02:00
REMOTE_MEDIA_DIR = " $ROOTFS_MOUNT /home/ $PI_USER /Videos "
2022-10-28 19:02:46 +02:00
# WIFI AP config
2022-10-19 18:39:23 +02:00
IP_RANGE = "10.42.0.1"
2022-10-18 17:18:55 +02:00
SSID = "omen"
PASSWD = "EpQmSmXH123"
IFW = "wlo1"
#Band (bg = 2.4Ghz, a= 5Ghz)
BAND = "bg"
# Hidden SSID
#~ HIDE="802-11-wireless.hidden false"
# Set channel manually
2022-10-25 18:43:20 +02:00
CHAN = "802-11-wireless.channel 1"
2022-10-18 17:18:55 +02:00
#
#
# 0. Create AP connection
#
2022-10-30 14:46:08 +01:00
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
2022-10-18 17:18:55 +02:00
# 0.a set IP range on server
2022-10-30 14:46:08 +01:00
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
2022-10-18 17:18:55 +02:00
# 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
2022-10-30 14:46:08 +01:00
# 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
2022-10-18 17:18:55 +02:00
green " Got $CLIENT_NUMBER ...\n "
2022-10-20 18:27:26 +02:00
# 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
# Get first IP in specified range
#~ IP=$(echo $IP_RANGE | awk -F. '{print $4}')
FIRST = 1
IP = 10
2022-10-30 14:46:08 +01:00
#~ echo -e "First IP is $(($RANGE_START)) ...\n"
2022-10-18 17:18:55 +02:00
# Remove IP's last byte
IP_RANGE_3B = $( echo $IP_RANGE | awk -F. '{print $1"."$2"."$3"."}' )
2022-10-30 14:46:08 +01:00
echo -e " First IP is $IP_RANGE_3B $IP ...\n "
2022-10-18 17:18:55 +02:00
# Generate SSL cert with IPs in IP_RANGE
2022-10-20 18:27:26 +02:00
IP_CNT = $IP
2022-10-18 17:18:55 +02:00
IP_ARRAY = ( )
2022-10-30 14:46:08 +01:00
while [ [ " $IP_CNT " -lt $(( " ${ CLIENT_NUMBER - } " + " ${ IP - } " )) ] ]
2022-10-18 17:18:55 +02:00
do
IP_ARRAY += ( " IP: $IP_RANGE_3B $IP_CNT " )
( ( IP_CNT++) )
done
# Convert array to string
HOST_LIST = " $( IFS = "," ; echo " ${ IP_ARRAY [*] } " ) "
2022-10-30 14:46:08 +01:00
green " Got host list : ${ HOST_LIST - } \n "
2022-10-18 17:18:55 +02:00
# 5. Generate valid ssl cert/key for every IP in range
# https://unix.stackexchange.com/questions/104171/create-ssl-certificate-non-interactively
2022-10-30 14:46:08 +01:00
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
2022-10-18 17:18:55 +02:00
# Proceed with each host
for HOST in " ${ IP_ARRAY [@] } "
do
HOST = $( echo $HOST | awk -F: '{print $2}' )
2022-10-20 18:27:26 +02:00
HOST_NAME = " pilpil- $( echo $HOST | awk -F. '{print $4}' ) "
2022-10-18 17:18:55 +02:00
# 1. Copy img to sd
2022-10-30 14:46:08 +01:00
green " 1/14 : Imaging $SDCARD with the file $DISK_IMAGE ...\n "
2022-10-19 18:39:23 +02:00
red " Are you sure you want to ERASE THE CONTENT of $SDCARD ? Type uppercase 'yes' to confirm. "
2022-10-18 17:18:55 +02:00
read -n 4 GO_DD
2022-10-28 19:02:46 +02:00
if [ [ " $GO_DD " != "YES" ] ]
2022-10-18 17:18:55 +02:00
then
2022-10-28 19:02:46 +02:00
red "Answer was different from 'YES'. Aborting..." >& 2
2022-10-18 17:18:55 +02:00
break
fi
red " Received answer $GO_DD . Running dd on $SDCARD in 5 seconds. "
2022-10-30 14:46:08 +01:00
if [ [ ! " $DRY_RUN " ] ] ; then
sleep 5
fi
2022-10-18 17:18:55 +02:00
GO_DD = 0
# unmount / remount new filesystem
2022-10-30 14:46:08 +01:00
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 "
2022-10-19 18:39:23 +02:00
echo "Remounting..."
2022-10-30 14:46:08 +01:00
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
2022-10-18 17:18:55 +02:00
## Enable SSH
2022-10-30 14:46:08 +01:00
green " 5/14 : Enabling SSH server on boot : $BOOT_MOUNT /ssh ...\n "
if [ [ ! " $DRY_RUN " ] ] ; then
touch " $BOOT_MOUNT /ssh "
sync
fi
2022-10-18 17:18:55 +02:00
## Generate SSH private/public key and install it - Disable passwd login
2022-10-30 14:46:08 +01:00
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
2022-10-18 17:18:55 +02:00
red " New SSH key pair generated as $HOME /.ssh/ $HOST_NAME . Add to ~/.ssh/config ? (y/n) "
read -n 2 ADD_SSH_CONF
2022-10-30 14:46:08 +01:00
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..."
2022-10-18 17:18:55 +02:00
fi
# Copy public key to rpi
2022-10-30 14:46:08 +01:00
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
2022-10-18 17:18:55 +02:00
# Disable PW login
2022-10-30 14:46:08 +01:00
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
2022-10-18 17:18:55 +02:00
# 3. Configure wifi with static IP
2022-10-30 14:46:08 +01:00
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
2022-10-20 18:27:26 +02:00
update_config = 1
country = FR
network = {
ssid = \" $SSID \" # Nom du réseau auquel on se connecte
psk = \" $PASSWD \" # Mot de passe wifi
# Optional parameters
# scan_ssid=1 # hidden ssid
# Specify 2.4 or 5G freq
# https://w1.fi/cgit/hostap/tree/wpa_supplicant/wpa_supplicant.conf#n910
# 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"
2022-10-30 14:46:08 +01:00
sync
fi
2022-10-18 17:18:55 +02:00
# Request specific IP to dhcp server
2022-10-30 14:46:08 +01:00
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
2022-10-18 17:18:55 +02:00
# 5. Install previously generated SSL key/crt
2022-10-30 14:46:08 +01:00
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
2022-10-22 14:22:38 +02:00
#~ # 6. Copy medias
2022-10-30 14:46:08 +01:00
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
2022-10-22 14:22:38 +02:00
fi
2022-10-30 14:46:08 +01:00
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
2022-10-22 14:22:38 +02:00
fi
2022-10-18 17:18:55 +02:00
# Unmount FS
2022-10-30 14:46:08 +01:00
green "14/14 : Unmounting filesystems"
if [ [ ! " $DRY_RUN " ] ] ; then
umount " $BOOT_MOUNT "
umount " $ROOTFS_MOUNT "
fi
2022-10-22 15:04:00 +02:00
yellow " Client $(( $IP - 9 )) / $CLIENT_NUMBER done. "
2022-10-30 14:46:08 +01:00
#~ echo "$IP / $(($IP_CNT-1))"
if [ [ " $IP " -lt " $(( $IP_CNT - 1 )) " ] ] ; then
2022-10-25 18:43:20 +02:00
red "Please swap sd card in reader and enter uppercase 'yes' to proceed with next client or hit Ctrl-C:"
2022-10-18 17:18:55 +02:00
read -n 4 GO_ON
if [ " $GO_ON " != "YES" ]
then
2022-10-28 19:02:46 +02:00
red "Answer was different from 'YES'. Aborting...\n" >& 2
2022-10-18 17:18:55 +02:00
break
fi
GO_ON = 0
( ( IP++) )
else
green "All done !"
exit 1
fi
done
yellow "Nothing more to do."
exit 0
#~ rm "$CONFIG_DIR/selfCA.key"