From 17aa3655abc0d7b7cdd86e9bf525d162a605cef7 Mon Sep 17 00:00:00 2001
From: Massimiliano Adamo <maxadamo@gmail.com>
Date: Wed, 22 Sep 2021 21:24:54 +0200
Subject: [PATCH] add version number

---
 .gitignore                     |   3 +-
 .gitlab-ci-psnc.yml            |  20 ++-
 acme-downloader-artifactory.sh | 296 +++++++++++++++++++++++++++++++++
 acme-downloader.sh             |  13 +-
 build-bash.sh                  |  20 +++
 build.sh => build-go.sh        |   0
 6 files changed, 336 insertions(+), 16 deletions(-)
 create mode 100755 acme-downloader-artifactory.sh
 create mode 100755 build-bash.sh
 rename build.sh => build-go.sh (100%)

diff --git a/.gitignore b/.gitignore
index 0420207..f95f979 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,4 +1,5 @@
 .*.sw[op]
 .DS_Store
 acme-downloader
-acme-downloader.exe
\ No newline at end of file
+acme-downloader.exe
+acme-downloader-afactory.sh
\ No newline at end of file
diff --git a/.gitlab-ci-psnc.yml b/.gitlab-ci-psnc.yml
index 03b71cf..708fea6 100644
--- a/.gitlab-ci-psnc.yml
+++ b/.gitlab-ci-psnc.yml
@@ -6,8 +6,8 @@ stages:
   - upload_windows_amd64_binary
 
 variables:
-  ARTIFACTORY_BASE_URL: https://artifactory.software.geant.org/artifactory/acme-downloader
-  ARTIFACTORY_METADATA_URL: https://artifactory.software.geant.org/artifactory/api/storage/acme-downloader
+  AFACTORY_BASE_URL: https://artifactory.software.geant.org/artifactory/acme-downloader
+  AFACTORY_METADATA_URL: https://artifactory.software.geant.org/artifactory/api/storage/acme-downloader
   ARTIFACT_NAME: acme-downloader
 
 sonarqube:
@@ -27,7 +27,9 @@ upload_linux_shell:
   only:
     - tags
   script:
-    - 'curl -sSf -H "X-JFrog-Art-Api:${ARTIFACTORY_TOKEN}" -X PUT -T ./${ARTIFACT_NAME}.sh ${ARTIFACTORY_BASE_URL}/${ARTIFACT_NAME}.sh'
+    - ./build-bash.sh ${CI_COMMIT_TAG:1}
+    - 'curl -sSf -H "X-JFrog-Art-Api:${AFACTORY_TOKEN}" -X PUT -T ./${ARTIFACT_NAME}-afactory.sh ${AFACTORY_BASE_URL}/${ARTIFACT_NAME}.sh'
+    - 'curl -sSf -H "X-JFrog-Art-Api:${AFACTORY_TOKEN}" -X PUT "${AFACTORY_BASE_URL}/${ARTIFACT_NAME}.sh?properties=version=${CI_COMMIT_TAG:1}'
   tags:
     - acme
 
@@ -36,9 +38,9 @@ upload_linux_amd64_binary:
   only:
     - tags
   script:
-    - ./build.sh --os=linux --arch=amd64 --version=$CI_COMMIT_TAG --upx
-    - 'curl -sSf -H "X-JFrog-Art-Api:${ARTIFACTORY_TOKEN}" -X PUT -T ./$ARTIFACT_NAME ${ARTIFACTORY_BASE_URL}/${ARTIFACT_NAME}_linux_amd64'
-    - 'curl -sSf -H "X-JFrog-Art-Api:${ARTIFACTORY_TOKEN}" -X PUT "${ARTIFACTORY_METADATA_URL}/${ARTIFACT_NAME}_linux_amd64?properties=version=${CI_COMMIT_TAG:1}"'
+    - ./build-go.sh --os=linux --arch=amd64 --version=$CI_COMMIT_TAG --upx
+    - 'curl -sSf -H "X-JFrog-Art-Api:${AFACTORY_TOKEN}" -X PUT -T ./$ARTIFACT_NAME ${AFACTORY_BASE_URL}/${ARTIFACT_NAME}_linux_amd64'
+    - 'curl -sSf -H "X-JFrog-Art-Api:${AFACTORY_TOKEN}" -X PUT "${AFACTORY_METADATA_URL}/${ARTIFACT_NAME}_linux_amd64?properties=version=${CI_COMMIT_TAG:1}"'
   tags:
     - acme
 
