Complete refactoring

This commit is contained in:
Victor LABORIE 2018-01-17 12:21:39 +01:00
parent 6684fb4d71
commit 771066ff5d
3 changed files with 216 additions and 205 deletions

View file

@ -1,24 +1,38 @@
# shellpki # shellpki
This script is a wrapper around openssl to manage all the pki stuff This script is a wrapper around openssl to manage a small PKI.
for openvpn.
# Usage ## Install
First create the directory, put the script in it and the openssl ~~~
configuration file. You may certainly need to edit the configuration. mkdir /etc/shellpki
useradd shellpki --system -M --home-dir /etc/shellpki --shell /usr/sbin/nologin
install -m 0640 openssl.cnf /etc/shellpki/
install -m 0755 shellpki.sh /usr/local/sbin/shellpki
~~~
mkdir -p /etc/openvpn/ssl ## Usage
cp /path/to/shellpki.sh /etc/openvpn/ssl/
cp /path/to/openssl.cnf /etc/openvpn/ssl/
$EDITOR /etc/openvpn/ssl/openssl.cnf
Then you'll need to initialize the pki. Initialize PKI creating CA key and certificate :
cd /etc/openvpn/ssl ~~~
sh shellpki.sh init shellpki init
~~~
Once it's done, you can create all the certificates you need. Create a certificate and key on the server :
sh shellpki.sh create ~~~
shellpki create
~~~
Create a certificate without key from a CSR :
~~~
shellpki fromcsr
~~~
Revoke a certificate :
~~~
shellpki revoke
~~~

View file

@ -2,13 +2,13 @@
default_ca = CA_default default_ca = CA_default
[ CA_default ] [ CA_default ]
dir = /etc/openvpn/ssl/ca dir = /etc/shellpki/ca
certs = /etc/openvpn/ssl/certs certs = $dir/certs
new_certs_dir = /etc/openvpn/ssl/ca/tmp new_certs_dir = $dir/tmp
database = $dir/index.txt database = $dir/index.txt
certificate = $dir/cacert.pem certificate = $dir/cacert.pem
serial = $dir/serial serial = $dir/serial
crl = /etc/openvpn/ssl/crl.pem crl = $dir/crl.pem
private_key = $dir/private.key private_key = $dir/private.key
RANDFILE = $dir/.rand RANDFILE = $dir/.rand
default_days = 365 default_days = 365
@ -51,5 +51,3 @@ commonName_max = 64
emailAddress = Email Address emailAddress = Email Address
emailAddress_default = security@evolix.net emailAddress_default = security@evolix.net
emailAddress_max = 40 emailAddress_max = 40

View file

