使用 ssl-on-demand 脚本管理您的 SSL 证书

跟踪证书到期时间,以防止 ssl-on-demand 脚本出现问题。
105 位读者喜欢这篇文章。
Lock

JanBaby, via Pixabay CC0。

这种情况时有发生,即使是最大的公司也会遇到。重要的证书没有及时续订,导致服务无法访问。微软 Teams 在 2020 年 2 月初就发生了这种情况,尴尬的是,这发生在他们推出一项大型电视广告活动,将其宣传为 Slack 的竞争对手之后。尽管这可能令人尴尬,但未来肯定还会有人遇到这种情况。

在现代网络上,过期的 证书可能会给网站带来重大问题,从无法连接到网站的不满用户到利用证书未续订漏洞的恶意行为者构成的安全威胁。

Ssl-on-demand 是一组 SSL 脚本,旨在帮助网站所有者管理证书。它用于按需生成和验证证书,并且可以创建证书签名请求 (CSR) 并预测现有证书的到期时间。

自动化 SSL 过期检查

 USAGE: SSLexpiryPredictions.sh -[cdewh]

  DESCRIPTION: This script predicts the expiring SSL certificates based on the end date.

  OPTIONS:

  -c|   sets the value for configuration file which has server:port or host:port details.
        
  -d|   sets the value of directory containing the certificate files in crt or pem format.

  -e|   sets the value of certificate extention, e.g crt, pem, cert.
        crt: default [to be used with -d, if certificate file extention is other than .crt]

  -w|   sets the value for writing the script output to a file.

  -h|   prints this help and exit.

示例

要创建一个包含所有服务器及其端口号的文件,以便进行 SSL 握手,请使用

cat > servers.list
         server1:port1
         server2:port2
         server3:port3
        (ctrl+d)
        
$ ./SSLexpiryPredictions.sh -c server.list

通过提供证书位置和扩展名(如果不是 .crt)来运行脚本

$ ./SSLexpiryPredictions.sh -d /path/to/certificates/dir -e pem

自动化 CSR 和私钥的创建

Usage: 	genSSLcsr.sh [options] -[cdmshx]
  [-c (common name)]
  [-d (domain name)]
  [-s (SSL certificate subject)]
  [-p (password)]
  [-m (email address)] *(Experimental)
  [-r (remove pasphrase) default:true]
  [-h (help)]
  [-x (optional)]

[OPTIONS]
  -c|   Sets the value for common name.
        A valid common name is something that ends with 'xyz.com'

  -d|   Sets the domain name.

  -s|   Sets the subject to be applied to the certificates.
        '/C=country/ST=state/L=locality/O=organization/OU=organizationalunit/emailAddress=email'

  -p|   Sets the password for private key.

  -r|   Sets the value of remove passphrase.
        true:[default] passphrase will be removed from key.
        false: passphrase will not be removed and key wont get printed.

  -m|   Sets the mailing capability to the script.
        (Experimental at this time and requires a lot of work)

  -x|   Creates the certificate request and key but do not print on screen.
        To be used when script is used just to create the key and CSR with no need
        + to generate the certficate on the go.

  -h|   Displays the usage. No further functions are performed.

  Example: genSSLcsr.sh -c mywebsite.xyz.com -m myemail@mydomain.com

脚本

1. SSLexpiryPredictions.sh

#!/bin/bash
##############################################
#
#	PURPOSE: The script to predict expiring SSL certificates.
#
# 	AUTHOR: 'Abhishek.Tamrakar'
#
# 	VERSION: 0.0.1
#
# 	COMPANY: Self
#
# 	EMAIL: abhishek.tamrakar08@gmail.com
#
# 	GENERATED: on 2018-05-20
#
#	LICENSE: Copyright (C) 2018 Abhishek Tamrakar
#
#  Licensed under the Apache License, Version 2.0 (the "License");
#  you may not use this file except in compliance with the License.
#  You may obtain a copy of the License at
#
#       https://apache.ac.cn/licenses/LICENSE-2.0
#
#   Unless required by applicable law or agreed to in writing, software
#   distributed under the License is distributed on an "AS IS" BASIS,
#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#   See the License for the specific language governing permissions and
#   limitations under the License.
##############################################