@@ -47,8 +49,8 @@ upload_windows_amd64_binary:
   only:
     - tags
   script:
-    - ./build.sh --os=windows --arch=amd64 --version=$CI_COMMIT_TAG --upx
-    - 'curl -sSf -H "X-JFrog-Art-Api:${ARTIFACTORY_TOKEN}" -X PUT -T ./${ARTIFACT_NAME}.exe ${ARTIFACTORY_BASE_URL}/${ARTIFACT_NAME}_windows_amd64.exe'
-    - 'curl -sSf -H "X-JFrog-Art-Api:${ARTIFACTORY_TOKEN}" -X PUT "${ARTIFACTORY_METADATA_URL}/${ARTIFACT_NAME}_windows_amd64.exe?properties=version=${CI_COMMIT_TAG:1}"'
+    - ./build-go.sh --os=windows --arch=amd64 --version=$CI_COMMIT_TAG --upx
+    - 'curl -sSf -H "X-JFrog-Art-Api:${AFACTORY_TOKEN}" -X PUT -T ./${ARTIFACT_NAME}.exe ${AFACTORY_BASE_URL}/${ARTIFACT_NAME}_windows_amd64.exe'
+    - 'curl -sSf -H "X-JFrog-Art-Api:${AFACTORY_TOKEN}" -X PUT "${AFACTORY_METADATA_URL}/${ARTIFACT_NAME}_windows_amd64.exe?properties=version=${CI_COMMIT_TAG:1}"'
   tags:
     - acme
