From f4e53c374ae740e475a1c62e7fac7f005a5c73c6 Mon Sep 17 00:00:00 2001 From: Jeremy Lecour Date: Thu, 30 Apr 2020 16:00:34 +0200 Subject: [PATCH 01/89] ask for CA password before user password --- shellpki | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/shellpki b/shellpki index 9fb1d94..df9927c 100755 --- a/shellpki +++ b/shellpki @@ -222,6 +222,9 @@ create() { # check if CN already exist [ -f "${CRTDIR}/${cn}.crt" ] && error "${cn} already used !" + # ask for CA passphrase + ask_ca_password 0 + # ask for client key passphrase if [ "${with_pass}" -eq 0 ]; then trap 'unset PASSWORD' 0 @@ -232,9 +235,6 @@ create() { printf "\n" fi - # ask for CA passphrase - ask_ca_password 0 - # generate private key if [ "${with_pass}" -eq 0 ]; then PASSWORD="${PASSWORD}" "$OPENSSL" genrsa \ @@ -331,7 +331,7 @@ revoke() { [ ! -f "${CRTDIR}/${cn}.crt" ] && error "Unknow CN : ${cn}" # check if CRT is a valid - "${OPENSSL}" x509 -noout -subject -in "${CRTDIR}/${cn}.crt" >/dev/null 2>&1 || error "${CRTDIR}/${cn}.crt is not a valid CRT, you msust delete it !" + "${OPENSSL}" x509 -noout -subject -in "${CRTDIR}/${cn}.crt" >/dev/null 2>&1 || error "${CRTDIR}/${cn}.crt is not a valid CRT, you must delete it !" # ask for CA passphrase ask_ca_password 0 From 921cba15b689e901e9464892078c95a74543e14d Mon Sep 17 00:00:00 2001 From: Jeremy Lecour Date: Mon, 4 May 2020 14:21:58 +0200 Subject: [PATCH 02/89] accept a password file --- shellpki | 128 ++++++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 98 insertions(+), 30 deletions(-) diff --git a/shellpki b/shellpki index df9927c..f2ef68f 100755 --- a/shellpki +++ b/shellpki @@ -154,35 +154,79 @@ ask_ca_password() { } create() { - from_csr=1 - with_pass=1 + from_csr=0 + ask_user_pass=0 + # Parse options + # based on https://gist.github.com/deshion/10d3cb5f88a21671e17a while :; do - case "${1}" in - -f|--file) - shift - [ ! -f "${1}" ] && error "${1} must be a file" - from_csr=0 - csr_file=$(readlink -f "${1}") - shift;; + case $1 in + -f|--file|--csr-file) + # csr-file option, with value separated by space + if [ -n "$2" ]; then + from_csr=1 + csr_file=$(readlink -f -- "${2}") + shift + else + printf 'ERROR: "--csr-file" requires a non-empty option argument.\n' >&2 + exit 1 + fi + ;; + --file=?*|--csr-file=?*) + from_csr=1 + # csr-file option, with value separated by = + csr_file=$(readlink -f -- "${1#*=}") + ;; + --file=|--csr-file=) + # csr-file options, without value + printf 'ERROR: "--csr-file" requires a non-empty option argument.\n' >&2 + exit 1 + ;; -p|--password) - with_pass=0 - shift;; + ask_pass=1 + ;; + --password-file) + # password-file option, with value separated by space + if [ -n "$2" ]; then + password_file=$(readlink -f -- "${2}") + shift + else + printf 'ERROR: "--password-file" requires a non-empty option argument.\n' >&2 + exit 1 + fi + ;; + --password-file=?*) + # password-file option, with value separated by = + password_file=$(readlink -f -- "${1#*=}") + ;; + --password-file=) + # password-file options, without value + printf 'ERROR: "--password-file" requires a non-empty option argument.\n' >&2 + exit 1 + ;; --) + # End of all options. shift - break;; + break + ;; -?*) - warning "unknow option ${1} (ignored)" - shift;; + # ignore unknown options + printf 'WARN: Unknown option (ignored): %s\n' "$1" >&2 + ;; *) - break;; + # Default case: If no more options then break out of the loop. + break + ;; esac + + shift done cn="${1:-}" - if [ "${from_csr}" -eq 0 ]; then - [ "${with_pass}" -eq 0 ] && warning "Warning: -p made nothing with -f" + if [ "${from_csr}" -eq 1 ]; then + [ "${ask_pass}" -eq 1 ] && warning "Warning: -p|--password is ignored with -f|--file|--crt-file" + [ -n "${password_file}" ] && warning "Warning: --password-file is ignored with -f|--file|--crt-file" # ask for CA passphrase ask_ca_password 0 @@ -192,7 +236,7 @@ create() { -noout -subject \ -in "${csr_file}" \ >/dev/null 2>&1 \ - || error "${csr_file} is not a valid CSR !" + || error "${csr_file} is not a valid CSR !" # check if csr_file contain a CN "${OPENSSL}" req \ @@ -200,7 +244,7 @@ create() { -in "${csr_file}" \ | grep -Eo "CN\s*=[^,/]*" \ >/dev/null 2>&1 \ - || error "${csr_file} don't contain a CommonName !" + || error "${csr_file} don't contain a CommonName !" # get CN from CSR cn=$("${OPENSSL}" req -noout -subject -in "${csr_file}"|grep -Eo "CN\s*=[^,/]*"|cut -d'=' -f2|xargs) @@ -225,18 +269,25 @@ create() { # ask for CA passphrase ask_ca_password 0 - # ask for client key passphrase - if [ "${with_pass}" -eq 0 ]; then + if [ -n "${password_file}" ] && [ -r "${password_file}" ]; then + PASSWORD=$(head -n 1 "${password_file}" | tr -d '\n') + if [ -z "${PASSWORD}" ]; then + warning "Warning: empty password from file \`${password_file}'" + fi + elif [ "${ask_pass}" -eq 1 ]; then trap 'unset PASSWORD' 0 stty -echo printf "Password for user key : " read -r PASSWORD stty echo printf "\n" + if [ -z "${PASSWORD}" ]; then + warning "Warning: empty password from input" + fi fi # generate private key - if [ "${with_pass}" -eq 0 ]; then + if [ -n "${PASSWORD}" ]; then PASSWORD="${PASSWORD}" "$OPENSSL" genrsa \ -aes256 -passout env:PASSWORD \ -out "${KEYDIR}/${cn}-${TIMESTAMP}.key" \ @@ -247,7 +298,7 @@ create() { 2048 >/dev/null 2>&1 fi - if [ "${with_pass}" -eq 0 ]; then + if [ -n "${PASSWORD}" ]; then # generate csr req PASSWORD="${PASSWORD}" "$OPENSSL" req \ -batch -new \ @@ -280,9 +331,9 @@ EOF # check if CRT is a valid "${OPENSSL}" x509 \ -noout -subject \ - -in "${CRTDIR}/${cn}.crt" \ + -in "${CRTDIR}/${cn}.crt" \ >/dev/null 2>&1 \ - || rm -f "${CRTDIR}/${cn}.crt" + || rm -f "${CRTDIR}/${cn}.crt" [ -f "${CRTDIR}/${cn}.crt" ] || error "Error in CSR creation" @@ -291,10 +342,23 @@ EOF echo "The CRT file is available in ${CRTDIR}/${cn}.crt" # generate pkcs12 format - if [ "${with_pass}" -eq 0 ]; then - PASSWORD="${PASSWORD}" "${OPENSSL}" pkcs12 -export -nodes -passin env:PASSWORD -passout env:PASSWORD -inkey "${KEYDIR}/${cn}-${TIMESTAMP}.key" -in "${CRTDIR}/${cn}.crt" -out "${PKCS12DIR}/${cn}-${TIMESTAMP}.p12" + if [ -n "${PASSWORD}" ]; then + PASSWORD="${PASSWORD}" "${OPENSSL}" pkcs12 \ + -export \ + -nodes \ + -passin env:PASSWORD \ + -passout env:PASSWORD \ + -inkey "${KEYDIR}/${cn}-${TIMESTAMP}.key" \ + -in "${CRTDIR}/${cn}.crt" \ + -out "${PKCS12DIR}/${cn}-${TIMESTAMP}.p12" else - "${OPENSSL}" pkcs12 -export -nodes -passout pass: -inkey "${KEYDIR}/${cn}-${TIMESTAMP}.key" -in "${CRTDIR}/${cn}.crt" -out "${PKCS12DIR}/${cn}-${TIMESTAMP}.p12" + "${OPENSSL}" pkcs12 \ + -export \ + -nodes \ + -passout pass: \ + -inkey "${KEYDIR}/${cn}-${TIMESTAMP}.key" \ + -in "${CRTDIR}/${cn}.crt" \ + -out "${PKCS12DIR}/${cn}-${TIMESTAMP}.p12" fi chmod 640 "${PKCS12DIR}/${cn}-${TIMESTAMP}.p12" @@ -414,8 +478,12 @@ main() { # default config # TODO : override with /etc/default/shellpki CONFFILE="/etc/shellpki/openssl.cnf" - PKIUSER="shellpki" - [ "$(uname)" = "OpenBSD" ] && PKIUSER="_shellpki" + + if [ "$(uname)" = "OpenBSD" ]; then + PKIUSER="_shellpki" + else + PKIUSER="shellpki" + fi [ "${USER}" != "root" ] || [ "${USER}" != "${PKIUSER}" ] || error "Please become root before running ${0} !" From 2e6c4f541f003c4511a5d88ec9b85c1046de637c Mon Sep 17 00:00:00 2001 From: Jeremy Lecour Date: Mon, 4 May 2020 17:41:21 +0200 Subject: [PATCH 03/89] Create a CHANGELOG --- CHANGELOG.md | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 CHANGELOG.md diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..d9f3194 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,21 @@ +# Changelog +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## [Unreleased] + +### Added + +* Create a changelog + +### Changed + +### Deprecated + +### Removed + +### Fixed + +### Security From 536de976cc0d1865ac197fab92142bce3bcc1c0b Mon Sep 17 00:00:00 2001 From: Jeremy Lecour Date: Mon, 4 May 2020 17:42:01 +0200 Subject: [PATCH 04/89] Check on $USER was always true --- CHANGELOG.md | 2 ++ shellpki | 4 +++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d9f3194..016f93e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,4 +18,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed +* Check on $USER was always true + ### Security diff --git a/shellpki b/shellpki index f2ef68f..bccc1b9 100755 --- a/shellpki +++ b/shellpki @@ -485,7 +485,9 @@ main() { PKIUSER="shellpki" fi - [ "${USER}" != "root" ] || [ "${USER}" != "${PKIUSER}" ] || error "Please become root before running ${0} !" + if [ "${USER}" != "root" ] && [ "${USER}" != "${PKIUSER}" ]; then + error "Please become root before running ${0} !" + fi # retrieve CA path from config file CADIR=$(grep -E "^dir" "${CONFFILE}" | cut -d'=' -f2|xargs -n1) From 48b282c2df33a80dc11693068db49e672fbde5e7 Mon Sep 17 00:00:00 2001 From: Jeremy Lecour Date: Mon, 4 May 2020 17:43:09 +0200 Subject: [PATCH 05/89] Add a version number and `version` command --- CHANGELOG.md | 1 + shellpki | 28 ++++++++++++++++++++++++++++ 2 files changed, 29 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 016f93e..491c19c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added * Create a changelog +* Add a version number and `version` command ### Changed diff --git a/shellpki b/shellpki index bccc1b9..52ddcc6 100755 --- a/shellpki +++ b/shellpki @@ -5,6 +5,29 @@ set -e +VERSION="1.0.0" + +show_version() { + cat <, + Thomas Martin , + Gregory Colpart , + Romain Dessort , + Benoit Série , + Victor Laborie , + Daniel Jakots , + Patrick Marchand , + Jérémy Lecour + and others. + +shellpki comes with ABSOLUTELY NO WARRANTY. This is free software, +and you are welcome to redistribute it under certain conditions. +See the MIT Licence for details. +END +} + init() { umask 0177 @@ -551,6 +574,11 @@ main() { check "$@" ;; + version) + show_version + exit 0 + ;; + *) usage >&2 exit 1 From 1443df56bc39618981e0ce833248413b6b74993c Mon Sep 17 00:00:00 2001 From: Jeremy Lecour Date: Mon, 4 May 2020 17:44:01 +0200 Subject: [PATCH 06/89] Rename internal function usage() to show_usage() --- CHANGELOG.md | 2 ++ shellpki | 33 +++++++++++++++++++++++++-------- 2 files changed, 27 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 491c19c..6ba9352 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed +* Rename internal function usage() to show_usage() + ### Deprecated ### Removed diff --git a/shellpki b/shellpki index 52ddcc6..a4edfbf 100755 --- a/shellpki +++ b/shellpki @@ -38,7 +38,10 @@ init() { [ -f "${SERIAL}" ] || echo "01" > "${SERIAL}" cn="${1:-}" - [ -z "${cn}" ] && usage >&2 && exit 1 + if [ -z "${cn}" ]; then + show_usage >&2 + exit 1 + fi if [ -f "${CAKEY}" ]; then printf "%s already exists, do you really want to erase it ? [y/N] " "${CAKEY}" @@ -79,7 +82,10 @@ ocsp() { umask 0177 ocsp_uri="${1:-}" - [ -z "${ocsp_uri}" ] && usage >&2 && exit 1 + if [ -z "${ocsp_uri}" ]; then + show_usage >&2 + exit 1 + fi url=$(echo "${ocsp_uri}"|cut -d':' -f1) port=$(echo "${ocsp_uri}"|cut -d':' -f2) @@ -113,7 +119,7 @@ EOF exec "${OPENSSL}" ocsp -ignore_err -index "${INDEX}" -port "${port}" -rsigner "${OCSPCERT}" -rkey "${OCSPKEY}" -CA "${CACERT}" -text } -usage() { +show_usage() { cat < [options] [CommonName] @@ -126,9 +132,9 @@ Run OCSPD server : ${0} ocsp Create a client cert with key and CSR directly generated on server -(use -p for set a password on client key) : +(use -p or --password-file to set a password on the client key) : - ${0} create [-p] + ${0} create [-p|--password-file=] Create a client cert from a CSR (doesn't need key) : @@ -284,7 +290,10 @@ create() { echo "The CRT file is available in ${CRTDIR}/${cn}.crt" else - [ -z "${cn}" ] && usage >&2 && exit 1 + if [ -z "${cn}" ]; then + show_usage >&2 + exit 1 + fi # check if CN already exist [ -f "${CRTDIR}/${cn}.crt" ] && error "${cn} already used !" @@ -409,7 +418,10 @@ EOF } revoke() { - [ "${1}" = "" ] && usage >&2 && exit 1 + if [ "${1}" = "" ]; then + show_usage >&2 + exit 1 + fi # get CN from param cn="${1}" @@ -579,8 +591,13 @@ main() { exit 0 ;; + help) + show_usage + exit 0 + ;; + *) - usage >&2 + show_usage >&2 exit 1 ;; esac From 480077b60002686ee8f74d15208f7222cade0253 Mon Sep 17 00:00:00 2001 From: Jeremy Lecour Date: Mon, 4 May 2020 17:45:28 +0200 Subject: [PATCH 07/89] update CHANGELOG for password-file option --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6ba9352..ec32439 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 * Create a changelog * Add a version number and `version` command +* Accept a `password-file` command line option to read password from a file ### Changed From f63caa07798a5f8551ee380b73b272de1867e01a Mon Sep 17 00:00:00 2001 From: Jeremy Lecour Date: Mon, 4 May 2020 17:58:13 +0200 Subject: [PATCH 08/89] fix variable name --- shellpki | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shellpki b/shellpki index a4edfbf..917a73e 100755 --- a/shellpki +++ b/shellpki @@ -184,7 +184,7 @@ ask_ca_password() { create() { from_csr=0 - ask_user_pass=0 + ask_pass=0 # Parse options # based on https://gist.github.com/deshion/10d3cb5f88a21671e17a From 420fcddb9096048976495a20997c51c6de8d370f Mon Sep 17 00:00:00 2001 From: Jeremy Lecour Date: Mon, 4 May 2020 18:07:20 +0200 Subject: [PATCH 09/89] whitespaces and if/then normalization --- shellpki | 268 ++++++++++++++++++++++++++++++++++--------------------- 1 file changed, 166 insertions(+), 102 deletions(-) diff --git a/shellpki b/shellpki index 917a73e..8e97010 100755 --- a/shellpki +++ b/shellpki @@ -46,36 +46,48 @@ init() { if [ -f "${CAKEY}" ]; then printf "%s already exists, do you really want to erase it ? [y/N] " "${CAKEY}" read -r REPLY - resp=$(echo "${REPLY}"|tr 'Y' 'y') - [ "${resp}" = "y" ] && rm -f "${CAKEY}" "${CACERT}" + resp=$(echo "${REPLY}" | tr 'Y' 'y') + if [ "${resp}" = "y" ]; then + rm -f "${CAKEY}" "${CACERT}" + fi fi - [ ! -f "${CAKEY}" ] && "$OPENSSL" \ - genrsa \ - -out "${CAKEY}" \ - -aes256 4096 >/dev/null 2>&1 + if [ ! -f "${CAKEY}" ]; then + "$OPENSSL" genrsa \ + -out "${CAKEY}" \ + -aes256 4096 \ + >/dev/null 2>&1 + fi if [ -f "${CACERT}" ]; then printf "%s already exists, do you really want to erase it ? [y/N] " "${CACERT}" read -r REPLY - resp=$(echo "${REPLY}"|tr 'Y' 'y') - [ "${resp}" = "y" ] && rm "${CACERT}" + resp=$(echo "${REPLY}" | tr 'Y' 'y') + if [ "${resp}" = "y" ]; then + rm "${CACERT}" + fi fi - [ ! -f "${CACERT}" ] && ask_ca_password 0 + if [ ! -f "${CACERT}" ]; then + ask_ca_password 0 + fi - [ ! -f "${CACERT}" ] && CA_PASSWORD="${CA_PASSWORD}" "${OPENSSL}" \ - req -new \ - -batch -sha512 \ - -x509 -days 3650 \ - -extensions v3_ca \ - -key "${CAKEY}" \ - -out "${CACERT}" \ - -passin env:CA_PASSWORD \ - -config /dev/stdin </dev/null 2>&1 + if [ ! -f "${OCSPKEY}" ]; then + "$OPENSSL" genrsa \ + -out "${OCSPKEY}" \ + 2048 \ + >/dev/null 2>&1 + fi - "$OPENSSL" req \ - -batch -new \ - -key "${OCSPKEY}" \ - -out "${CSRDIR}/ocsp.csr" \ + "$OPENSSL" req \ + -batch \ + -new \ + -key "${OCSPKEY}" \ + -out "${CSRDIR}/ocsp.csr" \ -config /dev/stdin </dev/null 2>&1 \ - || ask_ca_password "${attempt}" + + if [ -z "${CA_PASSWORD}" ]; then + ask_ca_password "${attempt}" + fi + CA_PASSWORD="${CA_PASSWORD}" "${OPENSSL}" rsa \ + -in "${CAKEY}" \ + -passin env:CA_PASSWORD \ + >/dev/null 2>&1 \ + || ask_ca_password "${attempt}" } create() { @@ -254,38 +284,44 @@ create() { cn="${1:-}" if [ "${from_csr}" -eq 1 ]; then - [ "${ask_pass}" -eq 1 ] && warning "Warning: -p|--password is ignored with -f|--file|--crt-file" - [ -n "${password_file}" ] && warning "Warning: --password-file is ignored with -f|--file|--crt-file" + if [ "${ask_pass}" -eq 1 ]; then + warning "Warning: -p|--password is ignored with -f|--file|--crt-file" + fi + if [ -n "${password_file}" ]; then + warning "Warning: --password-file is ignored with -f|--file|--crt-file" + fi # ask for CA passphrase ask_ca_password 0 # check if csr_file is a CSR - "${OPENSSL}" req \ - -noout -subject \ - -in "${csr_file}" \ - >/dev/null 2>&1 \ + "${OPENSSL}" req \ + -noout -subject \ + -in "${csr_file}" \ + >/dev/null 2>&1 \ || error "${csr_file} is not a valid CSR !" # check if csr_file contain a CN - "${OPENSSL}" req \ - -noout -subject \ - -in "${csr_file}" \ - | grep -Eo "CN\s*=[^,/]*" \ - >/dev/null 2>&1 \ + "${OPENSSL}" req \ + -noout -subject \ + -in "${csr_file}" \ + | grep -Eo "CN\s*=[^,/]*" \ + >/dev/null 2>&1 \ || error "${csr_file} don't contain a CommonName !" # get CN from CSR - cn=$("${OPENSSL}" req -noout -subject -in "${csr_file}"|grep -Eo "CN\s*=[^,/]*"|cut -d'=' -f2|xargs) + cn=$("${OPENSSL}" req -noout -subject -in "${csr_file}" | grep -Eo "CN\s*=[^,/]*" | cut -d'=' -f2 | xargs) # check if CN already exist - [ -f "${CRTDIR}/${cn}.crt" ] && error "${cn} already used !" + if [ -f "${CRTDIR}/${cn}.crt" ]; then + error "${cn} already used !" + fi # ca sign and generate cert - CA_PASSWORD="${CA_PASSWORD}" "${OPENSSL}" ca \ - -config "${CONFFILE}" \ - -in "${csr_file}" \ - -passin env:CA_PASSWORD \ + CA_PASSWORD="${CA_PASSWORD}" "${OPENSSL}" ca \ + -config "${CONFFILE}" \ + -in "${csr_file}" \ + -passin env:CA_PASSWORD \ -out "${CRTDIR}/${cn}.crt" echo "The CRT file is available in ${CRTDIR}/${cn}.crt" @@ -296,7 +332,9 @@ create() { fi # check if CN already exist - [ -f "${CRTDIR}/${cn}.crt" ] && error "${cn} already used !" + if [ -f "${CRTDIR}/${cn}.crt" ]; then + error "${cn} already used !" + fi # ask for CA passphrase ask_ca_password 0 @@ -313,6 +351,7 @@ create() { read -r PASSWORD stty echo printf "\n" + if [ -z "${PASSWORD}" ]; then warning "Warning: empty password from input" fi @@ -320,22 +359,26 @@ create() { # generate private key if [ -n "${PASSWORD}" ]; then - PASSWORD="${PASSWORD}" "$OPENSSL" genrsa \ - -aes256 -passout env:PASSWORD \ + PASSWORD="${PASSWORD}" "$OPENSSL" genrsa \ + -aes256 \ + -passout env:PASSWORD \ -out "${KEYDIR}/${cn}-${TIMESTAMP}.key" \ - 2048 >/dev/null 2>&1 + 2048 \ + >/dev/null 2>&1 else "$OPENSSL" genrsa \ -out "${KEYDIR}/${cn}-${TIMESTAMP}.key" \ - 2048 >/dev/null 2>&1 + 2048 \ + >/dev/null 2>&1 fi if [ -n "${PASSWORD}" ]; then # generate csr req - PASSWORD="${PASSWORD}" "$OPENSSL" req \ - -batch -new \ + PASSWORD="${PASSWORD}" "$OPENSSL" req \ + -batch \ + -new \ -key "${KEYDIR}/${cn}-${TIMESTAMP}.key" \ - -passin env:PASSWORD \ + -passin env:PASSWORD \ -out "${CSRDIR}/${cn}-${TIMESTAMP}.csr" \ -config /dev/stdin </dev/null 2>&1 \ + "${OPENSSL}" x509 \ + -noout \ + -subject \ + -in "${CRTDIR}/${cn}.crt" \ + >/dev/null 2>&1 \ || rm -f "${CRTDIR}/${cn}.crt" - [ -f "${CRTDIR}/${cn}.crt" ] || error "Error in CSR creation" + if [ ! -f "${CRTDIR}/${cn}.crt" ]; then + error "Error in CSR creation" + fi chmod 640 "${CRTDIR}/${cn}.crt" @@ -427,29 +474,38 @@ revoke() { cn="${1}" # check if CRT exists - [ ! -f "${CRTDIR}/${cn}.crt" ] && error "Unknow CN : ${cn}" + if [ ! -f "${CRTDIR}/${cn}.crt" ]; then + error "Unknow CN : ${cn}" + fi # check if CRT is a valid - "${OPENSSL}" x509 -noout -subject -in "${CRTDIR}/${cn}.crt" >/dev/null 2>&1 || error "${CRTDIR}/${cn}.crt is not a valid CRT, you must delete it !" + "${OPENSSL}" x509 \ + -noout \ + -subject \ + -in "${CRTDIR}/${cn}.crt" \ + >/dev/null 2>&1 \ + || error "${CRTDIR}/${cn}.crt is not a valid CRT, you must delete it !" # ask for CA passphrase ask_ca_password 0 echo "Revoke certificate ${CRTDIR}/${cn}.crt :" - CA_PASSWORD="${CA_PASSWORD}" "$OPENSSL" ca \ - -config "${CONFFILE}" \ - -passin env:CA_PASSWORD \ - -revoke "${CRTDIR}/${cn}.crt" \ + CA_PASSWORD="${CA_PASSWORD}" "$OPENSSL" ca \ + -config "${CONFFILE}" \ + -passin env:CA_PASSWORD \ + -revoke "${CRTDIR}/${cn}.crt" \ && rm "${CRTDIR}/${cn}.crt" CA_PASSWORD="${CA_PASSWORD}" "$OPENSSL" ca \ - -config "${CONFFILE}" \ - -passin env:CA_PASSWORD \ - -gencrl -out "${CRL}" + -config "${CONFFILE}" \ + -passin env:CA_PASSWORD \ + -gencrl -out "${CRL}" } list() { - [ -f "${INDEX}" ] || exit 0 + if [ ! -f "${INDEX}" ]; then + exit 0 + fi list_valid=0 list_revoked=1 @@ -479,11 +535,17 @@ list() { esac done - [ "${list_valid}" -eq 0 ] && certs=$(grep "^V" "${INDEX}") + if [ "${list_valid}" -eq 0 ]; then + certs=$(grep "^V" "${INDEX}") + fi - [ "${list_revoked}" -eq 0 ] && certs=$(grep "^R" "${INDEX}") + if [ "${list_revoked}" -eq 0 ]; then + certs=$(grep "^R" "${INDEX}") + fi - [ "${list_valid}" -eq 0 ] && [ "${list_revoked}" -eq 0 ] && certs=$(cat "${INDEX}") + if [ "${list_valid}" -eq 0 ] && [ "${list_revoked}" -eq 0 ]; then + certs=$(cat "${INDEX}") + fi echo "${certs}" | grep -Eo "CN\s*=[^,/]*" | cut -d'=' -f2 | xargs -n1 } @@ -525,16 +587,16 @@ main() { fi # retrieve CA path from config file - CADIR=$(grep -E "^dir" "${CONFFILE}" | cut -d'=' -f2|xargs -n1) - CAKEY=$(grep -E "^private_key" "${CONFFILE}" | cut -d'=' -f2|xargs -n1|sed "s~\$dir~${CADIR}~") - CACERT=$(grep -E "^certificate" "${CONFFILE}" | cut -d'=' -f2|xargs -n1|sed "s~\$dir~${CADIR}~") + CADIR=$(grep -E "^dir" "${CONFFILE}" | cut -d'=' -f2 | xargs -n1) + CAKEY=$(grep -E "^private_key" "${CONFFILE}" | cut -d'=' -f2 | xargs -n1 | sed "s~\$dir~${CADIR}~") + CACERT=$(grep -E "^certificate" "${CONFFILE}" | cut -d'=' -f2 | xargs -n1 | sed "s~\$dir~${CADIR}~") OCSPKEY="${CADIR}/ocsp.key" OCSPCERT="${CADIR}/ocsp.pem" - CRTDIR=$(grep -E "^certs" "${CONFFILE}" | cut -d'=' -f2|xargs -n1|sed "s~\$dir~${CADIR}~") - TMPDIR=$(grep -E "^new_certs_dir" "${CONFFILE}" | cut -d'=' -f2|xargs -n1|sed "s~\$dir~${CADIR}~") - INDEX=$(grep -E "^database" "${CONFFILE}" | cut -d'=' -f2|xargs -n1|sed "s~\$dir~${CADIR}~") - SERIAL=$(grep -E "^serial" "${CONFFILE}" | cut -d'=' -f2|xargs -n1|sed "s~\$dir~${CADIR}~") - CRL=$(grep -E "^crl" "${CONFFILE}" | cut -d'=' -f2|xargs -n1|sed "s~\$dir~${CADIR}~") + CRTDIR=$(grep -E "^certs" "${CONFFILE}" | cut -d'=' -f2 | xargs -n1 | sed "s~\$dir~${CADIR}~") + TMPDIR=$(grep -E "^new_certs_dir" "${CONFFILE}" | cut -d'=' -f2 | xargs -n1 | sed "s~\$dir~${CADIR}~") + INDEX=$(grep -E "^database" "${CONFFILE}" | cut -d'=' -f2 | xargs -n1 | sed "s~\$dir~${CADIR}~") + SERIAL=$(grep -E "^serial" "${CONFFILE}" | cut -d'=' -f2 | xargs -n1 | sed "s~\$dir~${CADIR}~") + CRL=$(grep -E "^crl" "${CONFFILE}" | cut -d'=' -f2 | xargs -n1 | sed "s~\$dir~${CADIR}~") # directories for clients key, csr, crt KEYDIR="${CADIR}/private" @@ -549,7 +611,9 @@ main() { error "You must create ${PKIUSER} user and group !" fi - [ -e "${CONFFILE}" ] || error "${CONFFILE} is missing" + if [ ! -e "${CONFFILE}" ]; then + error "${CONFFILE} is missing" + fi mkdir -p "${CADIR}" "${CRTDIR}" "${KEYDIR}" "${CSRDIR}" "${PKCS12DIR}" "${OVPNDIR}" "${TMPDIR}" From b03e77d3070888fdf3662f1c3aeafd2f969bf6c9 Mon Sep 17 00:00:00 2001 From: Jeremy Lecour Date: Mon, 4 May 2020 18:16:07 +0200 Subject: [PATCH 10/89] More readable variable names --- CHANGELOG.md | 1 + shellpki | 274 ++++++++++++++++++++++++++------------------------- 2 files changed, 139 insertions(+), 136 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ec32439..3976a23 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed * Rename internal function usage() to show_usage() +* More readable variable names ### Deprecated diff --git a/shellpki b/shellpki index 8e97010..2bfc973 100755 --- a/shellpki +++ b/shellpki @@ -31,9 +31,9 @@ END init() { umask 0177 - [ -d "${CADIR}" ] || mkdir -m 0750 "${CADIR}" - [ -d "${CRTDIR}" ] || mkdir -m 0750 "${CRTDIR}" - [ -f "${INDEX}" ] || touch "${INDEX}" + [ -d "${CA_DIR}" ] || mkdir -m 0750 "${CA_DIR}" + [ -d "${CRT_DIR}" ] || mkdir -m 0750 "${CRT_DIR}" + [ -f "${INDEX_FILE}" ] || touch "${INDEX_FILE}" [ -f "${CRL}" ] || touch "${CRL}" [ -f "${SERIAL}" ] || echo "01" > "${SERIAL}" @@ -43,48 +43,48 @@ init() { exit 1 fi - if [ -f "${CAKEY}" ]; then - printf "%s already exists, do you really want to erase it ? [y/N] " "${CAKEY}" + if [ -f "${CA_KEY}" ]; then + printf "%s already exists, do you really want to erase it ? [y/N] " "${CA_KEY}" read -r REPLY resp=$(echo "${REPLY}" | tr 'Y' 'y') if [ "${resp}" = "y" ]; then - rm -f "${CAKEY}" "${CACERT}" + rm -f "${CA_KEY}" "${CA_CERT}" fi fi - if [ ! -f "${CAKEY}" ]; then - "$OPENSSL" genrsa \ - -out "${CAKEY}" \ + if [ ! -f "${CA_KEY}" ]; then + "${OPENSSL_BIN}" genrsa \ + -out "${CA_KEY}" \ -aes256 4096 \ >/dev/null 2>&1 fi - if [ -f "${CACERT}" ]; then - printf "%s already exists, do you really want to erase it ? [y/N] " "${CACERT}" + if [ -f "${CA_CERT}" ]; then + printf "%s already exists, do you really want to erase it ? [y/N] " "${CA_CERT}" read -r REPLY resp=$(echo "${REPLY}" | tr 'Y' 'y') if [ "${resp}" = "y" ]; then - rm "${CACERT}" + rm "${CA_CERT}" fi fi - if [ ! -f "${CACERT}" ]; then + if [ ! -f "${CA_CERT}" ]; then ask_ca_password 0 fi - if [ ! -f "${CACERT}" ]; then - CA_PASSWORD="${CA_PASSWORD}" "${OPENSSL}" req \ + if [ ! -f "${CA_CERT}" ]; then + CA_PASSWORD="${CA_PASSWORD}" "${OPENSSL_BIN}" req \ -new \ -batch \ -sha512 \ -x509 \ -days 3650 \ -extensions v3_ca \ - -key "${CAKEY}" \ - -out "${CACERT}" \ + -key "${CA_KEY}" \ + -out "${CA_CERT}" \ -passin env:CA_PASSWORD \ -config /dev/stdin </dev/null 2>&1 fi - "$OPENSSL" req \ + "${OPENSSL_BIN}" req \ -batch \ -new \ - -key "${OCSPKEY}" \ - -out "${CSRDIR}/ocsp.csr" \ + -key "${OCSP_KEY}" \ + -out "${CSR_DIR}/ocsp.csr" \ -config /dev/stdin < -Run OCSPD server : +Run OCSP_D server : ${0} ocsp @@ -190,7 +190,7 @@ warning() { } ask_ca_password() { - [ ! -f "${CAKEY}" ] && error "You must initialize your's PKI with shellpki init !" + [ ! -f "${CA_KEY}" ] && error "You must initialize your's PKI with shellpki init !" attempt=$((${1} + 1)) if [ "${attempt}" -gt 1 ]; then warning "Invalid password, retry." @@ -205,8 +205,8 @@ ask_ca_password() { if [ -z "${CA_PASSWORD}" ]; then ask_ca_password "${attempt}" fi - CA_PASSWORD="${CA_PASSWORD}" "${OPENSSL}" rsa \ - -in "${CAKEY}" \ + CA_PASSWORD="${CA_PASSWORD}" "${OPENSSL_BIN}" rsa \ + -in "${CA_KEY}" \ -passin env:CA_PASSWORD \ >/dev/null 2>&1 \ || ask_ca_password "${attempt}" @@ -295,14 +295,14 @@ create() { ask_ca_password 0 # check if csr_file is a CSR - "${OPENSSL}" req \ + "${OPENSSL_BIN}" req \ -noout -subject \ -in "${csr_file}" \ >/dev/null 2>&1 \ || error "${csr_file} is not a valid CSR !" # check if csr_file contain a CN - "${OPENSSL}" req \ + "${OPENSSL_BIN}" req \ -noout -subject \ -in "${csr_file}" \ | grep -Eo "CN\s*=[^,/]*" \ @@ -310,21 +310,21 @@ create() { || error "${csr_file} don't contain a CommonName !" # get CN from CSR - cn=$("${OPENSSL}" req -noout -subject -in "${csr_file}" | grep -Eo "CN\s*=[^,/]*" | cut -d'=' -f2 | xargs) + cn=$("${OPENSSL_BIN}" req -noout -subject -in "${csr_file}" | grep -Eo "CN\s*=[^,/]*" | cut -d'=' -f2 | xargs) # check if CN already exist - if [ -f "${CRTDIR}/${cn}.crt" ]; then + if [ -f "${CRT_DIR}/${cn}.crt" ]; then error "${cn} already used !" fi # ca sign and generate cert - CA_PASSWORD="${CA_PASSWORD}" "${OPENSSL}" ca \ - -config "${CONFFILE}" \ + CA_PASSWORD="${CA_PASSWORD}" "${OPENSSL_BIN}" ca \ + -config "${CONF_FILE}" \ -in "${csr_file}" \ -passin env:CA_PASSWORD \ - -out "${CRTDIR}/${cn}.crt" + -out "${CRT_DIR}/${cn}.crt" - echo "The CRT file is available in ${CRTDIR}/${cn}.crt" + echo "The CRT file is available in ${CRT_DIR}/${cn}.crt" else if [ -z "${cn}" ]; then show_usage >&2 @@ -332,7 +332,7 @@ create() { fi # check if CN already exist - if [ -f "${CRTDIR}/${cn}.crt" ]; then + if [ -f "${CRT_DIR}/${cn}.crt" ]; then error "${cn} already used !" fi @@ -359,107 +359,107 @@ create() { # generate private key if [ -n "${PASSWORD}" ]; then - PASSWORD="${PASSWORD}" "$OPENSSL" genrsa \ + PASSWORD="${PASSWORD}" "${OPENSSL_BIN}" genrsa \ -aes256 \ -passout env:PASSWORD \ - -out "${KEYDIR}/${cn}-${TIMESTAMP}.key" \ - 2048 \ + -out "${KEY_DIR}/${cn}-${SUFFIX}.key" \ + ${KEY_LENGTH} \ >/dev/null 2>&1 else - "$OPENSSL" genrsa \ - -out "${KEYDIR}/${cn}-${TIMESTAMP}.key" \ - 2048 \ + "${OPENSSL_BIN}" genrsa \ + -out "${KEY_DIR}/${cn}-${SUFFIX}.key" \ + ${KEY_LENGTH} \ >/dev/null 2>&1 fi if [ -n "${PASSWORD}" ]; then # generate csr req - PASSWORD="${PASSWORD}" "$OPENSSL" req \ + PASSWORD="${PASSWORD}" "${OPENSSL_BIN}" req \ -batch \ -new \ - -key "${KEYDIR}/${cn}-${TIMESTAMP}.key" \ + -key "${KEY_DIR}/${cn}-${SUFFIX}.key" \ -passin env:PASSWORD \ - -out "${CSRDIR}/${cn}-${TIMESTAMP}.csr" \ + -out "${CSR_DIR}/${cn}-${SUFFIX}.csr" \ -config /dev/stdin </dev/null 2>&1 \ - || rm -f "${CRTDIR}/${cn}.crt" + || rm -f "${CRT_DIR}/${cn}.crt" - if [ ! -f "${CRTDIR}/${cn}.crt" ]; then + if [ ! -f "${CRT_DIR}/${cn}.crt" ]; then error "Error in CSR creation" fi - chmod 640 "${CRTDIR}/${cn}.crt" + chmod 640 "${CRT_DIR}/${cn}.crt" - echo "The CRT file is available in ${CRTDIR}/${cn}.crt" + echo "The CRT file is available in ${CRT_DIR}/${cn}.crt" # generate pkcs12 format if [ -n "${PASSWORD}" ]; then - PASSWORD="${PASSWORD}" "${OPENSSL}" pkcs12 \ + PASSWORD="${PASSWORD}" "${OPENSSL_BIN}" pkcs12 \ -export \ -nodes \ -passin env:PASSWORD \ -passout env:PASSWORD \ - -inkey "${KEYDIR}/${cn}-${TIMESTAMP}.key" \ - -in "${CRTDIR}/${cn}.crt" \ - -out "${PKCS12DIR}/${cn}-${TIMESTAMP}.p12" + -inkey "${KEY_DIR}/${cn}-${SUFFIX}.key" \ + -in "${CRT_DIR}/${cn}.crt" \ + -out "${PKCS12_DIR}/${cn}-${SUFFIX}.p12" else - "${OPENSSL}" pkcs12 \ + "${OPENSSL_BIN}" pkcs12 \ -export \ -nodes \ -passout pass: \ - -inkey "${KEYDIR}/${cn}-${TIMESTAMP}.key" \ - -in "${CRTDIR}/${cn}.crt" \ - -out "${PKCS12DIR}/${cn}-${TIMESTAMP}.p12" + -inkey "${KEY_DIR}/${cn}-${SUFFIX}.key" \ + -in "${CRT_DIR}/${cn}.crt" \ + -out "${PKCS12_DIR}/${cn}-${SUFFIX}.p12" fi - chmod 640 "${PKCS12DIR}/${cn}-${TIMESTAMP}.p12" - echo "The PKCS12 config file is available in ${PKCS12DIR}/${cn}-${TIMESTAMP}.p12" + chmod 640 "${PKCS12_DIR}/${cn}-${SUFFIX}.p12" + echo "The PKCS12 config file is available in ${PKCS12_DIR}/${cn}-${SUFFIX}.p12" # generate openvpn format - if [ -e "${CADIR}/ovpn.conf" ]; then - cat "${CADIR}/ovpn.conf" - > "${OVPNDIR}/${cn}-${TIMESTAMP}.ovpn" < "${OVPN_DIR}/${cn}-${SUFFIX}.ovpn" < -$(cat "${CACERT}") +$(cat "${CA_CERT}") -$(cat "${CRTDIR}/${cn}.crt") +$(cat "${CRT_DIR}/${cn}.crt") -$(cat "${KEYDIR}/${cn}-${TIMESTAMP}.key") +$(cat "${KEY_DIR}/${cn}-${SUFFIX}.key") EOF - chmod 640 "${OVPNDIR}/${cn}-${TIMESTAMP}.ovpn" - echo "The OpenVPN config file is available in ${OVPNDIR}/${cn}-${TIMESTAMP}.ovpn" + chmod 640 "${OVPN_DIR}/${cn}-${SUFFIX}.ovpn" + echo "The OpenVPN config file is available in ${OVPN_DIR}/${cn}-${SUFFIX}.ovpn" fi fi } @@ -474,36 +474,36 @@ revoke() { cn="${1}" # check if CRT exists - if [ ! -f "${CRTDIR}/${cn}.crt" ]; then + if [ ! -f "${CRT_DIR}/${cn}.crt" ]; then error "Unknow CN : ${cn}" fi # check if CRT is a valid - "${OPENSSL}" x509 \ + "${OPENSSL_BIN}" x509 \ -noout \ -subject \ - -in "${CRTDIR}/${cn}.crt" \ + -in "${CRT_DIR}/${cn}.crt" \ >/dev/null 2>&1 \ - || error "${CRTDIR}/${cn}.crt is not a valid CRT, you must delete it !" + || error "${CRT_DIR}/${cn}.crt is not a valid CRT, you must delete it !" # ask for CA passphrase ask_ca_password 0 - echo "Revoke certificate ${CRTDIR}/${cn}.crt :" - CA_PASSWORD="${CA_PASSWORD}" "$OPENSSL" ca \ - -config "${CONFFILE}" \ + echo "Revoke certificate ${CRT_DIR}/${cn}.crt :" + CA_PASSWORD="${CA_PASSWORD}" "${OPENSSL_BIN}" ca \ + -config "${CONF_FILE}" \ -passin env:CA_PASSWORD \ - -revoke "${CRTDIR}/${cn}.crt" \ - && rm "${CRTDIR}/${cn}.crt" + -revoke "${CRT_DIR}/${cn}.crt" \ + && rm "${CRT_DIR}/${cn}.crt" - CA_PASSWORD="${CA_PASSWORD}" "$OPENSSL" ca \ - -config "${CONFFILE}" \ + CA_PASSWORD="${CA_PASSWORD}" "${OPENSSL_BIN}" ca \ + -config "${CONF_FILE}" \ -passin env:CA_PASSWORD \ -gencrl -out "${CRL}" } list() { - if [ ! -f "${INDEX}" ]; then + if [ ! -f "${INDEX_FILE}" ]; then exit 0 fi @@ -536,15 +536,15 @@ list() { done if [ "${list_valid}" -eq 0 ]; then - certs=$(grep "^V" "${INDEX}") + certs=$(grep "^V" "${INDEX_FILE}") fi if [ "${list_revoked}" -eq 0 ]; then - certs=$(grep "^R" "${INDEX}") + certs=$(grep "^R" "${INDEX_FILE}") fi if [ "${list_valid}" -eq 0 ] && [ "${list_revoked}" -eq 0 ]; then - certs=$(cat "${INDEX}") + certs=$(cat "${INDEX_FILE}") fi echo "${certs}" | grep -Eo "CN\s*=[^,/]*" | cut -d'=' -f2 | xargs -n1 @@ -556,7 +556,7 @@ check() { min_day=90 cur_epoch=$(date -u +'%s') - for cert in ${CRTDIR}/*; do + for cert in ${CRT_DIR}/*; do end_date=$(openssl x509 -noout -enddate -in "${cert}" | cut -d'=' -f2) end_epoch=$(date -ud "${end_date}" +'%s') diff_epoch=$((end_epoch - cur_epoch)) @@ -574,48 +574,50 @@ check() { main() { # default config # TODO : override with /etc/default/shellpki - CONFFILE="/etc/shellpki/openssl.cnf" + CONF_FILE="/etc/shellpki/openssl.cnf" if [ "$(uname)" = "OpenBSD" ]; then - PKIUSER="_shellpki" + PKI_USER="_shellpki" else - PKIUSER="shellpki" + PKI_USER="shellpki" fi - if [ "${USER}" != "root" ] && [ "${USER}" != "${PKIUSER}" ]; then + if [ "${USER}" != "root" ] && [ "${USER}" != "${PKI_USER}" ]; then error "Please become root before running ${0} !" fi # retrieve CA path from config file - CADIR=$(grep -E "^dir" "${CONFFILE}" | cut -d'=' -f2 | xargs -n1) - CAKEY=$(grep -E "^private_key" "${CONFFILE}" | cut -d'=' -f2 | xargs -n1 | sed "s~\$dir~${CADIR}~") - CACERT=$(grep -E "^certificate" "${CONFFILE}" | cut -d'=' -f2 | xargs -n1 | sed "s~\$dir~${CADIR}~") - OCSPKEY="${CADIR}/ocsp.key" - OCSPCERT="${CADIR}/ocsp.pem" - CRTDIR=$(grep -E "^certs" "${CONFFILE}" | cut -d'=' -f2 | xargs -n1 | sed "s~\$dir~${CADIR}~") - TMPDIR=$(grep -E "^new_certs_dir" "${CONFFILE}" | cut -d'=' -f2 | xargs -n1 | sed "s~\$dir~${CADIR}~") - INDEX=$(grep -E "^database" "${CONFFILE}" | cut -d'=' -f2 | xargs -n1 | sed "s~\$dir~${CADIR}~") - SERIAL=$(grep -E "^serial" "${CONFFILE}" | cut -d'=' -f2 | xargs -n1 | sed "s~\$dir~${CADIR}~") - CRL=$(grep -E "^crl" "${CONFFILE}" | cut -d'=' -f2 | xargs -n1 | sed "s~\$dir~${CADIR}~") + CA_DIR=$(grep -E "^dir" "${CONF_FILE}" | cut -d'=' -f2 | xargs -n1) + CA_KEY=$(grep -E "^private_key" "${CONF_FILE}" | cut -d'=' -f2 | xargs -n1 | sed "s~\$dir~${CA_DIR}~") + CA_CERT=$(grep -E "^certificate" "${CONF_FILE}" | cut -d'=' -f2 | xargs -n1 | sed "s~\$dir~${CA_DIR}~") + OCSP_KEY="${CA_DIR}/ocsp.key" + OCSP_CERT="${CA_DIR}/ocsp.pem" + CRT_DIR=$(grep -E "^certs" "${CONF_FILE}" | cut -d'=' -f2 | xargs -n1 | sed "s~\$dir~${CA_DIR}~") + TMP_DIR=$(grep -E "^new_certs_dir" "${CONF_FILE}" | cut -d'=' -f2 | xargs -n1 | sed "s~\$dir~${CA_DIR}~") + INDEX_FILE=$(grep -E "^database" "${CONF_FILE}" | cut -d'=' -f2 | xargs -n1 | sed "s~\$dir~${CA_DIR}~") + SERIAL=$(grep -E "^serial" "${CONF_FILE}" | cut -d'=' -f2 | xargs -n1 | sed "s~\$dir~${CA_DIR}~") + CRL=$(grep -E "^crl" "${CONF_FILE}" | cut -d'=' -f2 | xargs -n1 | sed "s~\$dir~${CA_DIR}~") # directories for clients key, csr, crt - KEYDIR="${CADIR}/private" - CSRDIR="${CADIR}/requests" - PKCS12DIR="${CADIR}/pkcs12" - OVPNDIR="${CADIR}/openvpn" + KEY_DIR="${CA_DIR}/private" + CSR_DIR="${CA_DIR}/requests" + PKCS12_DIR="${CA_DIR}/pkcs12" + OVPN_DIR="${CA_DIR}/openvpn" - OPENSSL=$(command -v openssl) - TIMESTAMP=$(/bin/date +"%s") + KEY_LENGTH=2048 - if ! getent passwd "${PKIUSER}" >/dev/null || ! getent group "${PKIUSER}" >/dev/null; then - error "You must create ${PKIUSER} user and group !" + OPENSSL_BIN=$(command -v openssl) + SUFFIX=$(/bin/date +"%s") + + if ! getent passwd "${PKI_USER}" >/dev/null || ! getent group "${PKI_USER}" >/dev/null; then + error "You must create ${PKI_USER} user and group !" fi - if [ ! -e "${CONFFILE}" ]; then - error "${CONFFILE} is missing" + if [ ! -e "${CONF_FILE}" ]; then + error "${CONF_FILE} is missing" fi - mkdir -p "${CADIR}" "${CRTDIR}" "${KEYDIR}" "${CSRDIR}" "${PKCS12DIR}" "${OVPNDIR}" "${TMPDIR}" + mkdir -p "${CA_DIR}" "${CRT_DIR}" "${KEY_DIR}" "${CSR_DIR}" "${PKCS12_DIR}" "${OVPN_DIR}" "${TMP_DIR}" command=${1:-help} @@ -667,10 +669,10 @@ main() { esac # fix right - chown -R "${PKIUSER}":"${PKIUSER}" "${CADIR}" - chmod 750 "${CADIR}" "${CRTDIR}" "${KEYDIR}" "${CSRDIR}" "${PKCS12DIR}" "${OVPNDIR}" "${TMPDIR}" - chmod 600 "${INDEX}"* "${SERIAL}"* "${CAKEY}" "${CRL}" - chmod 640 "${CACERT}" + chown -R "${PKI_USER}":"${PKI_USER}" "${CA_DIR}" + chmod 750 "${CA_DIR}" "${CRT_DIR}" "${KEY_DIR}" "${CSR_DIR}" "${PKCS12_DIR}" "${OVPN_DIR}" "${TMP_DIR}" + chmod 600 "${INDEX_FILE}"* "${SERIAL}"* "${CA_KEY}" "${CRL}" + chmod 640 "${CA_CERT}" } main "$@" From 21182a8dcfd299c43133b292d96843abe98f697c Mon Sep 17 00:00:00 2001 From: Jeremy Lecour Date: Mon, 4 May 2020 18:53:14 +0200 Subject: [PATCH 11/89] CA key length is configurable (minimum 4096) --- CHANGELOG.md | 1 + shellpki | 9 ++++++++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3976a23..bc2a320 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 * Create a changelog * Add a version number and `version` command * Accept a `password-file` command line option to read password from a file +* CA key length is configurable (minimum 4096) ### Changed diff --git a/shellpki b/shellpki index 2bfc973..29c9cc5 100755 --- a/shellpki +++ b/shellpki @@ -55,7 +55,7 @@ init() { if [ ! -f "${CA_KEY}" ]; then "${OPENSSL_BIN}" genrsa \ -out "${CA_KEY}" \ - -aes256 4096 \ + -aes256 ${CA_KEY_LENGTH} \ >/dev/null 2>&1 fi @@ -604,7 +604,14 @@ main() { PKCS12_DIR="${CA_DIR}/pkcs12" OVPN_DIR="${CA_DIR}/openvpn" + CA_KEY_LENGTH=4096 + if [ "${CA_KEY_LENGTH}" -lt 4096 ]; then + error "CA key must be at least 4096 bits long." + fi KEY_LENGTH=2048 + if [ "${KEY_LENGTH}" -lt 2048 ]; then + error "User key must be at least 2048 bits long." + fi OPENSSL_BIN=$(command -v openssl) SUFFIX=$(/bin/date +"%s") From a9b2fdd83209ec5e70c7ae8b20756587db12715c Mon Sep 17 00:00:00 2001 From: Jeremy Lecour Date: Mon, 4 May 2020 18:55:10 +0200 Subject: [PATCH 12/89] verify_ca_password() looks for a previously set password and verifies it --- CHANGELOG.md | 1 + shellpki | 49 +++++++++++++++++++++++++++++++------------------ 2 files changed, 32 insertions(+), 18 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bc2a320..6cb5a6f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 * Rename internal function usage() to show_usage() * More readable variable names +* verify_ca_password() looks for a previously set password and verifies it ### Deprecated diff --git a/shellpki b/shellpki index 29c9cc5..5396874 100755 --- a/shellpki +++ b/shellpki @@ -189,27 +189,40 @@ warning() { echo "${1}" >&2 } -ask_ca_password() { - [ ! -f "${CA_KEY}" ] && error "You must initialize your's PKI with shellpki init !" - attempt=$((${1} + 1)) - if [ "${attempt}" -gt 1 ]; then - warning "Invalid password, retry." - fi - trap 'unset CA_PASSWORD' 0 - stty -echo - printf "Password for CA key : " - read -r CA_PASSWORD - stty echo - printf "\n" - - if [ -z "${CA_PASSWORD}" ]; then - ask_ca_password "${attempt}" - fi +verify_ca_password() { CA_PASSWORD="${CA_PASSWORD}" "${OPENSSL_BIN}" rsa \ -in "${CA_KEY}" \ -passin env:CA_PASSWORD \ - >/dev/null 2>&1 \ - || ask_ca_password "${attempt}" + >/dev/null 2>&1 +} + +ask_ca_password() { + attempt=${1:-0} + max_attempt=3 + + trap 'unset CA_PASSWORD' 0 + + if [ ! -f "${CA_KEY}" ]; then + error "You must initialize your PKI with \`shellpki init' !" + fi + if [ "${attempt}" -gt 0 ]; then + warning "Invalid password, retry." + fi + if [ "${attempt}" -ge "${max_attempt}" ]; then + error "Maximum number of attempts reached (${max_attempt})." + fi + if [ -z "${CA_PASSWORD}" ]; then + stty -echo + printf "Password for CA key : " + read -r CA_PASSWORD + stty echo + printf "\n" + fi + if [ -z "${CA_PASSWORD}" ] || ! verify_ca_password; then + unset CA_PASSWORD + attempt=$(( attempt + 1 )) + ask_ca_password "${attempt}" + fi } create() { From df6d06d84803e9e451a0786474703cceb2d978a4 Mon Sep 17 00:00:00 2001 From: Jeremy Lecour Date: Mon, 4 May 2020 23:02:48 +0200 Subject: [PATCH 13/89] Add option to revoke the existing certificate when creating one. --- shellpki | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/shellpki b/shellpki index 5396874..5ebd8cb 100755 --- a/shellpki +++ b/shellpki @@ -327,7 +327,14 @@ create() { # check if CN already exist if [ -f "${CRT_DIR}/${cn}.crt" ]; then - error "${cn} already used !" + printf "%s already exists, do you revoke and recreate it ? [y/N] " "${cn}" + read -r REPLY + resp=$(echo "${REPLY}" | tr 'Y' 'y') + if [ "${resp}" = "y" ]; then + revoke "${cn}" + else + error "Abort" + fi fi # ca sign and generate cert @@ -346,7 +353,14 @@ create() { # check if CN already exist if [ -f "${CRT_DIR}/${cn}.crt" ]; then - error "${cn} already used !" + printf "%s already exists, do you revoke and recreate it ? [y/N] " "${cn}" + read -r REPLY + resp=$(echo "${REPLY}" | tr 'Y' 'y') + if [ "${resp}" = "y" ]; then + revoke "${cn}" + else + error "Abort" + fi fi # ask for CA passphrase From 857bb4b23954148cce2657da273b138b93bba5cc Mon Sep 17 00:00:00 2001 From: Jeremy Lecour Date: Mon, 4 May 2020 23:06:51 +0200 Subject: [PATCH 14/89] explicit checks on exit code --- shellpki | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/shellpki b/shellpki index 5ebd8cb..418282a 100755 --- a/shellpki +++ b/shellpki @@ -311,16 +311,20 @@ create() { "${OPENSSL_BIN}" req \ -noout -subject \ -in "${csr_file}" \ - >/dev/null 2>&1 \ - || error "${csr_file} is not a valid CSR !" + >/dev/null 2>&1 + if [ "$?" -ne 0 ]; then + error "${csr_file} is not a valid CSR !" + fi # check if csr_file contain a CN "${OPENSSL_BIN}" req \ -noout -subject \ -in "${csr_file}" \ | grep -Eo "CN\s*=[^,/]*" \ - >/dev/null 2>&1 \ - || error "${csr_file} don't contain a CommonName !" + >/dev/null 2>&1 + if [ "$?" -ne 0 ]; then + error "${csr_file} doesn't contain a CommonName !" + fi # get CN from CSR cn=$("${OPENSSL_BIN}" req -noout -subject -in "${csr_file}" | grep -Eo "CN\s*=[^,/]*" | cut -d'=' -f2 | xargs) @@ -436,8 +440,10 @@ EOF -noout \ -subject \ -in "${CRT_DIR}/${cn}.crt" \ - >/dev/null 2>&1 \ - || rm -f "${CRT_DIR}/${cn}.crt" + >/dev/null 2>&1 + if [ "$?" -ne 0 ]; then + rm -f "${CRT_DIR}/${cn}.crt" + fi if [ ! -f "${CRT_DIR}/${cn}.crt" ]; then error "Error in CSR creation" @@ -510,8 +516,10 @@ revoke() { -noout \ -subject \ -in "${CRT_DIR}/${cn}.crt" \ - >/dev/null 2>&1 \ - || error "${CRT_DIR}/${cn}.crt is not a valid CRT, you must delete it !" + >/dev/null 2>&1 + if [ "$?" -ne 0 ]; then + error "${CRT_DIR}/${cn}.crt is not a valid CRT, you must delete it !" + fi # ask for CA passphrase ask_ca_password 0 @@ -643,7 +651,7 @@ main() { OPENSSL_BIN=$(command -v openssl) SUFFIX=$(/bin/date +"%s") - if ! getent passwd "${PKI_USER}" >/dev/null || ! getent group "${PKI_USER}" >/dev/null; then + if ! getent passwd "${PKI_USER}" >/dev/null ! getent group "${PKI_USER}" >/dev/null; then error "You must create ${PKI_USER} user and group !" fi From 7630d8b1820f77c667af4574c7f34a121b96b148 Mon Sep 17 00:00:00 2001 From: Jeremy Lecour Date: Mon, 4 May 2020 23:08:19 +0200 Subject: [PATCH 15/89] whitespaces --- shellpki | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/shellpki b/shellpki index 418282a..fb97295 100755 --- a/shellpki +++ b/shellpki @@ -309,7 +309,8 @@ create() { # check if csr_file is a CSR "${OPENSSL_BIN}" req \ - -noout -subject \ + -noout \ + -subject \ -in "${csr_file}" \ >/dev/null 2>&1 if [ "$?" -ne 0 ]; then @@ -318,7 +319,8 @@ create() { # check if csr_file contain a CN "${OPENSSL_BIN}" req \ - -noout -subject \ + -noout \ + -subject \ -in "${csr_file}" \ | grep -Eo "CN\s*=[^,/]*" \ >/dev/null 2>&1 @@ -594,8 +596,8 @@ check() { for cert in ${CRT_DIR}/*; do end_date=$(openssl x509 -noout -enddate -in "${cert}" | cut -d'=' -f2) end_epoch=$(date -ud "${end_date}" +'%s') - diff_epoch=$((end_epoch - cur_epoch)) - diff_day=$((diff_epoch/60/60/24)) + diff_epoch=$(( end_epoch - cur_epoch )) + diff_day=$(( diff_epoch / 60 / 60 / 24 )) if [ "${diff_day}" -lt "${min_day}" ]; then if [ "${diff_day}" -le 0 ]; then echo "${cert} has expired" From d8a5d04fd08b346767a11e0fe9d3afaf1db2764a Mon Sep 17 00:00:00 2001 From: Jeremy Lecour Date: Mon, 4 May 2020 23:12:48 +0200 Subject: [PATCH 16/89] Extract function cert_end_date() --- CHANGELOG.md | 1 + shellpki | 6 +++++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6cb5a6f..0067443 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 * Rename internal function usage() to show_usage() * More readable variable names * verify_ca_password() looks for a previously set password and verifies it +* Extract function cert_end_date() ### Deprecated diff --git a/shellpki b/shellpki index fb97295..5b40354 100755 --- a/shellpki +++ b/shellpki @@ -587,6 +587,10 @@ list() { echo "${certs}" | grep -Eo "CN\s*=[^,/]*" | cut -d'=' -f2 | xargs -n1 } +cert_end_date() { + "${OPENSSL_BIN}" x509 -noout -enddate -in "${1}" | cut -d'=' -f2 +} + check() { # default expiration alert # TODO : permit override with parameters @@ -594,7 +598,7 @@ check() { cur_epoch=$(date -u +'%s') for cert in ${CRT_DIR}/*; do - end_date=$(openssl x509 -noout -enddate -in "${cert}" | cut -d'=' -f2) + end_date=$(cert_end_date "${cert}") end_epoch=$(date -ud "${end_date}" +'%s') diff_epoch=$(( end_epoch - cur_epoch )) diff_day=$(( diff_epoch / 60 / 60 / 24 )) From 09c1a7a579cf6a44b6cb7a07cbf8d4d8e4a5c768 Mon Sep 17 00:00:00 2001 From: Jeremy Lecour Date: Mon, 4 May 2020 23:12:56 +0200 Subject: [PATCH 17/89] wording --- shellpki | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shellpki b/shellpki index 5b40354..9afddf1 100755 --- a/shellpki +++ b/shellpki @@ -1,6 +1,6 @@ #!/bin/sh # -# shellpki is a wrapper around openssl to manage a small PKI +# shellpki is a wrapper around OpenSSL to manage a small PKI # set -e From a30be3872fa13d3043bd6ae2caef229cdb54d97b Mon Sep 17 00:00:00 2001 From: Jeremy Lecour Date: Mon, 4 May 2020 23:16:19 +0200 Subject: [PATCH 18/89] Extract is_user() and is_group() functions --- CHANGELOG.md | 3 ++- shellpki | 9 ++++++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0067443..49fe5c4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,7 +18,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 * Rename internal function usage() to show_usage() * More readable variable names * verify_ca_password() looks for a previously set password and verifies it -* Extract function cert_end_date() +* Extract cert_end_date() function +* Extract is_user() and is_group() functions ### Deprecated diff --git a/shellpki b/shellpki index 9afddf1..3b84973 100755 --- a/shellpki +++ b/shellpki @@ -612,6 +612,13 @@ check() { done } +is_user() { + getent passwd "${1}" >/dev/null +} +is_group() { + getent group "${1}" >/dev/null +} + main() { # default config # TODO : override with /etc/default/shellpki @@ -657,7 +664,7 @@ main() { OPENSSL_BIN=$(command -v openssl) SUFFIX=$(/bin/date +"%s") - if ! getent passwd "${PKI_USER}" >/dev/null ! getent group "${PKI_USER}" >/dev/null; then + if ! is_user "${PKI_USER}" || ! is_group "${PKI_USER}"; then error "You must create ${PKI_USER} user and group !" fi From 7506003f53ccd1033575e558ea04588210546889 Mon Sep 17 00:00:00 2001 From: Jeremy Lecour Date: Tue, 5 May 2020 00:22:35 +0200 Subject: [PATCH 19/89] Add --days and --end-date command line options --- CHANGELOG.md | 1 + shellpki | 59 ++++++++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 58 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 49fe5c4..8d90b04 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 * Create a changelog * Add a version number and `version` command * Accept a `password-file` command line option to read password from a file +* Accept `--days` and `--end-date` command line options * CA key length is configurable (minimum 4096) ### Changed diff --git a/shellpki b/shellpki index 3b84973..242e9c5 100755 --- a/shellpki +++ b/shellpki @@ -276,6 +276,44 @@ create() { printf 'ERROR: "--password-file" requires a non-empty option argument.\n' >&2 exit 1 ;; + --days) + # days option, with value separated by space + if [ -n "$2" ]; then + days=${2} + shift + else + printf 'ERROR: "--days" requires a non-empty option argument.\n' >&2 + exit 1 + fi + ;; + --days=?*) + # days option, with value separated by = + days=${1#*=} + ;; + --days=) + # days options, without value + printf 'ERROR: "--days" requires a non-empty option argument.\n' >&2 + exit 1 + ;; + --end-date) + # end-date option, with value separated by space + if [ -n "$2" ]; then + end_date=${2} + shift + else + printf 'ERROR: "--end-date" requires a non-empty option argument.\n' >&2 + exit 1 + fi + ;; + --end-date=?*) + # end-date option, with value separated by = + end_date=${1#*=} + ;; + --end-date=) + # end-date options, without value + printf 'ERROR: "--end-date" requires a non-empty option argument.\n' >&2 + exit 1 + ;; --) # End of all options. shift @@ -294,8 +332,23 @@ create() { shift done + # The name of the certificate cn="${1:-}" + # Set expiration argument + crt_expiration_arg="" + if [ -n "${days}" ] && [ "${days}" -gt 0 ]; then + crt_expiration_arg="-days ${days}" + fi + if [ -n "${end_date}" ]; then + cert_end_date=$(TZ=:Zulu date --date "${end_date}" +"%Y%m%d%H%M%SZ" 2> /dev/null) + if [ "$?" -ne 0 ]; then + error "Invalid end date format : \`${end_date}' can't be parsed by date(1)" + else + crt_expiration_arg="-enddate ${cert_end_date}" + fi + fi + if [ "${from_csr}" -eq 1 ]; then if [ "${ask_pass}" -eq 1 ]; then warning "Warning: -p|--password is ignored with -f|--file|--crt-file" @@ -348,7 +401,8 @@ create() { -config "${CONF_FILE}" \ -in "${csr_file}" \ -passin env:CA_PASSWORD \ - -out "${CRT_DIR}/${cn}.crt" + -out "${CRT_DIR}/${cn}.crt" \ + ${crt_expiration_arg} echo "The CRT file is available in ${CRT_DIR}/${cn}.crt" else @@ -435,7 +489,8 @@ EOF -config "${CONF_FILE}" \ -passin env:CA_PASSWORD \ -in "${CSR_DIR}/${cn}-${SUFFIX}.csr" \ - -out "${CRT_DIR}/${cn}.crt" + -out "${CRT_DIR}/${cn}.crt" \ + ${crt_expiration_arg} # check if CRT is a valid "${OPENSSL_BIN}" x509 \ From 165c96ca551e4685b0edd7557c2c87f7a324b7ea Mon Sep 17 00:00:00 2001 From: Jeremy Lecour Date: Tue, 5 May 2020 00:28:00 +0200 Subject: [PATCH 20/89] Extract variables for files --- CHANGELOG.md | 1 + shellpki | 93 +++++++++++++++++++++++++++++----------------------- 2 files changed, 53 insertions(+), 41 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8d90b04..10a9483 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,6 +21,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 * verify_ca_password() looks for a previously set password and verifies it * Extract cert_end_date() function * Extract is_user() and is_group() functions +* Extract variables for files ### Deprecated diff --git a/shellpki b/shellpki index 242e9c5..1c8cd68 100755 --- a/shellpki +++ b/shellpki @@ -98,6 +98,7 @@ ocsp() { show_usage >&2 exit 1 fi + ocsp_csr_file="${CSR_DIR}/ocsp.csr" url=$(echo "${ocsp_uri}" | cut -d':' -f1) port=$(echo "${ocsp_uri}" | cut -d':' -f2) @@ -113,7 +114,7 @@ ocsp() { -batch \ -new \ -key "${OCSP_KEY}" \ - -out "${CSR_DIR}/ocsp.csr" \ + -out "${ocsp_csr_file}" \ -config /dev/stdin <&2 exit 1 fi + csr_file="${CSR_DIR}/${cn}-${SUFFIX}.csr" + crt_file="${CRT_DIR}/${cn}.crt" + key_file="${KEY_DIR}/${cn}-${SUFFIX}.key" + ovpn_file="${OVPN_DIR}/${cn}-${SUFFIX}.ovpn" + pkcs12_file="${PKCS12_DIR}/${cn}-${SUFFIX}.p12" # check if CN already exist - if [ -f "${CRT_DIR}/${cn}.crt" ]; then + if [ -f "${crt_file}" ]; then printf "%s already exists, do you revoke and recreate it ? [y/N] " "${cn}" read -r REPLY resp=$(echo "${REPLY}" | tr 'Y' 'y') @@ -449,12 +457,12 @@ create() { PASSWORD="${PASSWORD}" "${OPENSSL_BIN}" genrsa \ -aes256 \ -passout env:PASSWORD \ - -out "${KEY_DIR}/${cn}-${SUFFIX}.key" \ + -out "${key_file}" \ ${KEY_LENGTH} \ >/dev/null 2>&1 else - "${OPENSSL_BIN}" genrsa \ - -out "${KEY_DIR}/${cn}-${SUFFIX}.key" \ + "${OPENSSL_BIN}" genrsa \ + -out "${key_file}" \ ${KEY_LENGTH} \ >/dev/null 2>&1 fi @@ -464,9 +472,9 @@ create() { PASSWORD="${PASSWORD}" "${OPENSSL_BIN}" req \ -batch \ -new \ - -key "${KEY_DIR}/${cn}-${SUFFIX}.key" \ + -key "${key_file}" \ -passin env:PASSWORD \ - -out "${CSR_DIR}/${cn}-${SUFFIX}.csr" \ + -out "${csr_file}" \ -config /dev/stdin </dev/null 2>&1 if [ "$?" -ne 0 ]; then - rm -f "${CRT_DIR}/${cn}.crt" + rm -f "${crt_file}" fi - - if [ ! -f "${CRT_DIR}/${cn}.crt" ]; then + if [ ! -f "${crt_file}" ]; then error "Error in CSR creation" fi - chmod 640 "${CRT_DIR}/${cn}.crt" + chmod 640 "${crt_file}" - echo "The CRT file is available in ${CRT_DIR}/${cn}.crt" + echo "The CRT file is available in ${crt_file}" # generate pkcs12 format if [ -n "${PASSWORD}" ]; then @@ -517,39 +524,39 @@ EOF -nodes \ -passin env:PASSWORD \ -passout env:PASSWORD \ - -inkey "${KEY_DIR}/${cn}-${SUFFIX}.key" \ - -in "${CRT_DIR}/${cn}.crt" \ - -out "${PKCS12_DIR}/${cn}-${SUFFIX}.p12" + -inkey "${key_file}" \ + -in "${crt_file}" \ + -out "${pkcs12_file}" else "${OPENSSL_BIN}" pkcs12 \ -export \ -nodes \ -passout pass: \ - -inkey "${KEY_DIR}/${cn}-${SUFFIX}.key" \ - -in "${CRT_DIR}/${cn}.crt" \ - -out "${PKCS12_DIR}/${cn}-${SUFFIX}.p12" + -inkey "${key_file}" \ + -in "${crt_file}" \ + -out "${pkcs12_file}" fi - chmod 640 "${PKCS12_DIR}/${cn}-${SUFFIX}.p12" - echo "The PKCS12 config file is available in ${PKCS12_DIR}/${cn}-${SUFFIX}.p12" + chmod 640 "${pkcs12_file}" + echo "The PKCS12 config file is available in ${pkcs12_file}" # generate openvpn format if [ -e "${CA_DIR}/ovpn.conf" ]; then - cat "${CA_DIR}/ovpn.conf" - > "${OVPN_DIR}/${cn}-${SUFFIX}.ovpn" < "${ovpn_file}" < $(cat "${CA_CERT}") -$(cat "${CRT_DIR}/${cn}.crt") +$(cat "${crt_file}") -$(cat "${KEY_DIR}/${cn}-${SUFFIX}.key") +$(cat "${key_file}") EOF - chmod 640 "${OVPN_DIR}/${cn}-${SUFFIX}.ovpn" - echo "The OpenVPN config file is available in ${OVPN_DIR}/${cn}-${SUFFIX}.ovpn" + chmod 640 "${ovpn_file}" + echo "The OpenVPN config file is available in ${ovpn_file}" fi fi } @@ -559,39 +566,43 @@ revoke() { show_usage >&2 exit 1 fi + crt_file="${CRT_DIR}/${cn}.crt" # get CN from param cn="${1}" # check if CRT exists - if [ ! -f "${CRT_DIR}/${cn}.crt" ]; then - error "Unknow CN : ${cn}" + if [ ! -f "${crt_file}" ]; then + error "Unknow CN : ${cn} (\`${crt_file}' not found)" fi # check if CRT is a valid "${OPENSSL_BIN}" x509 \ -noout \ -subject \ - -in "${CRT_DIR}/${cn}.crt" \ + -in "${crt_file}" \ >/dev/null 2>&1 if [ "$?" -ne 0 ]; then - error "${CRT_DIR}/${cn}.crt is not a valid CRT, you must delete it !" + error "${crt_file} is not a valid CRT, you must delete it !" fi # ask for CA passphrase ask_ca_password 0 - echo "Revoke certificate ${CRT_DIR}/${cn}.crt :" + echo "Revoke certificate ${crt_file} :" CA_PASSWORD="${CA_PASSWORD}" "${OPENSSL_BIN}" ca \ -config "${CONF_FILE}" \ -passin env:CA_PASSWORD \ - -revoke "${CRT_DIR}/${cn}.crt" \ - && rm "${CRT_DIR}/${cn}.crt" + -revoke "${crt_file}" + if [ "$?" -eq 0 ]; then + rm "${crt_file}" + fi CA_PASSWORD="${CA_PASSWORD}" "${OPENSSL_BIN}" ca \ -config "${CONF_FILE}" \ -passin env:CA_PASSWORD \ - -gencrl -out "${CRL}" + -gencrl \ + -out "${CRL}" } list() { From 8e92d46ecd3325845419d331ac82aa24f5451beb Mon Sep 17 00:00:00 2001 From: Jeremy Lecour Date: Tue, 5 May 2020 09:24:09 +0200 Subject: [PATCH 21/89] Let OpenSSL read the password file itself --- shellpki | 48 ++++++++++++++++++++++++++++++++++++------------ 1 file changed, 36 insertions(+), 12 deletions(-) diff --git a/shellpki b/shellpki index 1c8cd68..7b11394 100755 --- a/shellpki +++ b/shellpki @@ -80,9 +80,9 @@ init() { -x509 \ -days 3650 \ -extensions v3_ca \ + -passin env:CA_PASSWORD \ -key "${CA_KEY}" \ -out "${CA_CERT}" \ - -passin env:CA_PASSWORD \ -config /dev/stdin </dev/null 2>&1 + elif [ -n "${PASSWORD}" ]; then PASSWORD="${PASSWORD}" "${OPENSSL_BIN}" genrsa \ -aes256 \ -passout env:PASSWORD \ @@ -467,7 +469,19 @@ create() { >/dev/null 2>&1 fi - if [ -n "${PASSWORD}" ]; then + if [ -n "${password_file}" ]; then + # generate csr req + "${OPENSSL_BIN}" req \ + -batch \ + -new \ + -key "${key_file}" \ + -passin file:${password_file} \ + -out "${csr_file}" \ + -config /dev/stdin < Date: Tue, 5 May 2020 09:42:54 +0200 Subject: [PATCH 22/89] Simplify openssl commands composition --- shellpki | 113 +++++++++++++++++++------------------------------------ 1 file changed, 39 insertions(+), 74 deletions(-) diff --git a/shellpki b/shellpki index 7b11394..e05f488 100755 --- a/shellpki +++ b/shellpki @@ -448,63 +448,39 @@ create() { fi # generate private key + OPENSSL_ENV="" + PASS_ARGS="" if [ -n "${password_file}" ]; then - "${OPENSSL_BIN}" genrsa \ - -aes256 \ - -passout file:${password_file} \ - -out "${key_file}" \ - ${KEY_LENGTH} \ - >/dev/null 2>&1 + PASS_ARGS="-aes256 -passout file:${password_file}" elif [ -n "${PASSWORD}" ]; then - PASSWORD="${PASSWORD}" "${OPENSSL_BIN}" genrsa \ - -aes256 \ - -passout env:PASSWORD \ - -out "${key_file}" \ - ${KEY_LENGTH} \ - >/dev/null 2>&1 - else - "${OPENSSL_BIN}" genrsa \ - -out "${key_file}" \ - ${KEY_LENGTH} \ - >/dev/null 2>&1 + OPENSSL_ENV="PASSWORD=${PASSWORD}" + PASS_ARGS="-aes256 -passout env:PASSWORD" fi + "${OPENSSL_ENV}" "${OPENSSL_BIN}" genrsa \ + -out "${key_file}" \ + ${PASS_ARGS} \ + ${KEY_LENGTH} \ + >/dev/null 2>&1 + # generate csr req + OPENSSL_ENV="" + PASS_ARGS="" if [ -n "${password_file}" ]; then - # generate csr req - "${OPENSSL_BIN}" req \ - -batch \ - -new \ - -key "${key_file}" \ - -passin file:${password_file} \ - -out "${csr_file}" \ - -config /dev/stdin < Date: Tue, 5 May 2020 10:46:15 +0200 Subject: [PATCH 23/89] Use inline pass phrase arguments It doesn't seem more or less secure to embed the password as an argument than an environment variable written at the begining of the line. --- CHANGELOG.md | 1 + shellpki | 64 ++++++++++++++++++++++++++++------------------------ 2 files changed, 35 insertions(+), 30 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 10a9483..2be3197 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,6 +22,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 * Extract cert_end_date() function * Extract is_user() and is_group() functions * Extract variables for files +* Use inline pass phrase arguments ### Deprecated diff --git a/shellpki b/shellpki index e05f488..6657358 100755 --- a/shellpki +++ b/shellpki @@ -73,14 +73,14 @@ init() { fi if [ ! -f "${CA_CERT}" ]; then - CA_PASSWORD="${CA_PASSWORD}" "${OPENSSL_BIN}" req \ + "${OPENSSL_BIN}" req \ -new \ -batch \ -sha512 \ -x509 \ -days 3650 \ -extensions v3_ca \ - -passin env:CA_PASSWORD \ + -passin pass:${CA_PASSWORD} \ -key "${CA_KEY}" \ -out "${CA_CERT}" \ -config /dev/stdin </dev/null 2>&1 } @@ -400,10 +400,10 @@ create() { fi # ca sign and generate cert - CA_PASSWORD="${CA_PASSWORD}" "${OPENSSL_BIN}" ca \ + "${OPENSSL_BIN}" ca \ -config "${CONF_FILE}" \ -in "${csr_file}" \ - -passin env:CA_PASSWORD \ + -passin pass:${CA_PASSWORD} \ -out "${crt_file}" \ ${crt_expiration_arg} @@ -448,30 +448,25 @@ create() { fi # generate private key - OPENSSL_ENV="" PASS_ARGS="" if [ -n "${password_file}" ]; then PASS_ARGS="-aes256 -passout file:${password_file}" elif [ -n "${PASSWORD}" ]; then - OPENSSL_ENV="PASSWORD=${PASSWORD}" - PASS_ARGS="-aes256 -passout env:PASSWORD" + PASS_ARGS="-aes256 -passout pass:${PASSWORD}" fi - "${OPENSSL_ENV}" "${OPENSSL_BIN}" genrsa \ + "${OPENSSL_BIN}" genrsa \ -out "${key_file}" \ ${PASS_ARGS} \ - ${KEY_LENGTH} \ - >/dev/null 2>&1 + ${KEY_LENGTH} # generate csr req - OPENSSL_ENV="" PASS_ARGS="" if [ -n "${password_file}" ]; then PASS_ARGS="-passin file:${password_file}" elif [ -n "${PASSWORD}" ]; then - OPENSSL_ENV="PASSWORD=${PASSWORD}" - PASS_ARGS="-passin env:PASSWORD" + PASS_ARGS="-passin pass:${PASSWORD}" fi - "${OPENSSL_ENV}" "${OPENSSL_BIN}" req \ + "${OPENSSL_BIN}" req \ -batch \ -new \ -key "${key_file}" \ @@ -483,9 +478,9 @@ commonName_default = ${cn} EOF # ca sign and generate cert - CA_PASSWORD="${CA_PASSWORD}" "${OPENSSL_BIN}" ca \ + "${OPENSSL_BIN}" ca \ -config "${CONF_FILE}" \ - -passin env:CA_PASSWORD \ + -passin pass:${CA_PASSWORD} \ -in "${csr_file}" \ -out "${crt_file}" \ ${crt_expiration_arg} @@ -508,24 +503,33 @@ EOF echo "The CRT file is available in ${crt_file}" # generate pkcs12 format - OPENSSL_ENV="" PASS_ARGS="" if [ -n "${password_file}" ]; then - PASS_ARGS="-passin file:${password_file} -passout file:${password_file}" + # Hack for pkcs12 : + # If passin and passout files are the same path, it expects 2 lines + # so we make a temporary copy of the password file + password_file_out=$(mktemp) + cp "${password_file}" "${password_file_out}" + PASS_ARGS="-passin file:${password_file} -passout file:${password_file_out}" elif [ -n "${PASSWORD}" ]; then - OPENSSL_ENV="PASSWORD=${PASSWORD}" - PASS_ARGS="-passin env:PASSWORD -passout env:PASSWORD" + PASS_ARGS="-passin pass:${PASSWORD} -passout pass:${PASSWORD}" else PASS_ARGS="-passout pass:" fi - "${OPENSSL_ENV}" "${OPENSSL_BIN}" pkcs12 \ + "${OPENSSL_BIN}" pkcs12 \ -export \ -nodes \ -inkey "${key_file}" \ -in "${crt_file}" \ - -out "${pkcs12_file}" + -out "${pkcs12_file}" \ ${PASS_ARGS} + if [ -n "${password_file_out}" ]; then + # Hack for pkcs12 : + # Destroy the temporary file + rm -f "${password_file_out}" + fi + chmod 640 "${pkcs12_file}" echo "The PKCS12 config file is available in ${pkcs12_file}" @@ -579,17 +583,17 @@ revoke() { ask_ca_password 0 echo "Revoke certificate ${crt_file} :" - CA_PASSWORD="${CA_PASSWORD}" "${OPENSSL_BIN}" ca \ + "${OPENSSL_BIN}" ca \ -config "${CONF_FILE}" \ - -passin env:CA_PASSWORD \ + -passin pass:${CA_PASSWORD} \ -revoke "${crt_file}" if [ "$?" -eq 0 ]; then rm "${crt_file}" fi - CA_PASSWORD="${CA_PASSWORD}" "${OPENSSL_BIN}" ca \ + "${OPENSSL_BIN}" ca \ -config "${CONF_FILE}" \ - -passin env:CA_PASSWORD \ + -passin pass:${CA_PASSWORD} \ -gencrl \ -out "${CRL}" } From dfeaf77b9fd321e2822ac14a034ee06f8e91f01d Mon Sep 17 00:00:00 2001 From: Jeremy Lecour Date: Tue, 5 May 2020 10:47:09 +0200 Subject: [PATCH 24/89] Extract ask_user_password() function --- CHANGELOG.md | 1 + shellpki | 26 +++++++++++++++----------- 2 files changed, 16 insertions(+), 11 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2be3197..1aa0a78 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,6 +21,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 * verify_ca_password() looks for a previously set password and verifies it * Extract cert_end_date() function * Extract is_user() and is_group() functions +* Extract ask_user_password() function * Extract variables for files * Use inline pass phrase arguments diff --git a/shellpki b/shellpki index 6657358..2bc02e0 100755 --- a/shellpki +++ b/shellpki @@ -214,7 +214,7 @@ ask_ca_password() { fi if [ -z "${CA_PASSWORD}" ]; then stty -echo - printf "Password for CA key : " + printf "Password for CA key: " read -r CA_PASSWORD stty echo printf "\n" @@ -225,6 +225,19 @@ ask_ca_password() { ask_ca_password "${attempt}" fi } +ask_user_password() { + trap 'unset PASSWORD' 0 + + stty -echo + printf "Password for user key: " + read -r PASSWORD + stty echo + printf "\n" + + if [ -z "${PASSWORD}" ]; then + warning "Warning: empty password from input" + fi +} create() { from_csr=0 @@ -435,16 +448,7 @@ create() { ask_ca_password 0 if [ "${ask_pass}" -eq 1 ]; then - trap 'unset PASSWORD' 0 - stty -echo - printf "Password for user key : " - read -r PASSWORD - stty echo - printf "\n" - - if [ -z "${PASSWORD}" ]; then - warning "Warning: empty password from input" - fi + ask_user_password fi # generate private key From 3161e9385634c4af59319502a43bba8e0836a91e Mon Sep 17 00:00:00 2001 From: Jeremy Lecour Date: Tue, 5 May 2020 10:49:33 +0200 Subject: [PATCH 25/89] Restore forgotten output redirection It had been removed temporarily to debug an issue --- shellpki | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/shellpki b/shellpki index 2bc02e0..8ea6c07 100755 --- a/shellpki +++ b/shellpki @@ -461,7 +461,8 @@ create() { "${OPENSSL_BIN}" genrsa \ -out "${key_file}" \ ${PASS_ARGS} \ - ${KEY_LENGTH} + ${KEY_LENGTH} \ + >/dev/null 2>&1 # generate csr req PASS_ARGS="" From 229aab510a2126a83b94e858b064b0df4072533d Mon Sep 17 00:00:00 2001 From: Jeremy Lecour Date: Tue, 5 May 2020 11:30:37 +0200 Subject: [PATCH 26/89] Emit errors if files are missing --- shellpki | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/shellpki b/shellpki index 8ea6c07..7b10969 100755 --- a/shellpki +++ b/shellpki @@ -251,7 +251,7 @@ create() { # csr-file option, with value separated by space if [ -n "$2" ]; then from_csr=1 - csr_file=$(readlink -f -- "${2}") + csr_file=$(readlink --canonicalize --verbose -- "${2}") shift else printf 'ERROR: "--csr-file" requires a non-empty option argument.\n' >&2 @@ -261,7 +261,7 @@ create() { --file=?*|--csr-file=?*) from_csr=1 # csr-file option, with value separated by = - csr_file=$(readlink -f -- "${1#*=}") + csr_file=$(readlink --canonicalize --verbose -- "${1#*=}") ;; --file=|--csr-file=) # csr-file options, without value @@ -274,7 +274,7 @@ create() { --password-file) # password-file option, with value separated by space if [ -n "$2" ]; then - password_file=$(readlink -f -- "${2}") + password_file=$(readlink --canonicalize --verbose -- "${2}") shift else printf 'ERROR: "--password-file" requires a non-empty option argument.\n' >&2 @@ -283,7 +283,7 @@ create() { ;; --password-file=?*) # password-file option, with value separated by = - password_file=$(readlink -f -- "${1#*=}") + password_file=$(readlink --canonicalize --verbose -- "${1#*=}") ;; --password-file=) # password-file options, without value From fa5a344ef4dc7d234ed0f2e8e2143e3f45bff341 Mon Sep 17 00:00:00 2001 From: Jeremy Lecour Date: Tue, 5 May 2020 11:45:11 +0200 Subject: [PATCH 27/89] Remove "set -e" and add many return code checks --- CHANGELOG.md | 1 + shellpki | 39 ++++++++++++++++++++++++++++++++++----- 2 files changed, 35 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1aa0a78..7fa90a1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -24,6 +24,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 * Extract ask_user_password() function * Extract variables for files * Use inline pass phrase arguments +* Remove "set -e" and add many return code checks ### Deprecated diff --git a/shellpki b/shellpki index 7b10969..13027e7 100755 --- a/shellpki +++ b/shellpki @@ -3,8 +3,6 @@ # shellpki is a wrapper around OpenSSL to manage a small PKI # -set -e - VERSION="1.0.0" show_version() { @@ -57,6 +55,9 @@ init() { -out "${CA_KEY}" \ -aes256 ${CA_KEY_LENGTH} \ >/dev/null 2>&1 + if [ "$?" -ne 0 ]; then + error "Error generating the CA key: $?" + fi fi if [ -f "${CA_CERT}" ]; then @@ -87,6 +88,9 @@ init() { $(cat "${CONF_FILE}") commonName_default = ${cn} EOF + if [ "$?" -ne 0 ]; then + error "Error generating the CA certificate: $?" + fi fi } @@ -108,6 +112,9 @@ ocsp() { -out "${OCSP_KEY}" \ ${KEY_LENGTH} \ >/dev/null 2>&1 + if [ "$?" -ne 0 ]; then + error "Error generating the OCSP key: $?" + fi fi "${OPENSSL_BIN}" req \ @@ -121,6 +128,9 @@ commonName_default = ${url} [ usr_cert ] authorityInfoAccess = OCSP;URI:http://${ocsp_uri} EOF + if [ "$?" -ne 0 ]; then + error "Error generating the OCSP request: $?" + fi if [ ! -f "${OCSP_CERT}" ]; then ask_ca_password 0 @@ -133,6 +143,9 @@ EOF -out "${OCSP_CERT}" \ -passin pass:${CA_PASSWORD} \ -config "${CONF_FILE}" + if [ "$?" -ne 0 ]; then + error "Error generating the OCSP certificate: $?" + fi fi exec "${OPENSSL_BIN}" ocsp \ @@ -419,8 +432,12 @@ create() { -passin pass:${CA_PASSWORD} \ -out "${crt_file}" \ ${crt_expiration_arg} + if [ "$?" -ne 0 ]; then + error "Error generating the certificate: $?" + else + echo "The certificate file is available at \`${crt_file}'" + fi - echo "The CRT file is available in ${crt_file}" else if [ -z "${cn}" ]; then show_usage >&2 @@ -463,6 +480,9 @@ create() { ${PASS_ARGS} \ ${KEY_LENGTH} \ >/dev/null 2>&1 + if [ "$?" -ne 0 ]; then + error "Error generating the private key: $?" + fi # generate csr req PASS_ARGS="" @@ -481,6 +501,9 @@ create() { $(cat "${CONF_FILE}") commonName_default = ${cn} EOF + if [ "$?" -ne 0 ]; then + error "Error generating the CSR: $?" + fi # ca sign and generate cert "${OPENSSL_BIN}" ca \ @@ -489,6 +512,9 @@ EOF -in "${csr_file}" \ -out "${crt_file}" \ ${crt_expiration_arg} + if [ "$?" -ne 0 ]; then + error "Error generating the certificate: $?" + fi # check if CRT is a valid "${OPENSSL_BIN}" x509 \ @@ -528,6 +554,9 @@ EOF -in "${crt_file}" \ -out "${pkcs12_file}" \ ${PASS_ARGS} + if [ "$?" -ne 0 ]; then + error "Error generating the pkcs12 file: $?" + fi if [ -n "${password_file_out}" ]; then # Hack for pkcs12 : @@ -536,7 +565,7 @@ EOF fi chmod 640 "${pkcs12_file}" - echo "The PKCS12 config file is available in ${pkcs12_file}" + echo "The PKCS12 config file is available at \`${pkcs12_file}'" # generate openvpn format if [ -e "${CA_DIR}/ovpn.conf" ]; then @@ -554,7 +583,7 @@ $(cat "${key_file}") EOF chmod 640 "${ovpn_file}" - echo "The OpenVPN config file is available in ${ovpn_file}" + echo "The OpenVPN config file is available at \`${ovpn_file}'" fi fi } From d9f866fc3a7601fb8bffc32881875ffdc1192827 Mon Sep 17 00:00:00 2001 From: Jeremy Lecour Date: Tue, 5 May 2020 15:06:15 +0200 Subject: [PATCH 28/89] typo --- shellpki | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/shellpki b/shellpki index 13027e7..3115ad0 100755 --- a/shellpki +++ b/shellpki @@ -212,7 +212,7 @@ verify_ca_password() { ask_ca_password() { attempt=${1:-0} - max_attempt=3 + max_attempts=3 trap 'unset CA_PASSWORD' 0 @@ -222,8 +222,8 @@ ask_ca_password() { if [ "${attempt}" -gt 0 ]; then warning "Invalid password, retry." fi - if [ "${attempt}" -ge "${max_attempt}" ]; then - error "Maximum number of attempts reached (${max_attempt})." + if [ "${attempt}" -ge "${max_attempts}" ]; then + error "Maximum number of attempts reached (${max_attempts})." fi if [ -z "${CA_PASSWORD}" ]; then stty -echo From 0c4d36cb57f373a5b92ff453a81505b65773d28b Mon Sep 17 00:00:00 2001 From: Jeremy Lecour Date: Tue, 5 May 2020 15:24:06 +0200 Subject: [PATCH 29/89] improve error display --- shellpki | 40 ++++++++++++++++++++++++++-------------- 1 file changed, 26 insertions(+), 14 deletions(-) diff --git a/shellpki b/shellpki index 3115ad0..f4f25a2 100755 --- a/shellpki +++ b/shellpki @@ -56,7 +56,7 @@ init() { -aes256 ${CA_KEY_LENGTH} \ >/dev/null 2>&1 if [ "$?" -ne 0 ]; then - error "Error generating the CA key: $?" + error "Error generating the CA key" fi fi @@ -89,7 +89,7 @@ $(cat "${CONF_FILE}") commonName_default = ${cn} EOF if [ "$?" -ne 0 ]; then - error "Error generating the CA certificate: $?" + error "Error generating the CA certificate" fi fi } @@ -113,7 +113,7 @@ ocsp() { ${KEY_LENGTH} \ >/dev/null 2>&1 if [ "$?" -ne 0 ]; then - error "Error generating the OCSP key: $?" + error "Error generating the OCSP key" fi fi @@ -129,7 +129,7 @@ commonName_default = ${url} authorityInfoAccess = OCSP;URI:http://${ocsp_uri} EOF if [ "$?" -ne 0 ]; then - error "Error generating the OCSP request: $?" + error "Error generating the OCSP request" fi if [ ! -f "${OCSP_CERT}" ]; then @@ -144,7 +144,7 @@ EOF -passin pass:${CA_PASSWORD} \ -config "${CONF_FILE}" if [ "$?" -ne 0 ]; then - error "Error generating the OCSP certificate: $?" + error "Error generating the OCSP certificate" fi fi @@ -264,7 +264,10 @@ create() { # csr-file option, with value separated by space if [ -n "$2" ]; then from_csr=1 - csr_file=$(readlink --canonicalize --verbose -- "${2}") + csr_file=$(readlink --canonicalize -- "${2}") + if [ "$?" -ne 0 ]; then + error "Error accessing file \`${2}'" + fi shift else printf 'ERROR: "--csr-file" requires a non-empty option argument.\n' >&2 @@ -274,7 +277,10 @@ create() { --file=?*|--csr-file=?*) from_csr=1 # csr-file option, with value separated by = - csr_file=$(readlink --canonicalize --verbose -- "${1#*=}") + csr_file=$(readlink --canonicalize -- "${1#*=}") + if [ "$?" -ne 0 ]; then + error "Error accessing file \`${1#*=}'" + fi ;; --file=|--csr-file=) # csr-file options, without value @@ -287,7 +293,10 @@ create() { --password-file) # password-file option, with value separated by space if [ -n "$2" ]; then - password_file=$(readlink --canonicalize --verbose -- "${2}") + password_file=$(readlink --canonicalize -- "${2}") + if [ "$?" -ne 0 ]; then + error "Error accessing file \`${2}'" + fi shift else printf 'ERROR: "--password-file" requires a non-empty option argument.\n' >&2 @@ -296,7 +305,10 @@ create() { ;; --password-file=?*) # password-file option, with value separated by = - password_file=$(readlink --canonicalize --verbose -- "${1#*=}") + password_file=$(readlink --canonicalize -- "${1#*=}") + if [ "$?" -ne 0 ]; then + error "Error accessing file \`${1#*=}'" + fi ;; --password-file=) # password-file options, without value @@ -433,7 +445,7 @@ create() { -out "${crt_file}" \ ${crt_expiration_arg} if [ "$?" -ne 0 ]; then - error "Error generating the certificate: $?" + error "Error generating the certificate" else echo "The certificate file is available at \`${crt_file}'" fi @@ -481,7 +493,7 @@ create() { ${KEY_LENGTH} \ >/dev/null 2>&1 if [ "$?" -ne 0 ]; then - error "Error generating the private key: $?" + error "Error generating the private key" fi # generate csr req @@ -502,7 +514,7 @@ $(cat "${CONF_FILE}") commonName_default = ${cn} EOF if [ "$?" -ne 0 ]; then - error "Error generating the CSR: $?" + error "Error generating the CSR" fi # ca sign and generate cert @@ -513,7 +525,7 @@ EOF -out "${crt_file}" \ ${crt_expiration_arg} if [ "$?" -ne 0 ]; then - error "Error generating the certificate: $?" + error "Error generating the certificate" fi # check if CRT is a valid @@ -555,7 +567,7 @@ EOF -out "${pkcs12_file}" \ ${PASS_ARGS} if [ "$?" -ne 0 ]; then - error "Error generating the pkcs12 file: $?" + error "Error generating the pkcs12 file" fi if [ -n "${password_file_out}" ]; then From f94f7d8cd30d4038341483e6bbb99bf01105365c Mon Sep 17 00:00:00 2001 From: Jeremy Lecour Date: Tue, 5 May 2020 23:14:32 +0200 Subject: [PATCH 30/89] Add `--non-interactive` command line option --- CHANGELOG.md | 1 + shellpki | 19 +++++++++++++++++++ 2 files changed, 20 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7fa90a1..5482f91 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 * Accept a `password-file` command line option to read password from a file * Accept `--days` and `--end-date` command line options * CA key length is configurable (minimum 4096) +* Add `--non-interactive` command line option ### Changed diff --git a/shellpki b/shellpki index f4f25a2..5803daf 100755 --- a/shellpki +++ b/shellpki @@ -226,6 +226,9 @@ ask_ca_password() { error "Maximum number of attempts reached (${max_attempts})." fi if [ -z "${CA_PASSWORD}" ]; then + if [ "${non_interactive}" -eq 1 ]; then + error "In non-interactive mode, you must pass CA_PASSWORD as environment variable" + fi stty -echo printf "Password for CA key: " read -r CA_PASSWORD @@ -255,6 +258,7 @@ ask_user_password() { create() { from_csr=0 ask_pass=0 + non_interactive=0 # Parse options # based on https://gist.github.com/deshion/10d3cb5f88a21671e17a @@ -353,6 +357,9 @@ create() { printf 'ERROR: "--end-date" requires a non-empty option argument.\n' >&2 exit 1 ;; + --non-interactive) + non_interactive=1 + ;; --) # End of all options. shift @@ -387,6 +394,11 @@ create() { crt_expiration_arg="-enddate ${cert_end_date}" fi fi + if [ "${non_interactive}" -eq 1 ]; then + batch_arg="-batch" + else + batch_arg="" + fi if [ "${from_csr}" -eq 1 ]; then if [ "${ask_pass}" -eq 1 ]; then @@ -438,7 +450,13 @@ create() { fi # ca sign and generate cert + if [ "${non_interactive}" -eq 1 ]; then + batch_arg="-batch" + else + batch_arg="" + fi "${OPENSSL_BIN}" ca \ + ${batch_arg} \ -config "${CONF_FILE}" \ -in "${csr_file}" \ -passin pass:${CA_PASSWORD} \ @@ -519,6 +537,7 @@ EOF # ca sign and generate cert "${OPENSSL_BIN}" ca \ + ${batch_arg} \ -config "${CONF_FILE}" \ -passin pass:${CA_PASSWORD} \ -in "${csr_file}" \ From e04f6866513d8d17d7004bceaca36060979f1ce3 Mon Sep 17 00:00:00 2001 From: Jeremy Lecour Date: Tue, 5 May 2020 23:20:36 +0200 Subject: [PATCH 31/89] Prevent use of uninitialized variables --- CHANGELOG.md | 1 + shellpki | 8 ++++++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5482f91..3d07cec 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -26,6 +26,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 * Extract variables for files * Use inline pass phrase arguments * Remove "set -e" and add many return code checks +* Prevent use of uninitialized variables ### Deprecated diff --git a/shellpki b/shellpki index 5803daf..e409bc2 100755 --- a/shellpki +++ b/shellpki @@ -3,6 +3,8 @@ # shellpki is a wrapper around OpenSSL to manage a small PKI # +set -u + VERSION="1.0.0" show_version() { @@ -225,7 +227,7 @@ ask_ca_password() { if [ "${attempt}" -ge "${max_attempts}" ]; then error "Maximum number of attempts reached (${max_attempts})." fi - if [ -z "${CA_PASSWORD}" ]; then + if [ -z "${CA_PASSWORD:-}" ]; then if [ "${non_interactive}" -eq 1 ]; then error "In non-interactive mode, you must pass CA_PASSWORD as environment variable" fi @@ -235,7 +237,7 @@ ask_ca_password() { stty echo printf "\n" fi - if [ -z "${CA_PASSWORD}" ] || ! verify_ca_password; then + if [ -z "${CA_PASSWORD:-}" ] || ! verify_ca_password; then unset CA_PASSWORD attempt=$(( attempt + 1 )) ask_ca_password "${attempt}" @@ -259,6 +261,8 @@ create() { from_csr=0 ask_pass=0 non_interactive=0 + days="" + end_date="" # Parse options # based on https://gist.github.com/deshion/10d3cb5f88a21671e17a From 3e2bbe8de5f91df03b3176c4a1db937548246780 Mon Sep 17 00:00:00 2001 From: Jeremy Lecour Date: Tue, 5 May 2020 23:20:54 +0200 Subject: [PATCH 32/89] lowercase variable --- shellpki | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/shellpki b/shellpki index e409bc2..bfd0e87 100755 --- a/shellpki +++ b/shellpki @@ -503,15 +503,15 @@ create() { fi # generate private key - PASS_ARGS="" + pass_args="" if [ -n "${password_file}" ]; then - PASS_ARGS="-aes256 -passout file:${password_file}" + pass_args="-aes256 -passout file:${password_file}" elif [ -n "${PASSWORD}" ]; then - PASS_ARGS="-aes256 -passout pass:${PASSWORD}" + pass_args="-aes256 -passout pass:${PASSWORD}" fi "${OPENSSL_BIN}" genrsa \ -out "${key_file}" \ - ${PASS_ARGS} \ + ${pass_args} \ ${KEY_LENGTH} \ >/dev/null 2>&1 if [ "$?" -ne 0 ]; then @@ -519,18 +519,18 @@ create() { fi # generate csr req - PASS_ARGS="" + pass_args="" if [ -n "${password_file}" ]; then - PASS_ARGS="-passin file:${password_file}" + pass_args="-passin file:${password_file}" elif [ -n "${PASSWORD}" ]; then - PASS_ARGS="-passin pass:${PASSWORD}" + pass_args="-passin pass:${PASSWORD}" fi "${OPENSSL_BIN}" req \ -batch \ -new \ -key "${key_file}" \ -out "${csr_file}" \ - ${PASS_ARGS} \ + ${pass_args} \ -config /dev/stdin < Date: Tue, 5 May 2020 23:49:10 +0200 Subject: [PATCH 33/89] Use error() and warning() functions in options parsing --- shellpki | 26 +++++++++----------------- 1 file changed, 9 insertions(+), 17 deletions(-) diff --git a/shellpki b/shellpki index bfd0e87..02a24c5 100755 --- a/shellpki +++ b/shellpki @@ -278,8 +278,7 @@ create() { fi shift else - printf 'ERROR: "--csr-file" requires a non-empty option argument.\n' >&2 - exit 1 + error "Argument error: \`--csr-file' requires a value" fi ;; --file=?*|--csr-file=?*) @@ -292,8 +291,7 @@ create() { ;; --file=|--csr-file=) # csr-file options, without value - printf 'ERROR: "--csr-file" requires a non-empty option argument.\n' >&2 - exit 1 + error "Argument error: \`--csr-file' requires a value" ;; -p|--password) ask_pass=1 @@ -307,8 +305,7 @@ create() { fi shift else - printf 'ERROR: "--password-file" requires a non-empty option argument.\n' >&2 - exit 1 + error "Argument error: \`--password-file' requires a value" fi ;; --password-file=?*) @@ -320,8 +317,7 @@ create() { ;; --password-file=) # password-file options, without value - printf 'ERROR: "--password-file" requires a non-empty option argument.\n' >&2 - exit 1 + error "Argument error: \`--password-file' requires a value" ;; --days) # days option, with value separated by space @@ -329,8 +325,7 @@ create() { days=${2} shift else - printf 'ERROR: "--days" requires a non-empty option argument.\n' >&2 - exit 1 + error "Argument error: \`--days' requires a value" fi ;; --days=?*) @@ -339,8 +334,7 @@ create() { ;; --days=) # days options, without value - printf 'ERROR: "--days" requires a non-empty option argument.\n' >&2 - exit 1 + error "Argument error: \`--days' requires a value" ;; --end-date) # end-date option, with value separated by space @@ -348,8 +342,7 @@ create() { end_date=${2} shift else - printf 'ERROR: "--end-date" requires a non-empty option argument.\n' >&2 - exit 1 + error "Argument error: \`--end-date' requires a value" fi ;; --end-date=?*) @@ -358,8 +351,7 @@ create() { ;; --end-date=) # end-date options, without value - printf 'ERROR: "--end-date" requires a non-empty option argument.\n' >&2 - exit 1 + error "Argument error: \`--end-date' requires a value" ;; --non-interactive) non_interactive=1 @@ -371,7 +363,7 @@ create() { ;; -?*) # ignore unknown options - printf 'WARN: Unknown option (ignored): %s\n' "$1" >&2 + warning "Warning: unknown option (ignored): \`$1'" ;; *) # Default case: If no more options then break out of the loop. From 6bb05a636681b3a38786756dddd006928c18a77f Mon Sep 17 00:00:00 2001 From: Jeremy Lecour Date: Tue, 5 May 2020 23:50:04 +0200 Subject: [PATCH 34/89] Add `--revoke-existing` command line option --- CHANGELOG.md | 1 + shellpki | 66 ++++++++++++++++++++++++++++++++-------------------- 2 files changed, 42 insertions(+), 25 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3d07cec..55c644b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 * Accept `--days` and `--end-date` command line options * CA key length is configurable (minimum 4096) * Add `--non-interactive` command line option +* Add `--revoke-existing` command line option ### Changed diff --git a/shellpki b/shellpki index 02a24c5..729630e 100755 --- a/shellpki +++ b/shellpki @@ -246,21 +246,48 @@ ask_ca_password() { ask_user_password() { trap 'unset PASSWORD' 0 - stty -echo - printf "Password for user key: " - read -r PASSWORD - stty echo - printf "\n" - - if [ -z "${PASSWORD}" ]; then + if [ -z "${PASSWORD:-}" ]; then + if [ "${non_interactive}" -eq 1 ]; then + error "In non-interactive mode, you must pass PASSWORD as environment variable or use --password-file" + fi + stty -echo + printf "Password for user key: " + read -r PASSWORD + stty echo + printf "\n" + fi + if [ -z "${PASSWORD:-}" ]; then warning "Warning: empty password from input" fi } - +revoke_existing_or_abort() { + cn=${1:?} + if [ "${non_interactive}" -eq 1 ]; then + if [ "${revoke_existing}" -eq 1 ]; then + resp="y" + else + error "${cn} already exists, use \`--revoke-existing' to force" + fi + else + if [ "${revoke_existing}" -eq 1 ]; then + resp="y" + else + printf "%s already exists, do you want to revoke and recreate it ? [y/N] " "${cn}" + read -r REPLY + resp=$(echo "${REPLY}" | tr 'Y' 'y') + fi + fi + if [ "${resp}" = "y" ]; then + revoke "${cn}" + else + error "Aborted" + fi +} create() { from_csr=0 ask_pass=0 non_interactive=0 + revoke_existing=0 days="" end_date="" @@ -356,6 +383,9 @@ create() { --non-interactive) non_interactive=1 ;; + --revoke-existing) + revoke_existing=1 + ;; --) # End of all options. shift @@ -434,15 +464,8 @@ create() { cn=$("${OPENSSL_BIN}" req -noout -subject -in "${csr_file}" | grep -Eo "CN\s*=[^,/]*" | cut -d'=' -f2 | xargs) # check if CN already exist - if [ -f "${CRT_DIR}/${cn}.crt" ]; then - printf "%s already exists, do you revoke and recreate it ? [y/N] " "${cn}" - read -r REPLY - resp=$(echo "${REPLY}" | tr 'Y' 'y') - if [ "${resp}" = "y" ]; then - revoke "${cn}" - else - error "Abort" - fi + if [ -f "${crt_file}" ]; then + revoke_existing_or_abort "${cn}" fi # ca sign and generate cert @@ -477,14 +500,7 @@ create() { # check if CN already exist if [ -f "${crt_file}" ]; then - printf "%s already exists, do you revoke and recreate it ? [y/N] " "${cn}" - read -r REPLY - resp=$(echo "${REPLY}" | tr 'Y' 'y') - if [ "${resp}" = "y" ]; then - revoke "${cn}" - else - error "Abort" - fi + revoke_existing_or_abort "${cn}" fi # ask for CA passphrase From 123d5f5c05b81c9dc148f6de99a8e64bee970765 Mon Sep 17 00:00:00 2001 From: Jeremy Lecour Date: Wed, 6 May 2020 00:00:00 +0200 Subject: [PATCH 35/89] split lines --- shellpki | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/shellpki b/shellpki index 729630e..0c6e698 100755 --- a/shellpki +++ b/shellpki @@ -55,7 +55,8 @@ init() { if [ ! -f "${CA_KEY}" ]; then "${OPENSSL_BIN}" genrsa \ -out "${CA_KEY}" \ - -aes256 ${CA_KEY_LENGTH} \ + -aes256 \ + ${CA_KEY_LENGTH} \ >/dev/null 2>&1 if [ "$?" -ne 0 ]; then error "Error generating the CA key" From ab4e3e5de1664956b6bfb916d51c0b77bef07c84 Mon Sep 17 00:00:00 2001 From: Jeremy Lecour Date: Wed, 6 May 2020 00:38:57 +0200 Subject: [PATCH 36/89] Rename `--revoke-existing` to `--replace-existing` --- CHANGELOG.md | 2 +- shellpki | 18 +++++++++--------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 55c644b..2554e03 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,7 +14,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 * Accept `--days` and `--end-date` command line options * CA key length is configurable (minimum 4096) * Add `--non-interactive` command line option -* Add `--revoke-existing` command line option +* Add `--replace-existing` command line option ### Changed diff --git a/shellpki b/shellpki index 0c6e698..49efbcf 100755 --- a/shellpki +++ b/shellpki @@ -261,16 +261,16 @@ ask_user_password() { warning "Warning: empty password from input" fi } -revoke_existing_or_abort() { +replace_existing_or_abort() { cn=${1:?} if [ "${non_interactive}" -eq 1 ]; then - if [ "${revoke_existing}" -eq 1 ]; then + if [ "${replace_existing}" -eq 1 ]; then resp="y" else - error "${cn} already exists, use \`--revoke-existing' to force" + error "${cn} already exists, use \`--replace-existing' to force" fi else - if [ "${revoke_existing}" -eq 1 ]; then + if [ "${replace_existing}" -eq 1 ]; then resp="y" else printf "%s already exists, do you want to revoke and recreate it ? [y/N] " "${cn}" @@ -288,7 +288,7 @@ create() { from_csr=0 ask_pass=0 non_interactive=0 - revoke_existing=0 + replace_existing=0 days="" end_date="" @@ -384,8 +384,8 @@ create() { --non-interactive) non_interactive=1 ;; - --revoke-existing) - revoke_existing=1 + --replace-existing) + replace_existing=1 ;; --) # End of all options. @@ -466,7 +466,7 @@ create() { # check if CN already exist if [ -f "${crt_file}" ]; then - revoke_existing_or_abort "${cn}" + replace_existing_or_abort "${cn}" fi # ca sign and generate cert @@ -501,7 +501,7 @@ create() { # check if CN already exist if [ -f "${crt_file}" ]; then - revoke_existing_or_abort "${cn}" + replace_existing_or_abort "${cn}" fi # ask for CA passphrase From fdb9f46e355dfa6eaae02f35854d1db6b59b163f Mon Sep 17 00:00:00 2001 From: Jeremy Lecour Date: Wed, 6 May 2020 00:39:23 +0200 Subject: [PATCH 37/89] Display key file path on success --- shellpki | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/shellpki b/shellpki index 49efbcf..aedc068 100755 --- a/shellpki +++ b/shellpki @@ -523,7 +523,9 @@ create() { ${pass_args} \ ${KEY_LENGTH} \ >/dev/null 2>&1 - if [ "$?" -ne 0 ]; then + if [ "$?" -eq 0 ]; then + echo "The KEY file is available at \`${key_file}'" + else error "Error generating the private key" fi From 99e5b8a386786668c8da8d7936649796dc99215c Mon Sep 17 00:00:00 2001 From: Jeremy Lecour Date: Wed, 6 May 2020 00:39:39 +0200 Subject: [PATCH 38/89] whitespace --- shellpki | 1 - 1 file changed, 1 deletion(-) diff --git a/shellpki b/shellpki index aedc068..8521024 100755 --- a/shellpki +++ b/shellpki @@ -487,7 +487,6 @@ create() { else echo "The certificate file is available at \`${crt_file}'" fi - else if [ -z "${cn}" ]; then show_usage >&2 From a6c153b54654b639307f462baf6b4ce32da3014e Mon Sep 17 00:00:00 2001 From: Jeremy Lecour Date: Wed, 6 May 2020 00:40:36 +0200 Subject: [PATCH 39/89] Copy files if destination exists --- CHANGELOG.md | 1 + shellpki | 29 +++++++++++++++++++++++++++++ 2 files changed, 30 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2554e03..b7bc4bf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 * CA key length is configurable (minimum 4096) * Add `--non-interactive` command line option * Add `--replace-existing` command line option +* Copy files if destination exists ### Changed diff --git a/shellpki b/shellpki index 8521024..63d80df 100755 --- a/shellpki +++ b/shellpki @@ -630,6 +630,33 @@ EOF chmod 640 "${ovpn_file}" echo "The OpenVPN config file is available at \`${ovpn_file}'" fi + + # Copy files if destination exists + if [ -d "${COPY_DIR}" ]; then + for file in "${crt_file}" "${key_file}" "${pkcs12_file}" "${ovpn_file}"; do + if [ -f "${file}" ]; then + new_file="${COPY_DIR}/$(basename "${file}")" + if [ "${replace_existing}" -eq 1 ]; then + cp -f "${file}" "${COPY_DIR}/" + else + if [ "${non_interactive}" -eq 1 ]; then + if [ -f "${new_file}" ]; then + echo "File \`${file}' has not been copied to \`${new_file}', it already exists" >&2 + continue + else + cp "${file}" "${COPY_DIR}/" + fi + else + cp -i "${file}" "${COPY_DIR}/" + fi + fi + echo "File \`${file}' has been copied to \`${new_file}'" + fi + done + + chown -R ${PKI_USER}:${PKI_USER} "${COPY_DIR}/" + chmod -R u=rwX,g=rwX,o= "${COPY_DIR}/" + fi fi } @@ -790,6 +817,8 @@ main() { PKCS12_DIR="${CA_DIR}/pkcs12" OVPN_DIR="${CA_DIR}/openvpn" + COPY_DIR="$(dirname "${CONF_FILE}")/copy_output" + CA_KEY_LENGTH=4096 if [ "${CA_KEY_LENGTH}" -lt 4096 ]; then error "CA key must be at least 4096 bits long." From c335b30623864dd9cb4b4bc636b00b15fe7e52c9 Mon Sep 17 00:00:00 2001 From: Jeremy Lecour Date: Fri, 4 Sep 2020 14:50:13 +0200 Subject: [PATCH 40/89] `cert-expirations.sh` script to print out certificates expiration dates --- cert-expirations.sh | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 cert-expirations.sh diff --git a/cert-expirations.sh b/cert-expirations.sh new file mode 100644 index 0000000..a3081a8 --- /dev/null +++ b/cert-expirations.sh @@ -0,0 +1,16 @@ +#!/bin/sh + +echo "CA certificate:" +openssl x509 -enddate -noout -in /etc/shellpki/cacert.pem \ + | cut -d '=' -f 2 \ + | sed -e "s/^\(.*\)\ \(20..\).*/- \2 \1/" + +echo "" + +echo "Client certificates:" +grep "Not After" -r /etc/shellpki/certs/ \ + | sed -e "s/^.*certs\/\([-.a-z0-9]*\).*After\ :\ \(.*\).*GMT$/\2\1X/" \ + | sed -e "s/^\(.*\)\ \(20..\)\ \(.*\)$/- \2 \1 \3/" \ + | tr "X" "\n" \ + | sed '/^$/d' \ + | sort -n -k 2 -k 3M -k 4 From 530cd3b33307294f05c9caf618e572afb7edf890 Mon Sep 17 00:00:00 2001 From: Jeremy Lecour Date: Mon, 7 Sep 2020 09:49:53 +0200 Subject: [PATCH 41/89] update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index b7bc4bf..2c66671 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 * Add `--non-interactive` command line option * Add `--replace-existing` command line option * Copy files if destination exists +* `cert-expirations.sh` script to print out certificates expiration dates ### Changed From 75e36189c5a1ce46163d08834fe316b8abddf375 Mon Sep 17 00:00:00 2001 From: Jeremy Lecour Date: Mon, 12 Oct 2020 23:27:05 +0200 Subject: [PATCH 42/89] "shellpki init" can be executed interactively or not --- shellpki | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/shellpki b/shellpki index 63d80df..76ddda3 100755 --- a/shellpki +++ b/shellpki @@ -37,6 +37,33 @@ init() { [ -f "${CRL}" ] || touch "${CRL}" [ -f "${SERIAL}" ] || echo "01" > "${SERIAL}" + non_interactive=0 + + # Parse options + # based on https://gist.github.com/deshion/10d3cb5f88a21671e17a + while :; do + case $1 in + --non-interactive) + non_interactive=1 + ;; + --) + # End of all options. + shift + break + ;; + -?*) + # ignore unknown options + warning "Warning: unknown option (ignored): \`$1'" + ;; + *) + # Default case: If no more options then break out of the loop. + break + ;; + esac + + shift + done + cn="${1:-}" if [ -z "${cn}" ]; then show_usage >&2 From c83f2103879f83d942e7963dcc7ff56701525dc7 Mon Sep 17 00:00:00 2001 From: Jeremy Lecour Date: Mon, 12 Oct 2020 23:27:24 +0200 Subject: [PATCH 43/89] default values for variables in tests --- shellpki | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/shellpki b/shellpki index 76ddda3..d7d966a 100755 --- a/shellpki +++ b/shellpki @@ -458,7 +458,7 @@ create() { if [ "${ask_pass}" -eq 1 ]; then warning "Warning: -p|--password is ignored with -f|--file|--crt-file" fi - if [ -n "${password_file}" ]; then + if [ -n "${password_file:-}" ]; then warning "Warning: --password-file is ignored with -f|--file|--crt-file" fi @@ -539,9 +539,9 @@ create() { # generate private key pass_args="" - if [ -n "${password_file}" ]; then + if [ -n "${password_file:-}" ]; then pass_args="-aes256 -passout file:${password_file}" - elif [ -n "${PASSWORD}" ]; then + elif [ -n "${PASSWORD:-}" ]; then pass_args="-aes256 -passout pass:${PASSWORD}" fi "${OPENSSL_BIN}" genrsa \ @@ -557,9 +557,9 @@ create() { # generate csr req pass_args="" - if [ -n "${password_file}" ]; then + if [ -n "${password_file:-}" ]; then pass_args="-passin file:${password_file}" - elif [ -n "${PASSWORD}" ]; then + elif [ -n "${PASSWORD:-}" ]; then pass_args="-passin pass:${PASSWORD}" fi "${OPENSSL_BIN}" req \ @@ -607,14 +607,14 @@ EOF # generate pkcs12 format pass_args="" - if [ -n "${password_file}" ]; then + if [ -n "${password_file:-}" ]; then # Hack for pkcs12 : # If passin and passout files are the same path, it expects 2 lines # so we make a temporary copy of the password file password_file_out=$(mktemp) cp "${password_file}" "${password_file_out}" pass_args="-passin file:${password_file} -passout file:${password_file_out}" - elif [ -n "${PASSWORD}" ]; then + elif [ -n "${PASSWORD:-}" ]; then pass_args="-passin pass:${PASSWORD} -passout pass:${PASSWORD}" else pass_args="-passout pass:" @@ -630,7 +630,7 @@ EOF error "Error generating the pkcs12 file" fi - if [ -n "${password_file_out}" ]; then + if [ -n "${password_file_out:-}" ]; then # Hack for pkcs12 : # Destroy the temporary file rm -f "${password_file_out}" From 83d0ef24498972fc4e4b0af7209673029740bd4d Mon Sep 17 00:00:00 2001 From: Jeremy Lecour Date: Mon, 12 Oct 2020 23:38:32 +0200 Subject: [PATCH 44/89] "shellpki revoke" can be run interactively or not --- shellpki | 37 ++++++++++++++++++++++++++++++++----- 1 file changed, 32 insertions(+), 5 deletions(-) diff --git a/shellpki b/shellpki index d7d966a..89304e6 100755 --- a/shellpki +++ b/shellpki @@ -688,15 +688,42 @@ EOF } revoke() { - if [ "${1}" = "" ]; then + non_interactive=0 + + # Parse options + # based on https://gist.github.com/deshion/10d3cb5f88a21671e17a + while :; do + case $1 in + --non-interactive) + non_interactive=1 + ;; + --) + # End of all options. + shift + break + ;; + -?*) + # ignore unknown options + warning "Warning: unknown option (ignored): \`$1'" + ;; + *) + # Default case: If no more options then break out of the loop. + break + ;; + esac + + shift + done + + # The name of the certificate + cn="${1:-}" + + if [ -z "${cn}" ]; then show_usage >&2 exit 1 fi + crt_file="${CRT_DIR}/${cn}.crt" - - # get CN from param - cn="${1}" - # check if CRT exists if [ ! -f "${crt_file}" ]; then error "Unknow CN : ${cn} (\`${crt_file}' not found)" From 9f3b0a4cd4ca076891854d0fbc6f3589370e0082 Mon Sep 17 00:00:00 2001 From: Jeremy Lecour Date: Mon, 12 Oct 2020 23:49:45 +0200 Subject: [PATCH 45/89] list: better options parsing --- shellpki | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/shellpki b/shellpki index 89304e6..f04e552 100755 --- a/shellpki +++ b/shellpki @@ -215,7 +215,7 @@ Revoke a client cert with is commonName (CN) : List all actually valid commonName (CN) : - ${0} list [-a|v|r] + ${0} list [-a|--all|-v|--valid|-r|--revoked] Check expiration date of valid certificates : @@ -763,6 +763,11 @@ list() { exit 0 fi + if [ -z "${1}" ]; then + show_usage >&2 + exit 1 + fi + list_valid=0 list_revoked=1 From ff7737e733b4f9d00b0b59a897ba81523ccf51a3 Mon Sep 17 00:00:00 2001 From: Jeremy Dubois Date: Fri, 6 Nov 2020 10:14:03 +0100 Subject: [PATCH 46/89] Add backup carp check to cert-expirations.sh --- cert-expirations.sh | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/cert-expirations.sh b/cert-expirations.sh index a3081a8..7ae444f 100644 --- a/cert-expirations.sh +++ b/cert-expirations.sh @@ -1,5 +1,12 @@ #!/bin/sh +carp=$(/sbin/ifconfig carp0 2>/dev/null | grep 'status' | cut -d' ' -f2) + +if [ "$carp" = "backup" ]; then + exit 0 +fi + + echo "CA certificate:" openssl x509 -enddate -noout -in /etc/shellpki/cacert.pem \ | cut -d '=' -f 2 \ From 9deb73b54884198fb5cac777ec231e4d346e4dd3 Mon Sep 17 00:00:00 2001 From: Jeremy Dubois Date: Fri, 6 Nov 2020 10:53:00 +0100 Subject: [PATCH 47/89] cert-expirations.sh => certificates names can contain "@" in it --- cert-expirations.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cert-expirations.sh b/cert-expirations.sh index 7ae444f..7325ed7 100644 --- a/cert-expirations.sh +++ b/cert-expirations.sh @@ -16,7 +16,7 @@ echo "" echo "Client certificates:" grep "Not After" -r /etc/shellpki/certs/ \ - | sed -e "s/^.*certs\/\([-.a-z0-9]*\).*After\ :\ \(.*\).*GMT$/\2\1X/" \ + | sed -e "s/^.*certs\/\([-.@a-z0-9]*\).*After\ :\ \(.*\).*GMT$/\2\1X/" \ | sed -e "s/^\(.*\)\ \(20..\)\ \(.*\)$/- \2 \1 \3/" \ | tr "X" "\n" \ | sed '/^$/d' \ From 847694339c948aa6e28f499af901c1bec4934563 Mon Sep 17 00:00:00 2001 From: Jeremy Dubois Date: Fri, 6 Nov 2020 11:19:38 +0100 Subject: [PATCH 48/89] cert-expirations.sh => certificates names can contain "_" in it --- cert-expirations.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cert-expirations.sh b/cert-expirations.sh index 7325ed7..ca4d0d8 100644 --- a/cert-expirations.sh +++ b/cert-expirations.sh @@ -16,7 +16,7 @@ echo "" echo "Client certificates:" grep "Not After" -r /etc/shellpki/certs/ \ - | sed -e "s/^.*certs\/\([-.@a-z0-9]*\).*After\ :\ \(.*\).*GMT$/\2\1X/" \ + | sed -e "s/^.*certs\/\([-._@a-z0-9]*\).*After\ :\ \(.*\).*GMT$/\2\1X/" \ | sed -e "s/^\(.*\)\ \(20..\)\ \(.*\)$/- \2 \1 \3/" \ | tr "X" "\n" \ | sed '/^$/d' \ From 0bf2bfe60c321836e26c246c1d53305b111f486f Mon Sep 17 00:00:00 2001 From: Jeremy Dubois Date: Mon, 8 Feb 2021 15:36:31 +0100 Subject: [PATCH 49/89] cert-expirations.sh : warning about UTC hours --- cert-expirations.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/cert-expirations.sh b/cert-expirations.sh index ca4d0d8..773d820 100644 --- a/cert-expirations.sh +++ b/cert-expirations.sh @@ -6,6 +6,7 @@ if [ "$carp" = "backup" ]; then exit 0 fi +echo "Warning : all times are in UTC !\n" echo "CA certificate:" openssl x509 -enddate -noout -in /etc/shellpki/cacert.pem \ From fb22db8dacf2dca0006b380bba000f96064666c0 Mon Sep 17 00:00:00 2001 From: Jeremy Dubois Date: Tue, 2 Mar 2021 10:08:32 +0100 Subject: [PATCH 50/89] cert-expirations.sh => certificates names can contain upper case characters --- cert-expirations.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cert-expirations.sh b/cert-expirations.sh index 773d820..c59eea8 100644 --- a/cert-expirations.sh +++ b/cert-expirations.sh @@ -17,7 +17,7 @@ echo "" echo "Client certificates:" grep "Not After" -r /etc/shellpki/certs/ \ - | sed -e "s/^.*certs\/\([-._@a-z0-9]*\).*After\ :\ \(.*\).*GMT$/\2\1X/" \ + | sed -e "s/^.*certs\/\([-._@a-zA-Z0-9]*\).*After\ :\ \(.*\).*GMT$/\2\1X/" \ | sed -e "s/^\(.*\)\ \(20..\)\ \(.*\)$/- \2 \1 \3/" \ | tr "X" "\n" \ | sed '/^$/d' \ From 92ee8452079ce3a11c16cd36faaf325704dbaf2b Mon Sep 17 00:00:00 2001 From: Jeremy Dubois Date: Mon, 14 Jun 2021 14:30:34 +0200 Subject: [PATCH 51/89] New script cn-validation.sh for OpenVPN --- cn-validation.sh | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 cn-validation.sh diff --git a/cn-validation.sh b/cn-validation.sh new file mode 100644 index 0000000..f6710b8 --- /dev/null +++ b/cn-validation.sh @@ -0,0 +1,21 @@ +#!/bin/sh +# +# cn-validation.sh is a client-connect script for OpenVPN server +# When connecting using the PAM plugin, it allow clients to connect only if their CN is equal to their UNIX username +# +# You need this parameters in your's server config : +# +# script-security 2 +# client-connect /cn-validation.sh +# + +set -u + +if [ "${common_name}" = "${username}" ]; then + logger -i -t openvpn-cn-validation -p auth.info "Accepted login for ${common_name} from ${trusted_ip} port ${trusted_port}" + exit 0 +else + logger -i -t openvpn-cn-validation -p auth.notice "Failed login for CN ${common_name} / username ${username} from ${trusted_ip} port ${trusted_port}" +fi + +exit 1 From 4b2b8a95ff3fd4b46f74d52d7538e978eacbcce7 Mon Sep 17 00:00:00 2001 From: Jeremy Dubois Date: Fri, 18 Feb 2022 11:45:12 +0100 Subject: [PATCH 52/89] cert-expirations.sh: search for valid certificates in the index file rather than in a directory where files could be deleted with the certificates still being valids --- cert-expirations.sh | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/cert-expirations.sh b/cert-expirations.sh index c59eea8..9e27dcc 100644 --- a/cert-expirations.sh +++ b/cert-expirations.sh @@ -16,9 +16,11 @@ openssl x509 -enddate -noout -in /etc/shellpki/cacert.pem \ echo "" echo "Client certificates:" -grep "Not After" -r /etc/shellpki/certs/ \ - | sed -e "s/^.*certs\/\([-._@a-zA-Z0-9]*\).*After\ :\ \(.*\).*GMT$/\2\1X/" \ - | sed -e "s/^\(.*\)\ \(20..\)\ \(.*\)$/- \2 \1 \3/" \ - | tr "X" "\n" \ - | sed '/^$/d' \ +cat /etc/shellpki/index.txt \ + | grep ^V \ + | awk -F "/" '{print $1,$5}' \ + | awk '{print $2,$5}' \ + | sed 's/CN=//' \ + | sed -E 's/([[:digit:]]{2})([[:digit:]]{2})([[:digit:]]{2})([[:digit:]]{2})([[:digit:]]{2})([[:digit:]]{2})Z (.*)/- 20\1 \2 \3 \4:\5:\6 \7/' \ + | awk '{if ($3 == "01") $3="Jan"; else if ($3 == "02") $3="Feb"; else if ($3 == "03") $3="Mar"; else if ($3 == "04") $3="Apr"; else if ($3 == "05") $3="May"; else if ($3 == "06") $3="Jun"; else if ($3 == "07") $3="Jul"; else if ($3 == "08") $3="Aug"; else if ($3 == "09") $3="Sep"; else if ($3 == "10") $3="Oct"; else if ($3 == "11") $3="Nov"; else if ($3 == "12") $3="Dec"; print $0;}' \ | sort -n -k 2 -k 3M -k 4 From 593cf4a9f36b26ef8f00046336152d359ca356e2 Mon Sep 17 00:00:00 2001 From: Jeremy Lecour Date: Fri, 11 Mar 2022 11:36:20 +0100 Subject: [PATCH 53/89] show usage if list has no argument, instead of "set -u" error --- shellpki | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/shellpki b/shellpki index f04e552..e6b9b00 100755 --- a/shellpki +++ b/shellpki @@ -763,7 +763,7 @@ list() { exit 0 fi - if [ -z "${1}" ]; then + if [ -z "${1:-}" ]; then show_usage >&2 exit 1 fi @@ -772,7 +772,7 @@ list() { list_revoked=1 while :; do - case "${1}" in + case "${1:-}" in -a|--all) list_valid=0 list_revoked=0 From 41d0ca261dcf12732553aaca6c2a2e0cc99b93c9 Mon Sep 17 00:00:00 2001 From: Jeremy Lecour Date: Fri, 11 Mar 2022 11:38:01 +0100 Subject: [PATCH 54/89] extract get_real_path function to normalize readlink arguments --- shellpki | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/shellpki b/shellpki index e6b9b00..16423f1 100755 --- a/shellpki +++ b/shellpki @@ -239,6 +239,11 @@ verify_ca_password() { -passin pass:${CA_PASSWORD} \ >/dev/null 2>&1 } +get_real_path() { + # --canonicalize is supported on Linux + # -f is supported on Linux and OpenBSD + readlink -f -- "${1}" +} ask_ca_password() { attempt=${1:-0} @@ -327,7 +332,7 @@ create() { # csr-file option, with value separated by space if [ -n "$2" ]; then from_csr=1 - csr_file=$(readlink --canonicalize -- "${2}") + csr_file=$(get_real_path "${2}") if [ "$?" -ne 0 ]; then error "Error accessing file \`${2}'" fi @@ -339,7 +344,7 @@ create() { --file=?*|--csr-file=?*) from_csr=1 # csr-file option, with value separated by = - csr_file=$(readlink --canonicalize -- "${1#*=}") + csr_file=$(get_real_path "${1#*=}") if [ "$?" -ne 0 ]; then error "Error accessing file \`${1#*=}'" fi @@ -354,7 +359,7 @@ create() { --password-file) # password-file option, with value separated by space if [ -n "$2" ]; then - password_file=$(readlink --canonicalize -- "${2}") + password_file=$(get_real_path "${2}") if [ "$?" -ne 0 ]; then error "Error accessing file \`${2}'" fi @@ -365,7 +370,7 @@ create() { ;; --password-file=?*) # password-file option, with value separated by = - password_file=$(readlink --canonicalize -- "${1#*=}") + password_file=$(get_real_path "${1#*=}") if [ "$?" -ne 0 ]; then error "Error accessing file \`${1#*=}'" fi From 68e4648694cb15f5da24b36c3c87058f930bbf9f Mon Sep 17 00:00:00 2001 From: Jeremy Lecour Date: Fri, 11 Mar 2022 11:44:09 +0100 Subject: [PATCH 55/89] fix shellcheck violations --- shellpki | 41 +++++++++++++++++++++++++++++++---------- 1 file changed, 31 insertions(+), 10 deletions(-) diff --git a/shellpki b/shellpki index 16423f1..885de70 100755 --- a/shellpki +++ b/shellpki @@ -83,8 +83,9 @@ init() { "${OPENSSL_BIN}" genrsa \ -out "${CA_KEY}" \ -aes256 \ - ${CA_KEY_LENGTH} \ + "${CA_KEY_LENGTH}" \ >/dev/null 2>&1 + # shellcheck disable=SC2181 if [ "$?" -ne 0 ]; then error "Error generating the CA key" fi @@ -111,13 +112,14 @@ init() { -x509 \ -days 3650 \ -extensions v3_ca \ - -passin pass:${CA_PASSWORD} \ + -passin pass:"${CA_PASSWORD}" \ -key "${CA_KEY}" \ -out "${CA_CERT}" \ -config /dev/stdin </dev/null 2>&1 + # shellcheck disable=SC2181 if [ "$?" -ne 0 ]; then error "Error generating the OCSP key" fi @@ -158,6 +161,7 @@ commonName_default = ${url} [ usr_cert ] authorityInfoAccess = OCSP;URI:http://${ocsp_uri} EOF + # shellcheck disable=SC2181 if [ "$?" -ne 0 ]; then error "Error generating the OCSP request" fi @@ -171,8 +175,9 @@ EOF -extensions v3_ocsp \ -in "${ocsp_csr_file}" \ -out "${OCSP_CERT}" \ - -passin pass:${CA_PASSWORD} \ + -passin pass:"${CA_PASSWORD}" \ -config "${CONF_FILE}" + # shellcheck disable=SC2181 if [ "$?" -ne 0 ]; then error "Error generating the OCSP certificate" fi @@ -236,7 +241,7 @@ warning() { verify_ca_password() { "${OPENSSL_BIN}" rsa \ -in "${CA_KEY}" \ - -passin pass:${CA_PASSWORD} \ + -passin pass:"${CA_PASSWORD}" \ >/dev/null 2>&1 } get_real_path() { @@ -333,6 +338,7 @@ create() { if [ -n "$2" ]; then from_csr=1 csr_file=$(get_real_path "${2}") + # shellcheck disable=SC2181 if [ "$?" -ne 0 ]; then error "Error accessing file \`${2}'" fi @@ -345,6 +351,7 @@ create() { from_csr=1 # csr-file option, with value separated by = csr_file=$(get_real_path "${1#*=}") + # shellcheck disable=SC2181 if [ "$?" -ne 0 ]; then error "Error accessing file \`${1#*=}'" fi @@ -360,6 +367,7 @@ create() { # password-file option, with value separated by space if [ -n "$2" ]; then password_file=$(get_real_path "${2}") + # shellcheck disable=SC2181 if [ "$?" -ne 0 ]; then error "Error accessing file \`${2}'" fi @@ -371,6 +379,7 @@ create() { --password-file=?*) # password-file option, with value separated by = password_file=$(get_real_path "${1#*=}") + # shellcheck disable=SC2181 if [ "$?" -ne 0 ]; then error "Error accessing file \`${1#*=}'" fi @@ -447,6 +456,7 @@ create() { fi if [ -n "${end_date}" ]; then cert_end_date=$(TZ=:Zulu date --date "${end_date}" +"%Y%m%d%H%M%SZ" 2> /dev/null) + # shellcheck disable=SC2181 if [ "$?" -ne 0 ]; then error "Invalid end date format : \`${end_date}' can't be parsed by date(1)" else @@ -478,6 +488,7 @@ create() { -subject \ -in "${csr_file}" \ >/dev/null 2>&1 + # shellcheck disable=SC2181 if [ "$?" -ne 0 ]; then error "${csr_file} is not a valid CSR !" fi @@ -489,6 +500,7 @@ create() { -in "${csr_file}" \ | grep -Eo "CN\s*=[^,/]*" \ >/dev/null 2>&1 + # shellcheck disable=SC2181 if [ "$?" -ne 0 ]; then error "${csr_file} doesn't contain a CommonName !" fi @@ -511,9 +523,10 @@ create() { ${batch_arg} \ -config "${CONF_FILE}" \ -in "${csr_file}" \ - -passin pass:${CA_PASSWORD} \ + -passin pass:"${CA_PASSWORD}" \ -out "${crt_file}" \ ${crt_expiration_arg} + # shellcheck disable=SC2181 if [ "$?" -ne 0 ]; then error "Error generating the certificate" else @@ -552,8 +565,9 @@ create() { "${OPENSSL_BIN}" genrsa \ -out "${key_file}" \ ${pass_args} \ - ${KEY_LENGTH} \ + "${KEY_LENGTH}" \ >/dev/null 2>&1 + # shellcheck disable=SC2181 if [ "$?" -eq 0 ]; then echo "The KEY file is available at \`${key_file}'" else @@ -577,6 +591,7 @@ create() { $(cat "${CONF_FILE}") commonName_default = ${cn} EOF + # shellcheck disable=SC2181 if [ "$?" -ne 0 ]; then error "Error generating the CSR" fi @@ -589,6 +604,7 @@ EOF -in "${csr_file}" \ -out "${crt_file}" \ ${crt_expiration_arg} + # shellcheck disable=SC2181 if [ "$?" -ne 0 ]; then error "Error generating the certificate" fi @@ -599,6 +615,7 @@ EOF -subject \ -in "${crt_file}" \ >/dev/null 2>&1 + # shellcheck disable=SC2181 if [ "$?" -ne 0 ]; then rm -f "${crt_file}" fi @@ -631,6 +648,7 @@ EOF -in "${crt_file}" \ -out "${pkcs12_file}" \ ${pass_args} + # shellcheck disable=SC2181 if [ "$?" -ne 0 ]; then error "Error generating the pkcs12 file" fi @@ -686,6 +704,7 @@ EOF fi done + # shellcheck disable=SC2086 chown -R ${PKI_USER}:${PKI_USER} "${COPY_DIR}/" chmod -R u=rwX,g=rwX,o= "${COPY_DIR}/" fi @@ -740,6 +759,7 @@ revoke() { -subject \ -in "${crt_file}" \ >/dev/null 2>&1 + # shellcheck disable=SC2181 if [ "$?" -ne 0 ]; then error "${crt_file} is not a valid CRT, you must delete it !" fi @@ -750,15 +770,16 @@ revoke() { echo "Revoke certificate ${crt_file} :" "${OPENSSL_BIN}" ca \ -config "${CONF_FILE}" \ - -passin pass:${CA_PASSWORD} \ + -passin pass:"${CA_PASSWORD}" \ -revoke "${crt_file}" + # shellcheck disable=SC2181 if [ "$?" -eq 0 ]; then rm "${crt_file}" fi "${OPENSSL_BIN}" ca \ -config "${CONF_FILE}" \ - -passin pass:${CA_PASSWORD} \ + -passin pass:"${CA_PASSWORD}" \ -gencrl \ -out "${CRL}" } @@ -826,7 +847,7 @@ check() { min_day=90 cur_epoch=$(date -u +'%s') - for cert in ${CRT_DIR}/*; do + for cert in "${CRT_DIR}"/*; do end_date=$(cert_end_date "${cert}") end_epoch=$(date -ud "${end_date}" +'%s') diff_epoch=$(( end_epoch - cur_epoch )) From 6cc29fb1f837ec5a7a7e6a6280a748b48ad87649 Mon Sep 17 00:00:00 2001 From: Jeremy Lecour Date: Fri, 11 Mar 2022 14:09:58 +0100 Subject: [PATCH 56/89] reorder functions --- shellpki | 257 ++++++++++++++++++++++++++++--------------------------- 1 file changed, 129 insertions(+), 128 deletions(-) diff --git a/shellpki b/shellpki index 885de70..f8326e2 100755 --- a/shellpki +++ b/shellpki @@ -28,6 +28,135 @@ See the MIT Licence for details. END } +show_usage() { + cat < [options] [CommonName] + +Initialize PKI (create CA key and self-signed cert) : + + ${0} init + +Run OCSP_D server : + + ${0} ocsp + +Create a client cert with key and CSR directly generated on server +(use -p or --password-file to set a password on the client key) : + + ${0} create [-p|--password-file=] + +Create a client cert from a CSR (doesn't need key) : + + ${0} create -f + +Revoke a client cert with is commonName (CN) : + + ${0} revoke + +List all actually valid commonName (CN) : + + ${0} list [-a|--all|-v|--valid|-r|--revoked] + +Check expiration date of valid certificates : + + ${0} check + +EOF +} + +error() { + echo "${1}" >&2 + exit 1 +} + +warning() { + echo "${1}" >&2 +} + +verify_ca_password() { + "${OPENSSL_BIN}" rsa \ + -in "${CA_KEY}" \ + -passin pass:"${CA_PASSWORD}" \ + >/dev/null 2>&1 +} +get_real_path() { + # --canonicalize is supported on Linux + # -f is supported on Linux and OpenBSD + readlink -f -- "${1}" +} + +ask_ca_password() { + attempt=${1:-0} + max_attempts=3 + + trap 'unset CA_PASSWORD' 0 + + if [ ! -f "${CA_KEY}" ]; then + error "You must initialize your PKI with \`shellpki init' !" + fi + if [ "${attempt}" -gt 0 ]; then + warning "Invalid password, retry." + fi + if [ "${attempt}" -ge "${max_attempts}" ]; then + error "Maximum number of attempts reached (${max_attempts})." + fi + if [ -z "${CA_PASSWORD:-}" ]; then + if [ "${non_interactive}" -eq 1 ]; then + error "In non-interactive mode, you must pass CA_PASSWORD as environment variable" + fi + stty -echo + printf "Password for CA key: " + read -r CA_PASSWORD + stty echo + printf "\n" + fi + if [ -z "${CA_PASSWORD:-}" ] || ! verify_ca_password; then + unset CA_PASSWORD + attempt=$(( attempt + 1 )) + ask_ca_password "${attempt}" + fi +} +ask_user_password() { + trap 'unset PASSWORD' 0 + + if [ -z "${PASSWORD:-}" ]; then + if [ "${non_interactive}" -eq 1 ]; then + error "In non-interactive mode, you must pass PASSWORD as environment variable or use --password-file" + fi + stty -echo + printf "Password for user key: " + read -r PASSWORD + stty echo + printf "\n" + fi + if [ -z "${PASSWORD:-}" ]; then + warning "Warning: empty password from input" + fi +} +replace_existing_or_abort() { + cn=${1:?} + if [ "${non_interactive}" -eq 1 ]; then + if [ "${replace_existing}" -eq 1 ]; then + resp="y" + else + error "${cn} already exists, use \`--replace-existing' to force" + fi + else + if [ "${replace_existing}" -eq 1 ]; then + resp="y" + else + printf "%s already exists, do you want to revoke and recreate it ? [y/N] " "${cn}" + read -r REPLY + resp=$(echo "${REPLY}" | tr 'Y' 'y') + fi + fi + if [ "${resp}" = "y" ]; then + revoke "${cn}" + else + error "Aborted" + fi +} + init() { umask 0177 @@ -193,134 +322,6 @@ EOF -text } -show_usage() { - cat < [options] [CommonName] - -Initialize PKI (create CA key and self-signed cert) : - - ${0} init - -Run OCSP_D server : - - ${0} ocsp - -Create a client cert with key and CSR directly generated on server -(use -p or --password-file to set a password on the client key) : - - ${0} create [-p|--password-file=] - -Create a client cert from a CSR (doesn't need key) : - - ${0} create -f - -Revoke a client cert with is commonName (CN) : - - ${0} revoke - -List all actually valid commonName (CN) : - - ${0} list [-a|--all|-v|--valid|-r|--revoked] - -Check expiration date of valid certificates : - - ${0} check - -EOF -} - -error() { - echo "${1}" >&2 - exit 1 -} - -warning() { - echo "${1}" >&2 -} - -verify_ca_password() { - "${OPENSSL_BIN}" rsa \ - -in "${CA_KEY}" \ - -passin pass:"${CA_PASSWORD}" \ - >/dev/null 2>&1 -} -get_real_path() { - # --canonicalize is supported on Linux - # -f is supported on Linux and OpenBSD - readlink -f -- "${1}" -} - -ask_ca_password() { - attempt=${1:-0} - max_attempts=3 - - trap 'unset CA_PASSWORD' 0 - - if [ ! -f "${CA_KEY}" ]; then - error "You must initialize your PKI with \`shellpki init' !" - fi - if [ "${attempt}" -gt 0 ]; then - warning "Invalid password, retry." - fi - if [ "${attempt}" -ge "${max_attempts}" ]; then - error "Maximum number of attempts reached (${max_attempts})." - fi - if [ -z "${CA_PASSWORD:-}" ]; then - if [ "${non_interactive}" -eq 1 ]; then - error "In non-interactive mode, you must pass CA_PASSWORD as environment variable" - fi - stty -echo - printf "Password for CA key: " - read -r CA_PASSWORD - stty echo - printf "\n" - fi - if [ -z "${CA_PASSWORD:-}" ] || ! verify_ca_password; then - unset CA_PASSWORD - attempt=$(( attempt + 1 )) - ask_ca_password "${attempt}" - fi -} -ask_user_password() { - trap 'unset PASSWORD' 0 - - if [ -z "${PASSWORD:-}" ]; then - if [ "${non_interactive}" -eq 1 ]; then - error "In non-interactive mode, you must pass PASSWORD as environment variable or use --password-file" - fi - stty -echo - printf "Password for user key: " - read -r PASSWORD - stty echo - printf "\n" - fi - if [ -z "${PASSWORD:-}" ]; then - warning "Warning: empty password from input" - fi -} -replace_existing_or_abort() { - cn=${1:?} - if [ "${non_interactive}" -eq 1 ]; then - if [ "${replace_existing}" -eq 1 ]; then - resp="y" - else - error "${cn} already exists, use \`--replace-existing' to force" - fi - else - if [ "${replace_existing}" -eq 1 ]; then - resp="y" - else - printf "%s already exists, do you want to revoke and recreate it ? [y/N] " "${cn}" - read -r REPLY - resp=$(echo "${REPLY}" | tr 'Y' 'y') - fi - fi - if [ "${resp}" = "y" ]; then - revoke "${cn}" - else - error "Aborted" - fi -} create() { from_csr=0 ask_pass=0 From 10edbb19fa9fbbb9cb9a9c7de6503a95b88f7652 Mon Sep 17 00:00:00 2001 From: Jeremy Lecour Date: Fri, 11 Mar 2022 14:10:32 +0100 Subject: [PATCH 57/89] init can be "non-interactive" --- shellpki | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/shellpki b/shellpki index f8326e2..bcc9551 100755 --- a/shellpki +++ b/shellpki @@ -171,7 +171,7 @@ init() { # Parse options # based on https://gist.github.com/deshion/10d3cb5f88a21671e17a while :; do - case $1 in + case ${1:-} in --non-interactive) non_interactive=1 ;; @@ -200,17 +200,27 @@ init() { fi if [ -f "${CA_KEY}" ]; then - printf "%s already exists, do you really want to erase it ? [y/N] " "${CA_KEY}" - read -r REPLY - resp=$(echo "${REPLY}" | tr 'Y' 'y') - if [ "${resp}" = "y" ]; then - rm -f "${CA_KEY}" "${CA_CERT}" + if [ "${non_interactive}" -eq 1 ]; then + error "${CA_KEY} already exists, erase it manually if you want to start over." + else + printf "%s already exists, do you really want to erase it ? [y/N] " "${CA_KEY}" + read -r REPLY + resp=$(echo "${REPLY}" | tr 'Y' 'y') + if [ "${resp}" = "y" ]; then + rm -f "${CA_KEY}" "${CA_CERT}" + fi fi fi + passout_arg="" + if [ -n "${CA_PASSWORD:-}" ]; then + passout_arg="-passout pass:${CA_PASSWORD}" + fi + if [ ! -f "${CA_KEY}" ]; then "${OPENSSL_BIN}" genrsa \ -out "${CA_KEY}" \ + ${passout_arg} \ -aes256 \ "${CA_KEY_LENGTH}" \ >/dev/null 2>&1 From 4bb24707b01e8e9ccff7e080a97410712e8c7ab4 Mon Sep 17 00:00:00 2001 From: Jeremy Lecour Date: Fri, 11 Mar 2022 14:10:53 +0100 Subject: [PATCH 58/89] simplify "list" options parsing --- shellpki | 6 ------ 1 file changed, 6 deletions(-) diff --git a/shellpki b/shellpki index bcc9551..99e4d99 100755 --- a/shellpki +++ b/shellpki @@ -805,9 +805,6 @@ list() { exit 1 fi - list_valid=0 - list_revoked=1 - while :; do case "${1:-}" in -a|--all) @@ -822,9 +819,6 @@ list() { list_valid=1 list_revoked=0 shift;; - --) - shift - break;; -?*) warning "unknow option ${1} (ignored)" shift;; From e8ced039889e697ac386d2110c1bd6eba2962145 Mon Sep 17 00:00:00 2001 From: Jeremy Lecour Date: Fri, 11 Mar 2022 14:12:27 +0100 Subject: [PATCH 59/89] add .ovpn example --- ovpn.conf.example | 54 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) create mode 100644 ovpn.conf.example diff --git a/ovpn.conf.example b/ovpn.conf.example new file mode 100644 index 0000000..2115b86 --- /dev/null +++ b/ovpn.conf.example @@ -0,0 +1,54 @@ +# +# General settings +# + +user nobody +group nogroup + +# Do not try to re-read key file and reopen tun device on restart since it runs +# without root privileges. +persist-key +persist-tun +#persist-remote-ip +#persist-local-ip + +# Status file +status /var/log/openvpn/status.log 1 +#log /var/log/openvpn/openvpn.log +# Logging verbosity. Logs are sent to syslog. +verb 3 + +# Keepalive +keepalive 10 120 +#reneg-sec 300 + +# +# Network settings +# + +port 1194 +proto udp +dev tun + +# Enable compression +# comp-lzo +# compress lzo (OpenVPN 2.4+) + +# +# key/certificate +# + +### ca /etc/openvpn/ssl/ca/cacert.pem +### cert /etc/openvpn/ssl/files/fw.vpn.example.com-1278421834/fw.vpn.example.com.crt +### key /etc/openvpn/ssl/files/fw.vpn.example.com-1278421834/fw.vpn.example.com.key +dh /etc/openvpn/ssl/ca/dh2048.pem + +# +# private network +# + +server 192.0.2.0 255.255.0.0 +mode server + +# Management interface (used by check_openvpn for Nagios) +management 127.0.0.1 1195 /etc/openvpn/management-pwd From af24b1469d80b8164108fbc2c098dfc0fe2ceb68 Mon Sep 17 00:00:00 2001 From: Jeremy Dubois Date: Mon, 14 Mar 2022 10:55:06 +0100 Subject: [PATCH 60/89] Add nobind option to client config --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index bba6a18..f5477c1 100644 --- a/README.md +++ b/README.md @@ -49,6 +49,7 @@ proto udp remote ovpn.example.com 1194 +nobind persist-key persist-tun From c92f7a5a7e0032a0c868eef16e418217ce5858d4 Mon Sep 17 00:00:00 2001 From: Jeremy Dubois Date: Mon, 14 Mar 2022 10:55:28 +0100 Subject: [PATCH 61/89] Change ovpn example file to match the openvpn ansible role and wiki --- ovpn.conf.example | 60 ++++++++++++++++------------------------------- 1 file changed, 20 insertions(+), 40 deletions(-) diff --git a/ovpn.conf.example b/ovpn.conf.example index 2115b86..645c832 100644 --- a/ovpn.conf.example +++ b/ovpn.conf.example @@ -1,54 +1,34 @@ -# -# General settings -# - user nobody group nogroup -# Do not try to re-read key file and reopen tun device on restart since it runs -# without root privileges. +local 198.51.100.1 +port 1194 +proto udp +dev tun +mode server +keepalive 10 120 +tls-exit + +cipher AES-256-GCM # AES + persist-key persist-tun -#persist-remote-ip -#persist-local-ip -# Status file -status /var/log/openvpn/status.log 1 -#log /var/log/openvpn/openvpn.log -# Logging verbosity. Logs are sent to syslog. -verb 3 +ifconfig-pool-persist /etc/openvpn/ipp.txt -# Keepalive -keepalive 10 120 -#reneg-sec 300 +status /var/log/openvpn-status.log +log-append /var/log/openvpn.log -# -# Network settings -# +ca /etc/shellpki/cacert.pem +#cert /etc/shellpki/certs/fw.vpn.example.com.crt +#key /etc/shellpki/private/fw.vpn.example.com-1621504035.key +dh /etc/shellpki/dh2048.pem -port 1194 -proto udp -dev tun +crl-verify /etc/shellpki/crl.pem -# Enable compression -# comp-lzo -# compress lzo (OpenVPN 2.4+) +server 192.0.2.0 255.255.255.0 -# -# key/certificate -# - -### ca /etc/openvpn/ssl/ca/cacert.pem -### cert /etc/openvpn/ssl/files/fw.vpn.example.com-1278421834/fw.vpn.example.com.crt -### key /etc/openvpn/ssl/files/fw.vpn.example.com-1278421834/fw.vpn.example.com.key -dh /etc/openvpn/ssl/ca/dh2048.pem - -# -# private network -# - -server 192.0.2.0 255.255.0.0 -mode server +#push "route 192.0.3.0 255.255.255.0" # Management interface (used by check_openvpn for Nagios) management 127.0.0.1 1195 /etc/openvpn/management-pwd From 69db5a80aae3a64d9bbca03241a93ff2fb3e111d Mon Sep 17 00:00:00 2001 From: Jeremy Dubois Date: Mon, 14 Mar 2022 11:03:36 +0100 Subject: [PATCH 62/89] More conventional "list" parsing --- shellpki | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/shellpki b/shellpki index 99e4d99..5591c9c 100755 --- a/shellpki +++ b/shellpki @@ -810,21 +810,23 @@ list() { -a|--all) list_valid=0 list_revoked=0 - shift;; + ;; -v|--valid) list_valid=0 list_revoked=1 - shift;; + ;; -r|--revoked) list_valid=1 list_revoked=0 - shift;; + ;; -?*) warning "unknow option ${1} (ignored)" - shift;; + ;; *) - break;; + break + ;; esac + shift done if [ "${list_valid}" -eq 0 ]; then From d48dc132be426daafb79874561627cd835679342 Mon Sep 17 00:00:00 2001 From: Jeremy Lecour Date: Mon, 14 Mar 2022 14:40:42 +0100 Subject: [PATCH 63/89] fix replace-existing and non-interactive confict --- shellpki | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/shellpki b/shellpki index 5591c9c..90f7bad 100755 --- a/shellpki +++ b/shellpki @@ -137,24 +137,25 @@ replace_existing_or_abort() { cn=${1:?} if [ "${non_interactive}" -eq 1 ]; then if [ "${replace_existing}" -eq 1 ]; then - resp="y" + revoke --non-interactive "${cn}" else error "${cn} already exists, use \`--replace-existing' to force" fi else if [ "${replace_existing}" -eq 1 ]; then - resp="y" + revoke "${cn}" else printf "%s already exists, do you want to revoke and recreate it ? [y/N] " "${cn}" read -r REPLY resp=$(echo "${REPLY}" | tr 'Y' 'y') + + if [ "${resp}" = "y" ]; then + revoke "${cn}" + else + error "Aborted" + fi fi fi - if [ "${resp}" = "y" ]; then - revoke "${cn}" - else - error "Aborted" - fi } init() { From 4a2e5c93f12a95698bf79b4dae2524d240eb865d Mon Sep 17 00:00:00 2001 From: Jeremy Dubois Date: Tue, 22 Mar 2022 18:01:22 +0100 Subject: [PATCH 64/89] Update README file and show_usage function --- README.md | 63 ++++++++++++++++++++++++++++++++++++++++++++----------- shellpki | 46 +++++++++++++++++++++++++++++----------- 2 files changed, 85 insertions(+), 24 deletions(-) diff --git a/README.md b/README.md index f5477c1..a541db0 100644 --- a/README.md +++ b/README.md @@ -50,47 +50,86 @@ proto udp remote ovpn.example.com 1194 nobind +user nobody +group nogroup persist-key persist-tun -cipher AES-256-CBC +cipher AES-256-GCM ~~~ ## Usage ~~~ -Usage: ./shellpki [options] [CommonName] +Usage: shellpki [options] [CommonName] ~~~ Initialize PKI (create CA key and self-signed cert) : ~~~ - ./shellpki init + shellpki init + + Options + --non-interactive do not prompt the user, and exit if an error occurs ~~~ -Create a client cert with key and CSR directly generated on server -(use -p for set a password on client key) : +Create a client cert with key and CSR directly generated on server : ~~~ - ./shellpki create [-p] + shellpki create + + Options + -f, --file, --csr-file create a client cert from a CSR (doesn't need key) + -p, --password prompt the user for a password to set on the client key + --password-file if provided with a path to a readable file, the first line is read and set as password on the client key + --days specify how many days the certificate should be valid + --end-date specify until which date the certificate should be valid, in MM/DD/[YY]YY [hh:mm:ss] format + --non-interactive do not prompt the user, and exit if an error occurs + --replace-existing if the certificate already exists, revoke it before creating a new one ~~~ -Create a client cert from a CSR (doesn't need key) : +Revoke a client cert : ~~~ - ./shellpki create -f + shellpki revoke + + Options + --non-interactive do not prompt the user, and exit if an error occurs ~~~ -Revoke a client cert with is commonName (CN) : +List all certificates : ~~~ - ./shellpki revoke + shellpki list + + Options + -a, --all + -v, --valid + -r, --revoked ~~~ -List all actually valid commonName (CN) : +Check expiration date of valid certificates : ~~~ - ./shellpki list + shellpki check +~~~ + +Run OCSP_D server : + +~~~ + shellpki ocsp +~~~ + +Show version : + +~~~ + shellpki version +~~~ + +Show help : + +~~~ + shellpki help ~~~ ## License diff --git a/shellpki b/shellpki index 90f7bad..602fe04 100755 --- a/shellpki +++ b/shellpki @@ -36,31 +36,53 @@ Initialize PKI (create CA key and self-signed cert) : ${0} init -Run OCSP_D server : + Options + --non-interactive do not prompt the user, and exit if an error occurs - ${0} ocsp +Create a client cert with key and CSR directly generated on server : -Create a client cert with key and CSR directly generated on server -(use -p or --password-file to set a password on the client key) : + ${0} create - ${0} create [-p|--password-file=] + Options + -f, --file, --csr-file create a client cert from a CSR (doesn't need key) + -p, --password prompt the user for a password to set on the client key + --password-file if provided with a path to a readable file, the first line is read and set as password on the client key + --days specify how many days the certificate should be valid + --end-date specify until which date the certificate should be valid, in MM/DD/[YY]YY [hh:mm:ss] format + --non-interactive do not prompt the user, and exit if an error occurs + --replace-existing if the certificate already exists, revoke it before creating a new one -Create a client cert from a CSR (doesn't need key) : - - ${0} create -f - -Revoke a client cert with is commonName (CN) : +Revoke a client cert : ${0} revoke -List all actually valid commonName (CN) : + Options + --non-interactive do not prompt the user, and exit if an error occurs - ${0} list [-a|--all|-v|--valid|-r|--revoked] +List all certificates : + + ${0} list + + Options + -a, --all + -v, --valid + -r, --revoked Check expiration date of valid certificates : ${0} check +Run OCSP_D server : + + ${0} ocsp + +Show version : + + ${0} version + +Show help : + + ${0} help EOF } From da7809f3c0f5207fa297ed44208cf5cb80fa9c19 Mon Sep 17 00:00:00 2001 From: Jeremy Dubois Date: Tue, 22 Mar 2022 18:04:03 +0100 Subject: [PATCH 65/89] Update README file and show_usage function : forgotten information --- README.md | 6 +++--- shellpki | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index a541db0..745d3fb 100644 --- a/README.md +++ b/README.md @@ -103,9 +103,9 @@ List all certificates : shellpki list Options - -a, --all - -v, --valid - -r, --revoked + -a, --all list all certificates : valid and revoked ones + -v, --valid list all valid certificates + -r, --revoked list all revoked certificates ~~~ Check expiration date of valid certificates : diff --git a/shellpki b/shellpki index 602fe04..695f6ae 100755 --- a/shellpki +++ b/shellpki @@ -64,9 +64,9 @@ List all certificates : ${0} list Options - -a, --all - -v, --valid - -r, --revoked + -a, --all list all certificates : valid and revoked ones + -v, --valid list all valid certificates + -r, --revoked list all revoked certificates Check expiration date of valid certificates : From d0c6a55538fd9fb5ad8012eaed422870497a0b44 Mon Sep 17 00:00:00 2001 From: Jeremy Dubois Date: Tue, 22 Mar 2022 18:08:57 +0100 Subject: [PATCH 66/89] README file and show_usage function : replace "cert" with "certificate" --- README.md | 8 ++++---- shellpki | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 745d3fb..1d3022a 100644 --- a/README.md +++ b/README.md @@ -64,7 +64,7 @@ cipher AES-256-GCM Usage: shellpki [options] [CommonName] ~~~ -Initialize PKI (create CA key and self-signed cert) : +Initialize PKI (create CA key and self-signed certificate) : ~~~ shellpki init @@ -73,13 +73,13 @@ Initialize PKI (create CA key and self-signed cert) : --non-interactive do not prompt the user, and exit if an error occurs ~~~ -Create a client cert with key and CSR directly generated on server : +Create a client certificate with key and CSR directly generated on server : ~~~ shellpki create Options - -f, --file, --csr-file create a client cert from a CSR (doesn't need key) + -f, --file, --csr-file create a client certificate from a CSR (doesn't need key) -p, --password prompt the user for a password to set on the client key --password-file if provided with a path to a readable file, the first line is read and set as password on the client key --days specify how many days the certificate should be valid @@ -88,7 +88,7 @@ Create a client cert with key and CSR directly generated on server : --replace-existing if the certificate already exists, revoke it before creating a new one ~~~ -Revoke a client cert : +Revoke a client certificate : ~~~ shellpki revoke diff --git a/shellpki b/shellpki index 695f6ae..55bec09 100755 --- a/shellpki +++ b/shellpki @@ -32,19 +32,19 @@ show_usage() { cat < [options] [CommonName] -Initialize PKI (create CA key and self-signed cert) : +Initialize PKI (create CA key and self-signed certificate) : ${0} init Options --non-interactive do not prompt the user, and exit if an error occurs -Create a client cert with key and CSR directly generated on server : +Create a client certificate with key and CSR directly generated on server : ${0} create Options - -f, --file, --csr-file create a client cert from a CSR (doesn't need key) + -f, --file, --csr-file create a client certificate from a CSR (doesn't need key) -p, --password prompt the user for a password to set on the client key --password-file if provided with a path to a readable file, the first line is read and set as password on the client key --days specify how many days the certificate should be valid @@ -52,7 +52,7 @@ Create a client cert with key and CSR directly generated on server : --non-interactive do not prompt the user, and exit if an error occurs --replace-existing if the certificate already exists, revoke it before creating a new one -Revoke a client cert : +Revoke a client certificate : ${0} revoke From 50fc8c2d218335792d129522ade00d5a5c3bd740 Mon Sep 17 00:00:00 2001 From: Jeremy Dubois Date: Tue, 22 Mar 2022 18:11:17 +0100 Subject: [PATCH 67/89] README file : delete unnecessary leading spaces --- README.md | 48 ++++++++++++++++++++++++------------------------ 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/README.md b/README.md index 1d3022a..15df575 100644 --- a/README.md +++ b/README.md @@ -67,69 +67,69 @@ Usage: shellpki [options] [CommonName] Initialize PKI (create CA key and self-signed certificate) : ~~~ - shellpki init +shellpki init - Options - --non-interactive do not prompt the user, and exit if an error occurs +Options + --non-interactive do not prompt the user, and exit if an error occurs ~~~ Create a client certificate with key and CSR directly generated on server : ~~~ - shellpki create +shellpki create - Options - -f, --file, --csr-file create a client certificate from a CSR (doesn't need key) - -p, --password prompt the user for a password to set on the client key - --password-file if provided with a path to a readable file, the first line is read and set as password on the client key - --days specify how many days the certificate should be valid - --end-date specify until which date the certificate should be valid, in MM/DD/[YY]YY [hh:mm:ss] format - --non-interactive do not prompt the user, and exit if an error occurs - --replace-existing if the certificate already exists, revoke it before creating a new one +Options + -f, --file, --csr-file create a client certificate from a CSR (doesn't need key) + -p, --password prompt the user for a password to set on the client key + --password-file if provided with a path to a readable file, the first line is read and set as password on the client key + --days specify how many days the certificate should be valid + --end-date specify until which date the certificate should be valid, in MM/DD/[YY]YY [hh:mm:ss] format + --non-interactive do not prompt the user, and exit if an error occurs + --replace-existing if the certificate already exists, revoke it before creating a new one ~~~ Revoke a client certificate : ~~~ - shellpki revoke +shellpki revoke - Options - --non-interactive do not prompt the user, and exit if an error occurs +Options + --non-interactive do not prompt the user, and exit if an error occurs ~~~ List all certificates : ~~~ - shellpki list +shellpki list - Options - -a, --all list all certificates : valid and revoked ones - -v, --valid list all valid certificates - -r, --revoked list all revoked certificates +Options + -a, --all list all certificates : valid and revoked ones + -v, --valid list all valid certificates + -r, --revoked list all revoked certificates ~~~ Check expiration date of valid certificates : ~~~ - shellpki check +shellpki check ~~~ Run OCSP_D server : ~~~ - shellpki ocsp +shellpki ocsp ~~~ Show version : ~~~ - shellpki version +shellpki version ~~~ Show help : ~~~ - shellpki help +shellpki help ~~~ ## License From 5f27702f1704281a2557928a1131ea0c5c0eeab3 Mon Sep 17 00:00:00 2001 From: Jeremy Dubois Date: Tue, 29 Mar 2022 18:01:23 +0200 Subject: [PATCH 68/89] Delete ovpn.conf.example unnecessary here shellpki alone is not enough to install OpenVPN, and the openvpn role provides the openvpn server configuration --- ovpn.conf.example | 34 ---------------------------------- 1 file changed, 34 deletions(-) delete mode 100644 ovpn.conf.example diff --git a/ovpn.conf.example b/ovpn.conf.example deleted file mode 100644 index 645c832..0000000 --- a/ovpn.conf.example +++ /dev/null @@ -1,34 +0,0 @@ -user nobody -group nogroup - -local 198.51.100.1 -port 1194 -proto udp -dev tun -mode server -keepalive 10 120 -tls-exit - -cipher AES-256-GCM # AES - -persist-key -persist-tun - -ifconfig-pool-persist /etc/openvpn/ipp.txt - -status /var/log/openvpn-status.log -log-append /var/log/openvpn.log - -ca /etc/shellpki/cacert.pem -#cert /etc/shellpki/certs/fw.vpn.example.com.crt -#key /etc/shellpki/private/fw.vpn.example.com-1621504035.key -dh /etc/shellpki/dh2048.pem - -crl-verify /etc/shellpki/crl.pem - -server 192.0.2.0 255.255.255.0 - -#push "route 192.0.3.0 255.255.255.0" - -# Management interface (used by check_openvpn for Nagios) -management 127.0.0.1 1195 /etc/openvpn/management-pwd From 047c6e334a830886f6655c60b60f621cf4094070 Mon Sep 17 00:00:00 2001 From: Jeremy Dubois Date: Tue, 29 Mar 2022 18:10:47 +0200 Subject: [PATCH 69/89] Improve README and show_usage --- README.md | 8 ++++---- shellpki | 7 ++++--- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 15df575..29353ec 100644 --- a/README.md +++ b/README.md @@ -67,7 +67,7 @@ Usage: shellpki [options] [CommonName] Initialize PKI (create CA key and self-signed certificate) : ~~~ -shellpki init +shellpki init [options] Options --non-interactive do not prompt the user, and exit if an error occurs @@ -76,14 +76,14 @@ Options Create a client certificate with key and CSR directly generated on server : ~~~ -shellpki create +shellpki create [options] Options -f, --file, --csr-file create a client certificate from a CSR (doesn't need key) -p, --password prompt the user for a password to set on the client key --password-file if provided with a path to a readable file, the first line is read and set as password on the client key --days specify how many days the certificate should be valid - --end-date specify until which date the certificate should be valid, in MM/DD/[YY]YY [hh:mm:ss] format + --end-date specify until which date the certificate should be valid, in MM/DD/YYYY hh:mm:ss format --non-interactive do not prompt the user, and exit if an error occurs --replace-existing if the certificate already exists, revoke it before creating a new one ~~~ @@ -91,7 +91,7 @@ Options Revoke a client certificate : ~~~ -shellpki revoke +shellpki revoke [options] Options --non-interactive do not prompt the user, and exit if an error occurs diff --git a/shellpki b/shellpki index 55bec09..f255180 100755 --- a/shellpki +++ b/shellpki @@ -31,17 +31,18 @@ END show_usage() { cat < [options] [CommonName] +Warning: [options] always must be before [CommonName] and after Initialize PKI (create CA key and self-signed certificate) : - ${0} init + ${0} init [options] Options --non-interactive do not prompt the user, and exit if an error occurs Create a client certificate with key and CSR directly generated on server : - ${0} create + ${0} create [options] Options -f, --file, --csr-file create a client certificate from a CSR (doesn't need key) @@ -54,7 +55,7 @@ Create a client certificate with key and CSR directly generated on server : Revoke a client certificate : - ${0} revoke + ${0} revoke [options] Options --non-interactive do not prompt the user, and exit if an error occurs From 6d71a5a177d979e5a08f12fdf2b8fc3482dbff46 Mon Sep 17 00:00:00 2001 From: Jeremy Dubois Date: Tue, 29 Mar 2022 18:15:57 +0200 Subject: [PATCH 70/89] Fix end-date format depending on system --- shellpki | 27 +++++++++++++++++++++------ 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/shellpki b/shellpki index f255180..f1b4b1f 100755 --- a/shellpki +++ b/shellpki @@ -49,7 +49,7 @@ Create a client certificate with key and CSR directly generated on server : -p, --password prompt the user for a password to set on the client key --password-file if provided with a path to a readable file, the first line is read and set as password on the client key --days specify how many days the certificate should be valid - --end-date specify until which date the certificate should be valid, in MM/DD/[YY]YY [hh:mm:ss] format + --end-date specify until which date the certificate should be valid, in "MM/DD/YYYY hh:mm:ss" format --non-interactive do not prompt the user, and exit if an error occurs --replace-existing if the certificate already exists, revoke it before creating a new one @@ -490,12 +490,24 @@ create() { crt_expiration_arg="-days ${days}" fi if [ -n "${end_date}" ]; then - cert_end_date=$(TZ=:Zulu date --date "${end_date}" +"%Y%m%d%H%M%SZ" 2> /dev/null) - # shellcheck disable=SC2181 - if [ "$?" -ne 0 ]; then - error "Invalid end date format : \`${end_date}' can't be parsed by date(1)" + if [ "${SYSTEM}" = "linux" ]; then + cert_end_date=$(TZ=:Zulu date --date "${end_date}" +"%Y%m%d%H%M%SZ" 2> /dev/null) + # shellcheck disable=SC2181 + if [ "$?" -ne 0 ]; then + error "Invalid end date format: \`${end_date}' can't be parsed by date(1). Expected format: MM/DD/[YY]YY [hh[:mm[:ss]]]." + else + crt_expiration_arg="-enddate ${cert_end_date}" + fi + elif [ "${SYSTEM}" = "openbsd" ]; then + cert_end_date=$(TZ=:Zulu date -f "%m/%d/%C%y %H:%M:%S" -j "${end_date}" +"%Y%m%d%H%M%SZ" 2> /dev/null) + # shellcheck disable=SC2181 + if [ "$?" -ne 0 ]; then + error "Invalid end date format: \`${end_date}' can't be parsed by date(1). Expected format: MM/DD/YYYY hh:mm:ss." + else + crt_expiration_arg="-enddate ${cert_end_date}" + fi else - crt_expiration_arg="-enddate ${cert_end_date}" + error "System ${SYSTEM} not supported." fi fi if [ "${non_interactive}" -eq 1 ]; then @@ -901,6 +913,9 @@ is_group() { } main() { + # Know what system we are on, because OpenBSD and Linux do not implement date(1) in the same way + SYSTEM=$(uname | tr '[:upper:]' '[:lower:]') + # default config # TODO : override with /etc/default/shellpki CONF_FILE="/etc/shellpki/openssl.cnf" From a640892ecb5918215e16f83341d79b63e84dfa56 Mon Sep 17 00:00:00 2001 From: Jeremy Dubois Date: Tue, 29 Mar 2022 18:17:03 +0200 Subject: [PATCH 71/89] Syntax: no space before ":" --- shellpki | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/shellpki b/shellpki index f1b4b1f..e3d66f5 100755 --- a/shellpki +++ b/shellpki @@ -65,7 +65,7 @@ List all certificates : ${0} list Options - -a, --all list all certificates : valid and revoked ones + -a, --all list all certificates: valid and revoked ones -v, --valid list all valid certificates -r, --revoked list all revoked certificates @@ -797,7 +797,7 @@ revoke() { crt_file="${CRT_DIR}/${cn}.crt" # check if CRT exists if [ ! -f "${crt_file}" ]; then - error "Unknow CN : ${cn} (\`${crt_file}' not found)" + error "Unknow CN: ${cn} (\`${crt_file}' not found)" fi # check if CRT is a valid @@ -886,7 +886,7 @@ cert_end_date() { check() { # default expiration alert - # TODO : permit override with parameters + # TODO: permit override with parameters min_day=90 cur_epoch=$(date -u +'%s') @@ -917,7 +917,7 @@ main() { SYSTEM=$(uname | tr '[:upper:]' '[:lower:]') # default config - # TODO : override with /etc/default/shellpki + # TODO: override with /etc/default/shellpki CONF_FILE="/etc/shellpki/openssl.cnf" if [ "$(uname)" = "OpenBSD" ]; then From e42af2183cbd9d4e5b11966d062eec0cd3ef28e6 Mon Sep 17 00:00:00 2001 From: Jeremy Dubois Date: Tue, 29 Mar 2022 18:18:01 +0200 Subject: [PATCH 72/89] Fix --non-interactive behavior: there were still some prompts to the user --- shellpki | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/shellpki b/shellpki index e3d66f5..e8aae25 100755 --- a/shellpki +++ b/shellpki @@ -239,6 +239,8 @@ init() { passout_arg="" if [ -n "${CA_PASSWORD:-}" ]; then passout_arg="-passout pass:${CA_PASSWORD}" + elif [ "${non_interactive}" -eq 1 ]; then + error "In non-interactive mode, you must pass CA_PASSWORD as environment variable." fi if [ ! -f "${CA_KEY}" ]; then @@ -255,11 +257,15 @@ init() { fi if [ -f "${CA_CERT}" ]; then - printf "%s already exists, do you really want to erase it ? [y/N] " "${CA_CERT}" - read -r REPLY - resp=$(echo "${REPLY}" | tr 'Y' 'y') - if [ "${resp}" = "y" ]; then - rm "${CA_CERT}" + if [ "${non_interactive}" -eq 1 ]; then + error "${CA_CERT} already exists, erase it manually if you want to start over." + else + printf "%s already exists, do you really want to erase it ? [y/N] " "${CA_CERT}" + read -r REPLY + resp=$(echo "${REPLY}" | tr 'Y' 'y') + if [ "${resp}" = "y" ]; then + rm "${CA_CERT}" + fi fi fi From 191ba257d910a712c5c8635a1d807e5e68095ef6 Mon Sep 17 00:00:00 2001 From: Jeremy Dubois Date: Tue, 29 Mar 2022 18:19:33 +0200 Subject: [PATCH 73/89] Fix parsing options when no option is given --- shellpki | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/shellpki b/shellpki index e8aae25..c7abe98 100755 --- a/shellpki +++ b/shellpki @@ -373,7 +373,7 @@ create() { # Parse options # based on https://gist.github.com/deshion/10d3cb5f88a21671e17a while :; do - case $1 in + case ${1:-} in -f|--file|--csr-file) # csr-file option, with value separated by space if [ -n "$2" ]; then @@ -770,7 +770,7 @@ revoke() { # Parse options # based on https://gist.github.com/deshion/10d3cb5f88a21671e17a while :; do - case $1 in + case ${1:-} in --non-interactive) non_interactive=1 ;; From abf6fb131cd872f94dc59eeb2594a7fdc81a2483 Mon Sep 17 00:00:00 2001 From: Jeremy Dubois Date: Tue, 29 Mar 2022 18:20:16 +0200 Subject: [PATCH 74/89] Do not use --end-date and --days together --- shellpki | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/shellpki b/shellpki index c7abe98..69507af 100755 --- a/shellpki +++ b/shellpki @@ -369,6 +369,8 @@ create() { replace_existing=0 days="" end_date="" + days_set=0 + end_date_set=0 # Parse options # based on https://gist.github.com/deshion/10d3cb5f88a21671e17a @@ -433,6 +435,7 @@ create() { # days option, with value separated by space if [ -n "$2" ]; then days=${2} + days_set=1 shift else error "Argument error: \`--days' requires a value" @@ -441,6 +444,7 @@ create() { --days=?*) # days option, with value separated by = days=${1#*=} + days_set=1 ;; --days=) # days options, without value @@ -450,6 +454,7 @@ create() { # end-date option, with value separated by space if [ -n "$2" ]; then end_date=${2} + end_date_set=1 shift else error "Argument error: \`--end-date' requires a value" @@ -458,6 +463,7 @@ create() { --end-date=?*) # end-date option, with value separated by = end_date=${1#*=} + end_date_set=1 ;; --end-date=) # end-date options, without value @@ -487,6 +493,10 @@ create() { shift done + if [ "${days_set}" -eq 1 ] && [ "${end_date_set}" -eq 1 ]; then + error "Argument error: \`--end-date' and \`--days' cannot be used together." + fi + # The name of the certificate cn="${1:-}" From 9f13a4235595886c4059b63e806a783a6b093057 Mon Sep 17 00:00:00 2001 From: Jeremy Dubois Date: Tue, 29 Mar 2022 18:42:28 +0200 Subject: [PATCH 75/89] Handle the case where --days argument is not a number or a negative one Before this test, the error was displayed but ignored and the certificate was still created depending on the default_days value in openssl.cnf --- shellpki | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/shellpki b/shellpki index 69507af..4b97396 100755 --- a/shellpki +++ b/shellpki @@ -502,8 +502,13 @@ create() { # Set expiration argument crt_expiration_arg="" - if [ -n "${days}" ] && [ "${days}" -gt 0 ]; then - crt_expiration_arg="-days ${days}" + if [ -n "${days}" ]; then + if [ "${days}" -gt 0 ]; then + crt_expiration_arg="-days ${days}" + else + error "Argument error: \"${days}\" is not a valid value for \`--days'." + echo $days + fi fi if [ -n "${end_date}" ]; then if [ "${SYSTEM}" = "linux" ]; then From 85c33247136c08700ee9a04b8d9f66d3d279934b Mon Sep 17 00:00:00 2001 From: Jeremy Dubois Date: Tue, 29 Mar 2022 18:48:45 +0200 Subject: [PATCH 76/89] Update Copyright --- shellpki | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/shellpki b/shellpki index 4b97396..c84bb82 100755 --- a/shellpki +++ b/shellpki @@ -11,7 +11,7 @@ show_version() { cat <, +Copyright 2010-2022 Evolix , Thomas Martin , Gregory Colpart , Romain Dessort , @@ -19,7 +19,8 @@ Copyright 2010-2019 Evolix , Victor Laborie , Daniel Jakots , Patrick Marchand , - Jérémy Lecour + Jérémy Lecour , + Jérémy Dubois and others. shellpki comes with ABSOLUTELY NO WARRANTY. This is free software, From 554f6166c96bb307a1bb4d5c9d94edfab9194509 Mon Sep 17 00:00:00 2001 From: Jeremy Dubois Date: Tue, 29 Mar 2022 18:59:09 +0200 Subject: [PATCH 77/89] Forget to delete a debug line --- shellpki | 1 - 1 file changed, 1 deletion(-) diff --git a/shellpki b/shellpki index c84bb82..663020b 100755 --- a/shellpki +++ b/shellpki @@ -508,7 +508,6 @@ create() { crt_expiration_arg="-days ${days}" else error "Argument error: \"${days}\" is not a valid value for \`--days'." - echo $days fi fi if [ -n "${end_date}" ]; then From 1fa4ff205e5ac39ebc49d6a44f313ae4567dd2ec Mon Sep 17 00:00:00 2001 From: Jeremy Dubois Date: Mon, 4 Apr 2022 17:01:19 +0200 Subject: [PATCH 78/89] Parse date in ISO format rather than US format --- README.md | 2 +- shellpki | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 29353ec..1c786d7 100644 --- a/README.md +++ b/README.md @@ -83,7 +83,7 @@ Options -p, --password prompt the user for a password to set on the client key --password-file if provided with a path to a readable file, the first line is read and set as password on the client key --days specify how many days the certificate should be valid - --end-date specify until which date the certificate should be valid, in MM/DD/YYYY hh:mm:ss format + --end-date specify until which date the certificate should be valid, in YYYY/MM/DD hh:mm:ss format --non-interactive do not prompt the user, and exit if an error occurs --replace-existing if the certificate already exists, revoke it before creating a new one ~~~ diff --git a/shellpki b/shellpki index 663020b..79bd8b0 100755 --- a/shellpki +++ b/shellpki @@ -50,7 +50,7 @@ Create a client certificate with key and CSR directly generated on server : -p, --password prompt the user for a password to set on the client key --password-file if provided with a path to a readable file, the first line is read and set as password on the client key --days specify how many days the certificate should be valid - --end-date specify until which date the certificate should be valid, in "MM/DD/YYYY hh:mm:ss" format + --end-date specify until which date the certificate should be valid, in "YYYY/MM/DD hh:mm:ss" format --non-interactive do not prompt the user, and exit if an error occurs --replace-existing if the certificate already exists, revoke it before creating a new one @@ -515,15 +515,15 @@ create() { cert_end_date=$(TZ=:Zulu date --date "${end_date}" +"%Y%m%d%H%M%SZ" 2> /dev/null) # shellcheck disable=SC2181 if [ "$?" -ne 0 ]; then - error "Invalid end date format: \`${end_date}' can't be parsed by date(1). Expected format: MM/DD/[YY]YY [hh[:mm[:ss]]]." + error "Invalid end date format: \`${end_date}' can't be parsed by date(1). Expected format: YYYY/MM/DD [hh[:mm[:ss]]]." else crt_expiration_arg="-enddate ${cert_end_date}" fi elif [ "${SYSTEM}" = "openbsd" ]; then - cert_end_date=$(TZ=:Zulu date -f "%m/%d/%C%y %H:%M:%S" -j "${end_date}" +"%Y%m%d%H%M%SZ" 2> /dev/null) + cert_end_date=$(TZ=:Zulu date -f "%C%y/%m/%d %H:%M:%S" -j "${end_date}" +"%Y%m%d%H%M%SZ" 2> /dev/null) # shellcheck disable=SC2181 if [ "$?" -ne 0 ]; then - error "Invalid end date format: \`${end_date}' can't be parsed by date(1). Expected format: MM/DD/YYYY hh:mm:ss." + error "Invalid end date format: \`${end_date}' can't be parsed by date(1). Expected format: YYYY/MM/DD hh:mm:ss." else crt_expiration_arg="-enddate ${cert_end_date}" fi From c76b7a02cafe0c8a87a2bde4baf64bf55f770128 Mon Sep 17 00:00:00 2001 From: Jeremy Dubois Date: Mon, 4 Apr 2022 17:36:02 +0200 Subject: [PATCH 79/89] Split show_usage for each subcommand, add --version and --help in addition to version and help, update VERSION --- shellpki | 71 +++++++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 55 insertions(+), 16 deletions(-) diff --git a/shellpki b/shellpki index 79bd8b0..fe28f17 100755 --- a/shellpki +++ b/shellpki @@ -5,7 +5,7 @@ set -u -VERSION="1.0.0" +VERSION="22.04" show_version() { cat < [options] [CommonName] Warning: [options] always must be before [CommonName] and after +EOF +show_usage_init +show_usage_create +show_usage_revoke +show_usage_list +show_usage_check +show_usage_ocsp + + cat < @@ -41,6 +62,11 @@ Initialize PKI (create CA key and self-signed certificate) : Options --non-interactive do not prompt the user, and exit if an error occurs +EOF +} + +show_usage_create() { + cat < @@ -54,6 +80,11 @@ Create a client certificate with key and CSR directly generated on server : --non-interactive do not prompt the user, and exit if an error occurs --replace-existing if the certificate already exists, revoke it before creating a new one +EOF +} + +show_usage_revoke() { + cat < @@ -61,7 +92,12 @@ Revoke a client certificate : Options --non-interactive do not prompt the user, and exit if an error occurs -List all certificates : +EOF +} + +show_usage_list() { + cat < @@ -70,21 +106,24 @@ List all certificates : -v, --valid list all valid certificates -r, --revoked list all revoked certificates +EOF +} + +show_usage_check() { + cat < -Show version : - - ${0} version - -Show help : - - ${0} help EOF } @@ -220,7 +259,7 @@ init() { cn="${1:-}" if [ -z "${cn}" ]; then - show_usage >&2 + show_usage_init >&2 exit 1 fi @@ -301,7 +340,7 @@ ocsp() { ocsp_uri="${1:-}" if [ -z "${ocsp_uri}" ]; then - show_usage >&2 + show_usage_ocsp >&2 exit 1 fi ocsp_csr_file="${CSR_DIR}/ocsp.csr" @@ -602,7 +641,7 @@ create() { fi else if [ -z "${cn}" ]; then - show_usage >&2 + show_usage_create >&2 exit 1 fi csr_file="${CSR_DIR}/${cn}-${SUFFIX}.csr" @@ -811,7 +850,7 @@ revoke() { cn="${1:-}" if [ -z "${cn}" ]; then - show_usage >&2 + show_usage_revoke >&2 exit 1 fi @@ -858,7 +897,7 @@ list() { fi if [ -z "${1:-}" ]; then - show_usage >&2 + show_usage_list >&2 exit 1 fi @@ -1026,12 +1065,12 @@ main() { check "$@" ;; - version) + version|--version) show_version exit 0 ;; - help) + help|--help) show_usage exit 0 ;; From 14a65fa42d81217ff0795a8b5b8f2a89b18bbf54 Mon Sep 17 00:00:00 2001 From: Jeremy Dubois Date: Mon, 4 Apr 2022 17:55:37 +0200 Subject: [PATCH 80/89] Change SUFFIX to use human readable date instead of epoch --- shellpki | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shellpki b/shellpki index fe28f17..14b9b46 100755 --- a/shellpki +++ b/shellpki @@ -1020,7 +1020,7 @@ main() { fi OPENSSL_BIN=$(command -v openssl) - SUFFIX=$(/bin/date +"%s") + SUFFIX=$(TZ=:Zulu /bin/date +"%Y%m%d%H%M%SZ") if ! is_user "${PKI_USER}" || ! is_group "${PKI_USER}"; then error "You must create ${PKI_USER} user and group !" From 97f1affa1b4934b5f777ea0d17f5bf15baaa88d0 Mon Sep 17 00:00:00 2001 From: Jeremy Dubois Date: Mon, 4 Apr 2022 18:13:37 +0200 Subject: [PATCH 81/89] Create crl file after init of PKI --- shellpki | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/shellpki b/shellpki index 14b9b46..09c664b 100755 --- a/shellpki +++ b/shellpki @@ -333,6 +333,12 @@ EOF error "Error generating the CA certificate" fi fi + + "${OPENSSL_BIN}" ca \ + -config "${CONF_FILE}" \ + -passin pass:${CA_PASSWORD} \ + -gencrl \ + -out "${CRL}" } ocsp() { From ba2f553ef41ec6681c4ab6591fb65af2bbdc85a3 Mon Sep 17 00:00:00 2001 From: Jeremy Dubois Date: Thu, 14 Apr 2022 15:01:09 +0200 Subject: [PATCH 82/89] Do not use --password and --password-file together --- shellpki | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/shellpki b/shellpki index 09c664b..9fc7c5b 100755 --- a/shellpki +++ b/shellpki @@ -417,6 +417,8 @@ create() { end_date="" days_set=0 end_date_set=0 + password_set=0 + password_file_set=0 # Parse options # based on https://gist.github.com/deshion/10d3cb5f88a21671e17a @@ -451,6 +453,7 @@ create() { ;; -p|--password) ask_pass=1 + password_set=1 ;; --password-file) # password-file option, with value separated by space @@ -460,6 +463,7 @@ create() { if [ "$?" -ne 0 ]; then error "Error accessing file \`${2}'" fi + password_file_set=1 shift else error "Argument error: \`--password-file' requires a value" @@ -472,6 +476,7 @@ create() { if [ "$?" -ne 0 ]; then error "Error accessing file \`${1#*=}'" fi + password_file_set=1 ;; --password-file=) # password-file options, without value @@ -543,6 +548,10 @@ create() { error "Argument error: \`--end-date' and \`--days' cannot be used together." fi + if [ "${password_set}" -eq 1 ] && [ "${password_file_set}" -eq 1 ]; then + error "Argument error: \`--password' and \`--password-file' cannot be used together." + fi + # The name of the certificate cn="${1:-}" From 55e02c6a13eb2474a4dcae345c3f9c166174525d Mon Sep 17 00:00:00 2001 From: Jeremy Dubois Date: Thu, 14 Apr 2022 15:18:57 +0200 Subject: [PATCH 83/89] Check if CN already exists only after having asked for user password Otherwise, with "-p", "--replace-existing" and "--non-interactive", with CA_PASSWORD set but PASSWORD unset, the existing certificate was revoked but the new one could'nt be created. Now, PASSWORD must be set or the exisiting certificate won't be revoked --- shellpki | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/shellpki b/shellpki index 9fc7c5b..ed7584e 100755 --- a/shellpki +++ b/shellpki @@ -630,7 +630,7 @@ create() { # get CN from CSR cn=$("${OPENSSL_BIN}" req -noout -subject -in "${csr_file}" | grep -Eo "CN\s*=[^,/]*" | cut -d'=' -f2 | xargs) - # check if CN already exist + # check if CN already exists if [ -f "${crt_file}" ]; then replace_existing_or_abort "${cn}" fi @@ -665,11 +665,6 @@ create() { ovpn_file="${OVPN_DIR}/${cn}-${SUFFIX}.ovpn" pkcs12_file="${PKCS12_DIR}/${cn}-${SUFFIX}.p12" - # check if CN already exist - if [ -f "${crt_file}" ]; then - replace_existing_or_abort "${cn}" - fi - # ask for CA passphrase ask_ca_password 0 @@ -677,6 +672,11 @@ create() { ask_user_password fi + # check if CN already exists + if [ -f "${crt_file}" ]; then + replace_existing_or_abort "${cn}" + fi + # generate private key pass_args="" if [ -n "${password_file:-}" ]; then From 6165ccec6ce4cacd263bc7b18e7e1c9d2e585ebe Mon Sep 17 00:00:00 2001 From: Jeremy Dubois Date: Thu, 14 Apr 2022 15:51:07 +0200 Subject: [PATCH 84/89] Generate CRL only if (re)generating CA --- shellpki | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shellpki b/shellpki index ed7584e..42611ec 100755 --- a/shellpki +++ b/shellpki @@ -332,13 +332,13 @@ EOF if [ "$?" -ne 0 ]; then error "Error generating the CA certificate" fi - fi "${OPENSSL_BIN}" ca \ -config "${CONF_FILE}" \ -passin pass:${CA_PASSWORD} \ -gencrl \ -out "${CRL}" + fi } ocsp() { From 992fde0930c48832fa5afacac82715faac9c0c80 Mon Sep 17 00:00:00 2001 From: Jeremy Dubois Date: Thu, 14 Apr 2022 15:53:59 +0200 Subject: [PATCH 85/89] Precising that the --end-date hour is in UTC +0 --- README.md | 2 +- shellpki | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 1c786d7..3794863 100644 --- a/README.md +++ b/README.md @@ -83,7 +83,7 @@ Options -p, --password prompt the user for a password to set on the client key --password-file if provided with a path to a readable file, the first line is read and set as password on the client key --days specify how many days the certificate should be valid - --end-date specify until which date the certificate should be valid, in YYYY/MM/DD hh:mm:ss format + --end-date specify until which date the certificate should be valid, in YYYY/MM/DD hh:mm:ss format, UTC +0 --non-interactive do not prompt the user, and exit if an error occurs --replace-existing if the certificate already exists, revoke it before creating a new one ~~~ diff --git a/shellpki b/shellpki index 42611ec..5d13986 100755 --- a/shellpki +++ b/shellpki @@ -76,7 +76,7 @@ Create a client certificate with key and CSR directly generated on server : -p, --password prompt the user for a password to set on the client key --password-file if provided with a path to a readable file, the first line is read and set as password on the client key --days specify how many days the certificate should be valid - --end-date specify until which date the certificate should be valid, in "YYYY/MM/DD hh:mm:ss" format + --end-date specify until which date the certificate should be valid, in "YYYY/MM/DD hh:mm:ss" format, UTC +0 --non-interactive do not prompt the user, and exit if an error occurs --replace-existing if the certificate already exists, revoke it before creating a new one From 42de07cb66375c3d9dc408cfba66efe2e5ff04a2 Mon Sep 17 00:00:00 2001 From: Jeremy Dubois Date: Thu, 14 Apr 2022 16:21:38 +0200 Subject: [PATCH 86/89] Add version to files that will be copied out of this repo so that we easily know if they will need an update --- cert-expirations.sh | 2 ++ openssl.cnf | 2 ++ 2 files changed, 4 insertions(+) diff --git a/cert-expirations.sh b/cert-expirations.sh index 9e27dcc..f1b5601 100644 --- a/cert-expirations.sh +++ b/cert-expirations.sh @@ -1,5 +1,7 @@ #!/bin/sh +VERSION="22.04" + carp=$(/sbin/ifconfig carp0 2>/dev/null | grep 'status' | cut -d' ' -f2) if [ "$carp" = "backup" ]; then diff --git a/openssl.cnf b/openssl.cnf index 2c87f10..5e1e3c8 100644 --- a/openssl.cnf +++ b/openssl.cnf @@ -1,3 +1,5 @@ +# VERSION="22.04" + [ ca ] default_ca = CA_default From 7a034a2a17144d7c0d0be22afb176c782b439acb Mon Sep 17 00:00:00 2001 From: Jeremy Dubois Date: Thu, 14 Apr 2022 16:47:33 +0200 Subject: [PATCH 87/89] Some files must be copied to ansible-roles/openvpn --- README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/README.md b/README.md index 3794863..bdce064 100644 --- a/README.md +++ b/README.md @@ -3,6 +3,11 @@ This script is a wrapper around OpenSSL to manage a small [PKI](https://en.wikipedia.org/wiki/Public_key_infrastructure). +## Contribution + +After an update of this repo and if everything is working fine, some files must +be copied to [ansible-roles/openvpn](https://gitea.evolix.org/evolix/ansible-roles/src/branch/unstable/openvpn/files/shellpki) + ## Install ### Debian From d6140791385b80761ce78d4e82b6e81045c9b87e Mon Sep 17 00:00:00 2001 From: Jeremy Dubois Date: Thu, 14 Apr 2022 17:15:20 +0200 Subject: [PATCH 88/89] Update CHANGELOG --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2c66671..9529aa9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,11 +16,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 * Add `--non-interactive` command line option * Add `--replace-existing` command line option * Copy files if destination exists +* Generate the CRL file after initialization of the CA * `cert-expirations.sh` script to print out certificates expiration dates ### Changed * Rename internal function usage() to show_usage() +* Split show_usage() for each subcommand * More readable variable names * verify_ca_password() looks for a previously set password and verifies it * Extract cert_end_date() function @@ -28,6 +30,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 * Extract ask_user_password() function * Extract variables for files * Use inline pass phrase arguments +* Create files with a human readable date instead of epoch * Remove "set -e" and add many return code checks * Prevent use of uninitialized variables From 754c3455e0f6354003a8ea336dda1aacaad429ab Mon Sep 17 00:00:00 2001 From: Jeremy Dubois Date: Thu, 14 Apr 2022 17:20:04 +0200 Subject: [PATCH 89/89] Release 22.04 --- CHANGELOG.md | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9529aa9..67182cf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,18 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added +### Changed + +### Fixed + +### Removed + +### Security + +## [22.04] 2022-04-14 + +### Added + * Create a changelog * Add a version number and `version` command * Accept a `password-file` command line option to read password from a file @@ -34,12 +46,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 * Remove "set -e" and add many return code checks * Prevent use of uninitialized variables -### Deprecated - -### Removed - ### Fixed * Check on $USER was always true - -### Security