#your Variables go here
script=${0##/}
exitcode=''
WRITEFILE=0
CONFIG=0
DIR=0
# functions here
usage()
{
cat <<EOF

  USAGE: $script -[cdewh]"

  DESCRIPTION: This script predicts the expiring SSL certificates based on the end date.

  OPTIONS:

  -c|   sets the value for configuration file which has server:port or host:port details.

  -d|   sets the value of directory containing the certificate files in crt or pem format.

  -e|   sets the value of certificate extention, e.g crt, pem, cert.
        crt: default

  -w|   sets the value for writing the script output to a file.

  -h|   prints this help and exit.

EOF
exit 1
}
# print info messages
info()
{
  printf '\n%s: %6s\n' "INFO" "$@"
}
# print error messages
error()
{
  printf '\n%s: %6s\n' "ERROR" "$@"
  exit 1
}
# print warning messages
warn()
{
  printf '\n%s: %6s\n' "WARN" "$@"
}
# get expiry for the certificates
getExpiry()
{
  local expdate=$1
  local certname=$2
  today=$(date +%s)
  timetoexpire=$(( ($expdate - $today)/(60*60*24) ))

  expcerts=( ${expcerts[@]} "${certname}:$timetoexpire" )
}

# print all expiry that was found, typically if there is any.
printExpiry()
{
  local args=$#
  i=0
  if [[ $args -ne 0 ]]; then
    #statements
    printf '%s\n' "---------------------------------------------"
    printf '%s\n' "List of expiring SSL certificates"
    printf '%s\n' "---------------------------------------------"
    printf '%s\n' "$@"  | \
      sort -t':' -g -k2 | \
      column -s: -t     | \
      awk '{printf "%d.\t%s\n", NR, $0}'
    printf '%s\n' "---------------------------------------------"
  fi
}

# calculate the end date for the certificates first, finally to compare and predict when they are going to expire.
calcEndDate()
{
  sslcmd=$(which openssl)
  if [[ x$sslcmd = x ]]; then
    #statements
    error "$sslcmd command not found!"
  fi
  # when cert dir is given
  if [[ $DIR -eq 1 ]]; then
    #statements
    checkcertexists=$(ls -A $TARGETDIR| egrep "*.$EXT$")
    if [[ -z ${checkcertexists} ]]; then
      #statements
      error "no certificate files at $TARGETDIR with extention $EXT"
    fi
    for file in $TARGETDIR/*.${EXT:-crt}
    do
      expdate=$($sslcmd x509 -in $file -noout -enddate)
      expepoch=$(date -d "${expdate##*=}" +%s)
      certificatename=${file##*/}
      getExpiry $expepoch ${certificatename%%.*}
    done
  elif [[ $CONFIG -eq 1 ]]; then
    #statements
    while read line
    do
      if echo "$line" | \
      egrep -q '^[a-zA-Z0-9.]+:[0-9]+|^[a-zA-Z0-9]+_.*:[0-9]+';
      then
        expdate=$(echo | \
        openssl s_client -connect $line 2>/dev/null | \
        openssl x509 -noout -enddate 2>/dev/null);
        if [[ $expdate = '' ]]; then
          #statements
          warn "[error:0906D06C] Cannot fetch certificates for $line"
        else
          expepoch=$(date -d "${expdate##*=}" +%s);
          certificatename=${line%%:*};
          getExpiry $expepoch ${certificatename};
        fi
      else
        warn "[format error] $line is not in required format!"
      fi
    done < $CONFIGFILE
  fi
}
# your script goes here
while getopts ":c:d:w:e:h" options
do
case $options in
c )
  CONFIG=1
  CONFIGFILE="$OPTARG"
  if [[ ! -e $CONFIGFILE ]] || [[ ! -s $CONFIGFILE ]]; then
    #statements
    error "$CONFIGFILE does not exist or empty!"
  fi
	;;
e )
  EXT="$OPTARG"
  case $EXT in
    crt|pem|cert )
    info "Extention check complete."
    ;;
    * )
    error "invalid certificate extention $EXT!"
    ;;
  esac
  ;;
d )
  DIR=1
  TARGETDIR="$OPTARG"
  [ $TARGETDIR = '' ] && error "$TARGETDIR empty variable!"
  ;;
w )
  WRITEFILE=1
  OUTFILE="$OPTARG"
  ;;
h )
	usage
	;;
\? )
	usage
	;;
: )
	fatal "Argument required !!! see \'-h\' for help"
	;;
esac
done
shift $(($OPTIND - 1))
#
calcEndDate
#finally print the list
if [[ $WRITEFILE -eq 0 ]]; then
  #statements
  printExpiry ${expcerts[@]}
else
  printExpiry ${expcerts[@]} > $OUTFILE
fi

2. genSSLcsr.sh