@ -1,239 +1,238 @@
#!/bin/sh #!/bin/sh
#
PREFIX=/etc/openvpn/ssl # shellpki is a wrapper around openssl to manage a small PKI
CONFFILE=$PREFIX/openssl.cnf #
OPENSSL=$(which openssl)
TIMESTAMP=$(/bin/date +"%s")
WWWDIR=/var/www/htdocs/vpn/ssl
if [ "$(id -u)" != "0" ]; then
echo "Please become root before running ${0##*/}!" >&2
echo >&2
echo "Press return to continue..." >&2
read REPLY
exit 1
fi
init() { init() {
echo "Do you confirm ${0##*/} initialization?" umask 0177
echo
echo "Press return to continue..."
read REPLY
echo
if [ ! -d $PREFIX/ca ]; then mkdir -p $PREFIX/ca; fi if [ -f "${CADIR}/private.key" ]; then
if [ ! -d $PREFIX/ca/tmp ]; then mkdir -p $PREFIX/ca/tmp; fi echo "${CADIR}/private.key already exists, do you really want to erase it ?\n"
if [ ! -d $PREFIX/certs ]; then mkdir -p $PREFIX/certs; fi echo "Press return to continue..."
if [ ! -d $PREFIX/files ]; then mkdir -p $PREFIX/files; fi read -r REPLY
if [ ! -f $PREFIX/ca/index.txt ]; then touch $PREFIX/ca/index.txt; fi
if [ ! -f $PREFIX/files/ca/serial ]; then echo 01 > $PREFIX/ca/serial; fi
if [ ! -e "$CONFFILE" ]; then
echo "$CONFFILE is missing" >&2
echo >&2
echo "Press return to continue..." >&2
read REPLY
exit 1
fi fi
$OPENSSL dhparam -out $PREFIX/ca/dh2048.pem 2048 [ -d "${CADIR}" ] || mkdir -pm 0700 "${CADIR}"
$OPENSSL genrsa -out $PREFIX/ca/private.key 4096 [ -d "${CADIR}/certs" ] || mkdir -m 0777 "${CADIR}/certs"
[ -d "${CADIR}/tmp" ] || mkdir -m 0700 "${CADIR}/tmp"
[ -f "${CADIR}/index.txt" ] || touch "${CADIR}/index.txt"
[ -f "${CADIR}/serial" ] || echo "01" > "${CADIR}/serial"
$OPENSSL req \ "${OPENSSL}" req \
-config $CONFFILE \ -config "${CONFFILE}" \
-new -x509 -days 3650 \ -newkey rsa:4096 -sha512 \
-extensions v3_ca \ -x509 -days 3650 \
-keyout $PREFIX/ca/private.key \ -extensions v3_ca \
-out $PREFIX/ca/cacert.pem -keyout "${CADIR}/private.key" \
-out "${CADIR}/cacert.pem"
}
check_cn() {
cn="${1}"
if [ -f "${CADIR}/certs/${cn}.crt" ]; then
echo "Please revoke actual ${cn} cert before creating one"
echo
echo "Press return to continue..."
read -r REPLY
exit 1
fi
} }
create() { create() {
umask 0137
echo "Please enter your CN (Common Name)" echo "Please enter your CN (Common Name)"
read cn read -r cn
echo echo
echo "Your CN is '$cn'" echo "Your CN is '${cn}'"
echo "Press return to continue..." echo "Press return to continue..."
read REPLY read -r REPLY
echo echo
if [ -e $PREFIX/certs/$cn.crt ]; then # check if CN already exist
echo "Please revoke actual $cn cert before creating one" check_cn "${cn}"
echo
echo "Press return to continue..." # generate private key
read REPLY echo "Should private key be protected by a passphrase? [y/N] "
exit 1 read -r REPLY
if [ "$REPLY" = "y" ] || [ "$REPLY" = "Y" ]; then
"$OPENSSL" genrsa -aes256 -out "${KEYDIR}/${cn}-${TIMESTAMP}.key" 2048
else
"$OPENSSL" genrsa -out "${KEYDIR}/${cn}-${TIMESTAMP}.key" 2048
fi fi
DIR=$PREFIX/files/$cn-$TIMESTAMP # generate csr req
mkdir $DIR "$OPENSSL" req -batch \
-new \
-key "${KEYDIR}/${cn}-${TIMESTAMP}.key" \
-out "${CSRDIR}/${cn}-${TIMESTAMP}.csr" \
-config /dev/stdin <<EOF
$(cat "${CONFFILE}")
commonName_default = ${cn}
EOF
# generate private key # ca sign and generate cert
echo -n "Should private key be protected by a passphrase? [y/N] " "${OPENSSL}" ca \
read REPLY -config "${CONFFILE}" \
if [ "$REPLY" = "y" ] || [ "$REPLY" = "Y" ]; then -in "${CSRDIR}/${cn}-${TIMESTAMP}.csr" \
$OPENSSL genrsa -aes128 -out $DIR/$cn.key 2048 -out "${CADIR}/certs/${cn}.crt"
else
$OPENSSL genrsa -out $DIR/$cn.key 2048
fi
# generate csr req # generate pem format
$OPENSSL req \ cat "${CADIR}/certs/${cn}.crt" "${CADIR}/cacert.pem" "${KEYDIR}/${cn}-${TIMESTAMP}.key" >> "${PEMDIR}/${cn}-${TIMESTAMP}.pem"
-new \
-key $DIR/$cn.key \
-config $CONFFILE \
-out $DIR/$cn.csr
# ca sign and generate cert # generate pkcs12 format
$OPENSSL ca \ openssl pkcs12 -export -nodes -passout pass: -inkey "${KEYDIR}/${cn}-${TIMESTAMP}.key" -in "${CADIR}/certs/${cn}.crt" -out "${PKCS12DIR}/${cn}-${TIMESTAMP}.p12"
-config $CONFFILE \
-in $DIR/$cn.csr \
-out $DIR/$cn.crt
# pem cert style
cp $DIR/$cn.key $DIR/$cn.pem
cat $DIR/$cn.crt >> $DIR/$cn.pem
# copy to public certs dir
if [ -d "$WWWDIR" ]; then
echo
echo "copy cert to public certs dir"
echo
cp -i $DIR/$cn.crt $PREFIX/certs/
cp -i $DIR/$cn.crt $WWWDIR/
cp -i $DIR/$cn.key $WWWDIR/
chown -R root:www $WWWDIR
chmod -R u=rwX,g=rwX,o= $WWWDIR
echo
fi
# generate client configuration
if [ -e $PREFIX/template.conf ]; then
CA=$PREFIX/ca/cacert.pem
CERT=$WWWDIR/$cn.crt
KEY=$WWWDIR/$cn.key
REP=/tmp
cp $PREFIX/template.conf $REP/$cn.conf
echo "
# generate openvpn format
if [ -e "${PREFIX}/ovpn.conf" ]; then
cat "${PREFIX}/ovpn.conf" > "${OVPNDIR}/${cn}-${TIMESTAMP}.ovpn" <<EOF
<ca> <ca>
$(cat $CA) $(cat "${CADIR}/cacert.pem")
</ca> </ca>
<cert> <cert>
$(cat $CERT) $(cat "${CADIR}/certs/${cn}.crt")
</cert> </cert>
<key> <key>
$(cat $KEY) $(cat "${KEYDIR}/${cn}-${TIMESTAMP}.key")
</key> </key>
" >> $REP/$cn.conf EOF
echo "The configuration file is available in ${OVPNDIR}/${cn}.ovpn"
echo "The configuration file is available in $REP/$cn.conf" fi
fi
}
revoke() {
echo "Please enter CN (Common Name) to revoke"
read cn
echo
echo "CN '$cn' will be revoked"
echo "Press return to continue..."
read REPLY
echo
$OPENSSL ca \
-config $CONFFILE \
-revoke $PREFIX/certs/$cn.crt
rm -i $PREFIX/certs/$cn.crt
if [ -d "$WWWDIR" ]; then
rm -i $WWWDIR/$cn.crt
rm -i $WWWDIR/$cn.key
fi
} }
fromcsr() { fromcsr() {
echo "Please enter path for your CSR request file" echo "Please enter path for your CSR request file"
read path read -r path
echo echo
if [ ! -e $path ]; then if [ ! -e "${path}" ]; then
echo "Error in path..." >&2 echo "Error in path..." >&2
echo >&2 echo >&2
echo "Press return to continue..." >&2 echo "Press return to continue..." >&2
read REPLY read -r REPLY
exit 1 exit 1
fi fi
echo "Please enter the CN (Common Name)" path=$(readlink -f "${path}")
read cn
# get CN from CSR
cn=$(openssl req -noout -subject -in "${path}"|grep -Eo "CN=[^/]*"|cut -d'=' -f2)
# check if CN already exist
check_cn "${cn}"
# copy CSR to CSRDIR
cp "$path" "${CSRDIR}/${cn}-${TIMESTAMP}.csr"
# ca sign and generate cert
"${OPENSSL}" ca \
-config "${CONFFILE}" \
-in "${CSRDIR}/${cn}-${TIMESTAMP}.csr" \
-out "${CADIR}/certs/${cn}.crt"
}
revoke() {
echo "Please enter CN (Common Name) to revoke"
read -r cn
echo echo
echo "Your CN is '$cn'" echo "CN '${cn}' will be revoked"
echo "Press return to continue..." echo "Press return to continue..."
read REPLY read -r REPLY
echo echo
DIR=$PREFIX/files/req_$cn-$TIMESTAMP [ ! -f "${CADIR}/certs/${cn}.crt" ] && echo "Unknow CN : ${cn}" >&2 && exit 1
mkdir $DIR
cp $path $DIR echo "Revoke certificate ${CADIR}/certs/${cn}.crt :"
"$OPENSSL" ca \
# ca sign and generate cert -config "${CONFFILE}" \
$OPENSSL ca \ -revoke "${CADIR}/certs/${cn}.crt" \
-config $CONFFILE \ && rm "${CADIR}/certs/${cn}.crt"
-in $path \
-out $DIR/$cn.crt
# copy to public certs dir
echo
echo "copy cert to public certs dir"
echo
cp -i $DIR/$cn.crt $PREFIX/certs/
echo
echo "Update CRL :"
"$OPENSSL" ca \
-config "${CONFFILE}" \
-gencrl -out "${CADIR}/crl.pem"
} }
list() {
crl() { echo "* List of allowed CN :"
ls -1 "${CADIR}/certs"
$OPENSSL ca -gencrl \
-config $CONFFILE \
-out crl.pem
# TODO : a voir pour l'importation pdts Mozilla, Apple et Microsoft
#openssl crl2pkcs7 -in crl.pem -certfile /etc/ssl/certs/cacert.pem -out p7.pem
} }
case "$1" in main() {
init) if [ "$(id -u)" != "0" ]; then
init echo "Please become root before running ${0##*/}!" >&2
;; echo >&2
echo "Press return to continue..." >&2
read -r REPLY
exit 1
fi
create) # main vars
create PREFIX="/etc/shellpki"
;; PKIUSER="shellpki"
CONFFILE="${PREFIX}/openssl.cnf"
CADIR=$(grep -E "^dir" "${CONFFILE}" | cut -d'=' -f2|xargs -n1)
OPENSSL=$(command -v openssl)
TIMESTAMP=$(/bin/date +"%s")
# directories for clients key, csr, crt
KEYDIR="${PREFIX}/private"
CSRDIR="${PREFIX}/requests"
PEMDIR="${PREFIX}/pem"
PKCS12DIR="${PREFIX}/pkcs12"
OVPNDIR="${PREFIX}/openvpn"
fromcsr) if ! getent passwd "${PKIUSER}" >/dev/null || ! getent group "${PKIUSER}" >/dev/null; then
fromcsr echo "You must create ${PKIUSER} user and group !" >&2
;; exit 1
fi
revoke) if [ ! -e "${CONFFILE}" ]; then
revoke echo "${CONFFILE} is missing" >&2
;; >&2
echo "Press return to continue..." >&2
read -r REPLY
exit 1
fi
crl) # create needed dir
crl [ -d "${PREFIX}" ] || mkdir -p "${PREFIX}"
;; [ -d "${KEYDIR}" ] || mkdir -m 0750 "${KEYDIR}"
[ -d "${CSRDIR}" ] || mkdir -m 0755 "${CSRDIR}"
[ -d "${PEMDIR}" ] || mkdir -m 0750 "${PEMDIR}"
[ -d "${PKCS12DIR}" ] || mkdir -m 0750 "${PKCS12DIR}"
[ -d "${OVPNDIR}" ] || mkdir -m 0750 "${OVPNDIR}"
*) # fix right
echo "Usage: ${0##*/} {init|create|fromcsr|revoke|crl}" >&2 find "${PREFIX}" ! -path "${CADIR}" -exec chown "${PKIUSER}":"${PKIUSER}" {} \; -exec chmod u=rwX,g=rX,o= {} \;
exit 1
;;
esac
case "$1" in
init)
init
;;
create)
create
;;
fromcsr)
fromcsr
;;
revoke)
revoke
;;
list)
list
;;
*)
echo "Usage: ${0} {init|create|fromcsr|revoke|list}" >&2
exit 1
;;
esac
}
main "$@"