rewrite #5
51
CHANGELOG.md
Normal file
51
CHANGELOG.md
Normal file
|
@ -0,0 +1,51 @@
|
||||||
|
# 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
|
||||||
|
|
||||||
|
### 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
|
||||||
|
* Accept `--days` and `--end-date` command line options
|
||||||
|
* CA key length is configurable (minimum 4096)
|
||||||
|
* 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
|
||||||
|
* Extract is_user() and is_group() functions
|
||||||
|
* 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
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
* Check on $USER was always true
|
71
README.md
71
README.md
|
@ -3,6 +3,11 @@
|
||||||
This script is a wrapper around OpenSSL to manage a small
|
This script is a wrapper around OpenSSL to manage a small
|
||||||
[PKI](https://en.wikipedia.org/wiki/Public_key_infrastructure).
|
[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
|
## Install
|
||||||
|
|
||||||
### Debian
|
### Debian
|
||||||
|
@ -49,47 +54,87 @@ proto udp
|
||||||
|
|
||||||
remote ovpn.example.com 1194
|
remote ovpn.example.com 1194
|
||||||
|
|
||||||
|
nobind
|
||||||
|
user nobody
|
||||||
|
group nogroup
|
||||||
persist-key
|
persist-key
|
||||||
persist-tun
|
persist-tun
|
||||||
|
|
||||||
cipher AES-256-CBC
|
cipher AES-256-GCM
|
||||||
~~~
|
~~~
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
~~~
|
~~~
|
||||||
Usage: ./shellpki <subcommand> [options] [CommonName]
|
Usage: shellpki <subcommand> [options] [CommonName]
|
||||||
~~~
|
~~~
|
||||||
|
|
||||||
Initialize PKI (create CA key and self-signed cert) :
|
Initialize PKI (create CA key and self-signed certificate) :
|
||||||
|
|
||||||
~~~
|
~~~
|
||||||
./shellpki init <commonName_for_CA>
|
shellpki init [options] <commonName_for_CA>
|
||||||
|
|
||||||
|
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 :
|
||||||
(use -p for set a password on client key) :
|
|
||||||
|
|
||||||
~~~
|
~~~
|
||||||
./shellpki create [-p] <commonName>
|
shellpki create [options] <commonName>
|
||||||
|
|
||||||
|
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 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
|
||||||
~~~
|
~~~
|
||||||
|
|
||||||
Create a client cert from a CSR (doesn't need key) :
|
Revoke a client certificate :
|
||||||
|
|
||||||
~~~
|
~~~
|
||||||
./shellpki create -f <path>
|
shellpki revoke [options] <commonName>
|
||||||
|
|
||||||
|
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 <commonName>
|
shellpki list <options>
|
||||||
|
|
||||||
|
Options
|
||||||
|
-a, --all list all certificates : valid and revoked ones
|
||||||
|
-v, --valid list all valid certificates
|
||||||
|
-r, --revoked list all revoked certificates
|
||||||
~~~
|
~~~
|
||||||
|
|
||||||
List all actually valid commonName (CN) :
|
Check expiration date of valid certificates :
|
||||||
|
|
||||||
~~~
|
~~~
|
||||||
./shellpki list
|
shellpki check
|
||||||
|
~~~
|
||||||
|
|
||||||
|
Run OCSP_D server :
|
||||||
|
|
||||||
|
~~~
|
||||||
|
shellpki ocsp <ocsp_uri:ocsp_port>
|
||||||
|
~~~
|
||||||
|
|
||||||
|
Show version :
|
||||||
|
|
||||||
|
~~~
|
||||||
|
shellpki version
|
||||||
|
~~~
|
||||||
|
|
||||||
|
Show help :
|
||||||
|
|
||||||
|
~~~
|
||||||
|
shellpki help
|
||||||
~~~
|
~~~
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
28
cert-expirations.sh
Normal file
28
cert-expirations.sh
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
VERSION="22.04"
|
||||||
|
|
||||||
|
carp=$(/sbin/ifconfig carp0 2>/dev/null | grep 'status' | cut -d' ' -f2)
|
||||||
|
|
||||||
|
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 \
|
||||||
|
| cut -d '=' -f 2 \
|
||||||
|
| sed -e "s/^\(.*\)\ \(20..\).*/- \2 \1/"
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
echo "Client certificates:"
|
||||||
|
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
|
21
cn-validation.sh
Normal file
21
cn-validation.sh
Normal file
|
@ -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 <path-to-cn-filter>/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
|
|
@ -1,3 +1,5 @@
|
||||||
|
# VERSION="22.04"
|
||||||
|
|
||||||
[ ca ]
|
[ ca ]
|
||||||
default_ca = CA_default
|
default_ca = CA_default
|
||||||
|
|
||||||
|
|
1223
shellpki
1223
shellpki
|
@ -1,124 +1,116 @@
|
||||||
#!/bin/sh
|
#!/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
|
set -u
|
||||||
|
|||||||
|
|
||||||
init() {
|
VERSION="22.04"
|
||||||
umask 0177
|
|
||||||
|
|
||||||
[ -d "${CADIR}" ] || mkdir -m 0750 "${CADIR}"
|
show_version() {
|
||||||
[ -d "${CRTDIR}" ] || mkdir -m 0750 "${CRTDIR}"
|
cat <<END
|
||||||
[ -f "${INDEX}" ] || touch "${INDEX}"
|
shellpki version ${VERSION}
|
||||||
[ -f "${CRL}" ] || touch "${CRL}"
|
|
||||||
[ -f "${SERIAL}" ] || echo "01" > "${SERIAL}"
|
|
||||||
|
|
||||||
cn="${1:-}"
|
Copyright 2010-2022 Evolix <info@evolix.fr>,
|
||||||
[ -z "${cn}" ] && usage >&2 && exit 1
|
Thomas Martin <tmartin@evolix.fr>,
|
||||||
|
Gregory Colpart <reg@evolix.fr>,
|
||||||
|
Romain Dessort <rdessort@evolix.fr>,
|
||||||
|
Benoit Série <bserie@evolix.fr>,
|
||||||
|
Victor Laborie <vlaborie@evolix.fr>,
|
||||||
|
Daniel Jakots <djakots@evolix.fr>,
|
||||||
|
Patrick Marchand <pmarchand@evolix.fr>,
|
||||||
|
Jérémy Lecour <jlecour@evolix.fr>,
|
||||||
|
Jérémy Dubois <jdubois@evolix.fr>
|
||||||
|
and others.
|
||||||
|
|
||||||
if [ -f "${CAKEY}" ]; then
|
shellpki comes with ABSOLUTELY NO WARRANTY. This is free software,
|
||||||
printf "%s already exists, do you really want to erase it ? [y/N] " "${CAKEY}"
|
and you are welcome to redistribute it under certain conditions.
|
||||||
read -r REPLY
|
See the MIT Licence for details.
|
||||||
resp=$(echo "${REPLY}"|tr 'Y' 'y')
|
END
|
||||||
[ "${resp}" = "y" ] && rm -f "${CAKEY}" "${CACERT}"
|
|
||||||
fi
|
|
||||||
|
|
||||||
[ ! -f "${CAKEY}" ] && "$OPENSSL" \
|
|
||||||
genrsa \
|
|
||||||
-out "${CAKEY}" \
|
|
||||||
-aes256 4096 >/dev/null 2>&1
|
|
||||||
|
|
||||||
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}"
|
|
||||||
fi
|
|
||||||
|
|
||||||
[ ! -f "${CACERT}" ] && ask_ca_password 0
|
|
||||||
|
|
||||||
[ ! -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 <<EOF
|
|
||||||
$(cat "${CONFFILE}")
|
|
||||||
commonName_default = ${cn}
|
|
||||||
EOF
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ocsp() {
|
show_usage() {
|
||||||
umask 0177
|
|
||||||
|
|
||||||
ocsp_uri="${1:-}"
|
|
||||||
[ -z "${ocsp_uri}" ] && usage >&2 && exit 1
|
|
||||||
|
|
||||||
url=$(echo "${ocsp_uri}"|cut -d':' -f1)
|
|
||||||
port=$(echo "${ocsp_uri}"|cut -d':' -f2)
|
|
||||||
|
|
||||||
[ ! -f "${OCSPKEY}" ] && "$OPENSSL" \
|
|
||||||
genrsa \
|
|
||||||
-out "${OCSPKEY}" \
|
|
||||||
2048 >/dev/null 2>&1
|
|
||||||
|
|
||||||
"$OPENSSL" req \
|
|
||||||
-batch -new \
|
|
||||||
-key "${OCSPKEY}" \
|
|
||||||
-out "${CSRDIR}/ocsp.csr" \
|
|
||||||
-config /dev/stdin <<EOF
|
|
||||||
$(cat "${CONFFILE}")
|
|
||||||
commonName_default = ${url}
|
|
||||||
[ usr_cert ]
|
|
||||||
authorityInfoAccess = OCSP;URI:http://${ocsp_uri}
|
|
||||||
EOF
|
|
||||||
|
|
||||||
[ ! -f "${OCSPCERT}" ] && ask_ca_password 0
|
|
||||||
|
|
||||||
[ ! -f "${OCSPCERT}" ] && CA_PASSWORD="${CA_PASSWORD}" "${OPENSSL}" \
|
|
||||||
ca \
|
|
||||||
-extensions v3_ocsp \
|
|
||||||
-in "${CSRDIR}/ocsp.csr" \
|
|
||||||
-out "${OCSPCERT}" \
|
|
||||||
-passin env:CA_PASSWORD \
|
|
||||||
-config "${CONFFILE}"
|
|
||||||
|
|
||||||
exec "${OPENSSL}" ocsp -ignore_err -index "${INDEX}" -port "${port}" -rsigner "${OCSPCERT}" -rkey "${OCSPKEY}" -CA "${CACERT}" -text
|
|
||||||
}
|
|
||||||
|
|
||||||
usage() {
|
|
||||||
cat <<EOF
|
cat <<EOF
|
||||||
Usage: ${0} <subcommand> [options] [CommonName]
|
Usage: ${0} <subcommand> [options] [CommonName]
|
||||||
|
Warning: [options] always must be before [CommonName] and after <subcommand>
|
||||||
|
|
||||||
Initialize PKI (create CA key and self-signed cert) :
|
EOF
|
||||||
|
show_usage_init
|
||||||
|
show_usage_create
|
||||||
|
show_usage_revoke
|
||||||
|
show_usage_list
|
||||||
|
show_usage_check
|
||||||
|
show_usage_ocsp
|
||||||
|
|
||||||
${0} init <commonName_for_CA>
|
cat <<EOF
|
||||||
|
Show version :
|
||||||
|
|
||||||
Run OCSPD server :
|
${0} --version
|
||||||
|
|
||||||
${0} ocsp <ocsp_uri:ocsp_port>
|
Show help :
|
||||||
|
|
||||||
Create a client cert with key and CSR directly generated on server
|
${0} --help
|
||||||
(use -p for set a password on client key) :
|
EOF
|
||||||
|
}
|
||||||
|
|
||||||
${0} create [-p] <commonName>
|
show_usage_init() {
|
||||||
|
cat <<EOF
|
||||||
|
Initialize PKI (create CA key and self-signed certificate) :
|
||||||
|
|
||||||
Create a client cert from a CSR (doesn't need key) :
|
${0} init [options] <commonName_for_CA>
|
||||||
|
|
||||||
${0} create -f <path>
|
Options
|
||||||
|
--non-interactive do not prompt the user, and exit if an error occurs
|
||||||
|
|
||||||
Revoke a client cert with is commonName (CN) :
|
EOF
|
||||||
|
}
|
||||||
|
|
||||||
${0} revoke <commonName>
|
show_usage_create() {
|
||||||
|
cat <<EOF
|
||||||
|
Create a client certificate with key and CSR directly generated on server :
|
||||||
|
|
||||||
List all actually valid commonName (CN) :
|
${0} create [options] <commonName>
|
||||||
|
|
||||||
${0} list [-a|v|r]
|
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 "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
|
||||||
|
|
||||||
|
EOF
|
||||||
|
}
|
||||||
|
|
||||||
|
show_usage_revoke() {
|
||||||
|
cat <<EOF
|
||||||
|
Revoke a client certificate :
|
||||||
|
|
||||||
|
${0} revoke [options] <commonName>
|
||||||
|
|
||||||
|
Options
|
||||||
|
--non-interactive do not prompt the user, and exit if an error occurs
|
||||||
|
|
||||||
|
EOF
|
||||||
|
}
|
||||||
|
|
||||||
|
show_usage_list() {
|
||||||
|
cat <<EOF
|
||||||
|
List certificates :
|
||||||
|
|
||||||
|
${0} list <options>
|
||||||
|
|
||||||
|
Options
|
||||||
|
-a, --all list all certificates: valid and revoked ones
|
||||||
|
-v, --valid list all valid certificates
|
||||||
|
-r, --revoked list all revoked certificates
|
||||||
|
|
||||||
|
EOF
|
||||||
|
}
|
||||||
|
|
||||||
|
show_usage_check() {
|
||||||
|
cat <<EOF
|
||||||
Check expiration date of valid certificates :
|
Check expiration date of valid certificates :
|
||||||
|
|
||||||
${0} check
|
${0} check
|
||||||
|
@ -126,6 +118,15 @@ Check expiration date of valid certificates :
|
||||||
EOF
|
EOF
|
||||||
}
|
}
|
||||||
|
|
||||||
|
show_usage_ocsp() {
|
||||||
|
cat <<EOF
|
||||||
|
Run OCSP_D server :
|
||||||
|
|
||||||
|
${0} ocsp <ocsp_uri:ocsp_port>
|
||||||
|
|
||||||
|
EOF
|
||||||
|
}
|
||||||
|
|
||||||
error() {
|
error() {
|
||||||
echo "${1}" >&2
|
echo "${1}" >&2
|
||||||
exit 1
|
exit 1
|
||||||
|
@ -135,268 +136,837 @@ warning() {
|
||||||
echo "${1}" >&2
|
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() {
|
ask_ca_password() {
|
||||||
[ ! -f "${CAKEY}" ] && error "You must initialize your's PKI with shellpki init !"
|
attempt=${1:-0}
|
||||||
attempt=$((${1} + 1))
|
max_attempts=3
|
||||||
[ "${attempt}" -gt 1 ] && warning "Invalid password, retry."
|
|
||||||
trap 'unset CA_PASSWORD' 0
|
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
|
stty -echo
|
||||||
printf "Password for CA key: "
|
printf "Password for CA key: "
|
||||||
read -r CA_PASSWORD
|
read -r CA_PASSWORD
|
||||||
stty echo
|
stty echo
|
||||||
printf "\n"
|
printf "\n"
|
||||||
[ "${CA_PASSWORD}" != "" ] || ask_ca_password "${attempt}"
|
fi
|
||||||
CA_PASSWORD="${CA_PASSWORD}" "${OPENSSL}" rsa \
|
if [ -z "${CA_PASSWORD:-}" ] || ! verify_ca_password; then
|
||||||
-in "${CAKEY}" \
|
unset CA_PASSWORD
|
||||||
-passin env:CA_PASSWORD \
|
attempt=$(( attempt + 1 ))
|
||||||
>/dev/null 2>&1 \
|
ask_ca_password "${attempt}"
|
||||||
|| ask_ca_password "${attempt}"
|
fi
|
||||||
}
|
}
|
||||||
|
ask_user_password() {
|
||||||
create() {
|
|
||||||
from_csr=1
|
|
||||||
with_pass=1
|
|
||||||
|
|
||||||
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;;
|
|
||||||
-p|--password)
|
|
||||||
with_pass=0
|
|
||||||
shift;;
|
|
||||||
--)
|
|
||||||
shift
|
|
||||||
break;;
|
|
||||||
-?*)
|
|
||||||
warning "unknow option ${1} (ignored)"
|
|
||||||
shift;;
|
|
||||||
*)
|
|
||||||
break;;
|
|
||||||
esac
|
|
||||||
done
|
|
||||||
|
|
||||||
cn="${1:-}"
|
|
||||||
|
|
||||||
if [ "${from_csr}" -eq 0 ]; then
|
|
||||||
[ "${with_pass}" -eq 0 ] && warning "Warning: -p made nothing with -f"
|
|
||||||
|
|
||||||
# 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 \
|
|
||||||
|| 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 \
|
|
||||||
|| 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)
|
|
||||||
|
|
||||||
# check if CN already exist
|
|
||||||
[ -f "${CRTDIR}/${cn}.crt" ] && error "${cn} already used !"
|
|
||||||
|
|
||||||
# ca sign and generate cert
|
|
||||||
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"
|
|
||||||
else
|
|
||||||
[ -z "${cn}" ] && usage >&2 && exit 1
|
|
||||||
|
|
||||||
# check if CN already exist
|
|
||||||
[ -f "${CRTDIR}/${cn}.crt" ] && error "${cn} already used !"
|
|
||||||
|
|
||||||
# ask for client key passphrase
|
|
||||||
if [ "${with_pass}" -eq 0 ]; then
|
|
||||||
trap 'unset PASSWORD' 0
|
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
|
stty -echo
|
||||||
printf "Password for user key: "
|
printf "Password for user key: "
|
||||||
read -r PASSWORD
|
read -r PASSWORD
|
||||||
stty echo
|
stty echo
|
||||||
printf "\n"
|
printf "\n"
|
||||||
fi
|
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
|
||||||
|
revoke --non-interactive "${cn}"
|
||||||
|
else
|
||||||
|
error "${cn} already exists, use \`--replace-existing' to force"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
if [ "${replace_existing}" -eq 1 ]; then
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
init() {
|
||||||
|
umask 0177
|
||||||
|
|
||||||
|
[ -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}"
|
||||||
|
|
||||||
|
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_init >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -f "${CA_KEY}" ]; then
|
||||||
|
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}"
|
||||||
|
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
|
||||||
|
"${OPENSSL_BIN}" genrsa \
|
||||||
|
-out "${CA_KEY}" \
|
||||||
|
${passout_arg} \
|
||||||
|
-aes256 \
|
||||||
|
"${CA_KEY_LENGTH}" \
|
||||||
|
>/dev/null 2>&1
|
||||||
|
# shellcheck disable=SC2181
|
||||||
|
if [ "$?" -ne 0 ]; then
|
||||||
|
error "Error generating the CA key"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -f "${CA_CERT}" ]; then
|
||||||
|
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
|
||||||
|
|
||||||
|
if [ ! -f "${CA_CERT}" ]; then
|
||||||
|
ask_ca_password 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ ! -f "${CA_CERT}" ]; then
|
||||||
|
"${OPENSSL_BIN}" req \
|
||||||
|
-new \
|
||||||
|
-batch \
|
||||||
|
-sha512 \
|
||||||
|
-x509 \
|
||||||
|
-days 3650 \
|
||||||
|
-extensions v3_ca \
|
||||||
|
-passin pass:"${CA_PASSWORD}" \
|
||||||
|
-key "${CA_KEY}" \
|
||||||
|
-out "${CA_CERT}" \
|
||||||
|
-config /dev/stdin <<EOF
|
||||||
|
$(cat "${CONF_FILE}")
|
||||||
|
commonName_default = ${cn}
|
||||||
|
EOF
|
||||||
|
# shellcheck disable=SC2181
|
||||||
|
if [ "$?" -ne 0 ]; then
|
||||||
|
error "Error generating the CA certificate"
|
||||||
|
fi
|
||||||
|
|
||||||
|
"${OPENSSL_BIN}" ca \
|
||||||
|
-config "${CONF_FILE}" \
|
||||||
|
-passin pass:${CA_PASSWORD} \
|
||||||
|
-gencrl \
|
||||||
|
-out "${CRL}"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
ocsp() {
|
||||||
|
umask 0177
|
||||||
|
|
||||||
|
ocsp_uri="${1:-}"
|
||||||
|
if [ -z "${ocsp_uri}" ]; then
|
||||||
|
show_usage_ocsp >&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)
|
||||||
|
|
||||||
|
if [ ! -f "${OCSP_KEY}" ]; then
|
||||||
jdubois
commented
Option On Debian,
On OpenBSD,
I suggest to replace all "--canonicalize" occurences by "-f". Option `--canonicalize` is not compatible with OpenBSD, but option `-f` is :
On Debian, `man readlink` :
```
-f, --canonicalize
canonicalize by following every symlink in every component of
the given name recursively; all but the last component must ex‐
ist
```
On OpenBSD, `man readlink` :
```
-f Canonicalize by following every symlink in every component of the
given path recursively. readlink will resolve both absolute and
relative paths and return the absolute pathname corresponding to
file. The argument does not need to be a symbolic link.
```
I suggest to replace all "--canonicalize" occurences by "-f".
|
|||||||
|
"${OPENSSL_BIN}" genrsa \
|
||||||
|
-out "${OCSP_KEY}" \
|
||||||
|
"${KEY_LENGTH}" \
|
||||||
|
>/dev/null 2>&1
|
||||||
|
# shellcheck disable=SC2181
|
||||||
|
if [ "$?" -ne 0 ]; then
|
||||||
|
error "Error generating the OCSP key"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
"${OPENSSL_BIN}" req \
|
||||||
|
-batch \
|
||||||
|
-new \
|
||||||
|
-key "${OCSP_KEY}" \
|
||||||
|
-out "${ocsp_csr_file}" \
|
||||||
|
-config /dev/stdin <<EOF
|
||||||
|
$(cat "${CONF_FILE}")
|
||||||
|
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
|
||||||
|
|
||||||
|
if [ ! -f "${OCSP_CERT}" ]; then
|
||||||
|
ask_ca_password 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ ! -f "${OCSP_CERT}" ]; then
|
||||||
|
"${OPENSSL_BIN}" ca \
|
||||||
|
-extensions v3_ocsp \
|
||||||
|
-in "${ocsp_csr_file}" \
|
||||||
|
-out "${OCSP_CERT}" \
|
||||||
|
-passin pass:"${CA_PASSWORD}" \
|
||||||
|
-config "${CONF_FILE}"
|
||||||
|
# shellcheck disable=SC2181
|
||||||
|
if [ "$?" -ne 0 ]; then
|
||||||
|
error "Error generating the OCSP certificate"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
exec "${OPENSSL_BIN}" ocsp \
|
||||||
|
-ignore_err \
|
||||||
|
-index "${INDEX_FILE}" \
|
||||||
|
-port "${port}" \
|
||||||
|
-rsigner "${OCSP_CERT}" \
|
||||||
|
-rkey "${OCSP_KEY}" \
|
||||||
|
-CA "${CA_CERT}" \
|
||||||
|
-text
|
||||||
|
}
|
||||||
|
|
||||||
|
create() {
|
||||||
|
from_csr=0
|
||||||
|
ask_pass=0
|
||||||
|
non_interactive=0
|
||||||
|
replace_existing=0
|
||||||
|
days=""
|
||||||
|
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
|
||||||
|
while :; do
|
||||||
|
case ${1:-} in
|
||||||
|
-f|--file|--csr-file)
|
||||||
|
# csr-file option, with value separated by space
|
||||||
|
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
|
||||||
|
shift
|
||||||
|
else
|
||||||
|
error "Argument error: \`--csr-file' requires a value"
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
--file=?*|--csr-file=?*)
|
||||||
|
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
|
||||||
|
;;
|
||||||
|
--file=|--csr-file=)
|
||||||
|
# csr-file options, without value
|
||||||
|
error "Argument error: \`--csr-file' requires a value"
|
||||||
|
;;
|
||||||
|
-p|--password)
|
||||||
|
ask_pass=1
|
||||||
|
password_set=1
|
||||||
|
;;
|
||||||
|
--password-file)
|
||||||
|
# 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
|
||||||
|
password_file_set=1
|
||||||
|
shift
|
||||||
|
else
|
||||||
|
error "Argument error: \`--password-file' requires a value"
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
--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
|
||||||
|
password_file_set=1
|
||||||
|
;;
|
||||||
|
--password-file=)
|
||||||
|
# password-file options, without value
|
||||||
|
error "Argument error: \`--password-file' requires a value"
|
||||||
|
;;
|
||||||
|
--days)
|
||||||
|
# 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"
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
--days=?*)
|
||||||
|
# days option, with value separated by =
|
||||||
|
days=${1#*=}
|
||||||
|
days_set=1
|
||||||
|
;;
|
||||||
|
--days=)
|
||||||
|
# days options, without value
|
||||||
|
error "Argument error: \`--days' requires a value"
|
||||||
|
;;
|
||||||
|
--end-date)
|
||||||
|
# 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"
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
--end-date=?*)
|
||||||
|
# end-date option, with value separated by =
|
||||||
|
end_date=${1#*=}
|
||||||
|
end_date_set=1
|
||||||
|
;;
|
||||||
|
--end-date=)
|
||||||
|
# end-date options, without value
|
||||||
|
error "Argument error: \`--end-date' requires a value"
|
||||||
|
;;
|
||||||
|
--non-interactive)
|
||||||
|
non_interactive=1
|
||||||
|
;;
|
||||||
|
--replace-existing)
|
||||||
|
replace_existing=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
|
||||||
|
|
||||||
|
if [ "${days_set}" -eq 1 ] && [ "${end_date_set}" -eq 1 ]; then
|
||||||
|
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:-}"
|
||||||
|
|
||||||
|
# Set expiration argument
|
||||||
|
crt_expiration_arg=""
|
||||||
|
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'."
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
if [ -n "${end_date}" ]; then
|
||||||
|
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: 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 "%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: YYYY/MM/DD hh:mm:ss."
|
||||||
|
else
|
||||||
|
crt_expiration_arg="-enddate ${cert_end_date}"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
error "System ${SYSTEM} not supported."
|
||||||
|
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
|
||||||
|
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
|
||||||
|
|
||||||
|
crt_file="${CRT_DIR}/${cn}.crt"
|
||||||
|
|
||||||
# ask for CA passphrase
|
# ask for CA passphrase
|
||||||
ask_ca_password 0
|
ask_ca_password 0
|
||||||
|
|
||||||
# generate private key
|
# check if csr_file is a CSR
|
||||||
if [ "${with_pass}" -eq 0 ]; then
|
"${OPENSSL_BIN}" req \
|
||||||
PASSWORD="${PASSWORD}" "$OPENSSL" genrsa \
|
-noout \
|
||||||
-aes256 -passout env:PASSWORD \
|
-subject \
|
||||||
-out "${KEYDIR}/${cn}-${TIMESTAMP}.key" \
|
-in "${csr_file}" \
|
||||||
2048 >/dev/null 2>&1
|
>/dev/null 2>&1
|
||||||
else
|
# shellcheck disable=SC2181
|
||||||
"$OPENSSL" genrsa \
|
if [ "$?" -ne 0 ]; then
|
||||||
-out "${KEYDIR}/${cn}-${TIMESTAMP}.key" \
|
error "${csr_file} is not a valid CSR !"
|
||||||
2048 >/dev/null 2>&1
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ "${with_pass}" -eq 0 ]; then
|
# check if csr_file contain a CN
|
||||||
# generate csr req
|
"${OPENSSL_BIN}" req \
|
||||||
PASSWORD="${PASSWORD}" "$OPENSSL" req \
|
-noout \
|
||||||
-batch -new \
|
-subject \
|
||||||
-key "${KEYDIR}/${cn}-${TIMESTAMP}.key" \
|
-in "${csr_file}" \
|
||||||
-passin env:PASSWORD \
|
| grep -Eo "CN\s*=[^,/]*" \
|
||||||
-out "${CSRDIR}/${cn}-${TIMESTAMP}.csr" \
|
>/dev/null 2>&1
|
||||||
-config /dev/stdin <<EOF
|
# shellcheck disable=SC2181
|
||||||
$(cat "${CONFFILE}")
|
if [ "$?" -ne 0 ]; then
|
||||||
commonName_default = ${cn}
|
error "${csr_file} doesn't contain a CommonName !"
|
||||||
EOF
|
fi
|
||||||
else
|
|
||||||
# generate csr req
|
# get CN from CSR
|
||||||
"$OPENSSL" req \
|
cn=$("${OPENSSL_BIN}" req -noout -subject -in "${csr_file}" | grep -Eo "CN\s*=[^,/]*" | cut -d'=' -f2 | xargs)
|
||||||
-batch -new \
|
|
||||||
-key "${KEYDIR}/${cn}-${TIMESTAMP}.key" \
|
# check if CN already exists
|
||||||
-out "${CSRDIR}/${cn}-${TIMESTAMP}.csr" \
|
if [ -f "${crt_file}" ]; then
|
||||||
-config /dev/stdin <<EOF
|
replace_existing_or_abort "${cn}"
|
||||||
$(cat "${CONFFILE}")
|
|
||||||
commonName_default = ${cn}
|
|
||||||
EOF
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# ca sign and generate cert
|
# ca sign and generate cert
|
||||||
CA_PASSWORD="${CA_PASSWORD}" "${OPENSSL}" ca \
|
if [ "${non_interactive}" -eq 1 ]; then
|
||||||
-config "${CONFFILE}" \
|
batch_arg="-batch"
|
||||||
-passin env:CA_PASSWORD \
|
|
||||||
-in "${CSRDIR}/${cn}-${TIMESTAMP}.csr" \
|
|
||||||
-out "${CRTDIR}/${cn}.crt"
|
|
||||||
|
|
||||||
# check if CRT is a valid
|
|
||||||
"${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"
|
|
||||||
|
|
||||||
chmod 640 "${CRTDIR}/${cn}.crt"
|
|
||||||
|
|
||||||
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"
|
|
||||||
else
|
else
|
||||||
"${OPENSSL}" pkcs12 -export -nodes -passout pass: -inkey "${KEYDIR}/${cn}-${TIMESTAMP}.key" -in "${CRTDIR}/${cn}.crt" -out "${PKCS12DIR}/${cn}-${TIMESTAMP}.p12"
|
batch_arg=""
|
||||||
|
fi
|
||||||
|
"${OPENSSL_BIN}" ca \
|
||||||
|
${batch_arg} \
|
||||||
|
-config "${CONF_FILE}" \
|
||||||
|
-in "${csr_file}" \
|
||||||
|
-passin pass:"${CA_PASSWORD}" \
|
||||||
|
-out "${crt_file}" \
|
||||||
|
${crt_expiration_arg}
|
||||||
|
# shellcheck disable=SC2181
|
||||||
|
if [ "$?" -ne 0 ]; then
|
||||||
|
error "Error generating the certificate"
|
||||||
|
else
|
||||||
|
echo "The certificate file is available at \`${crt_file}'"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
if [ -z "${cn}" ]; then
|
||||||
|
show_usage_create >&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"
|
||||||
|
|
||||||
|
# ask for CA passphrase
|
||||||
|
ask_ca_password 0
|
||||||
|
|
||||||
|
if [ "${ask_pass}" -eq 1 ]; then
|
||||||
|
ask_user_password
|
||||||
fi
|
fi
|
||||||
|
|
||||||
chmod 640 "${PKCS12DIR}/${cn}-${TIMESTAMP}.p12"
|
# check if CN already exists
|
||||||
echo "The PKCS12 config file is available in ${PKCS12DIR}/${cn}-${TIMESTAMP}.p12"
|
if [ -f "${crt_file}" ]; then
|
||||||
|
replace_existing_or_abort "${cn}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# generate private key
|
||||||
|
pass_args=""
|
||||||
|
if [ -n "${password_file:-}" ]; then
|
||||||
|
pass_args="-aes256 -passout file:${password_file}"
|
||||||
|
elif [ -n "${PASSWORD:-}" ]; then
|
||||||
|
pass_args="-aes256 -passout pass:${PASSWORD}"
|
||||||
|
fi
|
||||||
|
"${OPENSSL_BIN}" genrsa \
|
||||||
|
-out "${key_file}" \
|
||||||
|
${pass_args} \
|
||||||
|
"${KEY_LENGTH}" \
|
||||||
|
>/dev/null 2>&1
|
||||||
|
# shellcheck disable=SC2181
|
||||||
|
if [ "$?" -eq 0 ]; then
|
||||||
|
echo "The KEY file is available at \`${key_file}'"
|
||||||
|
else
|
||||||
|
error "Error generating the private key"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# generate csr req
|
||||||
|
pass_args=""
|
||||||
|
if [ -n "${password_file:-}" ]; then
|
||||||
|
pass_args="-passin file:${password_file}"
|
||||||
|
elif [ -n "${PASSWORD:-}" ]; then
|
||||||
|
pass_args="-passin pass:${PASSWORD}"
|
||||||
|
fi
|
||||||
|
"${OPENSSL_BIN}" req \
|
||||||
|
-batch \
|
||||||
|
-new \
|
||||||
|
-key "${key_file}" \
|
||||||
|
-out "${csr_file}" \
|
||||||
|
${pass_args} \
|
||||||
|
-config /dev/stdin <<EOF
|
||||||
|
$(cat "${CONF_FILE}")
|
||||||
|
commonName_default = ${cn}
|
||||||
|
EOF
|
||||||
|
# shellcheck disable=SC2181
|
||||||
|
if [ "$?" -ne 0 ]; then
|
||||||
|
error "Error generating the CSR"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ca sign and generate cert
|
||||||
|
"${OPENSSL_BIN}" ca \
|
||||||
|
${batch_arg} \
|
||||||
|
-config "${CONF_FILE}" \
|
||||||
|
-passin pass:${CA_PASSWORD} \
|
||||||
|
-in "${csr_file}" \
|
||||||
|
-out "${crt_file}" \
|
||||||
|
${crt_expiration_arg}
|
||||||
|
# shellcheck disable=SC2181
|
||||||
|
if [ "$?" -ne 0 ]; then
|
||||||
|
error "Error generating the certificate"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# check if CRT is a valid
|
||||||
|
"${OPENSSL_BIN}" x509 \
|
||||||
|
-noout \
|
||||||
|
-subject \
|
||||||
|
-in "${crt_file}" \
|
||||||
|
>/dev/null 2>&1
|
||||||
|
# shellcheck disable=SC2181
|
||||||
|
if [ "$?" -ne 0 ]; then
|
||||||
|
rm -f "${crt_file}"
|
||||||
|
fi
|
||||||
|
if [ ! -f "${crt_file}" ]; then
|
||||||
|
error "Error in CSR creation"
|
||||||
|
fi
|
||||||
|
|
||||||
|
chmod 640 "${crt_file}"
|
||||||
|
|
||||||
|
echo "The CRT file is available in ${crt_file}"
|
||||||
|
|
||||||
|
# generate pkcs12 format
|
||||||
|
pass_args=""
|
||||||
|
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
|
||||||
|
pass_args="-passin pass:${PASSWORD} -passout pass:${PASSWORD}"
|
||||||
|
else
|
||||||
|
pass_args="-passout pass:"
|
||||||
|
fi
|
||||||
|
"${OPENSSL_BIN}" pkcs12 \
|
||||||
|
-export \
|
||||||
|
-nodes \
|
||||||
|
-inkey "${key_file}" \
|
||||||
jdubois
commented
This is not needed as "shellpki list" should, thanks to lines 771 and 772, list all valids certificates by default. This is not needed as "shellpki list" should, thanks to lines 771 and 772, list all valids certificates by default.
|
|||||||
|
-in "${crt_file}" \
|
||||||
|
-out "${pkcs12_file}" \
|
||||||
|
${pass_args}
|
||||||
|
# shellcheck disable=SC2181
|
||||||
|
if [ "$?" -ne 0 ]; then
|
||||||
|
error "Error generating the pkcs12 file"
|
||||||
|
fi
|
||||||
|
|
||||||
|
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 at \`${pkcs12_file}'"
|
||||||
|
|
||||||
# generate openvpn format
|
# generate openvpn format
|
||||||
if [ -e "${CADIR}/ovpn.conf" ]; then
|
if [ -e "${CA_DIR}/ovpn.conf" ]; then
|
||||||
cat "${CADIR}/ovpn.conf" - > "${OVPNDIR}/${cn}-${TIMESTAMP}.ovpn" <<EOF
|
cat "${CA_DIR}/ovpn.conf" - > "${ovpn_file}" <<EOF
|
||||||
<ca>
|
<ca>
|
||||||
$(cat "${CACERT}")
|
$(cat "${CA_CERT}")
|
||||||
</ca>
|
</ca>
|
||||||
|
|
||||||
<cert>
|
<cert>
|
||||||
$(cat "${CRTDIR}/${cn}.crt")
|
$(cat "${crt_file}")
|
||||||
</cert>
|
</cert>
|
||||||
|
|
||||||
<key>
|
<key>
|
||||||
$(cat "${KEYDIR}/${cn}-${TIMESTAMP}.key")
|
$(cat "${key_file}")
|
||||||
</key>
|
</key>
|
||||||
EOF
|
EOF
|
||||||
chmod 640 "${OVPNDIR}/${cn}-${TIMESTAMP}.ovpn"
|
chmod 640 "${ovpn_file}"
|
||||||
echo "The OpenVPN config file is available in ${OVPNDIR}/${cn}-${TIMESTAMP}.ovpn"
|
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
|
||||||
|
|
||||||
|
# shellcheck disable=SC2086
|
||||||
|
chown -R ${PKI_USER}:${PKI_USER} "${COPY_DIR}/"
|
||||||
|
chmod -R u=rwX,g=rwX,o= "${COPY_DIR}/"
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
revoke() {
|
revoke() {
|
||||||
[ "${1}" = "" ] && usage >&2 && exit 1
|
non_interactive=0
|
||||||
|
|
||||||
# get CN from param
|
# Parse options
|
||||||
cn="${1}"
|
# 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_revoke >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
crt_file="${CRT_DIR}/${cn}.crt"
|
||||||
# check if CRT exists
|
# check if CRT exists
|
||||||
[ ! -f "${CRTDIR}/${cn}.crt" ] && error "Unknow CN : ${cn}"
|
if [ ! -f "${crt_file}" ]; then
|
||||||
|
error "Unknow CN: ${cn} (\`${crt_file}' not found)"
|
||||||
|
fi
|
||||||
|
|
||||||
# check if CRT is a valid
|
# 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_BIN}" x509 \
|
||||||
|
-noout \
|
||||||
|
-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
|
||||||
|
|
||||||
# ask for CA passphrase
|
# ask for CA passphrase
|
||||||
ask_ca_password 0
|
ask_ca_password 0
|
||||||
|
|
||||||
echo "Revoke certificate ${CRTDIR}/${cn}.crt :"
|
echo "Revoke certificate ${crt_file} :"
|
||||||
CA_PASSWORD="${CA_PASSWORD}" "$OPENSSL" ca \
|
"${OPENSSL_BIN}" ca \
|
||||||
-config "${CONFFILE}" \
|
-config "${CONF_FILE}" \
|
||||||
-passin env:CA_PASSWORD \
|
-passin pass:"${CA_PASSWORD}" \
|
||||||
-revoke "${CRTDIR}/${cn}.crt" \
|
-revoke "${crt_file}"
|
||||||
&& rm "${CRTDIR}/${cn}.crt"
|
# shellcheck disable=SC2181
|
||||||
|
if [ "$?" -eq 0 ]; then
|
||||||
|
rm "${crt_file}"
|
||||||
|
fi
|
||||||
|
|
||||||
CA_PASSWORD="${CA_PASSWORD}" "$OPENSSL" ca \
|
"${OPENSSL_BIN}" ca \
|
||||||
-config "${CONFFILE}" \
|
-config "${CONF_FILE}" \
|
||||||
-passin env:CA_PASSWORD \
|
-passin pass:"${CA_PASSWORD}" \
|
||||||
-gencrl -out "${CRL}"
|
-gencrl \
|
||||||
|
-out "${CRL}"
|
||||||
}
|
}
|
||||||
|
|
||||||
list() {
|
list() {
|
||||||
[ -f "${INDEX}" ] || exit 0
|
if [ ! -f "${INDEX_FILE}" ]; then
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
list_valid=0
|
if [ -z "${1:-}" ]; then
|
||||||
list_revoked=1
|
show_usage_list >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
while :; do
|
while :; do
|
||||||
case "${1}" in
|
case "${1:-}" in
|
||||||
-a|--all)
|
-a|--all)
|
||||||
list_valid=0
|
list_valid=0
|
||||||
list_revoked=0
|
list_revoked=0
|
||||||
shift;;
|
;;
|
||||||
-v|--valid)
|
-v|--valid)
|
||||||
list_valid=0
|
list_valid=0
|
||||||
list_revoked=1
|
list_revoked=1
|
||||||
shift;;
|
;;
|
||||||
-r|--revoked)
|
-r|--revoked)
|
||||||
list_valid=1
|
list_valid=1
|
||||||
list_revoked=0
|
list_revoked=0
|
||||||
shift;;
|
;;
|
||||||
--)
|
|
||||||
shift
|
|
||||||
break;;
|
|
||||||
-?*)
|
-?*)
|
||||||
warning "unknow option ${1} (ignored)"
|
warning "unknow option ${1} (ignored)"
|
||||||
shift;;
|
;;
|
||||||
*)
|
*)
|
||||||
break;;
|
break
|
||||||
|
;;
|
||||||
esac
|
esac
|
||||||
|
shift
|
||||||
done
|
done
|
||||||
|
|
||||||
[ "${list_valid}" -eq 0 ] && certs=$(grep "^V" "${INDEX}")
|
if [ "${list_valid}" -eq 0 ]; then
|
||||||
|
certs=$(grep "^V" "${INDEX_FILE}")
|
||||||
|
fi
|
||||||
|
|
||||||
[ "${list_revoked}" -eq 0 ] && certs=$(grep "^R" "${INDEX}")
|
if [ "${list_revoked}" -eq 0 ]; then
|
||||||
|
certs=$(grep "^R" "${INDEX_FILE}")
|
||||||
|
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_FILE}")
|
||||||
|
fi
|
||||||
|
|
||||||
echo "${certs}" | grep -Eo "CN\s*=[^,/]*" | cut -d'=' -f2 | xargs -n1
|
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() {
|
check() {
|
||||||
# default expiration alert
|
# default expiration alert
|
||||||
# TODO: permit override with parameters
|
# TODO: permit override with parameters
|
||||||
min_day=90
|
min_day=90
|
||||||
cur_epoch=$(date -u +'%s')
|
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_date=$(cert_end_date "${cert}")
|
||||||
end_epoch=$(date -ud "${end_date}" +'%s')
|
end_epoch=$(date -ud "${end_date}" +'%s')
|
||||||
diff_epoch=$(( end_epoch - cur_epoch ))
|
diff_epoch=$(( end_epoch - cur_epoch ))
|
||||||
diff_day=$(( diff_epoch / 60 / 60 / 24 ))
|
diff_day=$(( diff_epoch / 60 / 60 / 24 ))
|
||||||
|
@ -410,43 +980,72 @@ check() {
|
||||||
done
|
done
|
||||||
}
|
}
|
||||||
|
|
||||||
|
is_user() {
|
||||||
|
getent passwd "${1}" >/dev/null
|
||||||
|
}
|
||||||
|
is_group() {
|
||||||
|
getent group "${1}" >/dev/null
|
||||||
|
}
|
||||||
|
|
||||||
main() {
|
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
|
# default config
|
||||||
# TODO: override with /etc/default/shellpki
|
# TODO: override with /etc/default/shellpki
|
||||||
CONFFILE="/etc/shellpki/openssl.cnf"
|
CONF_FILE="/etc/shellpki/openssl.cnf"
|
||||||
PKIUSER="shellpki"
|
|
||||||
[ "$(uname)" = "OpenBSD" ] && PKIUSER="_shellpki"
|
|
||||||
|
|
||||||
[ "${USER}" != "root" ] || [ "${USER}" != "${PKIUSER}" ] || error "Please become root before running ${0} !"
|
if [ "$(uname)" = "OpenBSD" ]; then
|
||||||
|
PKI_USER="_shellpki"
|
||||||
# retrieve CA path from config file
|
else
|
||||||
CADIR=$(grep -E "^dir" "${CONFFILE}" | cut -d'=' -f2|xargs -n1)
|
PKI_USER="shellpki"
|
||||||
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}~")
|
|
||||||
|
|
||||||
# directories for clients key, csr, crt
|
|
||||||
KEYDIR="${CADIR}/private"
|
|
||||||
CSRDIR="${CADIR}/requests"
|
|
||||||
PKCS12DIR="${CADIR}/pkcs12"
|
|
||||||
OVPNDIR="${CADIR}/openvpn"
|
|
||||||
|
|
||||||
OPENSSL=$(command -v openssl)
|
|
||||||
TIMESTAMP=$(/bin/date +"%s")
|
|
||||||
|
|
||||||
if ! getent passwd "${PKIUSER}" >/dev/null || ! getent group "${PKIUSER}" >/dev/null; then
|
|
||||||
error "You must create ${PKIUSER} user and group !"
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
[ -e "${CONFFILE}" ] || error "${CONFFILE} is missing"
|
if [ "${USER}" != "root" ] && [ "${USER}" != "${PKI_USER}" ]; then
|
||||||
|
error "Please become root before running ${0} !"
|
||||||
|
fi
|
||||||
|
|
||||||
mkdir -p "${CADIR}" "${CRTDIR}" "${KEYDIR}" "${CSRDIR}" "${PKCS12DIR}" "${OVPNDIR}" "${TMPDIR}"
|
# retrieve CA path from config file
|
||||||
|
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
|
||||||
|
KEY_DIR="${CA_DIR}/private"
|
||||||
|
CSR_DIR="${CA_DIR}/requests"
|
||||||
|
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."
|
||||||
|
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=$(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 !"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ ! -e "${CONF_FILE}" ]; then
|
||||||
|
error "${CONF_FILE} is missing"
|
||||||
|
fi
|
||||||
|
|
||||||
|
mkdir -p "${CA_DIR}" "${CRT_DIR}" "${KEY_DIR}" "${CSR_DIR}" "${PKCS12_DIR}" "${OVPN_DIR}" "${TMP_DIR}"
|
||||||
|
|
||||||
command=${1:-help}
|
command=${1:-help}
|
||||||
|
|
||||||
|
@ -481,17 +1080,27 @@ main() {
|
||||||
check "$@"
|
check "$@"
|
||||||
;;
|
;;
|
||||||
|
|
||||||
|
version|--version)
|
||||||
|
show_version
|
||||||
|
exit 0
|
||||||
|
;;
|
||||||
|
|
||||||
|
help|--help)
|
||||||
|
show_usage
|
||||||
|
exit 0
|
||||||
|
;;
|
||||||
|
|
||||||
*)
|
*)
|
||||||
usage >&2
|
show_usage >&2
|
||||||
exit 1
|
exit 1
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
|
|
||||||
# fix right
|
# fix right
|
||||||
chown -R "${PKIUSER}":"${PKIUSER}" "${CADIR}"
|
chown -R "${PKI_USER}":"${PKI_USER}" "${CA_DIR}"
|
||||||
chmod 750 "${CADIR}" "${CRTDIR}" "${KEYDIR}" "${CSRDIR}" "${PKCS12DIR}" "${OVPNDIR}" "${TMPDIR}"
|
chmod 750 "${CA_DIR}" "${CRT_DIR}" "${KEY_DIR}" "${CSR_DIR}" "${PKCS12_DIR}" "${OVPN_DIR}" "${TMP_DIR}"
|
||||||
chmod 600 "${INDEX}"* "${SERIAL}"* "${CAKEY}" "${CRL}"
|
chmod 600 "${INDEX_FILE}"* "${SERIAL}"* "${CA_KEY}" "${CRL}"
|
||||||
chmod 640 "${CACERT}"
|
chmod 640 "${CA_CERT}"
|
||||||
}
|
}
|
||||||
|
|
||||||
main "$@"
|
main "$@"
|
||||||
|
|
Loading…
Reference in a new issue
This cause somes bugs, especially with "list" options.
Whit "set -u" :
Without "set -u" :
This bug is also present on the current master version if I add "set -u" but I cannot find the reason.
Not sure if some code need a fix or not, but I think we should at least remove "set -u".
This option also cause the error messages added in the code to handle a missing variable to not be displayed.
I'm pretty sure we can find a way to keep the security added by
set -u
and fix this.