Bash script not reading from file - bash

Purpose
This script is meant to copy one's public ssh identity file to many hosts that are listed in a file
Problem
The script is not properly reading IP's from the file. I believe this is because it is not reading the lines of the file. When I echo out the line (IP) it is reading, it is blank.
Code
#!/bin/bash
usage="Usage: $(basename "$0") [-h|-u|-i|-p|-f] -- Copys your identity key to a list of remote hosts
where:
-h Show this help text
-u Username of ssh user
-i Location of identity file (Default: /home/$USER/.ssh/id_rsa.pub)
-p Password (not secure)
-f Location of hosts file (Default: ./inventory)"
# Default location for files
CURR_DIR="$(cd "$(dirname "$0")" && pwd)"
HOSTS_FILE=${CURR_DIR}/inventory
IDENT_FILE=/home/$USER/.ssh/id_rsa.pub
# required_flag=false
while getopts 'hu:i:p:f:' option; do
case $option in
# Help
h) echo "$usage"; exit;;
# Hosts file
f) HOSTS_FILE=$OPTARG;;
# Password
p) PASSWORD=$OPTARG;;
# Indentity file
i) IDENT_FILE=$OPTARG; echo "$IDENT_FILE";;
# Username
u) USERNAME=$OPTARG;;
# Missing args
:) printf "Option -%s\n requires an argument." "-$OPTARG" >&2; echo "$usage" >&2; exit 1;;
# Illegal option
\?) printf "Illegal option: -%s\n" "$OPTARG" >&2; echo "$usage" >&2; exit 1;;
esac
done
shift "$((OPTIND-1))"
# Decrements the argument pointer so it points to next argument.
# $1 now references the first non-option item supplied on the command-line
#+ if one exists.
# if ! $required_flag && [[ -d $1 ]]
# then
# echo "You must specify a hosts file. -h for more help." >&2
# exit 1
# fi
while IFS= read -r line;
do
echo "Current IP: " "$IP"
echo "Current Username: " "$USERNAME"
echo "Current Identity: " "$IDENT_FILE"
echo "Current Hosts: " "$HOSTS_FILE"
echo "Current Password: " "$PASSWORD"
sshpass -p "$PASSWORD" ssh-copy-id -i "$IDENT_FILE" "$USERNAME"#"$IP"
done < $HOSTS_FILE
Output
$ ./autocopyid -u user -p password
Current IP:
Current Username: user
Current Identity: /home/user/.ssh/id_rsa.pub
Current Hosts: /home/user/inventory
Current Password: password
/usr/bin/ssh-copy-id: INFO: Source of key(s) to be installed: "/home/user/.ssh/id_rsa.pub"
/usr/bin/ssh-copy-id: INFO: attempting to log in with the new key(s), to filter out any that are already installed
/usr/bin/ssh-copy-id: ERROR: ssh: Could not resolve hostname : Name or service not known

You are not using the variable line for anything. Assuming $HOSTS_FILE points to a file that contains one IP address per line, you now have the IP address in variable line instead of IP in your loop. But since you use the variable name IP in the body of the loop, you should use that in the read statement, too.
So try
while IFS= read -r IP
instead of
while IFS= read -r line

Related

Bash getopts parsing script arguments unexpectedly

I have the following bash script:
#!/bin/bash
function usage() {
printf "\n"
echo "Updates a Lambda with env vars stored in a Secrets Manager secret. Complete drop n' swap."
printf "\n"
echo "Syntax: bash $0 -a <ARN> -s <secretName> [-h]"
echo "options:"
echo "a the ARN of the Lambda to update"
echo "s the name of the secret in Secrets Manager to use"
echo "h display help"
printf "\n"
exit
}
while getopts ":has:" option; do
case $option in
h) # display help
usage
;;
a) # ARN of the lambda
arn=${OPTARG}
[[ -z "$arn" ]] && usage
;;
s) # Secrets Manager secret (name)
secretName=${OPTARG}
[[ -z "$secretName" ]] && usage
;;
*) # catchall
usage
esac
done
echo "all good! arn = $arn and secret = $secretName"
When I run this I get:
myuser#mymachine myapp % bash myscript.sh -a abba -s babba
Updates a Lambda with env vars stored in a Secrets Manager secret. Complete drop n' swap.
Syntax: bash myscript.sh -a <ARN> -s <secretName> [-h]
options:
a the ARN of the Lambda to update
s the name of the secret in Secrets Manager to use
h display help
I was expecting all of the arguments (parsed by getopts) to be valid and to see the "all good!..." output instead of the usage output.
Where am I going awry? Am I using/parsing getopts incorrectly, or am I invoking the script with argument incorrectly? Or both?! Thanks in advance!
Since -a is supposed to have an associated value you want to append a : to the a option, ie:
# change this:
while getopts ":has:" option
# to this:
while getopts ":ha:s:" option
^--------------

