diff --git a/.gitlab-ci-psnc.yml b/.gitlab-ci-psnc.yml index 548677eb7f01624e08c7759273074c7779cf244b..25b4acdd2337e22bc891b5221f399c6403ce33d6 100644 --- a/.gitlab-ci-psnc.yml +++ b/.gitlab-ci-psnc.yml @@ -1,6 +1,7 @@ --- stages: - sonarqube + - upload_linux_artifact sonarqube: image: sonarsource/sonar-scanner-cli:${SONAR_SCANNER_CLI_VERSION} stage: sonarqube @@ -10,3 +11,18 @@ sonarqube: - /usr/bin/entrypoint.sh sonar-scanner -Dsonar.projectKey="$CI_PROJECT_NAME" tags: - sonarqube +upload_linux_artifact: + script: ./build.sh --os=linux --arch=amd64 --upx + artifacts: + paths: + - /home/maxadamo/go/bin/acme-downloader +upload_windows_artifact: + script: ./build.sh --os=windows --arch=amd64 --upx + artifacts: + paths: + - /home/maxadamo/go/bin/windows_amd64/acme-downloader.exe +upload_shell_script: + artifacts: + paths: + - acme-downloader.sh + diff --git a/acme-downloader.sh b/acme-downloader.sh new file mode 100755 index 0000000000000000000000000000000000000000..6d1e4d2b88bbd4eff38a8e59ec0745c29ac83f26 --- /dev/null +++ b/acme-downloader.sh @@ -0,0 +1,231 @@ +#!/bin/bash +# +# exit 0: the certificate still looks good and it won't be replaced +# exit 1: a new certificate was installed and we need to reload our service +# exit 2: uh, oh 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 +# +LOCAL_MD5=$(md5sum $0 | awk '{print $1}') +REMOTE_MD5=$(curl -s https://repositories.geant.org/pub/acme/acme-downloader.sh | md5sum | awk '{print $1}') +if [[ $LOCAL_MD5 != $REMOTE_MD5 ]]; then + echo "$0 differs from https://repositories.geant.org/pub/acme/acme-downloader.sh" + echo "suggesting that you are running an older version" + echo "" +fi + +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 + exit 2 +} +trap 'clean_up' SIGINT + +if [ $(id -u) -ne 0 ]; then + echo -e "plase run this script as root\ngiving up..." + clean_up +elif ! which curl jq &>/dev/null; then + echo -e "please install curl and jq\ngiving up..." + clean_up +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 +elif [[ "$ID" == "ubuntu" ]] || [[ "$ID" == "debian" ]]; 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 +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...)" + 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 "" + clean_up +} + +OPTS=$(getopt -o "h" --longoptions "help,redis-token:,vault-token:,cert-name:,team-name:,days:,type:,cert-destination:,fullchain-destination:,key-destination:,ca-destination:,wildcard" -- "$@") +eval set -- "$OPTS" + +while true; do + case "$1" in + -h | --help) + usage + clean_up + ;; + --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' + ;; + --) + shift + break + ;; + esac + shift +done + +TYPE=$(echo $TYPE | tr '[:lower:]' '[:upper:]') + +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 +[[ -z $DAYS ]] && DAYS=30 +[[ -z $CERT_DESTINATION ]] && CERT_DESTINATION="${CERT_BASE}/${CERT_NAME}.crt" +[[ -z $FULLCHAIN_DESTINATION ]] && FULLCHAIN_DESTINATION="${CERT_BASE}/${CERT_NAME}_fullchain.crt" +[[ -z $KEY_DESTINATION ]] && KEY_DESTINATION="${KEY_BASE}/${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 + 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 + 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 +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 +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 +fi +if ! openssl x509 -in $TMP_CA -text -noout &>/dev/null; then + echo "the CA is malformed. Giving up" + clean_up +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 +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 +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 +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 1: if we are here we need to reload our service +exit 1 diff --git a/build.sh b/build.sh index ade9464d78327026c9b80959820ec539caa37e0d..2c155e339b9e46c8997771e8b819cefbdd1c7bff 100755 --- a/build.sh +++ b/build.sh @@ -7,6 +7,7 @@ if ! which go &>/dev/null; then echo "giving up..." exit fi +unset GOBIN BIN_NAME=acme-downloader PATH=$PATH:$(go env GOPATH)/bin GOPATH=$(go env GOPATH) @@ -14,9 +15,9 @@ PROG_VERSION="1.0" BUILDTIME=$(date -u '+%Y-%m-%d_%H:%M:%S') REPO_DIR=$(dirname $0) if [ "$REPO_DIR" = '.' ]; then - REPO_DIR=`pwd` + REPO_DIR=$(pwd) fi -ln -sf "$REPO_DIR" "${GOPATH}/src/" +test -L "${GOPATH}/src/${REPO_DIR}" || ln -sf "$REPO_DIR" "${GOPATH}/src/" REPO_NAME=$(basename "$REPO_DIR") cd "${GOPATH}/src/$REPO_NAME" @@ -27,13 +28,16 @@ usage() { echo " -h | --help [Print this help and exit]" echo " --os=OS (Compile binary for this OS)" echo " --arch=ARCH (Compile binary for this Architecture)" + echo " --upx (Enable UPX compression)" + echo " --no-upx (Disable UPX compression)" echo "" echo " Below is a list of supported OS/Arch combinations:" - echo "OS ARCH"; go tool dist list | column -t -s '/' --table-columns =======,======= + echo "OS ARCH" + go tool dist list | column -t -s '/' --table-columns =======,======= exit } -OPTS=$(getopt -o "h" --longoptions "help,os:,arch:" -- "$@") +OPTS=$(getopt -o "h" --longoptions "help,os:,arch:,upx,no-upx" -- "$@") eval set -- "$OPTS" while true; do @@ -49,6 +53,12 @@ while true; do shift ARCH="$1" ;; + --upx) + UPX='UPX' + ;; + --no-upx) + NOUPX='NOUPX' + ;; --) shift break @@ -85,21 +95,27 @@ run_upx() { upx --brute "$EXECUTABLE_PATH" } -while true; do - echo -e "\nUPX degrades performances but in the case of acme-downloader it is not noticeable" - echo -e "UPX does not work on some OS/Arch combination (I haven't tried them all)\n" - read -p "Do you wish to run upx against ${BIN_NAME}? (y/n) " yn - case $yn in - [Yy]*) - echo "" - run_upx - break - ;; - [Nn]*) break ;; - *) - echo "Please answer Y|y or N|n." - ;; - esac -done +if [ -n $UPX ]; then + run_upx +elif [ -n $NOUPX ]; then + true +else + while true; do + echo -e "\nUPX degrades performances but in the case of acme-downloader it is not noticeable" + echo -e "UPX does not work on some OS/Arch combination (I haven't tried them all)\n" + read -p "Do you wish to run upx against ${BIN_NAME}? (y/n) " yn + case $yn in + [Yy]*) + echo "" + run_upx + break + ;; + [Nn]*) break ;; + *) + echo "Please answer Y|y or N|n." + ;; + esac + done +fi echo -e "\nthe binary was compiled and it is avilable as:\n - \"$EXECUTABLE_PATH\"\n" diff --git a/go.mod b/go.mod new file mode 100644 index 0000000000000000000000000000000000000000..099344e29db9f49ec0d63aba81ebd535dc8b3d78 --- /dev/null +++ b/go.mod @@ -0,0 +1,11 @@ +module acme-downloader + +go 1.16 + +require ( + github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815 + github.com/go-ini/ini v1.62.0 + github.com/smartystreets/goconvey v1.6.4 // indirect + github.com/tidwall/gjson v1.8.0 + gopkg.in/ini.v1 v1.62.0 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000000000000000000000000000000000000..52097330124b9fd65896d73c223aba6b81737193 --- /dev/null +++ b/go.sum @@ -0,0 +1,25 @@ +github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815 h1:bWDMxwH3px2JBh6AyO7hdCn/PkvCZXii8TGj7sbtEbQ= +github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE= +github.com/go-ini/ini v1.62.0 h1:7VJT/ZXjzqSrvtraFp4ONq80hTcRQth1c9ZnQ3uNQvU= +github.com/go-ini/ini v1.62.0/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8= +github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8= +github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= +github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= +github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= +github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM= +github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= +github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s= +github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= +github.com/tidwall/gjson v1.8.0 h1:Qt+orfosKn0rbNTZqHYDqBrmm3UDA4KRkv70fDzG+PQ= +github.com/tidwall/gjson v1.8.0/go.mod h1:5/xDoumyyDNerp2U36lyolv46b3uF/9Bu6OfyQ9GImk= +github.com/tidwall/match v1.0.3 h1:FQUVvBImDutD8wJLN6c5eMzWtjgONK9MwIBCOrUJKeE= +github.com/tidwall/match v1.0.3/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM= +github.com/tidwall/pretty v1.1.0 h1:K3hMW5epkdAVwibsQEfR/7Zj0Qgt4DxtNumTq/VloO8= +github.com/tidwall/pretty v1.1.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +gopkg.in/ini.v1 v1.62.0 h1:duBzk771uxoUuOlyRLkHsygud9+5lrlGjdFBb4mSKDU= +gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=