diff --git a/acme-downloader-artifactory.sh b/acme-downloader-artifactory.sh
new file mode 100755
index 0000000..851535b
--- /dev/null
+++ b/acme-downloader-artifactory.sh
@@ -0,0 +1,296 @@
+#!/bin/bash
+#
+# exit 0: the certificate still looks good and it won't be replaced
+# exit 64: a new certificate was installed and we need to reload our service
+# anything else: something went wrong
+#
+# the certificate will be checked, if it is valid, if the key matches
+# and if it contains at least the cert-name
+#
+VERSION="1.0.9"
+BUILDTIME="2021-09-22_19:04:16"
+REDIS_URL="https://redis.geant.org/GET"
+VAULT_URL="https://vault.geant.org/v1"
+TMP_CERT=$(mktemp)
+TMP_FULLCHAIN=$(mktemp)
+TMP_CA=$(mktemp)
+TMP_KEY=$(mktemp)
+
+stty -echoctl # hide ^C
+
+# function called by trap
+clean_up() {
+    rm -f $TMP_CERT $TMP_FULLCHAIN $TMP_CA $TMP_KEY
+    if [ "$#" -eq 1 ]; then
+        exit $1
+    fi
+    exit
+}
+trap 'clean_up' SIGINT
+
+if [ $(id -u) -ne 0 ]; then
+    echo -e "plase run this script as root\ngiving up..."
+    clean_up 2
+elif ! which jq &>/dev/null; then
+    echo -e "please install jq\ngiving up..."
+    clean_up 2
+elif ! which curl &>/dev/null; then
+    echo -e "please install curl\ngiving up..."
+    clean_up 2
+fi
+
+check_version() {
+    # check upstrem version
+    SCRIPT_URL="https://artifactory.software.geant.org/artifactory/acme-downloader/acme-downloader.sh"
+    METADATA_URL="https://artifactory.software.geant.org/artifactory/api/storage/acme-downloader/acme-downloader.sh?properties=version"
+    REMOTE_VERSION=$(curl -s $METADATA_URL | jq -j .properties.version[0])
+    if [[ $VERSION != $REMOTE_VERSION ]]; then
+        echo ""
+        echo "you are running version $(basename $0) ${VERSION}"
+        echo "version $REMOTE_VERSION is available"
+        echo "in order to fetch and install the new version you can use the option: --update"
+        echo ""
+    else
+        if [[ -n $CHECK ]]; then
+            echo "you are running the latest version"
+        fi
+    fi
+    if [[ -n $CHECK ]]; then
+        exit 0
+    fi
+}
+
+# lsb_release is not always installed
+if ! source /etc/os-release &>/dev/null; then
+    echo "no idea what to do with this OS: I was not able to access /etc/os-release"
+    echo ""
+    clean_up 2
+elif [[ "$ID" == "ubuntu" ]] || [[ "$ID" == "debian" ]] || [[ "$ID" == "arch"* ]]; then
+    CERT_BASE="/etc/ssl/certs"
+    KEY_BASE="/etc/ssl/private"
+    GROUPNAME="ssl-cert"
+elif [[ "$ID" == "centos" ]] || [[ "$ID" == "rhel" ]]; then
+    CERT_BASE="/etc/pki/tls/certs"
+    KEY_BASE="/etc/pki/tls/private"
+    GROUPNAME="root"
+else
+    echo "no idea what to do with OS: $ID"
+    echo "please amend the script accordingly"
+    echo ""
+    clean_up 2
+fi
+
+usage() {
+    echo "Usage: $(basename $0)"
+    echo "    -h | --help [Print this help and exit]"
+    echo "    --redis-token (Redis access token)"
+    echo "    --vault-token (Vault access token)"
+    echo "    --cert-name (Certificate name)"
+    echo "    --team-name (Team name: swd, dream_team, it, ne, ti, nmaas...)"
+    echo "    --days [OPTIONAL check days before expiration. Default: 30)"
+    echo "    --type [OPTIONAL. OV or EV. Default: EV]"
+    echo "    --cert-destination [OPTIONAL Default: ${CERT_BASE}/<cert-name>.crt]"
+    echo "    --fullchain-destination [OPTIONAL Default: ${CERT_BASE}/<cert-name>_fullchain.crt]"
+    echo "    --key-destination [OPTIONAL Default: ${KEY_BASE}/<cert-name>.key]"
+    echo "    --ca-destination [OPTIONAL Default: ${CERT_BASE}/COMODO_<type>.crt]"
+    echo "    --wildcard [OPTIONAL if the certificate is wildcard]"
+    echo "    --check-version [OPTIONAL check difference with upstream exit]"
+    echo "    --update [OPTIONAL self-updates the script and exit]"
+    echo "    --version [OPTIONAL print version number and creation date]"
+    echo ""
+    clean_up 2
+}
+
+OPTS=$(getopt -o "h" --longoptions "help,redis-token:,vault-token:,cert-name:,team-name:,days:,type:,cert-destination:,fullchain-destination:,key-destination:,ca-destination:,check-version,update,version,wildcard" -- "$@")
+eval set -- "$OPTS"
+
+while true; do
+    case "$1" in
+    -h | --help)
+        usage
+        ;;
+    --redis-token)
+        shift
+        REDIS_TOKEN="${1}"
+        ;;
+    --vault-token)
+        shift
+        VAULT_TOKEN="${1}"
+        ;;
+    --cert-name)
+        shift
+        CERT_NAME="${1}"
+        ;;
+    --team-name)
+        shift
+        TEAM_NAME="${1}"
+        ;;
+    --days)
+        shift
+        DAYS="${1}"
+        ;;
+    --type)
+        shift
+        TYPE="${1}"
+        ;;
+    --cert-destination)
+        shift
+        CERT_DESTINATION="${1}"
+        ;;
+    --fullchain-destination)
+        shift
+        FULLCHAIN_DESTINATION="${1}"
+        ;;
+    --key-destination)
+        shift
+        KEY_DESTINATION="${1}"
+        ;;
+    --ca-destination)
+        shift
+        CA_DESTINATION="${1}"
+        ;;
+    --wildcard)
+        WILDCARD='WILDCARD'
+        ;;
+    --update)
+        UPDATE='UPDATE'
+        ;;
+    --check-version)
+        CHECK='CHECK'
+        ;;
+    --version)
+        SCRIPT_VERSION='SCRIPT_VERSION'
+        ;;
+    --)
+        shift
+        break
+        ;;
+    esac
+    shift
+done
+
+if [[ -n $SCRIPT_VERSION ]]; then
+    echo "$(basename $0) version: $VERSION built on $BUILDTIME"
+    clean_up 0
+fi
+
+TYPE=$(echo $TYPE | tr '[:lower:]' '[:upper:]')
+
+check_version $0
+
+if [[ -n $UPDATE ]]; then
+    curl $SCRIPT_URL -o $0
+    UPDATE_STATUS=$?
+    if [ $UPDATE_STATUS == 0 ]; then
+        echo -e "\n$0 updated successfully\n"
+    else
+        echo -e "\nfailed to update $0"
+        echo -e "Please download the script manually from this URL: ${SCRIPT_URL}\n"
+    fi
+    clean_up $UPDATE_STATUS
+fi
+
+if [[ -z $REDIS_TOKEN ]] || [[ -z $VAULT_TOKEN ]] || [[ -z $CERT_NAME ]] || [[ -z $TEAM_NAME ]]; then
+    echo -e "\n--redis-token, --vault-token, --cert-name and --team-name are mandatory\n"
+    usage
+fi
+
+[[ -z $TYPE ]] && TYPE="EV" # let's default to EV type
+if [[ $TYPE != "EV" ]] && [[ $TYPE != "OV" ]]; then
+    echo "type must be either EV, ev, OV, ov"
+    usage
+fi
+if [[ -z $WILDCARD ]]; then
+    MODIFIED_CERT_NAME=$CERT_NAME
+else
+    MODIFIED_CERT_NAME="wildcard_${CERT_NAME}"
+fi
+[[ -z $DAYS ]] && DAYS=30
+[[ -z $CERT_DESTINATION ]] && CERT_DESTINATION="${CERT_BASE}/${MODIFIED_CERT_NAME}.crt"
+[[ -z $FULLCHAIN_DESTINATION ]] && FULLCHAIN_DESTINATION="${CERT_BASE}/${MODIFIED_CERT_NAME}_fullchain.crt"
+[[ -z $KEY_DESTINATION ]] && KEY_DESTINATION="${KEY_BASE}/${MODIFIED_CERT_NAME}.key"
+[[ -z $CA_DESTINATION ]] && CA_DESTINATION="${CERT_BASE}/COMODO_${TYPE}.crt"
+
+UNDERSCORED_CERT_NAME=$(echo $CERT_NAME | sed -e 's,\.,_,g')
+MINUTES=$((${DAYS} * 86400))
+
+# give up if the certificate expiration is still within a proper range
+if openssl x509 -checkend $MINUTES -noout -in $FULLCHAIN_DESTINATION &>/dev/null; then
+    echo "the certificate $FULLCHAIN_DESTINATION is still valid"
+    rm -f $TMP_CERT $TMP_FULLCHAIN $TMP_CA $TMP_KEY
+    exit 0
+fi
+
+# download certificates and delete the last empty line if it exists and remove the first line from Webdis
+if [[ -z $WILDCARD ]]; then
+    curl -s -u redis:$REDIS_TOKEN ${REDIS_URL}/${TEAM_NAME}:${CERT_NAME}:redis_${UNDERSCORED_CERT_NAME}_pem.txt >$TMP_CERT
+    curl -s -u redis:$REDIS_TOKEN ${REDIS_URL}/${TEAM_NAME}:${CERT_NAME}:redis_${UNDERSCORED_CERT_NAME}_fullchain_pem.txt >$TMP_FULLCHAIN
+    curl -s -u redis:$REDIS_TOKEN ${REDIS_URL}/${TEAM_NAME}:${CERT_NAME}:redis_${UNDERSCORED_CERT_NAME}_chain_pem.txt >$TMP_CA
+    curl -s -H "X-Vault-Token: ${VAULT_TOKEN}" ${VAULT_URL}/${TEAM_NAME}/${CERT_NAME}/vault_${UNDERSCORED_CERT_NAME}_key | jq -j .data.value >$TMP_KEY
+else
+    if [[ "$TEAM_NAME" == "puppet" ]]; then
+        curl -s -u redis:$REDIS_TOKEN ${REDIS_URL}/${TEAM_NAME}:common:redis_sectigo_ov_${UNDERSCORED_CERT_NAME}_pem.txt >$TMP_CERT
+        curl -s -u redis:$REDIS_TOKEN ${REDIS_URL}/${TEAM_NAME}:common:redis_sectigo_ov_${UNDERSCORED_CERT_NAME}_fullchain_pem.txt >$TMP_FULLCHAIN
+        curl -s -u redis:$REDIS_TOKEN ${REDIS_URL}/${TEAM_NAME}:common:redis_sectigo_ov_${UNDERSCORED_CERT_NAME}_chain_pem.txt >$TMP_CA
+        curl -s -H "X-Vault-Token: ${VAULT_TOKEN}" ${VAULT_URL}/${TEAM_NAME}/common/vault_sectigo_ov_wildcard_${UNDERSCORED_CERT_NAME}_key | jq -j .data.value >$TMP_KEY
+    else
+        curl -s -u redis:$REDIS_TOKEN ${REDIS_URL}/${TEAM_NAME}:${CERT_NAME}:redis_sectigo_ov_${UNDERSCORED_CERT_NAME}_pem.txt >$TMP_CERT
+        curl -s -u redis:$REDIS_TOKEN ${REDIS_URL}/${TEAM_NAME}:${CERT_NAME}:redis_sectigo_ov_${UNDERSCORED_CERT_NAME}_fullchain_pem.txt >$TMP_FULLCHAIN
+        curl -s -u redis:$REDIS_TOKEN ${REDIS_URL}/${TEAM_NAME}:${CERT_NAME}:redis_sectigo_ov_${UNDERSCORED_CERT_NAME}_chain_pem.txt >$TMP_CA
+        curl -s -H "X-Vault-Token: ${VAULT_TOKEN}" ${VAULT_URL}/${TEAM_NAME}/${CERT_NAME}/vault_sectigo_ov_wildcard_${UNDERSCORED_CERT_NAME}_key | jq -j .data.value >$TMP_KEY
+        echo "curl -s -H "X-Vault-Token: ${VAULT_TOKEN}" ${VAULT_URL}/${CERT_NAME}/${TEAM_NAME}/vault_sectigo_ov_wildcard_${UNDERSCORED_CERT_NAME}_key"
+    fi
+fi
+
+# Before installing any certificate we need to check the validity of
+# Certificate, Full Chain, CA and Key that we downloaded
+
+# checking if certificates are valid
+if ! openssl x509 -checkend $MINUTES -noout -in $TMP_CERT &>/dev/null; then
+    echo "the Certificate is malformed or is expiring. Giving up"
+    clean_up 2
+fi
+if ! openssl x509 -checkend $MINUTES -noout -in $TMP_FULLCHAIN &>/dev/null; then
+    echo "the Full Chain is malformed or is expiring. Giving up"
+    clean_up 2
+fi
+if ! openssl x509 -in $TMP_CA -text -noout &>/dev/null; then
+    echo "the CA is malformed. Giving up"
+    clean_up 2
+fi
+
+# checking if key matches the certificate and the full-chain
+KEY_MD5=$(openssl rsa -noout -modulus -in $TMP_KEY | openssl md5 | awk '{print $NF}')
+FULLCHAIN_MD5=$(openssl x509 -noout -modulus -in $TMP_FULLCHAIN | openssl md5 | awk '{print $NF}')
+CRT_MD5=$(openssl x509 -noout -modulus -in $TMP_CERT | openssl md5 | awk '{print $NF}')
+if [[ $KEY_MD5 != $CRT_MD5 ]] || [[ $KEY_MD5 != $FULLCHAIN_MD5 ]]; then
+    echo "the Key $TMP_KEY is either malformed or it does not match the certificate. Giving up"
+    clean_up 2
+fi
+
+# checking if the certificate contains at least our cert_name
+if ! openssl x509 -noout -text -in $TMP_CERT | grep -qw $CERT_NAME; then
+    echo "the certificate does not match your CN $CERT_NAME"
+    clean_up 2
+fi
+if ! openssl x509 -noout -text -in $TMP_FULLCHAIN | grep -qw $CERT_NAME; then
+    echo "the full chain certificate does not match your CN $CERT_NAME"
+    clean_up 2
+fi
+
+# let's install the certificates
+install --owner=root --group=root --mode=0755 --directory $CERT_BASE
+install --owner=root --group=$GROUPNAME --mode=0750 --directory $KEY_BASE
+install --owner=root --group=root --mode=0644 -T ${TMP_CERT} ${CERT_DESTINATION}
+install --owner=root --group=root --mode=0644 -T ${TMP_FULLCHAIN} ${FULLCHAIN_DESTINATION}
+install --owner=root --group=root --mode=0644 -T ${TMP_CA} ${CA_DESTINATION}
+install --owner=root --group=$GROUPNAME --mode=0640 -T ${TMP_KEY} ${KEY_DESTINATION}
+rm -f $TMP_CERT $TMP_FULLCHAIN $TMP_CA $TMP_KEY
+
+echo "installed: ${CERT_DESTINATION}"
+echo "installed: ${FULLCHAIN_DESTINATION}"
+echo "installed: ${CA_DESTINATION}"
+echo "installed: ${KEY_DESTINATION}"
+
+# exiting 64: if we are here we need to reload our service
+exit 64
diff --git a/acme-downloader.sh b/acme-downloader.sh
index 575c3c9..3f96024 100755
--- a/acme-downloader.sh
+++ b/acme-downloader.sh
@@ -7,6 +7,8 @@
 # the certificate will be checked, if it is valid, if the key matches
 # and if it contains at least the cert-name
 #