#!/bin/bash -
#===============================================================================
#
#          FILE: genSSLcsr.sh
#
#         USAGE: ./genSSLcsr.sh [options]
#
#   DESCRIPTION: ++++version 1.0.2
# 		Fixed few bugs from previous script
#		+Removing passphrase after CSR generation
# 		Extended use of functions
# 		Checks for valid common name
#		++++1.0.3
# 		Fixed line breaks
# 		Work directory to be created at the start
# 		Used getopts for better code arrangements
#   ++++1.0.4
#     Added mail feature (experimental at this time and needs
#     a mail server running locally.)
#     Added domain input and certificate subject inputs
#
#       OPTIONS: ---
#  REQUIREMENTS: openssl, mailx
#          BUGS: ---
#         NOTES: ---
#        AUTHOR: Abhishek Tamrakar (), abhishek.tamrakar08@gmail.com
#  ORGANIZATION: Self
#       CREATED: 6/24/2016
#      REVISION: 4
# COPYRIGHT AND
#	LICENSE: Copyright (C) 2016 Abhishek Tamrakar
#
#  Licensed under the Apache License, Version 2.0 (the "License");
#  you may not use this file except in compliance with the License.
#  You may obtain a copy of the License at
#
#       https://apache.ac.cn/licenses/LICENSE-2.0
#
#   Unless required by applicable law or agreed to in writing, software
#   distributed under the License is distributed on an "AS IS" BASIS,
#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#   See the License for the specific language governing permissions and
#   limitations under the License.
#===============================================================================

#variables ges here
#set basename to scriptname
SCRIPT=${0##*/}

#set flags
TFOUND=0
CFOUND=0
MFOUND=0
XFOUND=0
SFOUND=0
logdir=/var/log
# edit these below values to replace with yours
homedir=''
yourdomain=''
country=IN
state=Maharashtra
locality=Pune
organization="your_organization"
organizationalunit="your_organizational_unit"
email=your_email@your_domain
password=your_ssl_password
# OS is declared and will be used in its next version
OS=$(egrep -io 'Redhat|centos|fedora|ubuntu' /etc/issue)

### function declarations ###

info()
{
  printf '\n%s\t%s\t' "INFO" "$@"
}

#exit on error with a custom error message
#the extra function was removed and replaced withonly one.
#using FAILED\n\e<message> is a way but not necessarily required.
#

fatal()
{
 printf '\n%s\t%s\n' "ERROR" "$@"
 exit 1
}

checkperms()
{
if [[ -z ${homedir} ]]; then
homedir=$(pwd)
fi
if [[ -w ${homedir} ]]; then
info "Permissions acquired for ${SCRIPT} on ${homedir}."
else
fatal "InSufficient permissions to run the ${SCRIPT}."
fi
}

checkDomain()
{
info "Initializing Domain ${cn} check ? "
if [[ ! -z ${yourdomain} ]]; then
workdir=${homedir}/${yourdomain}
echo -e "${cn}"|grep -E -i -q "${yourdomain}$" && echo -n "[OK]" || fatal "InValid domain in ${cn}"
else
workdir=${homedir}/${cn#*.}
echo -n "[NULL]"
info "WARNING: No domain declared to check."
confirmUserAction
fi
}	# end function checkDomain

usage()
{
cat << EOF

Usage: 	$SCRIPT [options] -[cdmshx]
  [-c (common name)]
  [-d (domain name)]
  [-s (SSL certificate subject)]
  [-p (password)]
  [-m (email address)] *(Experimental)
  [-r (remove pasphrase) default:true]
  [-h (help)]
  [-x (optional)]

[OPTIONS]
  -c|   Sets the value for common name.
        A valid common name is something that ends with 'xyz.com'

  -d|   Sets the domain name.

  -s|   Sets the subject to be applied to the certificates.
        '/C=country/ST=state/L=locality/O=organization/OU=organizationalunit/emailAddress=email'

  -p|   Sets the password for private key.

  -r|   Sets the value of remove passphrase.
        true:[default] passphrase will be removed from key.
        false: passphrase will not be removed and key wont get printed.

  -m|   Sets the mailing capability to the script.
        (Experimental at this time and requires a lot of work)

  -x|   Creates the certificate request and key but do not print on screen.
        To be used when script is used just to create the key and CSR with no need
        + to generate the certficate on the go.

  -h|   Displays the usage. No further functions are performed.

  Example: $SCRIPT -c mywebsite.xyz.com -m myemail@mydomain.com

EOF
exit 1
}	# end usage

confirmUserAction() {
while true; do
read -p "Do you wish to continue? ans: " yn
case $yn in
[Yy]* ) info "Initiating the process";
break;;
[Nn]* ) exit 1;;
* ) info "Please answer yes or no.";;
esac
done
}	# end function confirmUserAction

parseSubject()
{
  local subject="$1"
  parsedsubject=$(echo $subject|sed 's/\// /g;s/^ //g')
  for i in ${parsedsubject}; do
      case ${i%%=*} in
        'C' )
        country=${i##*=}
        ;;
        'ST' )
        state=${i##*=}
        ;;
        'L' )
        locality=${i##*=}
        ;;
        'O' )
        organization=${i##*=}
        ;;
        'OU' )
        organizationalunit=${i##*=}
        ;;
        'emailAddress' )
        email=${i##*=}
      ;;
    esac
  done
}

