diff --git a/README.md b/README.md index 56efa2c..bcd65dd 100644 --- a/README.md +++ b/README.md @@ -1,21 +1,37 @@ # Project Name -TODO: Write a project description +This script checks the validity of a certificate and renews it if necessary ## Requirements -TODO: Requirments +A pki on /\ ## Installation -TODO: Describe the installation process +```bash +sudo ./install.sh +``` +A cron file is created +## Parameters +See `/etc/renew-cert/renew-cert.conf` ## Usage -TODO: Write usage instructions +```bash +./renew-cert.sh --help +``` +```bash +Usage: ./renew-cert.sh options +Renew wilcard certificat +Options: + -d, --domain - domain name + -p, --param - display parameters + -h, --help - display this help + -v, --version - display script version + +Example + Renew wildcard certificat for domain gmolab.net + ./renew-cert.sh --domain gmolab.net +``` ### Changelog -### [1.0.0] - 2025-09-06 +### [1.0.0] - 2025-09-11 #### Added -- New features and functionality. -#### Modified -- Changes to existing functionality (backwards compatible). -#### Fixed -- Bug fixes. -#### Removed -- Deprecated or removed features (breaking changes). +- check expiration date certificate +- renew certification +- send mail #### Project initialization - initialization by [GMo](mailto:gilles.mouchet@gmail.com) diff --git a/install.sh b/install.sh index f7ada8b..43ec953 100755 --- a/install.sh +++ b/install.sh @@ -37,17 +37,21 @@ installPackage() { #----------------------------------------------------------- # variables +user=gilles +group=gilles fullScriptName=renew-cert.sh shortScriptName=`echo $fullScriptName | sed -e 's|.*/||g' | cut -f1 -d.` -destPath=/usr/local/bin/ +destPath=/usr/local/bin/gmotools configFile=$shortScriptName.conf configFilePath=/etc/$shortScriptName/ logRotateFile=$shortScriptName logRotateFilePath=/etc/logrotate.d/$logRotateFile +cronFile=$shortScriptName.cron + # check if the effective user ID is 0 (root) if [[ $EUID -ne 0 ]]; then - echo "This script must be run as root or with sudo." + echo "[ERROR] - this script must be run as root or with sudo." exit 1 fi @@ -58,18 +62,18 @@ if command -v apt &> /dev/null; then elif command -v dnf &> /dev/null; then packageManager="dnf" else - echo "Erreur : No supported package managers (apt, dnf) were found." + echo "[ERROR] - no supported package managers (apt, dnf) were found." exit 1 fi -echo "Package manager detected: $packageManager" +echo "[INFO] - package manager detected: $packageManager" # check if logPath exist if [ ! -d $configFilePath ]; then mkdir $configFilePath &> /dev/null rc=$? if [ "$rc" != "0" ];then - echo "[ERROR] - An error occurred while creating $configFilePath ($rc)" - else echo "[SUCCESS] - The folder $configFilePath was created successfully." + echo "[ERROR] - an error occurred while creating $configFilePath ($rc)" + else echo "[SUCCESS] - the folder $configFilePath was created successfully." fi fi # install package (example) @@ -79,22 +83,51 @@ installPackage mutt &> /dev/null cp $fullScriptName $destPath/. &> /dev/null rc=$? if [ "$rc" != "0" ];then - echo "[ERROR] - An error occurred while copying $fullScriptName to $destPath ($rc)" -else echo "[SUCCESS] - The script ${fullScriptName} to $destPath was copied successfully." + echo "[ERROR] - an error occurred while copying $fullScriptName to $destPath ($rc)" + exit 1 +else echo "[SUCCESS] - the script ${fullScriptName} to $destPath was copied successfully." fi # copy config file cp $configFile $configFilePath &> /dev/null rc=$? if [ "$rc" != "0" ];then - echo "[ERROR] - An error occurred while copying $configFile to $configFilePath ($rc)" -else echo "[SUCCESS] - The script $configFile to $configFilepath was copied successfully." + echo "[ERROR] - an error occurred while copying $configFile to $configFilePath ($rc)" + exit 1 +else echo "[SUCCESS] - the script $configFile to $configFilepath was copied successfully." fi cp $logRotateFile.logrotate $logRotateFilePath &> /dev/null rc=$? if [ "$rc" != "0" ];then - echo "[ERROR] - An error occurred while copying $logRotateFile.logrotate to $logRotateFilePath ($rc)" -else echo "[SUCCESS] - The script $logRotateFile.logrotate to $logRotateFilePath was copied successfully." + echo "[ERROR] - an error occurred while copying $logRotateFile.logrotate to $logRotateFilePath ($rc)" + exit 1 +else echo "[SUCCESS] - the script $logRotateFile.logrotate to $logRotateFilePath was copied successfully." fi -echo "Installation completed." \ No newline at end of file + +cp $shortScriptName.cron /etc/cron.d/$shortScriptName &> /dev/null +rc=$? +if [ "$rc" != "0" ];then + echo "[ERROR] - n error occurred while copying $shortScriptName.cron to /etc/cron.d/$shortScriptName ($rc)" + exit 1 +else echo "[SUCCESS] - $shortScriptName.cron to /etc/cron.d/$shortScriptName was copied successfully." +fi + +# create log file if not exist +if [ ! -d /var/log/$shortScriptName ]; then + mkdir /var/log/$shortScriptName &> /dev/null + rc=$? + if [ "$rc" != "0" ];then + echo "[ERROR] - An error occurred while creating /var/log/$shortScriptName ($rc)" + exit 1 + else echo "[SUCCESS] - /var/log/$shortScriptName was created successfully." + fi +fi +sudo chown $user:$group /var/log/$shortScriptName +rc=$? +if [ "$rc" != "0" ];then + echo "[ERROR] - An error occurred while applying user ($user) and group ($group) owner /var/log/$shortScriptName ($rc)" + exit 1 +else echo "[SUCCESS] - User ($user) and group ($group) owner on /var/log/$shortScriptName was applied successfully." +fi +echo "Installation completed" diff --git a/renew-cert.conf b/renew-cert.conf index e048c30..59cb5cc 100644 --- a/renew-cert.conf +++ b/renew-cert.conf @@ -1,3 +1,47 @@ -# mail recipient -msgRecipient=exploit.gmotech@gmail.com +#onScreen mode - display message log on screen +# 0 no display log on screen +# 1 display log entry on screen +onScreen=0 +# debug mode - display more var value +#. 0 disable debug mode +# 1 enable debug mode +debug=0 + +# folder where the root of the domain PKI is located +pkiRootPath=/home/gilles/certs + +# credentials file for the domain certificate .key file +credFilePath=/home/gilles + +# number of days the certificate is valid upon renewal +certDaysValidity=30 + +# warning message sent x days +# before the end of the certificate validity +warningDays=7 + +# critical message sent x days +# before the end of the certificate validity +criticalDays=3 + +# certificate renewal x days before +# before the end of the certificate validity +renewDays=1 + +# send warning mail +warningMail=1 + +# send critical mail +criticalMail=1 + +# send renewcert mail +renewMail=1 + +# send info mail +#if 1 a message is sent each time the file is executed +#even if there is no error +infoMail=0 + +# mail recipient +msgRecipient=exploit.gmotech@gmail.com \ No newline at end of file diff --git a/renew-cert.cron b/renew-cert.cron new file mode 100644 index 0000000..2e68491 --- /dev/null +++ b/renew-cert.cron @@ -0,0 +1,3 @@ +# there are no rediect /dev/null. It's normal +# if redirect not input in log file +01 00 * * * gilles /usr/local/bin/gmotools/renew-cert.sh -d gmolab.net \ No newline at end of file diff --git a/renew.logrotate b/renew-cert.logrotate similarity index 50% rename from renew.logrotate rename to renew-cert.logrotate index e036149..f931cc5 100644 --- a/renew.logrotate +++ b/renew-cert.logrotate @@ -1,8 +1,8 @@ -/var/log/template/template.log { +/var/log/renew-log/renew-log.log { daily rotate 7 compress missingok notifempty - create 644 root root + create 644 gilles gilles } \ No newline at end of file diff --git a/renew-cert.sh b/renew-cert.sh index f11784a..29233b4 100755 --- a/renew-cert.sh +++ b/renew-cert.sh @@ -1,8 +1,8 @@ #!/bin/bash ############################################################ -# Decription: Template script +# Decription: Renew certificate # Author: Gilles Mouchet (gilles.mouchet@gmail.com) -# Creation Date: 06-Sep-2025 +# Creation Date: 11-Sep-2025 # Version: 1.0.0 # # Changelog: @@ -11,16 +11,23 @@ # - Creation of script from scratch # ############################################################ - #----------------------------------------------------------------- # DON'T CHANGE ANYTHING FROM HERE #----------------------------------------------------------------- + version="1.0.0" +# config +progName=`echo $0 | sed -e 's|.*/||g' | cut -f1 -d.` +confDir=/etc/$progName +cfgFile=$confDir/$progName.conf +logPath=/var/log/$progName +logFile=$logPath/$progName.log +# mail mailSubject="[SUCCESS] - script result on `hostname`" mailHeader="my_hdr From: GMO Check System " mailBody="" -mailFooter="\n\nTemplate script $version by Exploit GMoTech" -tmpFile=/tmp/list.txt +mailFooter="\n\n$progName script V$version on `hostname` by Exploit GMoTech" +tmpFile=/tmp/$progName.log #----------------------------------------------------------- # FUNCTIONS @@ -28,14 +35,64 @@ tmpFile=/tmp/list.txt function usage() { cat << EOF Usage: ./$(basename "$0") options -Template script +Renew wilcard certificat Options: + -d, --domain - domain name -p, --param - display parameters -h, --help - display this help -v, --version - display script version + +Example + Renew wildcard certificat for domain gmolab.net + ./$(basename "$0") --domain gmolab.net EOF } #----------------------------------------------------------- +function renewCertificate(){ + log I "renew certificate" + log D "ca key file use $fullpkiRootPath/$caKeyFile" + log D "ca crt file use $fullpkiRootPath/$caCrtFile" + log D "ca csr file use $fullpkiRootPath/$certDomain.csr" + log D "bakup path file use $fullpkiRootPath/backup/" + # backup crt file + log I "copy $certFile to $fullpkiRootPath/backup/$certFile" + cp $fullpkiRootPath/$certFile $fullpkiRootPath/backup/$certFile-$(date +"%Y-%m-%d-%H-%M-%S") > $tmpFile + rc=$? + if [ "$rc" != "0" ];then + log I "send error message" + log E "a problem occurred while copying $certDomain certificate on $fullpkiRootPath/backup/$certFile ($rc)" + mailSubject="[ERROR] - a problem occurred while copying $certDomain certificate on $fullpkiRootPath/backup/$certFile ($rc)" + mailBody="Bad new!\n A problem occurred while copying $certDomain certificate on $fullpkiRootPath/backup/$certFile ($rc).\nHave a good day" + sendMail + endScript + fi + # renewal certificate + openssl x509 -req -in $fullpkiRootPath/$certDomain.csr \ + -passin file:$caKeyCredFile \ + -CA $fullpkiRootPath/$caCrtFile \ + -CAkey $fullpkiRootPath/$caKeyFile \ + -CAcreateserial \ + -extfile $fullpkiRootPath/wilcard.cnf -out $fullpkiRootPath/$certFile \ + -days $certDaysValidity -sha256 > $tmpFile 2>&1 + rc=$? + if [ "$rc" != "0" ]; then + log I "send error message" + log E "a problem occurred while renewing $certDomain certificate ($rc)" + mailSubject="[ERROR] - a problem occurred while renewing $certDomain certificate ($rc)" + mailBody="Bad new!\n A problem occurred while renewing $certDomain certificate ($rc).\nHave a good day" + sendMail + rm -rf $tmpFile + else + endDateFo=$(openssl x509 -in $fullpkiRootPath/$certFile -noout -enddate | awk -F'=' '{print $2}' | xargs -I {} date -d "{}" '+%Y-%m-%d') + log I "certificate successfully renewed. New expiry date: $endDateFo" + log I "send a success mesaage" + mailSubject="[SUCCESS] - certificate for $certDomain successfully renewed. New expiry date: $endDateFo" + mailBody="Bad new!\n Certificate or $certDomain successfully renewed. New expiry date: $endDateFo.\nHave a good day" + sendMail + rm -rf $tmpFile + fi +} +#----------------------------------------------------------- function sendMail() { if [ -f "$tmpFile" ];then echo -e "$mailBody $mailFooter" | mutt -e "$mailHeader" -s "${mailSubject}" $msgRecipient -a $tmpFile @@ -48,12 +105,7 @@ function sendMail() { # parameter # $1 define entry type (info, warning, error) # $2 define text -# $3 define display on screen or not (nothing=no, 1=yes) function log() { - if [ -z "$3" ]; then - displayScreen=0 - else displayScreen=1 - fi case "$1" in I) logType="[info]" @@ -62,35 +114,49 @@ function log() { logType="[warning]" ;; E) - logType="[error]" - ;; + logType="[error]" + ;; + C) + logType="[critical]" + ;; + D) + logType="[debug]" + ;; esac - # on screen and logfile - #echo "$(date "+%Y-%m-%d")-$(date "+%H:%M:%S") - $logType - $2" | tee -a "$logFile" - #echo "[$(date "+%Y-%m-%d")-$(date "+%H:%M:%S")] - $logType - $2" >> "$logFile" - # true to display screen to - if [ "${displayScreen}" -eq 1 ];then - echo "[$(date "+%Y-%m-%d")-$(date "+%H:%M:%S")] - $logType - $2" | tee -a "$logFile" - else + + # write all entries except [debug] + if [ "$logType" != "[debug]" ]; then echo "[$(date "+%Y-%m-%d")-$(date "+%H:%M:%S")] - $logType - $2" >> "$logFile" + # display log entry on screen if onScreen is enabled + if [ "$onScreen" -eq 1 ];then + echo "[$(date "+%Y-%m-%d")-$(date "+%H:%M:%S")] - $logType - $2" + fi fi + + # write [debug] entries only if debug is enabled + if [ "$logType" == "[debug]" ] && [ "$debug" -eq 1 ]; then + echo "[$(date "+%Y-%m-%d")-$(date "+%H:%M:%S")] - $logType - $2" >> "$logFile" + # display log entry on screen if onScreen is enabled + if [ "$onScreen" -eq 1 ];then + echo "[$(date "+%Y-%m-%d")-$(date "+%H:%M:%S")] - $logType - $2" + fi + fi + +} +#----------------------------------------------------------- +function endScript(){ + log I "----- end script -----" + exit } #----------------------------------------------------------- # MAIN #----------------------------------------------------------- # check if the effective user ID is 0 (root) -if [[ $EUID -ne 0 ]]; then - echo "This script must be run as root or with sudo." - exit 1 -fi - -# config -progName=`echo $0 | sed -e 's|.*/||g' | cut -f1 -d.` -confDir=/etc/$progName -cfgFile=$confDir/$progName.conf -logPath=/var/log/$progName -logFile=$logPath/$progName.log +#if [[ $EUID -ne 0 ]]; then +# echo "This script must be run as root or with sudo." +# exit 1 +#fi # check if conf file or passphrase file exist if [ ! -f $cfgFile ]; then @@ -98,24 +164,38 @@ if [ ! -f $cfgFile ]; then exit 1 fi - # read config file . $cfgFile -log I "script start" 1 + +log I "----- script start -----" + # check if logPath exist if [ ! -d $logPath ]; then - mkdir $logPath + echo "$progName not installed correctly. Please run install.sh script" + endScript fi # check param exist. Uncomment if your script need parameters -#if [ -z "$1" ]; then -# usage -# exit -#fi +if [ -z "$1" ]; then + usage + endScript +fi while [[ "$#" -gt 0 ]]; do case "$1" in + -d|--domain) + if [ -z "$2" ]; then + usage + endScript + else + certFile=$2.crt + certDomain=$2 + log I "cert file $certFile" + fi + shift 2 + ;; -p|--param) + log I "script run with -p or --param option" cat << EOF ------------------------------------------------------------------------------- Parameters @@ -130,46 +210,175 @@ Defined in script ------------------------------------------------------------------------------- Defined in $cfgFile ------------------------------------------------------------------------------- - message recipient: $msgRecipient + message recipient: $msgRecipient + pki path: $pkiRootPath + display log on screen: $onScreen + debug mode: $debug + + send warning mail + x days before + the end of validity: $warningDays + + send critical mail + x days before + the end of validity: $criticalDays + + certificate renewal + x days before + before the end of +the certificate validity: $renewDays + + number of days + the certificate + is valid upon renewal: $certDaysValidity + sending an infor email: $infoMail + sending a warning email: $warningMail +sending a critical email: $criticalMail + sending a renewal email: $renewMail EOF - exit + endScript ;; - version|-v|--version) + -v|--version) + log I "script run with -v or --verion option" cat << EOF $(basename "$0") v$version (c) 1990 - $(date +%Y) by Gilles Mouchet Non-Commercial Use License – See LICENSE for details EOF - exit + endScript ;; # must be in the last block of the case because of * - *|help|-h|--help) + *|help|-h|--help) + log I "script run with -h, --help option" usage - exit + endScript ;; esac shift done -# success message -log I "send a success message" 1 -mailSubject="[SUCCESS] - script result on `hostname`" -mailBody=" This is a success test mail\nHave a good day" -sendMail +# check if pki folder and pki files for domain +fullpkiRootPath="$pkiRootPath/`echo $certFile | sed -e 's|.*/||g' | cut -f1 -d.`" -# warnig message -log W "send a warning message" 1 -mailSubject="[WARNING] - script result on `hostname`" -mailBody=" This is a warning test mail\nHave a good day" -sendMail +# check if backupcrtpath exist +if [ ! -d "$fullpkiRootPath/backup" ]; then + mkdir $fullpkiRootPath/backup +fi -# error message -log E "send an error message" 1 -cat << EOF > $tmpFile -This file contain the description error -or log file -EOF -mailSubject="[ERROR] - script result on `hostname`" -mailBody=" This is a warning test mail\nHave a good day" -sendMail -rm -rf $tmpFile \ No newline at end of file +# set credFile and check if exist +caKeyCredFile=$credFilePath/.$certDomain +if [ ! -e "$caKeyCredFile" ]; then + log E "$caKeyCredFile doesn't exist !" + log I "send error message" + mailSubject="[ERROR] - $caKeyCredFile doesn't exist !" + mailBody="Oups !\n$caKeyCredFile doesn't exist !.\nHave a good day" + sendMail + endScript +fi +log I "cred file $caKeyCredFile" + +# set CA file name +caCrtFile="`echo $certFile | sed -e 's|.*/||g' | cut -f1 -d.`CA.crt" +caKeyFile="`echo $certFile | sed -e 's|.*/||g' | cut -f1 -d.`CA.key" +log I "ca files $caCrtFile and $caKeyFile are used" +# check if cert exist +if [ ! -f "$fullpkiRootPath/$certFile" ] || [ ! -f "$fullpkiRootPath/$caCrtFile" ] || [ ! -f "$fullpkiRootPath/$caKeyFile" ]; then + log E "$certFile, $caCrtFile or $caKeyFile on $fullpkiRootPath doesn't exist." + log I "send error message" + mailSubject="[ERROR] - $certFile, $caCrtFile or $caKeyFile on $fullpkiRootPath doesn't exist" + mailBody="Problem !\nT$certFile, $caCrtFile or $caKeyFile on $fullpkiRootPath doesn't exist.\nHave a good day" + sendMail + endScript +fi +log I "all files are present on $fullpkiRootPath" + +# set end cert expiration date an convert in timestamp +endDateFo=$(openssl x509 -in $fullpkiRootPath/$certFile -noout -enddate | awk -F'=' '{print $2}' | xargs -I {} date -d "{}" '+%Y-%m-%d') +endDateTs=$(date -d "$endDateFo" +%s) + +# set current date and convert it in timestamp +currentDateFo=$(date "+%Y-%m-%d") +currentDateTs=$(date -d "$currentDateFo" +%s) + +#-------------------------------------------------------------------------------------------------------------------- +#FOR DEV +# set debug=1 and onScreen=1 in /etc/renew-cert/renew-cert.conf +#endDateFo="2025-11-14" +#endDateTs=$(date -d "$endDateFo" +%s) +#currentDateFo="2025-10-10" +#currentDateTs=$(date -d "$currentDateFo" +%s) +# END FOR DEV +#-------------------------------------------------------------------------------------------------------------------- + +# set the low and the hight threshold for warning message +warningMsgLowDateTs=$((endDateTs - (warningDays * 86400))) +warningMsgLowDateFo=$(date -d "@$warningMsgLowDateTs" "+%Y-%m-%d") +warningMsgHighDateTs=$((endDateTs - (criticalDays * 86400)- 86400)) +warningMsgHighDateFo=$(date -d "@$warningMsgHighDateTs" "+%Y-%m-%d") + +# set the low and the hight threshold for critical message +criticalMsgLowDateTs=$((endDateTs - (criticalDays * 86400))) +criticalMsgLowDateFo=$(date -d "@$criticalMsgLowDateTs" "+%Y-%m-%d") +criticalMsgHighDateTs=$((endDateTs - (renewDays * 86400))) +criticalMsgHighDateFo=$(date -d "@$criticalMsgHighDateTs" "+%Y-%m-%d") + +# set the renewal dat +tmpNbrDaysTd=$((renewDays * 86400 )) +renewDateTs=$((endDateTs-tmpNbrDaysTd)) +renewDateFo=$(date -d "@$renewDateTs" "+%Y-%m-%d") + +# set number days before end today and expiration date +daysLeftTs=$((endDateTs-currentDateTs)) +daysLeftFo=$((daysLeftTs / 86400)) + +daysPassTs=$((currentDateTs-endDateTs)) +daysPassFo=$((daysPassTs / 86400)) + +log D "current date : $currentDateFo ($currentDateTs)" $onScreen +log D "certificate expiration date : $endDateFo ($endDateTs)" $onScreen +log D "nbr. of days before cert expires : $daysLeftFo ($daysLeftTs)" $onScreen +log D "warning mail will be send from $warningMsgLowDateFo ($warningMsgLowDateTs) to $warningMsgHighDateFo ($warningMsgHighDateTs)" $onScreen +log D "critical mail will be send from $criticalMsgLowDateFo ($criticalMsgLowDateTs) to $criticalMsgHighDateFo ($criticalMsgHighDateTs)" $onScreen +log D "certificate renew date to $renewDateFo ($renewDateTs)" $onScreen + +# actions to do based on the number of days remaining +# Warning +if [ "$daysLeftFo" -le "$warningDays" ] && [ "$daysLeftFo" -gt "$criticalDays" ]; then + log W "the $certDomain certificate will expire in $daysLeftFo days ($endDateFo)." + if [ "$warningMail" -eq 1 ]; then + log I "send warning message" + mailSubject="[WARNING] - the $certDomain certificate will expire in $daysLeftFo days ($endDateFo)." + mailBody="Demands your attention!\nThe $certDomain certificate will expire in $daysLeftFo days ($endDateFo).\nHave a good day" + sendMail + fi +# critical +elif [ "$daysLeftFo" -le "$criticalDays" ] && [ "$daysLeftFo" -gt "$renewDays" ]; then + log C "the $certDomain certificate will expire in $daysLeftFo days ($endDateFo). Urgent renewal required." + if [ "$criticalMail" -eq 1 ]; then + log I "send critical message" + mailSubject="[CRITICAL] - the $certDomain certificate will expire in $daysLeftFo days ($endDateFo). Urgent renewal required." + mailBody="ATTENTION!\nThe $certDomain certificate will expire in $daysLeftFo days ($endDateFo). Urgent renewal required.\nHave a good day" + sendMail + fi +# renew +elif [ "$daysLeftFo" -eq "$renewDays" ]; then + log C "the '$certDomain' certificate expires in $daysLeftFo ($endDateFo) day. Renew immediately!" + renewCertificate +# expired +elif [ "$daysLeftFo" -le 0 ]; then + log C "the '$certDomain' certificate expired $daysPassFo ($endDateFo) days ago." + log I "send error message" + mailSubject="[CRITICAL] - the '$certDomain' certificate expired $daysPassFo ($endDateFo) days ago." + mailBody="Bad new !\nThe '$certDomain' certificate expired $daysPassFo ($endDateFo) days ago. \nHave a good day" + sendMail +else +# certificat ok + log I "the '$certDomain' certificate is valid. There are $daysLeftFo days left before expiration ($endDateFo)." + if [ "$infoMail" -eq 1 ]; then + log I "send info message" + mailSubject="[INFO] - the '$certDomain' certificate is valid. There are $daysLeftFo days left before expiration ($endDateFo)." + mailBody="All is ok!\nThe '$certDomain' certificate is valid. There are $daysLeftFo days left before expiration ($endDateFo).\nHave a good day" + sendMail + fi + fi +log I "----- end script -----"