Complete refactoring
This commit is contained in:
parent
6684fb4d71
commit
771066ff5d
3 changed files with 216 additions and 205 deletions
42
README.md
42
README.md
|
@ -1,24 +1,38 @@
|
|||
# shellpki
|
||||
|
||||
This script is a wrapper around openssl to manage all the pki stuff
|
||||
for openvpn.
|
||||
This script is a wrapper around openssl to manage a small PKI.
|
||||
|
||||
# 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
|
||||
cp /path/to/shellpki.sh /etc/openvpn/ssl/
|
||||
cp /path/to/openssl.cnf /etc/openvpn/ssl/
|
||||
$EDITOR /etc/openvpn/ssl/openssl.cnf
|
||||
## Usage
|
||||
|
||||
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
|
||||
~~~
|
||||
|
|
10
openssl.cnf
10
openssl.cnf
|
@ -2,13 +2,13 @@
|
|||
default_ca = CA_default
|
||||
|
||||
[ CA_default ]
|
||||
dir = /etc/openvpn/ssl/ca
|
||||
certs = /etc/openvpn/ssl/certs
|
||||
new_certs_dir = /etc/openvpn/ssl/ca/tmp
|
||||
dir = /etc/shellpki/ca
|
||||
certs = $dir/certs
|
||||
new_certs_dir = $dir/tmp
|
||||
database = $dir/index.txt
|
||||
certificate = $dir/cacert.pem
|
||||
serial = $dir/serial
|
||||
crl = /etc/openvpn/ssl/crl.pem
|
||||
crl = $dir/crl.pem
|
||||
private_key = $dir/private.key
|
||||
RANDFILE = $dir/.rand
|
||||
default_days = 365
|
||||
|
@ -51,5 +51,3 @@ commonName_max = 64
|
|||
emailAddress = Email Address
|
||||
emailAddress_default = security@evolix.net
|
||||
emailAddress_max = 40
|
||||
|
||||
|
||||
|
|
369
shellpki.sh
369
shellpki.sh
|
@ -1,239 +1,238 @@
|
|||
#!/bin/sh
|
||||
|
||||
PREFIX=/etc/openvpn/ssl
|
||||
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
|
||||
#
|
||||
# shellpki is a wrapper around openssl to manage a small PKI
|
||||
#
|
||||
|
||||
init() {
|
||||
echo "Do you confirm ${0##*/} initialization?"
|
||||
echo
|
||||
echo "Press return to continue..."
|
||||
read REPLY
|
||||
echo
|
||||
umask 0177
|
||||
|
||||
if [ ! -d $PREFIX/ca ]; then mkdir -p $PREFIX/ca; fi
|
||||
if [ ! -d $PREFIX/ca/tmp ]; then mkdir -p $PREFIX/ca/tmp; fi
|
||||
if [ ! -d $PREFIX/certs ]; then mkdir -p $PREFIX/certs; fi
|
||||
if [ ! -d $PREFIX/files ]; then mkdir -p $PREFIX/files; fi
|
||||
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
|
||||
if [ -f "${CADIR}/private.key" ]; then
|
||||
echo "${CADIR}/private.key already exists, do you really want to erase it ?\n"
|
||||
echo "Press return to continue..."
|
||||
read -r REPLY
|
||||
fi
|
||||
|
||||
$OPENSSL dhparam -out $PREFIX/ca/dh2048.pem 2048
|
||||
$OPENSSL genrsa -out $PREFIX/ca/private.key 4096
|
||||
[ -d "${CADIR}" ] || mkdir -pm 0700 "${CADIR}"
|
||||
[ -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 \
|
||||
-config $CONFFILE \
|
||||
-new -x509 -days 3650 \
|
||||
-extensions v3_ca \
|
||||
-keyout $PREFIX/ca/private.key \
|
||||
-out $PREFIX/ca/cacert.pem
|
||||
"${OPENSSL}" req \
|
||||
-config "${CONFFILE}" \
|
||||
-newkey rsa:4096 -sha512 \
|
||||
-x509 -days 3650 \
|
||||
-extensions v3_ca \
|
||||
-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() {
|
||||
umask 0137
|
||||
echo "Please enter your CN (Common Name)"
|
||||
read cn
|
||||
read -r cn
|
||||
echo
|
||||
echo "Your CN is '$cn'"
|
||||
echo "Your CN is '${cn}'"
|
||||
echo "Press return to continue..."
|
||||
read REPLY
|
||||
read -r REPLY
|
||||
echo
|
||||
|
||||
if [ -e $PREFIX/certs/$cn.crt ]; then
|
||||
echo "Please revoke actual $cn cert before creating one"
|
||||
echo
|
||||
echo "Press return to continue..."
|
||||
read REPLY
|
||||
exit 1
|
||||
# check if CN already exist
|
||||
check_cn "${cn}"
|
||||
|
||||
# generate private key
|
||||
echo "Should private key be protected by a passphrase? [y/N] "
|
||||
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
|
||||
|
||||
DIR=$PREFIX/files/$cn-$TIMESTAMP
|
||||
mkdir $DIR
|
||||
# generate csr req
|
||||
"$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
|
||||
echo -n "Should private key be protected by a passphrase? [y/N] "
|
||||
read REPLY
|
||||
if [ "$REPLY" = "y" ] || [ "$REPLY" = "Y" ]; then
|
||||
$OPENSSL genrsa -aes128 -out $DIR/$cn.key 2048
|
||||
else
|
||||
$OPENSSL genrsa -out $DIR/$cn.key 2048
|
||||
fi
|
||||
# ca sign and generate cert
|
||||
"${OPENSSL}" ca \
|
||||
-config "${CONFFILE}" \
|
||||
-in "${CSRDIR}/${cn}-${TIMESTAMP}.csr" \
|
||||
-out "${CADIR}/certs/${cn}.crt"
|
||||
|
||||
# generate csr req
|
||||
$OPENSSL req \
|
||||
-new \
|
||||
-key $DIR/$cn.key \
|
||||
-config $CONFFILE \
|
||||
-out $DIR/$cn.csr
|
||||
# generate pem format
|
||||
cat "${CADIR}/certs/${cn}.crt" "${CADIR}/cacert.pem" "${KEYDIR}/${cn}-${TIMESTAMP}.key" >> "${PEMDIR}/${cn}-${TIMESTAMP}.pem"
|
||||
|
||||
# ca sign and generate cert
|
||||
$OPENSSL ca \
|
||||
-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 pkcs12 format
|
||||
openssl pkcs12 -export -nodes -passout pass: -inkey "${KEYDIR}/${cn}-${TIMESTAMP}.key" -in "${CADIR}/certs/${cn}.crt" -out "${PKCS12DIR}/${cn}-${TIMESTAMP}.p12"
|
||||
|
||||
# generate openvpn format
|
||||
if [ -e "${PREFIX}/ovpn.conf" ]; then
|
||||
cat "${PREFIX}/ovpn.conf" > "${OVPNDIR}/${cn}-${TIMESTAMP}.ovpn" <<EOF
|
||||
<ca>
|
||||
$(cat $CA)
|
||||
$(cat "${CADIR}/cacert.pem")
|
||||
</ca>
|
||||
|
||||
<cert>
|
||||
$(cat $CERT)
|
||||
$(cat "${CADIR}/certs/${cn}.crt")
|
||||
</cert>
|
||||
|
||||
<key>
|
||||
$(cat $KEY)
|
||||
$(cat "${KEYDIR}/${cn}-${TIMESTAMP}.key")
|
||||
</key>
|
||||
" >> $REP/$cn.conf
|
||||
|
||||
echo "The configuration file is available in $REP/$cn.conf"
|
||||
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
|
||||
EOF
|
||||
echo "The configuration file is available in ${OVPNDIR}/${cn}.ovpn"
|
||||
fi
|
||||
}
|
||||
|
||||
fromcsr() {
|
||||
echo "Please enter path for your CSR request file"
|
||||
read path
|
||||
read -r path
|
||||
echo
|
||||
|
||||
if [ ! -e $path ]; then
|
||||
echo "Error in path..." >&2
|
||||
echo >&2
|
||||
echo "Press return to continue..." >&2
|
||||
read REPLY
|
||||
exit 1
|
||||
if [ ! -e "${path}" ]; then
|
||||
echo "Error in path..." >&2
|
||||
echo >&2
|
||||
echo "Press return to continue..." >&2
|
||||
read -r REPLY
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Please enter the CN (Common Name)"
|
||||
read cn
|
||||
path=$(readlink -f "${path}")
|
||||
|
||||
# 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 "Your CN is '$cn'"
|
||||
echo "CN '${cn}' will be revoked"
|
||||
echo "Press return to continue..."
|
||||
read REPLY
|
||||
read -r REPLY
|
||||
echo
|
||||
|
||||
DIR=$PREFIX/files/req_$cn-$TIMESTAMP
|
||||
mkdir $DIR
|
||||
[ ! -f "${CADIR}/certs/${cn}.crt" ] && echo "Unknow CN : ${cn}" >&2 && exit 1
|
||||
|
||||
cp $path $DIR
|
||||
|
||||
# ca sign and generate cert
|
||||
$OPENSSL ca \
|
||||
-config $CONFFILE \
|
||||
-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 "Revoke certificate ${CADIR}/certs/${cn}.crt :"
|
||||
"$OPENSSL" ca \
|
||||
-config "${CONFFILE}" \
|
||||
-revoke "${CADIR}/certs/${cn}.crt" \
|
||||
&& rm "${CADIR}/certs/${cn}.crt"
|
||||
|
||||
echo "Update CRL :"
|
||||
"$OPENSSL" ca \
|
||||
-config "${CONFFILE}" \
|
||||
-gencrl -out "${CADIR}/crl.pem"
|
||||
}
|
||||
|
||||
|
||||
crl() {
|
||||
|
||||
$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
|
||||
|
||||
list() {
|
||||
echo "* List of allowed CN :"
|
||||
ls -1 "${CADIR}/certs"
|
||||
}
|
||||
|
||||
case "$1" in
|
||||
init)
|
||||
init
|
||||
;;
|
||||
main() {
|
||||
if [ "$(id -u)" != "0" ]; then
|
||||
echo "Please become root before running ${0##*/}!" >&2
|
||||
echo >&2
|
||||
echo "Press return to continue..." >&2
|
||||
read -r REPLY
|
||||
exit 1
|
||||
fi
|
||||
|
||||
create)
|
||||
create
|
||||
;;
|
||||
# main vars
|
||||
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)
|
||||
fromcsr
|
||||
;;
|
||||
if ! getent passwd "${PKIUSER}" >/dev/null || ! getent group "${PKIUSER}" >/dev/null; then
|
||||
echo "You must create ${PKIUSER} user and group !" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
revoke)
|
||||
revoke
|
||||
;;
|
||||
if [ ! -e "${CONFFILE}" ]; then
|
||||
echo "${CONFFILE} is missing" >&2
|
||||
>&2
|
||||
echo "Press return to continue..." >&2
|
||||
read -r REPLY
|
||||
exit 1
|
||||
fi
|
||||
|
||||
crl)
|
||||
crl
|
||||
;;
|
||||
# create needed dir
|
||||
[ -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}"
|
||||
|
||||
*)
|
||||
echo "Usage: ${0##*/} {init|create|fromcsr|revoke|crl}" >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
# fix right
|
||||
find "${PREFIX}" ! -path "${CADIR}" -exec chown "${PKIUSER}":"${PKIUSER}" {} \; -exec chmod u=rwX,g=rX,o= {} \;
|
||||
|
||||
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 "$@"
|
||||
|
|
Loading…
Add table
Reference in a new issue