You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

354 lines
8.5 KiB

6 years ago
#!/bin/sh
# Filename: gencert.sh
6 years ago
# Description: This script generates x509 server certificate (with all IPs in
# SAN) signed by a self-signed CA.
# Version: 1.6 - 2018 December 27
6 years ago
# Author: Andrey Arapov <andrey.arapov@nixaid.com>
# License: GPLv3
ME=$(printf '%s\n' "${0##*/}")
6 years ago
print_help() {
printf "[${ME}] HELP: I accept following arguments:
--help - show this message
--cn - certificate's CN name\t\t(MANDATORY)
--key - server key name\t\t\t(default: private.key)
--cert - server cert name\t\t\t(default: public.crt)
--days - server cert expiration in days\t(default: 365)
--cakey - CA key name\t\t\t(default: ca.key)
--ca - CA cert name\t\t\t(default: ca.crt)
--cadays - CA cert expiration in days\t(default: 3650)\n
--noautosan - do not automatically discover IPs for SAN records\n
--san-ip - specify custom SAN IP records manually. Implies --noautosan\n
--san-dns - specify custom SAN DNS records manually.\n
--debug - show extra information\n
--rsa - generate RSA keys instead of ECDSA\n
--rsa-size - set RSA key size\n"
6 years ago
}
# Parse command line arguments
##
6 years ago
parse_arguments() {
# A POSIX variable
OPTIND=1 # Reset in case getopts has been used previously in the shell.
# read arguments
opts=$(getopt \
--longoptions "help,cn:,key:,cert:,days:,cakey:,ca:,cadays:,noautosan,san-ip:,san-dns:,debug,rsa,rsa-size:" \
6 years ago
--name "$(basename "$0")" \
--options "" \
-- "$@"
)
eval set --$opts
while [ $# -gt 0 ]; do
case "$1" in
--help)
print_help;
exit 0
;;
--cn)
ARG_CN=$2
shift 2
;;
--key)
ARG_KEY=$2
shift 2
;;
--cert)
ARG_CERT=$2
shift 2
;;
--days)
ARG_DAYS=$2
shift 2
;;
--cakey)
ARG_CAKEY=$2
shift 2
;;
--ca)
ARG_CA=$2
shift 2
;;
--cadays)
ARG_CADAYS=$2
shift 2
;;
--noautosan)
ARG_NOAUTOSAN=1
shift 1
6 years ago
;;
--san-ip)
ARG_NOAUTOSAN=1
ARG_SAN_IP=$2
shift 2
;;
--san-dns)
ARG_SAN_DNS=$2
shift 2
;;
6 years ago
--debug)
ARG_DEBUG=1
shift 1
;;
--rsa)
ARG_RSA=1
shift 1
;;
--rsa-size)
ARG_RSA_SIZE=$2
6 years ago
shift 2
;;
*)
break
;;
esac
done
6 years ago
# prepare common variables
##
OPENSSL_CONFIG="openssl.cnf"
CN="${ARG_CN}"
6 years ago
CA_KEY="${ARG_CAKEY:-ca.key}"
CA_CERT="${ARG_CA:-ca.crt}"
CA_DAYS="${ARG_CADAYS:-3650}"
SERVER_KEY="${ARG_KEY:-private.key}"
SERVER_CERT="${ARG_CERT:-public.crt}"
DAYS="${ARG_DAYS:-365}"
NOAUTOSAN="${ARG_NOAUTOSAN}"
SAN_IP="${ARG_SAN_IP}"
SAN_DNS="${ARG_SAN_DNS}"
DEBUG="${ARG_DEBUG}"
RSA="${ARG_RSA}"
RSA_SIZE="${ARG_RSA_SIZE:-2048}"
if [ -z "${CN}" ]; then
echo "[${ME}] ERROR: Please specify CN, example \"--cn your.site.com\""
print_help;
exit 1
fi
# For debugging purposes
if [ "${DEBUG}" -eq 1 ]; then
echo CN=$CN
echo KEY=$KEY
echo CERT=$CERT
echo DAYS=$DAYS
echo CAKEY=$CAKEY
echo CA=$CA
echo CADAYS=$CADAYS
echo NOAUTOSAN=$NOAUTOSAN
echo SAN_IP=$SAN_IP
echo SAN_DNS=$SAN_DNS
echo DEBUG=$DEBUG
echo RSA=$RSA
echo RSA_SIZE=$RSA_SIZE
fi
6 years ago
}
6 years ago
# install openssl
##
has_openssl() {
type openssl >/dev/null 2>&1
if [ $? -eq 0 ]; then
return;
fi
if [ $(id -u) -ne 0 ]; then
echo "This script must be run as root in order to install openssl package."
echo "If you cannot run this script as root, then make sure you have the openssl package."
exit 1
fi
if [ -f /etc/debian_version ]; then
echo "[${ME}] Installing openssl in Debian/Ubuntu"
export DEBIAN_FRONTEND=noninteractive
apt-get update
apt-get -y install openssl
elif [ -f /etc/alpine-release ]; then
echo "[${ME}] Installing openssl in Alpine"
apk add --update openssl
elif [ -f /etc/centos-release ]; then
echo "[${ME}] Installing openssl in CentOS"
yum -y install openssl
fi
type openssl >/dev/null
if [ $? -ne 0 ]; then
echo "[${ME}] ERROR: Could not install openssl. Exitting."
exit 1
fi
}
# install getopt
##
has_getopt() {
type getopt >/dev/null 2>&1
if [ $? -eq 0 ]; then
return;
fi
if [ $(id -u) -ne 0 ]; then
echo "This script must be run as root in order to install getopt tool."
echo "If you cannot run this script as root, then make sure you have the getopt tool."
exit 1
fi
if [ -f /etc/debian_version ]; then
echo "[${ME}] Installing getopt in Debian/Ubuntu"
export DEBIAN_FRONTEND=noninteractive
apt-get update
apt-get -y install util-linux
elif [ -f /etc/alpine-release ]; then
echo "[${ME}] Installing getopt in Alpine"
apk add --update busybox
ln -sv $(type -p busybox) /usr/bin/getopt
elif [ -f /etc/centos-release ]; then
echo "[${ME}] Installing getopt in CentOS"
yum -y install util-linux
fi
type getopt >/dev/null
if [ $? -ne 0 ]; then
echo "[${ME}] ERROR: Could not install getopt. Exitting."
exit 1
fi
}
# generate openssl config
##
6 years ago
gen_openssl_config() {
6 years ago
OPENSSL_CONFIG_CONTENT="[ req ]
distinguished_name = req_distinguished_name
[req_distinguished_name]
[ v3_ca ]
basicConstraints = critical, CA:TRUE
keyUsage = critical, digitalSignature, keyEncipherment, keyCertSign
[ v3_req_server ]
basicConstraints = CA:FALSE
keyUsage = critical, digitalSignature, keyEncipherment
extendedKeyUsage = serverAuth
subjectAltName = @alt_names
[ alt_names ]
DNS.1=${CN}"
6 years ago
if [ ! -z "$SAN_IP" ]; then
echo "[${ME}] Using user-provided SAN records: " ${SAN_IP}
i=1
IFS=,
PAYLOAD="$(for IP in $SAN_IP; do echo "IP.${i} = ${IP}" ; i=$((i + 1)); done)"
unset IFS
fi
if [ ! -z "$SAN_DNS" ]; then
echo "[${ME}] Using user-provided SAN records: " ${SAN_DNS}
i=1
IFS=,
PAYLOAD="${PAYLOAD}\n$(for DNS in $SAN_DNS; do echo "DNS.${i} = ${DNS}" ; i=$((i + 1)); done)"
unset IFS
fi
if [ -z "$NOAUTOSAN" ]; then
# Gather IPs for SAN
i=1
IPS="$( (getent ahostsv4 $(hostname) 2>/dev/null || getent hosts $(hostname) 2>/dev/null) | awk '{print $1}' |sort | uniq)"
echo "[${ME}] Found these IPs: " ${IPS}
PAYLOAD="${PAYLOAD}\n$(for IP in $IPS; do echo "IP.${i} = ${IP}" ; i=$((i + 1)); done)"
fi
6 years ago
6 years ago
printf "${OPENSSL_CONFIG_CONTENT}\n${PAYLOAD}\n" > "${OPENSSL_CONFIG}"
6 years ago
}
# generate CA certificate
##
6 years ago
gen_ca() {
6 years ago
echo "[${ME}] Generating new CA: ${CA_KEY} / ${CA_CERT} ..."
if [ -z "${RSA}" ]; then
openssl ecparam -name prime256v1 -genkey -noout -out "${CA_KEY}"
else
openssl genrsa -out "${CA_KEY}" "${RSA_SIZE}"
fi
6 years ago
chmod 0600 "${CA_KEY}"
openssl req -x509 -new -sha256 -nodes -key "${CA_KEY}" -days "${CA_DAYS}" -out "${CA_CERT}" \
6 years ago
-subj "/CN=my-CA" -extensions v3_ca -config "${OPENSSL_CONFIG}"
6 years ago
}
# generate server certificate
##
6 years ago
gen_server_x509() {
6 years ago
echo "[${ME}] Generating new server x509: ${SERVER_KEY} / ${SERVER_CERT} ..."
if [ -z "${RSA}" ]; then
openssl ecparam -name prime256v1 -genkey -noout -out "${SERVER_KEY}"
else
openssl genrsa -out "${SERVER_KEY}" "${RSA_SIZE}"
fi
6 years ago
chmod 0600 "${SERVER_KEY}"
openssl req -new -sha256 -key "${SERVER_KEY}" -subj "/CN=${CN}" \
6 years ago
| openssl x509 -req -sha256 -CA "${CA_CERT}" -CAkey "${CA_KEY}" -CAcreateserial \
-out ${SERVER_CERT} -days "${DAYS}" \
6 years ago
-extensions v3_req_server -extfile "${OPENSSL_CONFIG}"
6 years ago
}
6 years ago
start() {
6 years ago
echo "[${ME}] Started in ${PWD} directory."
has_getopt;
6 years ago
has_openssl;
parse_arguments "$@";
6 years ago
gen_openssl_config;
if [ ! -f "${CA_KEY}" ]; then
echo "[${ME}] Could not find ${CA_KEY} file so I will generate a new one."
gen_ca;
fi
6 years ago
openssl x509 -in "${CA_CERT}" -noout -checkend 2592000 >/dev/null
if [ $? -ne 0 ]; then
6 years ago
echo "[${ME}] WARNING! Your CA certificate will expire in less than 30 days."
fi
6 years ago
openssl x509 -in "${CA_CERT}" -noout -checkend 1 >/dev/null
if [ $? -ne 0 ]; then
6 years ago
echo "[${ME}] WARNING! Your CA certificate has expired, so we will generate a new one."
gen_ca;
fi
# Generate a new server certificate with the detected IPs.
gen_server_x509;
echo "[${ME}] The certificates have been generated in ${PWD} directory."
CERT_INFO="$(openssl x509 -in "${SERVER_CERT}" -noout -text)"
echo "${CERT_INFO}" | grep -E "CN=|DNS:|IP Address|Not\ "
6 years ago
}
# script starts here
##
6 years ago
start "$@";