Date comparison with EPOCH to find an expiry in bash script - bash

#!/bin/bash
for ADDR in `netstat -plant|grep LISTEN|grep http|awk '{print $4}'|egrep -v ':80$|:5555$'|sort -u`; do
EXPDATE=`openssl s_time 2>/dev/null | openssl s_client -connect $ADDR 2>/dev/null | openssl x509 -dates 2>/dev/null | grep ^notA | cut -f2 -d= | sed -e "s/ GMT//"`
printf "\t\t\t|%s\t|%s\t|\t%s\t|\n" "$ADDR" "$EXPDATE"
done
EXPDATES="$(echo "$EXPDATE" | awk '{print $1,$2,$4,$3}')"
CURREPOCH="$(date +%s)"
for i in "$EXPDATES"; do
CREXPEPOCH="$(date +%s -d "$i")"
if [[ "$CURREPOCH" -gt "$CREXPEPOCH" ]]; then
echo "No Expiry Found."
else
echo "Cert expired"
fi
done
Here, I'm getting dates from EXPDATE which has multiple date values as shown below,
Jul 12 12:00:00 2019
Jun 18 12:00:00 2019
May 8 00:00:00 2018
Nov 14 00:00:00 2017
And, converting to EPOCH time for better comparison with current EPOCH..
If any past date found, script should return "expired", else "no expiry found"..
I tried above script which is not working..
How I can do that? Any help?

The below tracks contents in an array rather than trying to abuse strings as iterable.
#!/usr/bin/env bash
# return all addresses that smell like HTTP
get_addrs() {
netstat -plant \
| awk '/LISTEN/ && /http/ && ! ($4 ~ /:(80|5555)$/) { print $4; }' \
| sort -u
}
# Given a local server address, return a usable client address
# converts wildcard addresses to loopback ones.
clientAddr() {
local addr=$1
case $addr in
0.0.0.0:*) addr=127.0.0.1:${addr#0.0.0.0:} ;;
:::*) addr='localhost:'"${addr#:::}" ;;
esac
printf '%s\n' "$addr"
}
# Given a local address that runs a HTTPS server, return the last valid date for its certificate
endDateForAddr() {
local addr endDate
addr=$(clientAddr "$1") || return
endDate=$(openssl s_client -connect "${addr}" </dev/null 2>/dev/null \
| openssl x509 -dates \
| awk -F= '/^notAfter/ { print $2; exit }')
[[ $endDate ]] && printf '%s\n' "$endDate"
}
# Loop over our local HTTPS services...
expDates=( )
while read -r addr; do
# find an address we can use to refer to each...
addr=$(clientAddr "$addr") || continue
# ...and use that to find its certificate expirey date.
result=$(endDateForAddr "$addr") || continue
# then add that to our array.
expDates+=( "$result" )
done < <(get_addrs)
# in bash 4.3, this is more efficiently written: printf -v curr_epoch '%(%s)T' -1
curr_epoch="$(date +%s)"
for expdate in "${expDates[#]}"; do
exp_epoch=$(date +%s -d "$expdate")
if (( curr_epoch > exp_epoch )); then
echo "$expdate is in the past"
else
echo "$expdate is in the future"
fi
done
...its output (correct as of this writing):
Jul 12 12:00:00 2019 is in the future
Jun 18 12:00:00 2019 is in the future
May 8 00:00:00 2018 is in the future
Nov 14 00:00:00 2017 is in the future

Related

How to convert date in Macbook (Terminal)?

I need to convert the date of the macbook via CLI because I'm running a script:
Date we have to convert 2019-09-17 15:32:27
Final format Jun 17 15:32:27
I tried it with the following script:
date_raw=`grep "^.*,.*,.*,.*,.*,.*,"$i"," ${SNIFFER_BUFFER_USAGE} | head -1 | awk -F ',' '{ print $2}'`
if [ $OSX -eq 1 ];
then
dataHoraMinuto=$(date -j -f "%Y-%m-%d %H:%M:%S" "$date_raw" +"%b %d %H:%M:%S")
else
dataHoraMinuto=`date +"%b %d %H:%M:%S" -d "$date_raw"`
fi
$ date -d"2019-09-17 15:32:27" +"%b %d %T"
Sep 17 15:32:27

Select directories with date in their names from a date range