bash - using a command line argument (hostname) to run an external command

First time post, please forgive any missing information.
I have a script that is supposed to work with icinga. I need icinga to log into my Linux box and run a command like "script ". The script will then run a command to that hostname like sudo /etc/init.d/apache2 status then report back "running or unused" and an exit status of 0 or 2.
I'm wondering how I could add another command and have it one or the other run depending on what hostname it's given. Half of them need apache2 to be running and the other half need to have a process called dss to be running. I'd rather not have two separate scripts. Here is the working script and sorry it's sloppy but I haven't done any clean up and I'm not real good at bash yet.
so the user would run the script ./chkdss2 or
#!/bin/bash
ec=0
ec1=2
var3=run
var4=unused
for host in "$#"
do
var1=`ssh $host sudo /etc/init.d/dss status|awk '{print $6}'`
var2="$( echo $var1 | cut -c 3-5 )"
if [[ "$var2" == "$var3" ]]; then
echo "$host is running"
echo $ec
else
echo "$host is not running"
echo $ec1
fi
done
There are a couple ways to test if a particular hostname is for apache or dss. You only need to have a list of hostnames for each case, and check if the received hostnames are included in said lists.
Method 1: using arrays
#!/bin/bash
# Method 1, using array lists of hosts
apachehosts=('ap1' 'ap2' 'ap3')
dsshosts=('dss1' 'dss2' 'dss3')
for host in "$#"
do
if printf '%s\n' "${apachehosts[#]}" | grep -Fxq "$host"
then
echo "$host: APACHE HOST"
elif printf '%s\n' "${dsshosts[#]}" | grep -Fxq "$host"
then
echo "$host: DSS HOST"
else
echo "ERROR, $host: unknown host"
fi
done
To modify the lists of hosts, simply add or remove values in the declaration of arrays apachehosts and dsshosts.
Method 2: using case
#!/bin/bash
# Method 2, using case
for host in "$#"
do
case "$host" in
'ap1'|'ap2'|'ap3')
echo "CASE, $host: APACHE HOST"
;;
'dss1'|'dss2'|'dss3')
echo "CASE, $host: DSS HOST"
;;
*)
echo "ERROR CASE, $host: unknown host"
;;
esac
done
Here, you edit the patterns in each case.
Method 3: using if
#!/bin/bash
# Method 3, using if
for host in "$#"
do
if [[ "$host" == 'ap1' || "$host" == 'ap2' || "$host" == 'ap3' ]]
then
echo "IF, $host: APACHE HOST"
elif [[ "$host" == 'dss1' || "$host" == 'dss2' || "$host" == 'dss3' ]]
then
echo "IF, $host: DSS HOST"
else
echo "IF, $host: unknown host"
fi
done
Here you modify the if conditions. I prefer the other methods, since this one is more complicated to edit, it is not as clear, especially if your list of hosts is long.
Method 4: condition on the hostnames
If you are lucky, there is some pattern to your hostnames. Ex. all apache servers start with letters ap, all your dss servers include dss in the name, ...
You can then simply use 2 if statements to decide which is which.
#!/bin/bash
# Method 4, patterns
for host in "$#"
do
if [[ $(echo "$host" | grep -c -e "^ap") -ne 0 ]]
then
echo "PATTERNS, $host: APACHE HOST"
elif [[ $(echo "$host" | grep -c -e "dss") -ne 0 ]]
then
echo "PATTERNS, $host: DSS host"
else
echo "PATTERNS, $host: unknown host"
fi
done
Note: hostname apdss1 would come out as an Apache server here. Previous methods would respond "unknown host". You patterns must be strict enough to avoid mismatches.
I had a similar task to get few report items using single ssh request.
I had to retrieve in singel ssh command:
Full hostname (FQDN)
Linux version
IP address of its Docker host if exist, or "none"
I got my script to work in 3 stage.
1. Get multiple lines of information from remote host
ssh -q dudi-HP-Compaq-Elite-8300-MT <<< '
date +%F:%T # line 1: time stamp
hostname -f # line 2: hostname
awk "/DESCR/{print \$3}" /etc/lsb-release # line 3 : host linux distribution version
ip a | awk "/inet / && !/127.0.0.1/{sub(\"/.*\",\"\",\$2);printf(\"%s \", \$2)}" # line 4: list IP address to the host
'
Results:
2022-03-05:22:22:21
dudi-HP-Compaq-Elite-8300-MT
20
192.168.2.111 192.168.122.1 172.17.0.1
2. Process multiple lines of information from remote host
Read lines of information from remote host, into an array sshResultsArr.
readarray -t sshResultsArr < <(ssh -q dudi-HP-Compaq-Elite-8300-MT <<< '
date +%F:%T # line 1: time stamp
hostname -f # line 2: hostname
awk "/DESCR/{print \$3}" /etc/lsb-release # line 3 : host linux distribution version
ip a | awk "/inet / && !/127.0.0.1/{sub(\"/.*\",\"\",\$2);printf(\"%s \", \$2)}" # line 4: list IP address to the host
')
hostname=${sshResultsArr[1]}
osVersion=${sshResultsArr[2]}
hasDockerIp=$(grep -Eo "172(.[[:digit:]]{1,3}){3}" <<< "${sshResultsArr[3]}") # find IP starting with 172
hasDockerIp=${hasDockerIp:="none"} # if not found IP set to "NONE"
printf "%s \t OS version: %s \t has Docker IP: %s\n" "$hostname" "$osVersion" "$hasDockerIp"
Result:
dudi-HP-Compaq-Elite-8300-MT OS version: 20 has Docker IP: 172.17.0.1
3. Process each remote host in a loop
#!/bin/bash
for host in "$#"; do
readarray -t sshResultsArr < <(ssh -q $host <<< '
date +%F:%T # line 1: time stamp
hostname -f # line 2: hostname
awk "/DESCR/{print \$3}" /etc/lsb-release # line 3 : host linux distribution version
ip a | awk "/inet / && !/127.0.0.1/{sub(\"/.*\",\"\",\$2);printf(\"%s \", \$2)}" # line 4: list IP address to the host
')
hostname=${sshResultsArr[1]}
osVersion=${sshResultsArr[2]}
hasDockerIp=$(grep -Eo "172(.[[:digit:]]{1,3}){3}" <<< "${sshResultsArr[3]}") # find IP starting with 172
hasDockerIp=${hasDockerIp:="none"} # if not found IP set to "NONE"
printf "%s \t OS version: %s \t has Docker IP: %s\n" "$hostname" "$osVersion" "$hasDockerIp"
done
I was able to take a little bit from the answers I received and put together something that works well. Thank you all for your answers.
for host in "$#"
do
case "$host" in
('vho1uc1-primary'|'vho1uc2-backup'|'vho2uc1-primary'|'vho2uc2-backup'|'vho3uc1-primary'|'vho3uc2-backup'|'vho10uc1-primary')
var1=`ssh "$host" sudo /etc/init.d/apache2 status|awk '{print $4}'`
var2="$( echo $var1 | cut -c 3-5 )"
if [[ "$var2" == "$var3" ]]; then
echo "Apache2 on $host is running"
echo "0"
else
echo "Apache2 on $host is not running"
echo "2"
fi
;;
*)
esac
done

Curl command in bash script error

I'm trying to run a curl command within a bash script and I'm coming across the following error:
curl: (26) failed creating formpost data
When I echo the curl command and run that from the command line, that has no problems running. I've ensured there are read permissions for everyone on the file and the folder it's in, as well as using relative and absolute paths.
Here's a sample of the script:
#!/bin/bash
usage(){
echo "usage: <command> options: <u|p|f> "
}
while getopts u:p:f: option
do
case "${option}"
in
u) USERNAME=${OPTARG};;
p) PORT=${OPTARG};;
f) FILE=$OPTARG;;
esac
done
if [ $OPTIND -eq 1 ]; then
#echo "No options were passed";
usage
exit
fi
echo "Please enter your password: "
read PASSWORD
TEST="curl -u $USERNAME:$PASSWORD -F file=#\"$FILE\" -F force=true -F install=true http://localhost:$PORT/test.jsp"
echo $TEST
$TEST
echo $?
Thanks!