+VERSION="VERSION_SET_BY_CI"
+BUILDTIME="BUILDTIME_SET_BY_CI"
 REDIS_URL="https://redis.geant.org/GET"
 VAULT_URL="https://vault.geant.org/v1"
 TMP_CERT=$(mktemp)
@@ -39,14 +41,13 @@ fi
 
 check_version() {
     # check upstrem version
-    LOCAL_MD5=$(md5sum $1 | awk '{print $1}')
     SCRIPT_URL="https://artifactory.software.geant.org/artifactory/acme-downloader/acme-downloader.sh"
-    METADATA_URL="https://artifactory.software.geant.org/artifactory/api/storage/acme-downloader/acme-downloader.sh"
-    REMOTE_MD5=$(curl -s $METADATA_URL | jq -j .checksums.md5)
-    if [[ $LOCAL_MD5 != $REMOTE_MD5 ]]; then
+    METADATA_URL="https://artifactory.software.geant.org/artifactory/api/storage/acme-downloader/acme-downloader.sh?properties=version"
+    REMOTE_VERSION=$(curl -s $METADATA_URL | jq -j .properties.version[0])
+    if [[ $VERSION != $REMOTE_VERSION ]]; then
         echo ""
-        echo "$1 differs from $SCRIPT_URL"
-        echo "suggesting that you are running an older version"
+        echo "you are running version ${VERSION}"
+        echo "version $REMOTE_VERSION is available"
         echo "in order to fetch and install the new version you can use the option: --update"
         echo ""
     else
diff --git a/build-bash.sh b/build-bash.sh
new file mode 100755
index 0000000..41985e8
--- /dev/null
+++ b/build-bash.sh
@@ -0,0 +1,20 @@
+#!/bin/bash
+#
+# upload a copy of the script with version number and buildtime
+#
+if [ "$#" -gt 0 ]; then
+    VERSION=$1
+else
+    VERSION=$(git describe --tags $(git rev-list --tags --max-count=1) | tr -d v)
+fi
+BUILDTIME=$(date -u '+%Y-%m-%d_%H:%M:%S')
+REPO_DIR=$(dirname $0)
+if [[ "$REPO_DIR" = '.' ]]; then
+    REPO_DIR=$(pwd)
+fi
+
+test -f acme-downloader-afactory.sh && rm -f acme-downloader-afactory.sh
+cp acme-downloader.sh acme-downloader-afactory.sh
+
+sed -i s/VERSION_SET_BY_CI/$VERSION/ acme-downloader-afactory.sh
+sed -i s/BUILDTIME_SET_BY_CI/$BUILDTIME/ acme-downloader-afactory.sh
diff --git a/build.sh b/build-go.sh
similarity index 100%
rename from build.sh
rename to build-go.sh
-- 
GitLab