I'm creating a list of directories with the requested date range in their name.
Directories are all labeled other_2019-07-18T00-00-00. The T is messing me up!
Copied this loop from somewhere.
#!/bin/bash
curdate=$(date +%Y-%m-%d%H-%M-%S)
#
for o in other_*; do
tmp=${o##other_}
tmp=$(echo "$tmp" | sed 's/T//') # clean up prefixes
fdate=$(date -d "${tmp}")
(( curdate < fdate )) && echo "$o"
done
I expect the final echo to include the path of all dir that match.
Unlike AWK, Bash comparison operator < works only on numerical values.
Please try instead:
#!/bin/bash
curdate=$(date +%Y%m%d%H%M%S)
for o in other_*; do
tmp=${o##other_}
fdate=$(echo "$tmp" | sed 's/[-T]//g') # numeralization
(( curdate < fdate )) && echo "$o"
done
As an alternative, you can compare epoch times:
#!/bin/bash
curdate=$(date +%s)
for o in other_*; do
tmp=${o##other_}
tmp=$(echo "$tmp" | sed 's/T/ /' | sed 's/\([0-9][0-9]\)-\([0-9][0-9]\)-\([0-9][0-9]\)$/\1:\2:\3/')
fdate=$(date -d "$tmp" +%s)
(( curdate < fdate )) && echo "$o"
done
Hope this helps.
Instead of dropping T...
date -d 2019-03-23T00-06-28
date: invalid date '2019-03-23T00-06-28'
ok, but:
date -d 2019-03-23T00:06:28
Sat Mar 23 00:06:28 UTC 2019
So we have to replace last two dashes by ::
As your question is tagged bash:
file="somepath/other_2019-07-18T00-00-00.extension"
time=${file#*other_} # suppress from left until 'other_'
time=${time%.*} # suppress extension
time=${time//-/:} # replace all dash by a `:`
time=${time/:/-} # replace 1nd `:` by a dash
time=${time/:/-} # replace 1nd `:` by a dash (again)
date -d $time
Thu Jul 18 00:00:00 UTC 2019
This could by written:
printf -v now "%(%s)T" -1 # bashism for current date to variable $now
for file in somepath/other_*.ext ;do
time=${file#*other_} time=${time%.*} time=${time//-/:}
time=${time/:/-} time=${time/:/-}
read fdate < <(date +%s -d $time)
((fdate > now)) && { echo $file: $((fdate - now)) ; }
done
Reducing forks (to date) improve quickness:
for matching your sample, you could replace for file in somepath/other_*.ext ;do by for file in other_*; do. This must work quite same.
fifo=/tmp/fifo-date-$$
mkfifo $fifo
exec 5> >(exec stdbuf -o0 date -f - +%s >$fifo 2>&1)
echo now 1>&5
exec 6< $fifo
read -t 1 -u 6 now
rm $fifo
for file in otherdates/*.ext ; do
time=${file#*other_} time=${time%.*} time=${time//-/:}
time=${time/:/-} time=${time/:/-}
echo $time 1>&5 && read -t 1 -u 6 fdate
((fdate > now)) && {
echo $file: $((fdate - now))
}
done
exec 6>&-
exec 5>&-
In this, I run date +%s in background, with -f argument, date will interpret each incoming line, then answer UNIX_TIME. So $now is first read from date process, by:
echo now >&5 && # now, the string
read -u 6 now # read will populate `$now` variable
Nota, once fifo opened by both input and output, they could be deleted. It will remain for process until process close them.
there is no space between day and hour, which causes date not to be able to read the date. Try:
sed 's/T/ /'

Linux Extract login history of all users

I currently have this bash script that is able to extract login history of all users in a certain time period, but I am trying to do so for all history not just between a certain time period.
Would it be more beneficial to grab this data from /var/log/secure?
Can anyone help steer me in the right direction?
last | while read line
do
date=`date -d "$(echo $line | awk '{ print $5" "$6" "$7 }')" +%s`
[[ $date -ge `date -d "Aug 25 00:00" +%s` && $date -le `date -d "Aug 28 00:00" +%s` ]] && echo $line
done
For the whole history just run last
Or
from last --help
-s --since
-t --until
Ex:
last -s 2019-01-30
For older login history you can try
last -s YYYY-MM-DD -f /var/log/wtmp.1 #

Determine how many days a certificate is still valid from within bash script

I want to check how many days the certificate of a website is valid from within a bash script which runs on a standard Ubuntu 14.04 server. openssl is available.
I already figured out that I can use openssl to get the target date
$ echo | openssl s_client -connect google.com:443 2>/dev/null|openssl x509 -noout -enddate
notAfter=Dec 22 16:37:00 2016 GMT
But how do I parse the resulting date and subtract the current one? Or might there be a better solution?
Just in single line
echo "(" $(date -d "`openssl x509 -in /etc/letsencrypt/live/d-herrmann.de/cert.pem -text -noout | grep "Not After" | cut -c 25-`" +%s) - $(date -d "now" +%s) ")" / 86400 | bc
With GNU date's %j to get day of the year and arithmetic expansion for subtraction:
$ echo $(( $(date -d "$(cut -d= -f2 <(echo 'notAfter=Dec 22 16:37:00 2016 GMT'))" '+%j') - $(date '+%j')))
73
$(date -d "$(cut -d= -f2 <(echo 'notAfter=Dec 22 16:37:00 2016 GMT'))" '+%j') gets us the day of the year from the date we have got, replace teh echo command inside process substitution, <(echo 'notAfter=Dec 22 16:37:00 2016 GMT') with the openssl command you have used initially
$(date '+%j') gets us today's date as day of the year
$(()) is used for subtracting the integers
date1="Dec 22 16:37:00 2016 GMT" # Future date
date2=$(date) # Current date
diff=$(($(date -d "$date1" +%j)-$(date -d "$date2" +%j))) #Diff between two date, %j is to show day of the year.
echo $diff #Display difference
73
Or just in one line:
echo $(($(date -d "Dec 22 16:37:00 2016 GMT" +%j)-$(date +%j)))
73
Here is a little script, which returns days, hours or minutes a cert is still valid, I just saved it in an executable file e.g. check_cert_age.sh
#!/bin/bash
# input domain as first parameter eg. ./heck_cert_age.sh stackoverflow.com
input="$1"
domain=$(echo "$input" | sed -e 's|^[^/]*//||' -e 's|/.*$||')
#domain=stackoverflow.com
echo
echo "Check the cert of Domain $domain"
datetime=$(timeout 5 openssl s_client -servername $domain -connect $domain:443 2>/dev/null | openssl x509 -noout -dates | sed 's/notAfter=\(.*\)$/\1/' | tail -n1)
if [ -z "$datetime" ]; then
echo
echo "ERROR"
echo
exit 0
fi
localtime=$(date -d "${datetime}" '+%R')
localdate=$(date -d "${datetime}" '+%Y-%m-%d')
timestamp=$(date -d "${datetime}" '+%s')
now=$(date '+%s')
diff=$((($timestamp-$now)/60))
if [ "$diff" -gt 3599 ] || [ "$diff" -lt 3599 ]; then
diffstr="$(expr $diff / 60 / 24) days"
elif [ "$diff" -gt 59 ] || [ "$diff" -lt 59 ] ; then
diffstr="$(expr $diff / 60) h"
else
diffstr="$diff min"
fi
echo
echo "The cert is valid until $localdate at $localtime ($diffstr)"
echo
The output will be:
Check the cert of Domain stackoverflow.com
The cert is valid until 2023-02-01 at 15:10 (50 days)
The output make a difference in days, hours, minutes and knows the difference to the local timezone. For minutes, it would look e.g. like that:
Check the cert of Domain ssl-cert-almost-expired.com
The cert is valid until 2022-12-13 at 15:39 (34 min)

Using wget and text array usage

Quick question. Currently using this to get header expiry info.
wget -O /dev/null -S $1 2>&1 | grep -q -m 1 "Expires: Sun, 19 Nov 1978 05:00:00 GMT" && echo "Yes" || echo "No"
With execution via "./is-drupal www.URL.com"
How could I iterate through an array in a text document that would be like
www.URL1.com
www.URL2.com
www.URL3.com
etc.
Then if the value returned "Yes" it would save the Yes URLs to a new text file.
Your best input is greatly appreciated!
#!/bin/bash
while read -r line; do
wget -O /dev/null -S "$line" 2>&1 | grep -q -m 1 "Expires: Sun, 19 Nov 1978 05:00:00 GMT"
if [ ${PIPESTATUS[1]} -eq 0 ]; then # check greps return code
echo "Yes"
echo "$line" >> yes_urls.txt
else
echo "No"
fi
done < text.txt

Resources