Jail user in SFTP + SSH

I want to jail an "normal" user with the special group developer for SFTP and SSH.
The user can only navigate in /srv/DEVELOPMENT (SSH/SFTP) AND over SSH, the user can only execute a limit of commands (see the script at the bottom).
Why i want this?
I work on a little project. Last days, another developers wan't to support the project with their experiences. The developer can be edit the "developer-website" and can start/stop/restart an Node.js application over SSH. The next think is: The user must use the shell to change their account password.
Currently, i have configured the SSH-Deamon with following steps:
Jail over SFTP
Match Group developer
X11Forwarding no
AllowTcpForwarding yes
ChrootDirectory /srv/DEVELOPMENT
ForceCommand internal-sftp
The user was added by following commands/options:
useradd --base-dir /srv/ --home-dir /srv/ --no-user-group --shell /srv/shell.sh $USERNAME
usermod -G developer $USERNAME
id $USERNAME
passwd $USERNAME
Current Directory Permissions
/srv developer:root 0755
/srv/DEVELOPMENT developer:root 0750
/srv/DEVELOPMENT/* developer:root 0777
With SFTP it work's correctly. The second part to jail the user over SSH is currently little bit harder. This step won't work currently, and thats is my question.
The chroot is limited on internal-sfpt. When i try to login, the connection will be aborted with the message, that the connection is only allowed for sftp:
ssh TestUser#example.com
TestUser#example.com's password:
This service allows sftp connections only.
Connection to example.com closed.
Here, i had remove ForceCommand on the SSH-Deamon config > The Login will be succeed.
But here is my problem
When i try to login, no executables cant be used:
ssh TestUser#example.com
TestUser#example.com's password:
Linux example.com 4.9.0-3-amd64 #1 SMP Debian 4.9.30-2+deb9u2 (2017-06-26) x86_64
Last login: Sun Jul 30 18:00:11 2017 from ****************
/srv/shell.sh: No such file or directory
Connection to example.com closed.
/srv/shell.sh is a custom shell-script to limit the commands, for sample:
#!/bin/bash
commands=("man" "passwd" "ls" "account", "whoami", "clear", "cd")
RED='\033[0;31m'
YELLOW='\033[0;33m'
MAGENTA='\033[0;35m'
CYAN='\033[0;36m'
NC='\033[0m' # No Color
INDENTATION=' '
SYSTEM_UPTIME=`uptime --pretty`
SYSTEM_USERS=`who -q`
SYSTEM_QUOTA="None"
SYSTEM_RAM="None"
timestamp(){
date +"%Y-%m-%d %H:%M:%S"
}
log(){
echo -e "[$(timestamp)]\t$1\t$(whoami)\t$2" >> /var/log/developer-user/shell.log;
}
execute() {
# EXIT
if [[ "$ln" == "exit" ]] || [[ "$ln" == "q" ]]
then
exit
# HELP
elif [[ "$ln" == "help" ]]
then
echo "Type exit or q to quit."
echo "Commands you can use:"
echo " account"
echo " help"
echo " echo"
echo " man <ManPage>"
echo " passwd"
echo " ls"
echo " clear"
echo " cd"
# CD
elif [[ "$ln" =~ ^cd\ .*$ ]]
then
LAST=`pwd`
$ln
CURRENT=`pwd`
if [[ $CURRENT == "/srv" ]]
then
log CHANGE_DIR FAILED_PERMISSIONS "$ln"
echo -e "${RED}ERROR:${NC} Sorry, you can't change to the previous directory ${YELLOW}\"${CURRENT}\"${NC}."
cd $LAST
elif [[ ! "$CURRENT" =~ ^/srv/DEVELOPMENT ]]
then
log CHANGE_DIR FAILED_PERMISSIONS "$ln"
echo -e "${RED}ERROR:${NC} Sorry, you can't change to the directory ${YELLOW}\"${CURRENT}\"${NC}."
cd $LAST
elif [[ `stat -c "%G" ${CURRENT}` == "friendlounge" ]]
then
log CHANGE_DIR "$ln"
else
log CHANGE_DIR FAILED_PERMISSIONS "$ln"
echo -e "${RED}ERROR:${NC} You have no permissions on ${YELLOW}\"${CURRENT}\"${NC}."
cd $LAST
fi
# ECHO
elif [[ "$ln" =~ ^echo\ .*$ ]]
then
$ln
log COMMAND "$ln"
# ACCOUNT
elif [[ "$ln" = "account" ]]
then
echo -e "YOUR ACCOUNT:"
echo -e "Username: $(whoami)"
# OTHERS
else
ok=false
for cmd in "${commands[#]}"
do
if [[ "$cmd" == "$ln" ]]
then
ok=true
fi
done
if $ok
then
$ln
else
echo -e "${RED}ERROR:${NC} You have no permissions to execute ${YELLOW}\"${ln}\"${NC}."
log DENIED "$ln"
fi
fi
}
# WELCOME MESSAGE
echo -e "${INDENTATION}${MAGENTA}Account:${NC}${INDENTATION}$(whoami)"
echo -e "${INDENTATION}${MAGENTA}Date:${NC}${INDENTATION}${INDENTATION}$(timestamp)"
echo -e "${INDENTATION}${MAGENTA}Uptime:${NC}${INDENTATION}${INDENTATION}${SYSTEM_UPTIME}"
echo -e "${INDENTATION}${MAGENTA}Users:${NC}${INDENTATION}${INDENTATION}${SYSTEM_USERS}"
echo -e "${INDENTATION}${MAGENTA}Quota:${NC}${INDENTATION}${INDENTATION}${SYSTEM_QUOTA}"
echo -e "${INDENTATION}${MAGENTA}RAM:${NC}${INDENTATION}${INDENTATION}${SYSTEM_RAM}"
log LOGIN "$#"
cd
trap "trap=\"\";log LOGOUT;exit" EXIT
# Optionally check for '-c custom_command' arguments passed directly to shell
# Then you can also use ssh user#host custom_command, which will execute /root/rbash.sh
if [[ "$1" == "-c" ]]
then
shift
execute "$#"
else
while echo -e -n "${RED}$(whoami)${YELLOW}#${CYAN}$(hostname) ${YELLOW}$(pwd) ${MAGENTA}#${NC} " && read ln
do
execute "$ln"
done
fi
This shell-script checks the permission of the user and force only to the /srv/DEVELOPMENT directory or subdirectorys.
It's irrelevant to set another login-shells like /bin/bash or other - On each login, the SSH-Demon close the connection after the error message XXXX: No such file or directory.
I had try to set different permissions and other. i can't resolve the problem to connect over ssh.
Anyone have an idea?
Before you answer
yes, i known the possible security reasons (for sample, to manage the permissions over my "own" shell-login-script)
no, i don't want to install huge alternatives like schroot or jailkit (found on google, reading the first minutes says, that these alternatives uses a completely decoupled system like a virtual-machine(?) - inform me, when it's wrong with explicit informations)
You may try this, It's is very easy to use.
you can jail a user in 3 steps.
add user
create a jail with a simple configuration file.
jail this user.
The tool is located at :https://github.com/pymumu/jail-shell

Executing a local script on a remote Machine

I have a script on my local machine, but need to run it on a remote machine without copying it over there (IE, I can't sftp it over and just run it there)
I currently have the following functioning command
echo 'cd /place/to/execute' | cat - test.sh | ssh -T user#hostname
However, I also need to provide a commandline argument to test.sh.
I tried just adding it after the .sh, like I would for local execution, but that didn't work:
echo 'cd /place/to/execute' | cat - test.sh "arg" | ssh -T user#hostname
"cat: arg: No such file or directory" is the resulting error
You need to override the arguments:
echo 'set -- arg; cd /place/to/execute' | cat - test.sh | ssh -T user#hostname
The above will set the first argument to arg.
Generally:
set -- arg1 arg2 arg3
will overwrite the $1, $2, $3 in bash.
This will basically make the result of cat - test.sh a standalone script that doesn't need any arguments`.
Depends on the complexity of the script that you have. You might want to rewrite it to be able to use rpcsh functionality to remotely execute shell functions from your script.
Using https://gist.github.com/Shadowfen/2b510e51da6915adedfb saved into /usr/local/include/rpcsh.inc (for example) you could have a script
#!/bin/sh
source /usr/local/include/rpcsh.inc
MASTER_ARG=""
function ahelper() {
# used by doremotely just to show that we can
echo "master arg $1 was passed in"
}
function doremotely() {
# this executes on the remote host
ahelper $MASTER_ARG > ~/sample_rpcsh.txt
}
# main
MASTER_ARG="newvalue"
# send the function(s) and variable to the remote host and then execute it
rpcsh -u user -h host -f "ahelper doremotely" -v MASTER_ARG -r doremotely
This will give you a ~/sample_rpcsh.txt file on the remote host that contains
master arg newvalue was passed in
Copy of rpcsh.inc (in case link goes bad):
#!/bin/sh
# create an inclusion guard (to prevent multiple inclusion)
if [ ! -z "${RPCSH_GUARD+xxx}" ]; then
# already sourced
return 0
fi
RPCSH_GUARD=0
# rpcsh -- Runs a function on a remote host
# This function pushes out a given set of variables and functions to
# another host via ssh, then runs a given function with optional arguments.
# Usage:
# rpcsh -h remote_host -u remote_login -v "variable list" \
# -f "function list" -r mainfunc [-- param1 [param2]* ]
#
# The "function list" is a list of shell functions to push to the remote host
# (including the main function to run, and any functions that it calls).
#
# Use the "variable list" to send a group of variables to the remote host.
#
# Finally "mainfunc" is the name of the function (from "function list")
# to execute on the remote side. Any additional parameters specified (after
# the --)gets passed along to mainfunc.
#
# You may specify multiple -v "variable list" and -f "function list" options.
#
# Requires that you setup passwordless access to the remote system for the script
# that will be running this.
rpcsh() {
if ! args=("$(getopt -l "host:,user:,pushvars:,pushfuncs:,run:" -o "h:u:v:f:r:A" -- "$#")")
then
echo getopt failed
logger -t ngp "rpcsh: getopt failed"
exit 1
fi
sshvars=( -q -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null )
eval set -- "${args[#]}"
pushvars=""
pushfuncs=""
while [ -n "$1" ]
do
case $1 in
-h|--host) host=$2;
shift; shift;;
-u|--user) user=$2;
shift; shift;;
-v|--pushvars) pushvars="$pushvars $2";
shift; shift;;
-f|--pushfuncs) pushfuncs="$pushfuncs $2";
shift; shift;;
-r|--run) run=$2;
shift; shift;;
-A) sshvars=( "${sshvars[#]}" -A );
shift;;
-i) sshvars=( "${sshvars[#]}" -i $2 );
shift; shift;;
--) shift; break;;
esac
done
remote_args=( "$#" )
vars=$([ -z "$pushvars" ] || declare -p $pushvars 2>/dev/null)
ssh ${sshvars[#]} ${user}#${host} "
#set -x
$(declare -p remote_args )
$vars
$(declare -f $pushfuncs )
$run ${remote_args[#]}
"
}

Resources