My Bash script won't cache an item - bash

I'm trying to use a Bash script I found and have slightly modified to automatically update the DNS settings on GoDaddy.
It sort of works, however I'm not getting the echo "no ip address, program exit" because I don't think on line 28 the cache file is being created. Would anybody be able to tell me what's going on? I'm using Raspbian. Many thanks in advance!
#/bin/bash
# This script is used to check and update your GoDaddy DNS server to the IP address of your current internet connection.
#
# Original PowerShell script by mfox: https://github.com/markafox/GoDaddy_Powershell_DDNS
# Ported to bash by sanctus
# Added AAAA record by Binny Chan
#
# Improved to take command line arguments and output information for log files by pollito
#
# First go to GoDaddy developer site to create a developer account and get your key and secret
#
# https://developer.godaddy.com/getstarted
#
# Be aware that there are 2 types of key and secret - one for the test server and one for the production server
# Get a key and secret for the production server
# Check an A record and a domain are both specified on the command line.
if [ $# -ne 2 ]; then
echo "usage: $0 type a_record domain_name"
echo "usage: $0 AAAA www my_domain"
exit 1
fi
# Set A record and domain to values specified by user
name=$1 # name of A record to update
domain=$2 # name of domain to update
cache=/tmp/.mcsync.$name.$domain.addr
[ -e $cache ] && old=`cat $cache`
# Modify the next two lines with your key and secret
key="key" # key for godaddy developer API
secret="secret" # secret for godaddy developer API
headers="Authorization: sso-key $key:$secret"
#echo $headers
# Get public ip address there are several websites that can do this.
ret=$(curl -s GET "http://ipinfo.io/json")
currentIp=$(echo $ret | grep -oE "\b([0-9]{1,3}\.){3}[0-9]{1,3}\b")
# Check empty ip address or not
if [ -z "$currentIp" ]; then
echo $name"."$domain": $(date): no ip address, program exit"
exit 1
fi
# Check cache ip, if matched, program exit
if [ "$old" = "$currentIp" ]; then
echo $name"."$domain": $(date): address unchanged, program exit $currentIp"
echo "IPs equal. Exiting..."
exit
else
echo $name"."$domain": $(date): currentIp:" $currentIp
fi
#Get dns ip
result=$(curl -s -k -X GET -H "$headers" \
"https://api.godaddy.com/v1/domains/$domain/records/A/$name")
dnsIp=$(echo $result | grep -oE "\b([0-9]{1,3}\.){3}[0-9]{1,3}\b")
echo $name"."$domain": $(date): dnsIp:" $dnsIp
# ip not match
if [ "$dnsIp" != $currentIp ];
then
echo $name"."$domain": $(date): IPs not equal. Updating."
request='{"data":"'$currentIp'","ttl":3600}'
#echo $request
nresult=$(curl -i -k -s -X PUT \
-H "$headers" \
-H "Content-Type: application/json" \
-d $request "https://api.godaddy.com/v1/domains/$domain/records/A/$name")
#echo $nresult
echo $name"."$domain": $(date): IPs not equal. Updated."
fi

No, the message is on line 44, which is written because $currentIp is empty, which is retrieved on line 39: reading the response of curl request to "http://ipinfo.io/json".
otherwise just remove the cache file
rm /tmp/.mcsync.$name.$domain.addr
where $name and $domain are replaced with first and second argument to script.

Related

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

How to write an output of a grep from an openssl command?

Consider you have list of thousand IP addresses in a text file - one per line. I want to be able to grab all possible anomalies from each IP address using openssl s_client command. So far, anomalies are certificate expired, self signed certificate, and issuer CN to include emailAddress=root#localhost.localdomain.
Overall, I want to be able to obtain concise error message per IP address if there is any. My current bash script looks like:
for ip in `awk '{print $1}' < general_certs.txt`; do
# Print IP address currently checking
echo -e $ip;
if timeout 30 openssl s_client -connect $ip:443| grep -i 'error' ; then
# Write error information and IP address to a file
echo `openssl s_client -connect $ip:443| grep -i 'error'` $ip >> general_errors;
else
# Write noerror information and IP address to another file
echo "noerror" $ip >> general_noerror;
fi;
done
First issue I have with the code is that it is not optimized and that I am skeptical if it returns accurate results. The end goal with the above script is to identify all untrusted certificate containing IPs.
Second issue I had with the above code that I could not echo $ip first because it would get truncated by the message itself. So, I ended up writing out $ip after the error message.
It is not necessary to use openssl if there is a more genuine solution to my question.
You can try to run them all in one shot by putting the processes in the background:
#!/bin/bash
max=100
counter=0
for ip in `awk '{print $1}' < general_certs.txt`; do
(( counter++ ))
(
results=$( timeout 30 openssl s_client -connect $ip:443 2> /dev/null )
if [ "$results" = "" ]; then
echo "$ip noresponse"
else
echo -n "$ip "
echo "$results" | grep -i 'error' || echo "noerror"
fi
) >> general_errors &
if [ $counter -eq $max ]; then
wait
counter=0
fi
done
wait
This was the input:
$ cat general_certs.txt
stackoverflow.com
redhat.com
google.com
8.8.8.8
vt.edu
bls.gov
8.8.4.4
This was the output:
$ cat general_errors
8.8.4.4 noerror
stackoverflow.com noerror
google.com noerror
vt.edu noerror
bls.gov noerror
redhat.com noerror
8.8.8.8 noresponse
If you have some that fail, I can test them.
If you're trying to get which ip's have a untrusted certificate, you could try, even if it's not the best option, with curl.
Something like:
for ip in `awk '{print $1}' < general_certs.txt`; do
echo -e $ip
curl https://${ip}:443 &> /dev/null
if [ $? == 51 ]; then
echo "upsis, https://${ip}:443 has a untrusted certificate" >> general_err
else
echo "yai, https://${ip}:443 doesn't have a untrusted certificate" >> general_noerr
fi
done
Notice that here you're only checking for unstrusted certificates ( error 51 in curl ), but the command could send any other error and it would go to general_noerror.

Bash script not reading from file

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

Monitoring a file and wanting to send an email with the `mail` command

I am going to monitor a file with a [ -s filename ] option and when that file gets data, to email me with appropriate information. I've searched the site and came up with a few options, but didn't click for me.
I'm trying to do the following to simply test "mail" I've also tried mailx. The user did not receive the email and the output provided is exactly the same for mail and mailx.
Ultimately, I'm going to edit the original bash script with the mail or mailx command, assuming I can get it to work.
This is what I'm doing and output
command line returns when I hit, enter.
Thank you for any input for this, I totally appreciate it.
[user#somehost ~]$ echo "TEST" | mail -s subject user#mail.com
[user#somehost ~]$ send-mail: warning: inet_protocols: IPv6 support is disabled: Address family not supported by protocol
send-mail: warning: inet_protocols: configuring for IPv4 support only
postdrop: warning: inet_protocols: IPv6 support is disabled: Address family not supported by protocol
postdrop: warning: inet_protocols: configuring for IPv4 support only
Better to use inotifywait for the event you're after (perhaps CLOSE_WRITE?).
You'll need to configure your mailer with an SMTP server, and possibly (hopefully) some credentials.
I usually use the mailx from the heirloom-mailx package, with the following script:
#!/bin/false
function send_email {
# args are: subject [to] [from_name] [from_address]
# subject is required
# to and from_* are optional, defaults below
# stdin takes the message body... don't forget
# this function uses mailx from heirloom-mailx
local SMTP_SRVR="${YOUR_SERVER}"
local SMTP_USER="${YOUR_USERNAME}"
local SMTP_PASS="${YOUR_PASSWORD}"
local DEFAULT_TO="${YOUR_RECIPIENT}"
local DEFAULT_FROM_NAME="${YOUR_SENDER_NAME}"
local DEFAULT_FROM_ADDR="${YOUR_SENDER_EMAIL}"
if [ $# -lt 1 ]; then
echo "${FUNCNAME}(): missing subject (arg 1)..." >&2
return 1
fi
local SUBJECT="$1"
shift
if [ $# -lt 1 ]; then
local TO="${DEFAULT_TO}"
else
local TO="$1"
shift
fi
if [ $# -lt 1 ]; then
local FROM="${DEFAULT_FROM_NAME}"
else
local FROM="$1"
shift
fi
if [ $# -lt 1 ]; then
FROM="${FROM} <${DEFAULT_FROM_ADDR}>"
else
FROM="${FROM} <$1>"
shift
fi
mailx -s"${SUBJECT}" -r"${FROM}" -Ssmtp="${SMTP_SRVR}" -Ssmtp-auth-user="${SMTP_USER}" -Ssmtp-auth-password="${SMTP_PASS}" "${TO}"
return $?
}
You can then use the above (e.g: my_send_email.inc) from another script like this:
#!/bin/bash
source my_send_email.inc
echo "Testing" | send_email "${MY_EMAIL}"

Send e-mail using command line and save IP rules

I am coding a script to select some IPs from a table in a DB, and then use IPTables rules to ban theses IPs, last step is to notify by e-mail, but I am getting 2 errors:
#!/bin/bash
Now=$(date +"%d-%m-%Y %T")
fileLocation="/var/lib/mysql/DBName/"
fileName="ip2ban.txt"
filePath=$fileLocation$fileName
curLocation=$(pwd)
#Connect to DB and select ban_ip
mysql -u root -pPASSWORD -D DBName -e 'SELECT ip INTO OUTFILE "'$filePath'" FROM ban_ip WHERE ip_tables = "0"' >> banIP.log 2>&1
selRes=$?
# If the command was successful
if [ $selRes -eq "0" ]
then
# We need to check if the file exists on the saved location
#find $fileLocation -type f -iname "ip2ban.txt" -empty => To check if file empty or not
if [ -f $filePath ]
then
mv $filePath $curLocation'/ip2ban.txt'
# Connect to DB and update the ban_ip
mysql -u root -pPASSWORD -D DBName -e 'UPDATE ban_ip SET ip_tables = "1" WHERE ip_tables = "0"' >> banIP.log 2>&1
upRes=$?
if [ $upRes -eq "0" ]
then
# Send message for succesful result
echo -e "Database updated with new banned IPs on $Now \nThank you for using this script" 2>&1 | sed '1!b;s/^/To: myID#gmail.com\nSubject: New banned IPs[Success]\n\n/' | sendmail -t
else
# Send message for failure result
echo -e "We cannot update the ban_ip table on $Now \nThank you for using this script" 2>&1 | sed '1!b;s/^/To: myID#gmail.com\nSubject: [Failure] New banned IPs\n\n/' | sendmail -t
fi
fi
else
echo 'Something wrong with Select statment on' $Now >> banIP.log
fi
# Save IPTables rules
iptables-save > /root/Scripts/IPTables/BannedIPs.conf // LIGNE 53
I am getting 2 errors:
line 53: iptables-save: command not found
line 37: sendmail: command not found
However the sendamil is already installed, with mail, postfix:
# which sendmail
/usr/sbin/sendmail
# which mail
/usr/bin/mail
# which postfix
/usr/sbin/postfix
Thanks for your usual support
According to the crontab(5) man page for Linux:
PATH is set to "/usr/bin:/bin".
Meaning your shell script will not be able to find anything under /usr/sbin or /sbin. Change this with by adding the following near the top of your script:
PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
Also the environment can be set from within the crontab. See https://stackoverflow.com/a/14694543/5766144 for how to do this.

Resources