sendMail()
{
 mailcmd=$(which mailx)
 if [[ x"$mailcmd" = "x" ]]; then
   fatal "Cannot send email! please install mailutils for linux"
 else
   echo "SSL CSR attached." | $mailcmd -s "SSL certificate request" \
   -t $email $ccemail -A ${workdir}/${cn}.csr \
   && info "mail sent" \
   || fatal "error in sending mail."
 fi
}

genCSRfile()
{
info "Creating signed key request for ${cn}"
#Generate a key
openssl genrsa -des3 -passout pass:$password -out ${workdir}/${cn}.key 4096 -noout 2>/dev/null && echo -n "[DONE]" || fatal "unable to generate key"

#Create the request
info "Creating Certificate request for ${cn}"
openssl req -new -key ${workdir}/${cn}.key -passin pass:$password -sha1 -nodes \
	-subj "/C=$country/ST=$state/L=$locality/O=$organization/OU=$organizationalunit/CN=$cn/emailAddress=$email" \
	-out ${workdir}/${cn}.csr && echo -n "[DONE]" || fatal "unable to create request"

if [[ "${REMOVEPASSPHRASE:-true}" = 'true' ]]; then
  #statements
  #Remove passphrase from the key. Comment the line out to keep the passphrase
  info "Removing passphrase from ${cn}.key"
  openssl rsa -in ${workdir}/${cn}.key \
  -passin pass:$password \
  -out ${workdir}/${cn}.insecure 2>/dev/null \
  && echo -n "[DONE]" || fatal "unable to remove passphrase"
  #swap the filenames
  info "Swapping the ${cn}.key to secure"
  mv ${workdir}/${cn}.key ${workdir}/${cn}.secure \
  && echo -n "[DONE]" || fatal "unable to perfom move"
  info "Swapping insecure key to ${cn}.key"
  mv ${workdir}/${cn}.insecure ${workdir}/${cn}.key \
  && echo -n "[DONE]" || fatal "unable to perform move"
else
  info "Flag '-r' is set, passphrase will not be removed."
fi
}

printCSR()
{
if [[ -e ${workdir}/${cn}.csr ]] && [[ -e ${workdir}/${cn}.key ]]
then
echo -e "\n\n----------------------------CSR-----------------------------"
cat ${workdir}/${cn}.csr
echo -e "\n----------------------------KEY-----------------------------"
cat ${workdir}/${cn}.key
echo -e "------------------------------------------------------------\n"
else
fatal "CSR or KEY generation failed !!"
fi
}

### END Functions ###

#Check the number of arguments. If none are passed, print help and exit.
NUMARGS=$#
if [ $NUMARGS -eq 0 ]; then
fatal "$NUMARGS Arguments provided !!!! See usage with '-h'"
fi

#Organisational details

while getopts ":c:d:s:m:p:rhx" atype
do
case $atype in
c )
	CFOUND=1
	cn="$OPTARG"
	;;
d )
  yourdomain="$OPTARG"
  ;;
s )
  SFOUND=1
  subj="$OPTARG"
  ;;
p )
  password="$OPTARG"
  ;;
r )
  REMOVEPASSPHRASE='false'
  ;;
m )
  MFOUND=1
  ccemail="$OPTARG"
  ;;
x )
	XFOUND=1
  ;;
h )
	usage
	;;
\? )
	usage
	;;
: )
	fatal "Argument required !!! see \'-h\' for help"
	;;
esac
done
shift $(($OPTIND - 1))

#### END CASE #### START MAIN ####

if [ $CFOUND -eq 1 ]
then
# take current dir as homedir by default.
checkperms ${homedir}
checkDomain

  if [[ ! -d ${workdir} ]]
  then
    mkdir ${workdir:-${cn#*.}} 2>/dev/null && info "${workdir} created."
  else
    info "${workdir} exists."
  fi # end workdir check
  parseSubject "$subj"
  genCSRfile
  if [ $XFOUND -eq 0 ]
  then
    sleep 2
    printCSR
  fi	# end x check
  if [[ $MFOUND -eq 1 ]]; then
    sendMail
  fi
else
	fatal "Nothing to do!"
fi	# end common name check

##### END MAIN #####

这最初是作为 ssl-on-demand 的 GitHub 存储库中的 README 发布的,并经过授权重复使用。

接下来阅读什么
iamabhi
我是一名 Lead DevOps,也是一名程序员。 我是一个开源爱好者、博主、作家。 你会发现我主要在帮助人们学习。

2 条评论

纽约市文章

墨尔本出租车

Creative Commons License此作品采用 Creative Commons Attribution-Share Alike 4.0 International License 许可。
© . All rights reserved.