Prompt IP address selection during backend installation

Prompt IP address selection during backend installation

Assign
Date
‣
Status
Completed

References

Goals

  1. Write a bash shell script to list all IP addresses into an array
  1. Show the addresses as numeric options with a timed (eg. 10 seconds) prompt, see stackoverflow link above
  1. Print the selected address, otherwise default to 127.0.0.1

YJ's Documentation

Aim

Create a shell script to prompt IP address selection during backend installation. This is to do away with modifying .env after installation: https://github.com/jymcheong/OpenEDR/wiki/0.-Installation#modify-env-to-reassign-frontend_ip

Shell Script

How does it work?

  1. Store all IP addresses of the machine into an array.
  1. For each IP address in the array, print out the IP address with its assigned number i.e. index.
  1. Ask user to enter the number that corresponds to the desired IP address.
      • If user entered a valid number from the list, set FRONTEND_IP to the selected IP address
        • notion image
      • If user entered a number NOT from the list, set FRONTEND_IP to default 127.0.0.1
        • notion image
      • If user does not enter within 10 seconds, set FRONTEND_IP to default 127.0.0.1
        • notion image
      • If user enters without any input i.e. presses enter key without typing anything, set FRONTEND_IP to default 127.0.0.1
        • notion image
      • If user's input is not a digit e.g. contains alphabets/special characters, set FRONTEND_IP to default 127.0.0.1
        • notion image

Codes

#!/bin/bash # Store all IP addresses of the machine into an array mapfile -t ipArray < <(ip a | awk '$1 == "inet" {gsub(/\/.*$/, "", $2); print $2}') # List out the IP addresses stored in the array echo "List of IP addresses" for key in "${!ipArray[@]}" do echo "$key: ${ipArray[$key]}" done # Set FRONTEND_IP to default 127.0.0.1 FRONTEND_IP=127.0.0.1 # Ask user to enter the desired IP address read -t 10 -p "Select the IP address that you wish to use e.g. 1: " option # If user does not enter within 10 seconds, set FRONTEND_IP to default 127.0.0.1 if [[ $? -gt 128 ]] ; then echo -e "\nTimeout. Setting IP address to default 127.0.0.1..." else # Check if input only contain numbers if ! [[ "$option" =~ [^[:digit:]]+ ]] ; then # If user enters without any input, set FRONTEND_IP to default 127.0.0.1 if [[ $option = "" ]]; then echo "No input entered. Setting IP address to default 127.0.0.1..." # If user entered a valid number from the list, set FRONTEND_IP to the selected IP address elif [[ -v "ipArray[option]" ]] ; then echo "Valid number selected. Setting IP address to ${ipArray[option]}..." FRONTEND_IP=${ipArray[option]} # If user entered a number not from the list, it is an invalid number. Set FRONTEND_IP to default 127.0.0.1 else echo "Invalid number selected. Setting IP address to default 127.0.0.1..." fi #If input does not contain only numbers, it is an invalid input. Set FRONTEND_IP to default 127.0.0.1 else echo "Invalid input. Setting IP address to default 127.0.0.1..." fi fi # Print the selected IP address echo "Selected IP address = $FRONTEND_IP"

References

Next Steps for Jym

There are a few ways to incorporate YJ's enhancements
  1. Integrate into installcontainers.sh but installation becomes more complex
  1. Use YJ's helper script to churn out a Caddy configuration to reverse proxy! Cleaner & flexible without the need to bring down the openedr-app container
I will try out option 2 since I don't wanna touch installcontainers script for now.
If there's a RCE vulnerability with Caddy, there could be a chance to cause it to expose an otherwise unreachable web service (orientDB frontend)

Portable Bash Script can be a pain...

ip a not very portable

For instance ip a won't work in macOS bash. Good thing, a minor tweak solves it:
ifconfig | awk '$1 == "inet" {gsub(/\/.*$/, "", $2); print $2}'
worked for both Ubuntu & macOS (haven't made time to test CentOS or whatever in fashion).
YJ brought up a good point that net-tools is not installed (deprecated) for newer Ubuntu. So looks like I need check for ifconfig first.. then switch to ip when necessary.

macOS bash is v3 due to licensing constraint

Next, mapfile is not available in macOS bash which is older (3.2.57 within Big Sur macOS), only bash4 & beyond has that.

Simplify Input Check

./t.sh: line 28: syntax error near `"ipArray[option]"'
./t.sh: line 28: `        elif [[ -v "ipArray[option]" ]] ; then'
The input check is rather complex... adding a track variable simplifies the whole logic that works for both macOS & most linux with Bash3 too:
#!/bin/bash # Store all IP addresses of the machine into an array IPcmd="ifconfig" if ! command -v ifconfig &> /dev/null then # cases where ifconfig or net-tools is not installed or not there like macOS IPcmd="ip a" fi I=0 declare -a ipArray ipArray=(`$IPcmd | awk '$1 == "inet" {gsub(/\/.*$/, "", $2); print $2}'`) # List out the IP addresses stored in the array echo "List of IP addresses" for key in "${!ipArray[@]}" do echo "$key: ${ipArray[$key]}" I=$((I+1)) done # Set FRONTEND_IP to default 127.0.0.1 FRONTEND_IP=127.0.0.1 # Ask user to enter the desired IP address read -t 10 -p "Select the IP address that you wish to use e.g. 1: " option # If user does not enter within 10 seconds, set FRONTEND_IP to default 127.0.0.1 if [[ $? -gt 128 ]] ; then echo -e "\nTimeout. Setting IP address to default 127.0.0.1..." else # Check if input only contain numbers if [[ $option < $I ]] ; then # If user enters without any input, set FRONTEND_IP to default 127.0.0.1 echo "Valid number selected. Setting IP address to ${ipArray[option]}..." FRONTEND_IP=${ipArray[option]} else echo "Invalid input. Setting IP address to default 127.0.0.1..." fi fi # Print the selected IP address echo "Selected IP address = $FRONTEND_IP"
Good exercise. There are teaching points especially when it comes to mitigate risks :)
So instead of adding to installcontainer.sh to become even more complex, we can pass environment variables to a docker container to run a simplified generateENV.sh that is based on installcontainer.sh, before calling docker-compose up -d & deal with sftpconf.zip hosting.
This script in turns go to a new install.sh . I said good exercise because I realise my own silliness to check for curl, docker & what not in installcontainer.sh but it should be in install.sh!
Since I want to assign both SFTP_IP & FRONTEND_IP, I will need something like this (converting the prompt for input into a function) together with parameter passing to have a different string prompt (see https://linuxize.com/post/bash-functions/):
my_function () { local func_result="some result" echo "$func_result" } func_result="$(my_function)" echo $func_result
Otherwise, I will end up 2 blocks of repeated code which is not preferred. Btw, all variables are global scope in bash
#!/bin/bash # Set FRONTEND_IP to default 127.0.0.1 TIMEOUT=10 DEFAULT_ADDRESS=127.0.0.1 # Store all IP addresses of the machine into an array IPcmd="ifconfig" if ! command -v ifconfig &> /dev/null then # cases where ifconfig or net-tools is not installed or not there like macOS IPcmd="ip a" fi I=0 declare -a ipArray ipArray=(`$IPcmd | awk '$1 == "inet" {gsub(/\/.*$/, "", $2); print $2}'`) # List out the IP addresses stored in the array echo "List of IP addresses" for key in "${!ipArray[@]}" do echo "$key: ${ipArray[$key]}" I=$((I+1)) done prompt_address_selection() { # Ask user to enter the desired IP address return_value=$DEFAULT_ADDRESS read -t $TIMEOUT -p "$1" option # If user does not enter within 10 seconds, set FRONTEND_IP to default 127.0.0.1 if [[ $? -gt 128 ]] ; then echo -e "\nTimeout. Setting IP address to default $DEFAULT_ADDRESS..." else # Check if input only contain numbers if [[ $option < $I ]] ; then # If user enters without any input, set FRONTEND_IP to default 127.0.0.1 return_value=${ipArray[option]} else echo "Invalid input. Setting IP address to default $DEFAULT_ADDRESS..." fi fi } # Prompt for selections prompt_address_selection "Select FRONTEND address (set to $DEFAULT_ADDRESS after $TIMEOUT-sec timeout): " FRONTEND_IP=$return_value echo "Selected $FRONTEND_IP for FRONTEND web access." prompt_address_selection "Select SFTP address (set to $DEFAULT_ADDRESS after $TIMEOUT-sec timeout): " SFTP_IP=$return_value echo "Selected $SFTP_IP for SFTP event-collection access."
Bash functions are not really functions but COMMANDs... every variable is global scope.
Instead of install.sh calling installcontainers.sh, I basically merged all into one because
  1. fetching almost 300MB worth of image just to run a script to generate a .env file is stupid & slow
  1. the more bash scripts, the more pain-points to ensure scripts run in multiple OS like macOS & linux variants
Â