Blame | Last modification | View Log | RSS feed
#!/bin/sh# Easy-RSA 3 -- A Shell-based CA Utility## Copyright (C) 2018 by the Open-Source OpenVPN development community.# A full list of contributors can be found in the ChangeLog.## This code released under version 2 of the GNU GPL; see COPYING and the# Licensing/ directory of this project for full licensing details.# Help/usage output to stdoutusage() {# command help:print "Easy-RSA 3 usage and overviewUSAGE: easyrsa [options] COMMAND [command-options]A list of commands is shown below. To get detailed usage and help for acommand, run:./easyrsa help COMMANDFor a listing of options that can be supplied before the command, use:./easyrsa help optionsHere is the list of commands available with a short syntax reminder. Use the'help' command above to get full usage details.init-pkibuild-ca [ cmd-opts ]gen-dhgen-req <filename_base> [ cmd-opts ]sign-req <type> <filename_base>build-client-full <filename_base> [ cmd-opts ]build-server-full <filename_base> [ cmd-opts ]revoke <filename_base> [cmd-opts]renew <filename_base> [cmd-opts]build-serverClient-full <filename_base> [ cmd-opts ]gen-crlupdate-dbshow-req <filename_base> [ cmd-opts ]show-cert <filename_base> [ cmd-opts ]show-ca [ cmd-opts ]import-req <request_file_path> <short_basename>export-p7 <filename_base> [ cmd-opts ]export-p12 <filename_base> [ cmd-opts ]set-rsa-pass <filename_base> [ cmd-opts ]set-ec-pass <filename_base> [ cmd-opts ]upgrade <type>"# collect/show dir status:err_source="Not defined: vars autodetect failed and no value provided"work_dir="${EASYRSA:-$err_source}"pki_dir="${EASYRSA_PKI:-$err_source}"print "\DIRECTORY STATUS (commands would take effect on these locations)EASYRSA: $work_dirPKI: $pki_dir"} # => usage()# Detailed command help# When called with no args, calls usage(), otherwise shows help for a commandcmd_help() {text=""opts=""case "$1" ininit-pki|clean-all) text="init-pki [ cmd-opts ]Removes & re-initializes the PKI dir for a clean PKI" ;;build-ca) text="build-ca [ cmd-opts ]Creates a new CA"opts="nopass - do not encrypt the CA key (default is encrypted)subca - create an intermediate CA keypair and request (default is a root CA)intca - alias to the above" ;;gen-dh) text="gen-dhGenerates DH (Diffie-Hellman) parameters" ;;gen-req) text="gen-req <filename_base> [ cmd-opts ]Generate a standalone keypair and request (CSR)This request is suitable for sending to a remote CA for signing."opts="nopass - do not encrypt the private key (default is encrypted)" ;;sign|sign-req) text="sign-req <type> <filename_base>Sign a certificate request of the defined type. <type> must be a knowntype such as 'client', 'server', 'serverClient', or 'ca' (or a user-added type.)This request file must exist in the reqs/ dir and have a .req fileextension. See import-req below for importing reqs from other sources." ;;build|build-client-full|build-server-full|build-serverClient-full) text="build-client-full <filename_base> [ cmd-opts ]build-server-full <filename_base> [ cmd-opts ]build-serverClient-full <filename_base> [ cmd-opts ]Generate a keypair and sign locally for a client and/or serverThis mode uses the <filename_base> as the X509 CN."opts="nopass - do not encrypt the private key (default is encrypted)inline - create an inline credentials file for this node" ;;revoke) text="revoke <filename_base> [reason]Revoke a certificate specified by the filename_base, with an optionalrevocation reason that is one of:unspecifiedkeyCompromiseCACompromiseaffiliationChangedsupersededcessationOfOperationcertificateHold";;renew) text="renew <filename_base> [ cmd-opts ]Renew a certificate specified by the filename_base"opts="nopass - do not encrypt the private key (default is encrypted)" ;;gen-crl) text="gen-crlGenerate a CRL" ;;update-db) text="update-dbUpdate the index.txt databaseThis command will use the system time to update the status of issuedcertificates." ;;show-req|show-cert) text="show-req <filename_base> [ cmd-opts ]show-cert <filename_base> [ cmd-opts ]Shows details of the req or cert referenced by filename_baseHuman-readable output is shown, including any requested cert options whenshowing a request."opts="full - show full req/cert info, including pubkey/sig data" ;;show-ca) text="show-ca [ cmd-opts ]Shows details of the CA certHuman-readable output is shown."opts="full - show full cert info, including pubkey/sig data" ;;import-req) text="import-req <request_file_path> <short_basename>Import a certificate request from a fileThis will copy the specified file into the reqs/ dir inpreparation for signing.The <short_basename> is the filename base to create.Example usage:import-req /some/where/bob_request.req bob" ;;export-p12) text="export-p12 <filename_base> [ cmd-opts ]Export a PKCS#12 file with the keypair specified by <filename_base>"opts="noca - do not include the ca.crt file in the PKCS12 outputnokey - do not include the private key in the PKCS12 output" ;;export-p7) text="export-p7 <filename_base> [ cmd-opts ]Export a PKCS#7 file with the pubkey specified by <filename_base>"opts="noca - do not include the ca.crt file in the PKCS7 output" ;;set-rsa-pass|set-ec-pass) text="set-rsa-pass <filename_base> [ cmd-opts ]set-ec-pass <filename_base> [ cmd-opts ]Set a new passphrase on an RSA or EC key for the listed <filename_base>."opts="nopass - use no password and leave the key unencryptedfile - (advanced) treat the file as a raw path, not a short-name" ;;upgrade) text="upgrade <type>Upgrade EasyRSA PKI and/or CA. <type> must be one of:pki - Upgrade EasyRSA v2.x PKI to EasyRSA v3.x PKI (includes CA below)ca - Upgrade EasyRSA v3.0.5 CA or older to EasyRSA v3.0.6 CA or later." ;;altname|subjectaltname|san) text="--subject-alt-name=SAN_FORMAT_STRINGThis global option adds a subjectAltName to the request or issuedcertificate. It MUST be in a valid format accepted by openssl orreq/cert generation will fail. Note that including multiple such namesrequires them to be comma-separated; further invocations of thisoption will REPLACE the value.Examples of the SAN_FORMAT_STRING shown below:DNS:alternate.example.netDNS:primary.example.net,DNS:alternate.example.netIP:203.0.113.29email:alternate@example.net" ;;options)opt_usage ;;"")usage ;;*) text="Unknown command: '$1' (try without commands for a list of commands)" ;;esac# display the help textprint "$text"[ -n "$opts" ] && print "cmd-opts is an optional set of command options from this list:$opts"} # => cmd_help()# Options usageopt_usage() {print "Easy-RSA Global Option FlagsThe following options may be provided before the command. Options specifiedat runtime override env-vars and any 'vars' file in use. Unless noted,non-empty values to options are mandatory.General options:--batch : set automatic (no-prompts when possible) mode--passin=ARG : set -passin ARG for openssl--passout=ARG : set -passout ARG for openssl--pki-dir=DIR : declares the PKI directory--vars=FILE : define a specific 'vars' file to use for Easy-RSA configCertificate & Request options: (these impact cert/req field values)--days=# : sets the signing validity to the specified number of days--digest=ALG : digest to use in the requests & certificates--dn-mode=MODE : DN mode to use (cn_only or org)--keysize=# : size in bits of keypair to generate--req-cn=NAME : default CN to use--subca-len=# : path length of signed intermediate CA certs; must be >= 0 if used--subject-alt-name : Add a subjectAltName. For more info and syntax, see:./easyrsa help altname--use-algo=ALG : crypto alg to use: choose rsa (default) or ec--curve=NAME : for elliptic curve, sets the named curve to use--copy-ext : Copy included request X509 extensions (namely subjAltNameOrganizational DN options: (only used with the 'org' DN mode)(values may be blank for org DN options)--req-c=CC : country code (2-letters)--req-st=NAME : State/Province--req-city=NAME : City/Locality--req-org=NAME : Organization--req-email=NAME : Email addresses--req-ou=NAME : Organizational UnitDeprecated features:--ns-cert=YESNO : yes or no to including deprecated NS extensions--ns-comment=COMMENT : NS comment to include (value may be blank)"} # => opt_usage()# Wrapper around printf - clobber print since it's not POSIX anyway# shellcheck disable=SC1117print() { printf "%s\n" "$*"; }# Exit fatally with a message to stderr# present even with EASYRSA_BATCH as these are fatal problemsdie() {print "Easy-RSA error:$1" 1>&2exit "${2:-1}"} # => die()# non-fatal warning outputwarn() {[ ! "$EASYRSA_BATCH" ] && \print "$1" 1>&2} # => warn()# informational notices to stdoutnotice() {[ ! "$EASYRSA_BATCH" ] && \print "$1"} # => notice()# yes/no case-insensitive match (operates on stdin pipe)# Returns 0 when input contains yes, 1 for no, 2 for no match# If both strings are present, returns 1; first matching line returns.awk_yesno() {#shellcheck disable=SC2016awkscript='BEGIN {IGNORECASE=1; r=2}{ if(match($0,"no")) {r=1; exit}if(match($0,"yes")) {r=0; exit}} END {exit r}'awk "$awkscript"} # => awk_yesno()# intent confirmation helper func# returns without prompting in EASYRSA_BATCHconfirm() {[ "$EASYRSA_BATCH" ] && returnprompt="$1"value="$2"msg="$3"input=""print "$msgType the word '$value' to continue, or any other input to abort."printf %s " $prompt"#shellcheck disable=SC2162read input[ "$input" = "$value" ] && returnnotice "Aborting without confirmation."exit 9} # => confirm()# mktemp wrappereasyrsa_mktemp() {[ -n "$EASYRSA_TEMP_DIR_session" ] || die "EASYRSA_TEMP_DIR_session not initialized!"[ -d "$EASYRSA_TEMP_DIR_session" ] || mkdir -p "$EASYRSA_TEMP_DIR_session" ||die "Could not create temporary directory '$EASYRSA_TEMP_DIR_session'. Permission or concurrency problem?"[ -d "$EASYRSA_TEMP_DIR_session" ] || die "Temporary directory '$EASYRSA_TEMP_DIR_session' does not exist"template="$EASYRSA_TEMP_DIR_session/tmp.XXXXXX"tempfile=$(mktemp "$template") || return# win32 mktemp shipped by easyrsa returns template as file!if [ "$template" = "$tempfile" ]; then# but win32 mktemp -d does work# but win32 mktemp -u does not worktempfile=$(mktemp -du "$tempfile") || returnprintf "" > "$tempfile" || returnfiecho "$tempfile"} # => easyrsa_mktemp# remove temp files and do terminal cleanupscleanup() {[ -z "$EASYRSA_TEMP_DIR_session" ] || rm -rf "$EASYRSA_TEMP_DIR_session"# shellcheck disable=SC2039(stty echo 2>/dev/null) || { (set -o echo 2>/dev/null) && set -o echo; }echo "" # just to get a clean line} # => cleanup()easyrsa_openssl() {openssl_command=$1; shiftcase $openssl_command inmakesafeconf) has_config=true;;ca|req|srp|ts) has_config=true;;*) has_config=false;;esacif ! $has_config; then"$EASYRSA_OPENSSL" "$openssl_command" "$@"returnfieasyrsa_openssl_conf=$(easyrsa_mktemp) || die "Failed to create temporary file"easyrsa_extra_exts=if [ -n "$EASYRSA_EXTRA_EXTS" ]; theneasyrsa_extra_exts=$(easyrsa_mktemp) || die "Failed to create temporary file"cat >"$easyrsa_extra_exts" <<-EOFreq_extensions = req_extra[ req_extra ]$EASYRSA_EXTRA_EXTSEOFfi# Make LibreSSL safe config file from OpenSSL config filesed \-e "s\`ENV::EASYRSA\`EASYRSA\`g" \-e "s\`\$dir\`$EASYRSA_PKI\`g" \-e "s\`\$EASYRSA_PKI\`$EASYRSA_PKI\`g" \-e "s\`\$EASYRSA_CERT_EXPIRE\`$EASYRSA_CERT_EXPIRE\`g" \-e "s\`\$EASYRSA_CRL_DAYS\`$EASYRSA_CRL_DAYS\`g" \-e "s\`\$EASYRSA_DIGEST\`$EASYRSA_DIGEST\`g" \-e "s\`\$EASYRSA_KEY_SIZE\`$EASYRSA_KEY_SIZE\`g" \-e "s\`\$EASYRSA_DIGEST\`$EASYRSA_DIGEST\`g" \-e "s\`\$EASYRSA_DN\`$EASYRSA_DN\`g" \-e "s\`\$EASYRSA_REQ_COUNTRY\`$EASYRSA_REQ_COUNTRY\`g" \-e "s\`\$EASYRSA_REQ_PROVINCE\`$EASYRSA_REQ_PROVINCE\`g" \-e "s\`\$EASYRSA_REQ_CITY\`$EASYRSA_REQ_CITY\`g" \-e "s\`\$EASYRSA_REQ_ORG\`$EASYRSA_REQ_ORG\`g" \-e "s\`\$EASYRSA_REQ_OU\`$EASYRSA_REQ_OU\`g" \-e "s\`\$EASYRSA_REQ_CN\`$EASYRSA_REQ_CN\`g" \-e "s\`\$EASYRSA_REQ_EMAIL\`$EASYRSA_REQ_EMAIL\`g" \${EASYRSA_EXTRA_EXTS:+-e "/^#%EXTRA_EXTS%/r $easyrsa_extra_exts"} \"$EASYRSA_SSL_CONF" > "$easyrsa_openssl_conf" ||die "Failed to update $easyrsa_openssl_conf"if [ "$openssl_command" = "makesafeconf" ]; thencp "$easyrsa_openssl_conf" "$EASYRSA_SAFE_CONF"err=$?else"$EASYRSA_OPENSSL" "$openssl_command" -config "$easyrsa_openssl_conf" "$@"err=$?firm -f "$easyrsa_openssl_conf"rm -f "$easyrsa_extra_exts"return $err} # => easyrsa_opensslvars_source_check() {# Check for defined EASYRSA_PKI[ -n "$EASYRSA_PKI" ] || die "\EASYRSA_PKI env-var undefined"} # => vars_source_check()# Verify supplied curve exists and generate curve file if neededverify_curve_ec() {if ! "$EASYRSA_OPENSSL" ecparam -name "$EASYRSA_CURVE" > /dev/null; thendie "\Curve $EASYRSA_CURVE not found. Run openssl ecparam -list_curves to show alist of supported curves."fi# Check that the ecparams dir exists[ -d "$EASYRSA_EC_DIR" ] || mkdir "$EASYRSA_EC_DIR" || die "\Failed creating ecparams dir (permissions?) at:$EASYRSA_EC_DIR"# Check that the required ecparams file existsout="$EASYRSA_EC_DIR/${EASYRSA_CURVE}.pem"[ -f "$out" ] && return 0"$EASYRSA_OPENSSL" ecparam -name "$EASYRSA_CURVE" -out "$out" || die "\Failed to generate ecparam file (permissions?) when writing to:$out"# Explicitly return success for callerreturn 0}# Verify if Edward Curve existsverify_curve_ed() {if [ "ed25519" = "$EASYRSA_CURVE" ] && "$EASYRSA_OPENSSL" genpkey -algorithm ED25519 > /dev/null; thenreturn 0elif [ "ed448" = "$EASYRSA_CURVE" ] && "$EASYRSA_OPENSSL" genpkey -algorithm ED448 > /dev/null; thenreturn 0fidie "Curve $EASYRSA_CURVE not found."}verify_ssl_lib () {# Verify EASYRSA_OPENSSL command gives expected outputif [ -z "$EASYRSA_SSL_OK" ]; thenval="$("$EASYRSA_OPENSSL" version)"case "${val%% *}" inOpenSSL|LibreSSL)print "\Using SSL: $EASYRSA_OPENSSL $("$EASYRSA_OPENSSL" version)" ;;*) die "\Missing or invalid OpenSSLExpected to find openssl command at: $EASYRSA_OPENSSL" ;;esacfiEASYRSA_SSL_OK=1# Verify EASYRSA_SSL_CONF file exists[ -f "$EASYRSA_SSL_CONF" ] || die "\The OpenSSL config file cannot be found.Expected location: $EASYRSA_SSL_CONF"} # => verify_ssl_lib ()# Basic sanity-check of PKI init and complain if missingverify_pki_init() {help_note="Run easyrsa without commands for usage and command help."# check that the pki dir existsvars_source_check[ -d "$EASYRSA_PKI" ] || die "\EASYRSA_PKI does not exist (perhaps you need to run init-pki)?Expected to find the EASYRSA_PKI at: $EASYRSA_PKI$help_note"# verify expected dirs present:for i in private reqs; do[ -d "$EASYRSA_PKI/$i" ] || die "\Missing expected directory: $i (perhaps you need to run init-pki?)$help_note"done# verify ssl libverify_ssl_lib} # => verify_pki_init()# Verify core CA files presentverify_ca_init() {help_note="Run without commands for usage and command help."# First check the PKI has been initializedverify_pki_init# Verify expected files are present. Allow files to be regular files# (or symlinks), but also pipes, for flexibility with ca.keyfor i in serial index.txt index.txt.attr ca.crt private/ca.key; doif [ ! -f "$EASYRSA_PKI/$i" ] && [ ! -p "$EASYRSA_PKI/$i" ]; then[ "$1" = "test" ] && return 1die "\Missing expected CA file: $i (perhaps you need to run build-ca?)$help_note"fidone# When operating in 'test' mode, return success.# test callers don't care about CA-specific dir structure[ "$1" = "test" ] && return 0# verify expected CA-specific dirs:for i in issued certs_by_serialdo[ -d "$EASYRSA_PKI/$i" ] || die "\Missing expected CA dir: $i (perhaps you need to run build-ca?)$help_note"done# explicitly return success for callersreturn 0} # => verify_ca_init()# init-pki backend:init_pki() {# If EASYRSA_PKI exists, confirm before we rm -rf (skiped with EASYRSA_BATCH)if [ -e "$EASYRSA_PKI" ]; thenconfirm "Confirm removal: " "yes" "WARNING!!!You are about to remove the EASYRSA_PKI at: $EASYRSA_PKIand initialize a fresh PKI here."# now remove it:rm -rf "$EASYRSA_PKI" || die "Removal of PKI dir failed. Check/correct errors above"fi# new dirs:for i in private reqs; domkdir -p "$EASYRSA_PKI/$i" || die "Failed to create PKI file structure (permissions?)"done# Create $EASYRSA_SAFE_CONF ($OPENSSL_CONF) prevents bogus warnings (especially useful on win32)if [ ! -f "$EASYRSA_SSL_CONF" ] && [ -f "$EASYRSA/openssl-easyrsa.cnf" ];thencp "$EASYRSA/openssl-easyrsa.cnf" "$EASYRSA_SSL_CONF"easyrsa_openssl makesafeconffinotice "\init-pki complete; you may now create a CA or requests.Your newly created PKI dir is: $EASYRSA_PKI"return 0} # => init_pki()hide_read_pass(){# shellcheck disable=SC2039if stty -echo 2>/dev/null; thenread -r "$@"stty echoelif (set +o echo 2>/dev/null); thenset +o echoread -r "$@"set -o echoelif (echo | read -r -s 2>/dev/null) ; thenread -r -s "$@"elsewarn "Could not disable echo. Password will be shown on screen!"read -r "$@"fi} # => hide_read_pass()# build-ca backend:build_ca() {opts=""sub_ca=""nopass=""crypto="-aes256"while [ -n "$1" ]; docase "$1" inintca) sub_ca=1 ;;subca) sub_ca=1 ;;nopass) nopass=1 ;;*) warn "Ignoring unknown command option: '$1'" ;;esacshiftdoneverify_pki_init[ "$EASYRSA_ALGO" = "ec" ] && verify_curve_ec[ "$EASYRSA_ALGO" = "ed" ] && verify_curve_ed# setup for the simpler intermediate CA situation and overwrite with root-CA if needed:out_file="$EASYRSA_PKI/reqs/ca.req"out_key="$EASYRSA_PKI/private/ca.key"if [ ! $sub_ca ]; thenout_file="$EASYRSA_PKI/ca.crt"opts="$opts -x509 -days $EASYRSA_CA_EXPIRE "fi# Test for existing CA, and complain if already presentif verify_ca_init test; thendie "\Unable to create a CA as you already seem to have one set up.If you intended to start a new CA, run init-pki first."fi# If a private key exists here, a intermediate ca was created but not signed.# Notify the user and require a signed ca.crt or a init-pki:[ -f "$out_key" ] && \die "\A CA private key exists but no ca.crt is found in your PKI dir of:$EASYRSA_PKIRefusing to create a new CA keypair as this operation would overwrite yourcurrent CA keypair. If you intended to start a new CA, run init-pki first."# create necessary files and dirs:err_file="Unable to create necessary PKI files (permissions?)"for i in issued certs_by_serial \revoked/certs_by_serial revoked/private_by_serial revoked/reqs_by_serial \renewed/certs_by_serial renewed/private_by_serial renewed/reqs_by_serial;domkdir -p "$EASYRSA_PKI/$i" || die "$err_file"doneprintf "" > "$EASYRSA_PKI/index.txt" || die "$err_file"printf "" > "$EASYRSA_PKI/index.txt.attr" || die "$err_file"print "01" > "$EASYRSA_PKI/serial" || die "$err_file"# Default CN only when not in global EASYRSA_BATCH mode:# shellcheck disable=SC2015[ "$EASYRSA_BATCH" ] && opts="$opts -batch" || export EASYRSA_REQ_CN="Easy-RSA CA"out_key_tmp="$(easyrsa_mktemp)" || die "Failed to create temporary file"out_file_tmp="$(easyrsa_mktemp)" || die "Failed to create temporary file"# Get password from user if necessaryif [ ! $nopass ] && ( [ -z "$EASYRSA_PASSOUT" ] || [ -z "$EASYRSA_PASSIN" ] ); thenout_key_pass_tmp="$(easyrsa_mktemp)" || die "Failed to create temporary file"echoprintf "Enter New CA Key Passphrase: "hide_read_pass kpassechoprintf "Re-Enter New CA Key Passphrase: "hide_read_pass kpass2echo# shellcheck disable=2154if [ "$kpass" = "$kpass2" ];thenprintf "%s" "$kpass" > "$out_key_pass_tmp"elsedie "Passphrases do not match."fifi# create the CA key using AES256crypto_opts=""if [ ! $nopass ]; thencrypto_opts="$crypto"[ -z "$EASYRSA_PASSOUT" ] && crypto_opts="$crypto_opts -passout file:$out_key_pass_tmp"fiif [ "$EASYRSA_ALGO" = "rsa" ]; then#shellcheck disable=SC2086"$EASYRSA_OPENSSL" genrsa -out "$out_key_tmp" $crypto_opts ${EASYRSA_PASSOUT:+-passout "$EASYRSA_PASSOUT"} "$EASYRSA_ALGO_PARAMS" || \die "Failed create CA private key"elif [ "$EASYRSA_ALGO" = "ec" ]; then#shellcheck disable=SC2086"$EASYRSA_OPENSSL" ecparam -in "$EASYRSA_ALGO_PARAMS" -genkey | \"$EASYRSA_OPENSSL" ec -out "$out_key_tmp" $crypto_opts ${EASYRSA_PASSOUT:+-passout "$EASYRSA_PASSOUT"} || \die "Failed create CA private key"elif [ "ed" = "$EASYRSA_ALGO" ]; thenif [ "ed25519" = "$EASYRSA_CURVE" ]; then"$EASYRSA_OPENSSL" genpkey -algorithm ED25519 -out $out_key_tmp || \die "Failed create CA private key"elif [ "ed448" = "$EASYRSA_CURVE" ]; then"$EASYRSA_OPENSSL" genpkey -algorithm ED448 -out $out_key_tmp || \die "Failed create CA private key"fifi# create the CA keypair:crypto_opts=""[ ! $nopass ] && [ -z "$EASYRSA_PASSIN" ] && crypto_opts="-passin file:$out_key_pass_tmp"#shellcheck disable=SC2086easyrsa_openssl req -utf8 -new -key "$out_key_tmp" \-keyout "$out_key_tmp" -out "$out_file_tmp" $crypto_opts $opts ${EASYRSA_PASSIN:+-passin "$EASYRSA_PASSIN"} || \die "Failed to build the CA"mv "$out_key_tmp" "$out_key"mv "$out_file_tmp" "$out_file"[ -f "$out_key_pass_tmp" ] && rm "$out_key_pass_tmp"# Success messagesif [ $sub_ca ]; thennotice "\NOTE: Your intermediate CA request is at $out_fileand now must be sent to your parent CA for signing. Place your resulting certat $EASYRSA_PKI/ca.crt prior to signing operations."else notice "\CA creation complete and you may now import and sign cert requests.Your new CA certificate file for publishing is at:$out_file"fireturn 0} # => build_ca()# gen-dh backend:gen_dh() {verify_pki_initout_file="$EASYRSA_PKI/dh.pem""$EASYRSA_OPENSSL" dhparam -out "$out_file" "$EASYRSA_KEY_SIZE" || \die "Failed to build DH params"notice "\DH parameters of size $EASYRSA_KEY_SIZE created at $out_file"return 0} # => gen_dh()# gen-req backend:gen_req() {# pull filename base and use as default interactive CommonName:[ -n "$1" ] || die "\Error: gen-req must have a file base as the first argument.Run easyrsa without commands for usage and commands."key_out="$EASYRSA_PKI/private/$1.key"req_out="$EASYRSA_PKI/reqs/$1.req"[ ! "$EASYRSA_BATCH" ] && EASYRSA_REQ_CN="$1"shift# function opts supportopts=while [ -n "$1" ]; docase "$1" innopass) opts="$opts -nodes" ;;# batch flag supports internal callers needing silent operationbatch) EASYRSA_BATCH=1 ;;*) warn "Ignoring unknown command option: '$1'" ;;esacshiftdoneverify_pki_init[ "$EASYRSA_ALGO" = "ec" ] && verify_curve_ec[ "$EASYRSA_ALGO" = "ed" ] && verify_curve_ed# don't wipe out an existing private key without confirmation[ -f "$key_out" ] && confirm "Confirm key overwrite: " "yes" "\WARNING!!!An existing private key was found at $key_outContinuing with key generation will replace this key."# When EASYRSA_EXTRA_EXTS is defined, append it to openssl's [req] section:if [ -n "$EASYRSA_EXTRA_EXTS" ]; then# Setup & insert the extra ext data keyed by a magic lineextra_exts="req_extensions = req_extra[ req_extra ]$EASYRSA_EXTRA_EXTS"#shellcheck disable=SC2016awkscript='{if ( match($0, "^#%EXTRA_EXTS%") ){ while ( getline<"/dev/stdin" ) {print} next }{print}}'conf_tmp="$(easyrsa_mktemp)" || die "Failed to create temporary file"print "$extra_exts" | \awk "$awkscript" "$EASYRSA_SSL_CONF" \> "$conf_tmp" \|| die "Copying SSL config to temp file failed"# Use this new SSL config for the rest of this functionEASYRSA_SSL_CONF="$conf_tmp"fikey_out_tmp="$(easyrsa_mktemp)" || die "Failed to create temporary file"req_out_tmp="$(easyrsa_mktemp)" || die "Failed to create temporary file"# generate request[ $EASYRSA_BATCH ] && opts="$opts -batch"# shellcheck disable=2086,2148algo_opts=""if [ "ed" != $EASYRSA_ALGO ];thenalgo_opts=" -newkey $EASYRSA_ALGO:$EASYRSA_ALGO_PARAMS "fieasyrsa_openssl req -utf8 -new $algo_opts \-keyout "$key_out_tmp" -out "$req_out_tmp" $opts ${EASYRSA_PASSOUT:+-passout "$EASYRSA_PASSOUT"} \|| die "Failed to generate request"mv "$key_out_tmp" "$key_out"mv "$req_out_tmp" "$req_out"notice "\Keypair and certificate request completed. Your files are:req: $req_outkey: $key_out"return 0} # => gen_req()# common signing backendsign_req() {crt_type="$1"opts=""req_in="$EASYRSA_PKI/reqs/$2.req"crt_out="$EASYRSA_PKI/issued/$2.crt"# Randomize Serial numberif [ "$EASYRSA_RAND_SN" != "no" ];theni=""serial=""check_serial=""for i in 1 2 3 4 5; do"$EASYRSA_OPENSSL" rand -hex -out "$EASYRSA_PKI/serial" 16serial="$(cat "$EASYRSA_PKI/serial")"check_serial="$("$EASYRSA_OPENSSL" ca -config "$EASYRSA_SSL_CONF" -status "$serial" 2>&1)"case "$check_serial" in*"not present in db"*) break ;;*) continue ;;esacdonefi# Support batch by internal caller:[ "$3" = "batch" ] && EASYRSA_BATCH=1verify_ca_init# Check argument sanity:[ -n "$2" ] || die "\Incorrect number of arguments provided to sign-req:expected 2, got $# (see command help for usage)"# Cert type must exist under the EASYRSA_EXT_DIR[ -r "$EASYRSA_EXT_DIR/$crt_type" ] || die "\Unknown cert type '$crt_type'"# Request file must exist[ -f "$req_in" ] || die "\No request found for the input: '$2'Expected to find the request at: $req_in"# Confirm input is a cert reqverify_file req "$req_in" || die "\The certificate request file is not in a valid X509 request format.Offending file: $req_in"# Display the request subject in an easy-to-read format# Confirm the user wishes to sign this requestconfirm "Confirm request details: " "yes" "You are about to sign the following certificate.Please check over the details shown below for accuracy. Note that this requesthas not been cryptographically verified. Please be sure it came from a trustedsource or that you have verified the request checksum with the sender.Request subject, to be signed as a $crt_type certificate for $EASYRSA_CERT_EXPIRE days:$(display_dn req "$req_in")" # => confirm end# Generate the extensions file for this cert:ext_tmp="$(easyrsa_mktemp)" || die "Failed to create temporary file"{# Append first any COMMON file (if present) then the cert-type extensionscat "$EASYRSA_EXT_DIR/COMMON"cat "$EASYRSA_EXT_DIR/$crt_type"# copy req extensions[ "$EASYRSA_CP_EXT" ] && print "copy_extensions = copy"# Support a dynamic CA path length when present:[ "$crt_type" = "ca" ] && [ -n "$EASYRSA_SUBCA_LEN" ] && \print "basicConstraints = CA:TRUE, pathlen:$EASYRSA_SUBCA_LEN"# Deprecated Netscape extension support, if enabledif print "$EASYRSA_NS_SUPPORT" | awk_yesno; then[ -n "$EASYRSA_NS_COMMENT" ] && \print "nsComment = \"$EASYRSA_NS_COMMENT\""case "$crt_type" inserverClient) print "nsCertType = serverClient" ;;server) print "nsCertType = server" ;;client) print "nsCertType = client" ;;ca) print "nsCertType = sslCA" ;;esacfi# If type is server and no subjectAltName was requested,# add one to the extensions fileif [ "$crt_type" = 'server' ] || [ "$crt_type" = 'serverClient' ];thenecho "$EASYRSA_EXTRA_EXTS" | grep -q subjectAltNameif [ $? -ne 0 ];thensan=$(display_san req "$req_in")if [ -n "$san" ];thenprint "subjectAltName = $san"elsedefault_server_san "$req_in"fififi# Add any advanced extensions supplied by env-var:[ -n "$EASYRSA_EXTRA_EXTS" ] && print "$EASYRSA_EXTRA_EXTS": # needed to keep die from inherting the above test} > "$ext_tmp" || die "\Failed to create temp extension file (bad permissions?) at:$ext_tmp"# sign requestcrt_out_tmp="$(easyrsa_mktemp)" || die "Failed to create temporary file"easyrsa_openssl ca -utf8 -in "$req_in" -out "$crt_out_tmp" \-extfile "$ext_tmp" -days "$EASYRSA_CERT_EXPIRE" -batch $opts ${EASYRSA_PASSIN:+-passin "$EASYRSA_PASSIN"} \|| die "signing failed (openssl output above may have more detail)"mv "$crt_out_tmp" "$crt_out"rm -f "$ext_tmp"notice "\Certificate created at: $crt_out"return 0} # => sign_req()# common build backend# used to generate+sign in 1 stepbuild_full() {verify_ca_init# pull filename base:[ -n "$2" ] || die "\Error: didn't find a file base name as the first argument.Run easyrsa without commands for usage and commands."crt_type="$1" name="$2"req_out="$EASYRSA_PKI/reqs/$2.req"key_out="$EASYRSA_PKI/private/$2.key"crt_out="$EASYRSA_PKI/issued/$2.crt"shift 2# function opts supportreq_opts=while [ -n "$1" ]; docase "$1" innopass) req_opts="$req_opts nopass" ;;inline) EASYRSA_INLINE=1 ;;*) warn "Ignoring unknown command option: '$1'" ;;esacshiftdone# abort on existing req/key/crt fileserr_exists="\file already exists. Aborting build to avoid overwriting this file.If you wish to continue, please use a different name or remove the file.Matching file found at: "[ -f "$req_out" ] && die "Request $err_exists $req_out"[ -f "$key_out" ] && die "Key $err_exists $key_out"[ -f "$crt_out" ] && die "Certificate $err_exists $crt_out"# create requestEASYRSA_REQ_CN="$name"#shellcheck disable=SC2086gen_req "$name" batch $req_opts# Sign it( sign_req "$crt_type" "$name" batch ) || {rm -f "$req_out" "$key_out"die "Failed to sign '$name'"}# inline itif [ $EASYRSA_INLINE ]; theninline_credsfi} # => build_full()# Create inline credentials file for this nodeinline_creds (){[ -f "$EASYRSA_PKI/$EASYRSA_REQ_CN.creds" ] \&& die "Inline file exists: $EASYRSA_PKI/$EASYRSA_REQ_CN.creds"{printf "%s\n" "# $crt_type: $EASYRSA_REQ_CN"printf "%s\n" ""printf "%s\n" "<ca>"cat "$EASYRSA_PKI/ca.crt"printf "%s\n" "</ca>"printf "%s\n" ""printf "%s\n" "<cert>"cat "$crt_out"printf "%s\n" "</cert>"printf "%s\n" ""printf "%s\n" "<key>"cat "$key_out"printf "%s\n" "</key>"printf "%s\n" ""} > "$EASYRSA_PKI/$EASYRSA_REQ_CN.creds"} # => inline_creds ()# revoke backendrevoke() {verify_ca_init# pull filename base:[ -n "$1" ] || die "\Error: didn't find a file base name as the first argument.Run easyrsa without commands for usage and command help."crt_in="$EASYRSA_PKI/issued/$1.crt"opts=""if [ "$2" ]; thenopts="$opts -crl_reason $2"fiverify_file x509 "$crt_in" || die "\Unable to revoke as the input file is not a valid certificate. Unexpectedinput in file: $crt_in"# confirm operation by displaying DN:confirm "Continue with revocation: " "yes" "Please confirm you wish to revoke the certificate with the following subject:$(display_dn x509 "$crt_in")" # => confirm end# referenced cert must exist:[ -f "$crt_in" ] || die "\Unable to revoke as no certificate was found. Certificate was expectedat: $crt_in"# shellcheck disable=SC2086easyrsa_openssl ca -utf8 -revoke "$crt_in" ${EASYRSA_PASSIN:+-passin "$EASYRSA_PASSIN"} $opts || die "\Failed to revoke certificate: revocation command failed."# move revoked files so we can reissue certificates with the same namemove_revoked "$1"notice "\IMPORTANT!!!Revocation was successful. You must run gen-crl and upload a CRL to yourinfrastructure in order to prevent the revoked cert from being accepted." # => notice endreturn 0} #= revoke()# move-revoked# moves revoked certificates to an alternative folder# allows reissuing certificates with the same namemove_revoked() {verify_ca_init[ -n "$1" ] || die "\Error: didn't find a file base name as the first argument.Run easyrsa without commands for usage and command help."crt_in="$EASYRSA_PKI/issued/$1.crt"key_in="$EASYRSA_PKI/private/$1.key"req_in="$EASYRSA_PKI/reqs/$1.req"verify_file x509 "$crt_in" || die "\Unable to move revoked input file. The file is not a valid certificate. Unexpectedinput in file: $crt_in"if [ -e "$req_in" ]thenverify_file req "$req_in" || die "\Unable to move request. The file is not a valid request. Unexpectedinput in file: $req_in"fi# get the serial number of the certificate -> serial=XXXXcert_serial="$(easyrsa_openssl x509 -in "$crt_in" -noout -serial)"# remove the serial= part -> we only need the XXXX partcert_serial=${cert_serial##*=}crt_by_serial="$EASYRSA_PKI/certs_by_serial/$cert_serial.pem"crt_by_serial_revoked="$EASYRSA_PKI/revoked/certs_by_serial/$cert_serial.crt"key_by_serial_revoked="$EASYRSA_PKI/revoked/private_by_serial/$cert_serial.key"req_by_serial_revoked="$EASYRSA_PKI/revoked/reqs_by_serial/$cert_serial.req"# make sure revoked dirs exist[ -d "$EASYRSA_PKI/revoked" ] || mkdir "$EASYRSA_PKI/revoked"[ -d "$EASYRSA_PKI/revoked/certs_by_serial" ] || mkdir "$EASYRSA_PKI/revoked/certs_by_serial"[ -d "$EASYRSA_PKI/revoked/private_by_serial" ] || mkdir "$EASYRSA_PKI/revoked/private_by_serial"[ -d "$EASYRSA_PKI/revoked/reqs_by_serial" ] || mkdir "$EASYRSA_PKI/revoked/reqs_by_serial"# move crt, key and req file to revoked foldersmv "$crt_in" "$crt_by_serial_revoked"# only move the req if we have it[ -e "$req_in" ] && mv "$req_in" "$req_by_serial_revoked"# only move the key if we have it[ -e "$key_in" ] && mv "$key_in" "$key_by_serial_revoked"# move the rest of the files (p12, p7, ...)# shellcheck disable=SC2231for file in $EASYRSA_PKI/private/$1\.???do# get file extensionfile_ext="${file##*.}"[ -f "$file" ] && mv "$file" "$EASYRSA_PKI/revoked/private_by_serial/$cert_serial.$file_ext"done# remove the dublicate certificate in the certs_by_serial folderrm "$crt_by_serial"return 0} #= move_revoked()# renew backendrenew() {verify_ca_init# pull filename base:[ -n "$1" ] || die "\Error: didn't find a file base name as the first argument.Run easyrsa without commands for usage and command help."crt_in="$EASYRSA_PKI/issued/$1.crt"opts=""if [ "$2" ]; thenopts="$2"fiverify_file x509 "$crt_in" || die "\Unable to renew as the input file is not a valid certificate. Unexpectedinput in file: $crt_in"# confirm operation by displaying DN:confirm "Continue with renew: " "yes" "Please confirm you wish to renew the certificate with the following subject:$(display_dn x509 "$crt_in")" # => confirm end# referenced cert must exist:[ -f "$crt_in" ] || die "\Unable to renew as no certificate was found. Certificate was expectedat: $crt_in"# Check if old cert is expired or expires within 30 daysexpire_date=$(easyrsa_openssl x509 -in "$crt_in" -noout -enddate |sed 's/^notAfter=//')case $(uname 2>/dev/null) in"Darwin"|*"BSD")expire_date=$(date -j -f '%b %d %T %Y %Z' "$expire_date" +%s)allow_renew_date=$(date -j -v"+${EASYRSA_CERT_RENEW}d" +%s);;*)# This works on Windows, too, since uname doesn't exist and this is catch-allexpire_date=$(date -d "$expire_date" +%s)allow_renew_date=$(date -d "+${EASYRSA_CERT_RENEW}day" +%s);;esac[ "$expire_date" -lt "$allow_renew_date" ] || die "\Certificate expires in more than $EASYRSA_CERT_RENEW days.Renewal not allowed."# Extract certificate usage from old certcert_ext_key_usage=$(easyrsa_openssl x509 -in "$crt_in" -noout -text |sed -n "/X509v3 Extended Key Usage:/{n;s/^ *//g;p;}")case $cert_ext_key_usage in"TLS Web Client Authentication")cert_type=client;;"TLS Web Server Authentication")cert_type=server;;"TLS Web Server Authentication, TLS Web Client Authentication")cert_type=serverClient;;esac# Use SAN from --subject-alt-name if set else use SAN from old certecho "$EASYRSA_EXTRA_EXTS" | grep -q subjectAltName || \{san=$(easyrsa_openssl x509 -in "$crt_in" -noout -text |sed -n "/X509v3 Subject Alternative Name:/{n;s/IP Address:/IP:/;s/ //g;p;}")[ -n "$san" ] && export EASYRSA_EXTRA_EXTS="\$EASYRSA_EXTRA_EXTSsubjectAltName = $san"}# move renewed files so we can reissue certificate with the same name# FIXME: Modify revoke() to also work on the renewed certs subdirmove_renewed "$1"# renew certificate# shellcheck disable=SC2086build_full $cert_type $1 $opts || die "\Failed to renew certificate: renew command failed."notice "\IMPORTANT!!!Renew was successful.You may want to revoke the old certificate once the new one has been deployed." # => notice endreturn 0} #= renew()# move-renewed# moves renewed certificates to an alternative folder# allows reissuing certificates with the same namemove_renewed() {verify_ca_init[ -n "$1" ] || die "\Error: didn't find a file base name as the first argument.Run easyrsa without commands for usage and command help."crt_in="$EASYRSA_PKI/issued/$1.crt"key_in="$EASYRSA_PKI/private/$1.key"req_in="$EASYRSA_PKI/reqs/$1.req"verify_file x509 "$crt_in" || die "\Unable to move renewed input file. The file is not a valid certificate. Unexpectedinput in file: $crt_in"if [ -e "$req_in" ]thenverify_file req "$req_in" || die "\Unable to move request. The file is not a valid request. Unexpectedinput in file: $req_in"fi# get the serial number of the certificate -> serial=XXXXcert_serial="$(easyrsa_openssl x509 -in "$crt_in" -noout -serial)"# remove the serial= part -> we only need the XXXX partcert_serial=${cert_serial##*=}crt_by_serial="$EASYRSA_PKI/certs_by_serial/$cert_serial.pem"crt_by_serial_renewed="$EASYRSA_PKI/renewed/certs_by_serial/$cert_serial.crt"key_by_serial_renewed="$EASYRSA_PKI/renewed/private_by_serial/$cert_serial.key"req_by_serial_renewed="$EASYRSA_PKI/renewed/reqs_by_serial/$cert_serial.req"# make sure renewed dirs exist[ -d "$EASYRSA_PKI/renewed" ] || mkdir "$EASYRSA_PKI/renewed"[ -d "$EASYRSA_PKI/renewed/certs_by_serial" ] || mkdir "$EASYRSA_PKI/renewed/certs_by_serial"[ -d "$EASYRSA_PKI/renewed/private_by_serial" ] || mkdir "$EASYRSA_PKI/renewed/private_by_serial"[ -d "$EASYRSA_PKI/renewed/reqs_by_serial" ] || mkdir "$EASYRSA_PKI/renewed/reqs_by_serial"# move crt, key and req file to renewed foldersmv "$crt_in" "$crt_by_serial_renewed"# only move the req if we have it[ -e "$req_in" ] && mv "$req_in" "$req_by_serial_renewed"# only move the key if we have it[ -e "$key_in" ] && mv "$key_in" "$key_by_serial_renewed"# move the rest of the files (p12, p7, ...)# shellcheck disable=SC2231for file in $EASYRSA_PKI/private/$1\.???do# get file extensionfile_ext="${file##*.}"[ -f "$file" ] && mv "$file" "$EASYRSA_PKI/renewed/private_by_serial/$cert_serial.$file_ext"done# remove the duplicate certificate in the certs_by_serial folderrm "$crt_by_serial"return 0} #= move_renewed()# gen-crl backendgen_crl() {verify_ca_initout_file="$EASYRSA_PKI/crl.pem"out_file_tmp="$(easyrsa_mktemp)" || die "Failed to create temporary file"easyrsa_openssl ca -utf8 -gencrl -out "$out_file_tmp" ${EASYRSA_PASSIN:+-passin "$EASYRSA_PASSIN"} || die "\CRL Generation failed."mv "$out_file_tmp" "$out_file"notice "\An updated CRL has been created.CRL file: $out_file"return 0} # => gen_crl()# import-req backendimport_req() {verify_pki_init# pull passed pathsin_req="$1" short_name="$2"out_req="$EASYRSA_PKI/reqs/$2.req"[ -n "$short_name" ] || die "\Unable to import: incorrect command syntax.Run easyrsa without commands for usage and command help."verify_file req "$in_req" || die "\The input file does not appear to be a certificate request. Aborting import.Offending file: $in_req"# destination must not exist[ -f "$out_req" ] && die "\Unable to import the request as the destination file already exists.Please choose a different name for your imported request file.Existing file at: $out_req"# now import itcp "$in_req" "$out_req"notice "\The request has been successfully imported with a short name of: $short_nameYou may now use this name to perform signing operations on this request."return 0} # => import_req()# export pkcs#12 or pkcs#7export_pkcs() {pkcs_type="$1"shift[ -n "$1" ] || die "\Unable to export p12: incorrect command syntax.Run easyrsa without commands for usage and command help."short_name="$1"crt_in="$EASYRSA_PKI/issued/$1.crt"key_in="$EASYRSA_PKI/private/$1.key"crt_ca="$EASYRSA_PKI/ca.crt"shiftverify_pki_init# opts supportwant_ca=1want_key=1while [ -n "$1" ]; docase "$1" innoca) want_ca="" ;;nokey) want_key="" ;;*) warn "Ignoring unknown command option: '$1'" ;;esacshiftdonepkcs_opts=if [ $want_ca ]; thenverify_file x509 "$crt_ca" || die "\Unable to include CA cert in the $pkcs_type output (missing file, or use noca option.)Missing file expected at: $crt_ca"pkcs_opts="$pkcs_opts -certfile $crt_ca"fi# input files must existverify_file x509 "$crt_in" || die "\Unable to export $pkcs_type for short name '$short_name' without the certificate.Missing cert expected at: $crt_in"case "$pkcs_type" inp12)pkcs_out="$EASYRSA_PKI/private/$short_name.p12"if [ $want_key ]; then[ -f "$key_in" ] || die "\Unable to export p12 for short name '$short_name' without the key(if you want a p12 without the private key, use nokey option.)Missing key expected at: $key_in"elsepkcs_opts="$pkcs_opts -nokeys"fi# export the p12:# shellcheck disable=SC2086easyrsa_openssl pkcs12 -in "$crt_in" -inkey "$key_in" -export \-out "$pkcs_out" $pkcs_opts ${EASYRSA_PASSIN:+-passin "$EASYRSA_PASSIN"} ${EASYRSA_PASSOUT:+-passout "$EASYRSA_PASSOUT"} || die "\Export of p12 failed: see above for related openssl errors.";;p7)pkcs_out="$EASYRSA_PKI/issued/$short_name.p7b"# export the p7:# shellcheck disable=SC2086easyrsa_openssl crl2pkcs7 -nocrl -certfile "$crt_in" \-out "$pkcs_out" $pkcs_opts ${EASYRSA_PASSIN:+-passin "$EASYRSA_PASSIN"} ${EASYRSA_PASSOUT:+-passout "$EASYRSA_PASSOUT"} || die "\Export of p7 failed: see above for related openssl errors.";;esacnotice "\Successful export of $pkcs_type file. Your exported file is at the followinglocation: $pkcs_out"return 0} # => export_pkcs()# set-pass backendset_pass() {verify_pki_init# key type, supplied internally from frontend command call (rsa/ec)key_type="$1"# values supplied by the user:raw_file="$2"file="$EASYRSA_PKI/private/$raw_file.key"[ -n "$raw_file" ] || die "\Missing argument to 'set-$key_type-pass' command: no name/file supplied.See help output for usage details."# parse command optionsshift 2crypto="-aes256"while [ -n "$1" ]; docase "$1" innopass) crypto="" ;;file) file="$raw_file" ;;*) warn "Ignoring unknown command option: '$1'" ;;esacshiftdone[ -f "$file" ] || die "\Missing private key: expected to find the private key component at:$file"notice "\If the key is currently encrypted you must supply the decryption passphrase.${crypto:+You will then enter a new PEM passphrase for this key.$NL}"out_key_tmp="$(easyrsa_mktemp)" || die "Failed to create temporary file"easyrsa_openssl "$key_type" -in "$file" -out "$out_key_tmp" $crypto ${EASYRSA_PASSIN:+-passin "$EASYRSA_PASSIN"} ${EASYRSA_PASSOUT:+-passout "$EASYRSA_PASSOUT"} || die "\Failed to change the private key passphrase. See above for possible opensslerror messages."mv "$out_key_tmp" "$file" || die "\Failed to change the private key passphrase. See above for error messages."notice "Key passphrase successfully changed"} # => set_pass()# update-db backendupdate_db() {verify_ca_initeasyrsa_openssl ca -utf8 -updatedb || die "\Failed to perform update-db: see above for related openssl errors."return 0} # => update_db()display_san() {format="$1" path="$2"echo "$EASYRSA_EXTRA_EXTS" | grep -q subjectAltNameif [ $? -eq 0 ]; thenprint "$(echo "$EASYRSA_EXTRA_EXTS" | grep subjectAltName | sed 's/^\s*subjectAltName\s*=\s*//')"elsesan=$("$EASYRSA_OPENSSL" "$format" -in "$path" -noout -text |sed -n "/X509v3 Subject Alternative Name:/{n;s/ //g;s/IPAddress:/IP:/g;s/RegisteredID/RID/;p;}")[ -n "$san" ] && print "$san"fi}# display cert DN info on a req/X509, passed by full pathnamedisplay_dn() {format="$1" path="$2"print "$("$EASYRSA_OPENSSL" "$format" -in "$path" -noout -subject -nameopt multiline)"san=$(display_san "$1" "$2")if [ -n "$san" ]; thenprint ""print "X509v3 Subject Alternative Name:"print " $san"fi} # => display_dn()# generate default SAN from req/X509, passed by full pathnamedefault_server_san() {path="$1"cn=$(easyrsa_openssl req -in "$path" -noout -subject -nameopt sep_multiline |awk -F'=' '/^ *CN=/{print $2}')echo "$cn" | grep -E -q '^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$'#shellcheck disable=SC2181if [ $? -eq 0 ]; thenprint "subjectAltName = IP:$cn"elseprint "subjectAltName = DNS:$cn"fi} # => default_server_san()# verify a file seems to be a valid req/X509verify_file() {format="$1"path="$2"easyrsa_openssl "$format" -in "$path" -noout 2>/dev/null || return 1return 0} # => verify_file()# show-* command backend# Prints req/cert details in a readable formatshow() {type="$1"name="$2"in_file=""format=""[ -n "$name" ] || die "\Missing expected filename_base argument.Run easyrsa without commands for usage help."shift 2# opts supportopts="-${type}opt no_pubkey,no_sigdump"while [ -n "$1" ]; docase "$1" infull)opts="";;*)warn "Ignoring unknown command option: '$1'";;esacshiftdone# Determine cert/req typeif [ "$type" = "cert" ]; thenverify_ca_initin_file="$EASYRSA_PKI/issued/${name}.crt"format="x509"elseverify_pki_initin_file="$EASYRSA_PKI/reqs/${name}.req"format="req"fi# Verify file exists and is of the correct type[ -f "$in_file" ] || die "\No such $type file with a basename of '$name' is present.Expected to find this file at:$in_file"verify_file $format "$in_file" || die "\This file is not a valid $type file:$in_file"notice "\Showing $type details for '$name'.This file is stored at:$in_file"easyrsa_openssl $format -in "$in_file" -noout -text\-nameopt multiline $opts || die "\OpenSSL failure to process the input"} # => show()# show-ca command backend# Prints CA cert details in a readable formatshow_ca() {# opts supportopts="-certopt no_pubkey,no_sigdump"while [ -n "$1" ]; docase "$1" infull) opts= ;;*) warn "Ignoring unknown command option: '$1'" ;;esacshiftdoneverify_ca_initin_file="$EASYRSA_PKI/ca.crt"format="x509"# Verify file exists and is of the correct type[ -f "$in_file" ] || die "\No such $type file with a basename of '$name' is present.Expected to find this file at:$in_file"verify_file $format "$in_file" || die "\This file is not a valid $type file:$in_file"notice "\Showing $type details for 'ca'.This file is stored at:$in_file"easyrsa_openssl $format -in "$in_file" -noout -text\-nameopt multiline $opts || die "\OpenSSL failure to process the input"} # => show_ca()# vars setup# Here sourcing of 'vars' if present occurs. If not present, defaults are used# to support running without a sourced config formatvars_setup() {# Try to locate a 'vars' file in order of location preference.# If one is found, source itvars=# set up program pathprog_file="$0"prog_file2="$(which -- "$prog_file" 2>/dev/null)" && prog_file="$prog_file2"prog_file2="$(readlink -f "$prog_file" 2>/dev/null)" && prog_file="$prog_file2"prog_dir="${prog_file%/*}"prog_vars="${prog_dir}/vars"# set up PKI pathpki_vars="${EASYRSA_PKI:-$PWD/pki}/vars"# command-line path:if [ ! -z "$EASYRSA_VARS_FILE" ]; thenif [ ! -f "$EASYRSA_VARS_FILE" ]; then# If the --vars option does not point to a file, show helpful error.die "The file '$EASYRSA_VARS_FILE' was not found."fivars="$EASYRSA_VARS_FILE"# PKI location, if present:elif [ -f "$pki_vars" ]; thenvars="$pki_vars"# EASYRSA, if defined:elif [ -n "$EASYRSA" ] && [ -f "$EASYRSA/vars" ]; thenvars="$EASYRSA/vars"# program location:elif [ -f "$prog_vars" ]; thenvars="$prog_vars"fi# If a vars file was located, source it# If $EASYRSA_NO_VARS is defined (not blank) this is skippedif [ -z "$EASYRSA_NO_VARS" ] && [ -n "$vars" ]; thenif grep -Eq 'EASYRSA_PASSIN|EASYRSA_PASSOUT' "$vars"; thendie "\Variable EASYRSA_PASSIN or EASYRSA_PASSOUT has been found in the configuration \file. Storing sensitive information in the configuration file is not \recommended - please remove it from there before continuing."fi#shellcheck disable=SC2034EASYRSA_CALLER=1# shellcheck disable=SC1090. "$vars"notice "\Note: using Easy-RSA configuration from: $vars"fi# Set defaults, preferring existing env-vars if presentset_var EASYRSA "$prog_dir"set_var EASYRSA_OPENSSL opensslset_var EASYRSA_PKI "$PWD/pki"set_var EASYRSA_DN cn_onlyset_var EASYRSA_REQ_COUNTRY "US"set_var EASYRSA_REQ_PROVINCE "California"set_var EASYRSA_REQ_CITY "San Francisco"set_var EASYRSA_REQ_ORG "Copyleft Certificate Co"set_var EASYRSA_REQ_EMAIL me@example.netset_var EASYRSA_REQ_OU "My Organizational Unit"set_var EASYRSA_ALGO rsaset_var EASYRSA_KEY_SIZE 2048set_var EASYRSA_CURVE secp384r1set_var EASYRSA_EC_DIR "$EASYRSA_PKI/ecparams"set_var EASYRSA_CA_EXPIRE 3650set_var EASYRSA_CERT_EXPIRE 825 # new default of 36 monthsset_var EASYRSA_CERT_RENEW 30set_var EASYRSA_CRL_DAYS 180set_var EASYRSA_NS_SUPPORT noset_var EASYRSA_NS_COMMENT "Easy-RSA (3.0.7) Generated Certificate"set_var EASYRSA_TEMP_DIR "$EASYRSA_PKI"set_var EASYRSA_REQ_CN ChangeMeset_var EASYRSA_DIGEST sha256set_var EASYRSA_SSL_CONF "$EASYRSA_PKI/openssl-easyrsa.cnf"set_var EASYRSA_SAFE_CONF "$EASYRSA_PKI/safessl-easyrsa.cnf"set_var EASYRSA_KDC_REALM "CHANGEME.EXAMPLE.COM"# Same as above for the x509-types extensions dirif [ -d "$EASYRSA_PKI/x509-types" ]; thenset_var EASYRSA_EXT_DIR "$EASYRSA_PKI/x509-types"else#TODO: This should be removed. Not really suitable for packaging.set_var EASYRSA_EXT_DIR "$EASYRSA/x509-types"fi# EASYRSA_ALGO_PARAMS must be set depending on selected algoif [ "ec" = "$EASYRSA_ALGO" ]; thenEASYRSA_ALGO_PARAMS="$EASYRSA_EC_DIR/${EASYRSA_CURVE}.pem"elif [ "rsa" = "$EASYRSA_ALGO" ]; thenEASYRSA_ALGO_PARAMS="${EASYRSA_KEY_SIZE}"elif [ "ed" != "$EASYRSA_ALGO" ]; thendie "Alg '$EASYRSA_ALGO' is invalid: must be 'rsa', 'ec' or 'ed' "fi# Assign value to $EASYRSA_TEMP_DIR_session and work around Windows mktemp bug when parent dir is missingif [ -z "$EASYRSA_TEMP_DIR_session" ]; thenif [ -d "$EASYRSA_TEMP_DIR" ]; thenEASYRSA_TEMP_DIR_session="$(mktemp -du "$EASYRSA_TEMP_DIR/easy-rsa-$$.XXXXXX")"else# If the directory does not exist then we have not run init-pkimkdir -p "$EASYRSA_TEMP_DIR" || die "Cannot create $EASYRSA_TEMP_DIR (permission?)"EASYRSA_TEMP_DIR_session="$(mktemp -du "$EASYRSA_TEMP_DIR/easy-rsa-$$.XXXXXX")"rm -rf "$EASYRSA_TEMP_DIR"fifi# Setting OPENSSL_CONF prevents bogus warnings (especially useful on win32)export OPENSSL_CONF="$EASYRSA_SAFE_CONF"# Upgrade to 306: Create $EASYRSA_SSL_CONF if it does not exist but only if $EASYRSA_PKI exists.if [ ! -f "$EASYRSA_SSL_CONF" ] && [ -f "$EASYRSA/openssl-easyrsa.cnf" ] && [ -d "$EASYRSA_PKI" ];thencp "$EASYRSA/openssl-easyrsa.cnf" "$EASYRSA_SSL_CONF"easyrsa_openssl makesafeconffi} # vars_setup()# variable assignment by indirection when undefined; merely exports# the variable when it is already defined (even if currently null)# Sets $1 as the value contained in $2 and exports (may be blank)set_var() {var=$1shiftvalue="$*"eval "export $var=\"\${$var-$value}\""} #=> set_var()############################################################################# Upgrade v2 PKI to v3 PKI# You can report problems on the normal openvpn support channels:# --------------------------------------------------------------------------# 1. The Openvpn Forum: https://forums.openvpn.net/viewforum.php?f=31# 2. The #easyrsa IRC channel at freenode# 3. Info: https://community.openvpn.net/openvpn/wiki/easyrsa-upgrade# --------------------------------------------------------------------------#up23_fail_upgrade (){# Replace die()unset EASYRSA_BATCHnotice "============================================================================The update has failed but NOTHING has been lost.ERROR: $1----------------------------------------------------------------------------Further info:* https://community.openvpn.net/openvpn/wiki/easyrsa-upgrade#ersa-up23-failsEasyrsa3 upgrade FAILED============================================================================"exit 9} #=> up23_fail_upgrade ()up23_verbose (){[ "$VERBOSE" ] || return 0printf "%s\n" "$1"} #=> up23_verbose ()up23_verify_new_pki (){# Fail now, before any changes are madeup23_verbose "> Verify DEFAULT NEW PKI does not exist .."EASYRSA_NEW_PKI="$EASYRSA/pki"[ -d "$EASYRSA_NEW_PKI" ] \&& up23_fail_upgrade "DEFAULT NEW PKI exists: $EASYRSA_NEW_PKI"up23_verbose "> Verify VERY-SAFE-PKI does not exist .."EASYRSA_SAFE_PKI="$EASYRSA/VERY-SAFE-PKI"[ -d "$EASYRSA_SAFE_PKI" ] \&& up23_fail_upgrade "VERY-SAFE-PKI exists: $EASYRSA_SAFE_PKI"up23_verbose "> Verify openssl-easyrsa.cnf does exist .."EASYRSA_SSL_CNFFILE="$EASYRSA/openssl-easyrsa.cnf"[ -f "$EASYRSA_SSL_CNFFILE" ] \|| up23_fail_upgrade "cannot find $EASYRSA_SSL_CNFFILE"up23_verbose "> Verify vars.example does exist .."EASYRSA_VARSV3_EXMP="$EASYRSA/vars.example"[ -f "$EASYRSA_VARSV3_EXMP" ] \|| up23_fail_upgrade "cannot find $EASYRSA_VARSV3_EXMP"up23_verbose "> OK"up23_verbose " Initial dirs & files are in a workable state."} #=> up23_verify_new_pki ()up23_verify_current_pki (){up23_verbose "> Verify CURRENT PKI vars .."# This can probably be improvedEASYRSA_NO_REM="$(grep '^set ' "$EASYRSA_VER2_VARSFILE")"# This list may not be complete# Not required: DH_KEY_SIZE PKCS11_MODULE_PATH PKCS11_PINfor i in KEY_DIR KEY_SIZE KEY_COUNTRY KEY_PROVINCE \KEY_CITY KEY_ORG KEY_EMAIL KEY_CN KEY_NAME KEY_OUdo# Effectively, source the v2 vars fileUNIQUE="set $i"KEY_grep="$(printf "%s\n" "$EASYRSA_NO_REM" | grep "$UNIQUE")"KEY_value="${KEY_grep##*=}"set_var $i "$KEY_value"done[ -d "$KEY_DIR" ] || up23_fail_upgrade "Cannot find CURRENT PKI KEY_DIR: $KEY_DIR"up23_verbose "> OK"up23_verbose " Current CURRENT PKI vars uses PKI in: $KEY_DIR"} #=> up23_verify_current_pki ()up23_verify_current_ca (){up23_verbose "> Find CA .."# $KEY_DIR is assigned in up23_verify_current_pki ()[ -f "$KEY_DIR/ca.crt" ] \|| up23_fail_upgrade "Cannot find current ca.crt: $KEY_DIR/ca.crt"up23_verbose "> OK"# If CA is already verified then returnin_file="$KEY_DIR/ca.crt"[ "$CURRENT_CA_IS_VERIFIED" = "$in_file" ] && return 0format="x509"# Current CA is unverified# Extract the current CA detailsCA_SUBJECT="$(easyrsa_openssl $format -in "$in_file" -subject -noout -nameopt multiline)"# Extract individual elementsCA_countryName="$(printf "%s\n" "$CA_SUBJECT" \| grep countryName | sed "s\`^.*=\ \`\`g")"CA_stateOrProvinceName="$(printf "%s\n" "$CA_SUBJECT" \| grep stateOrProvinceName | sed "s\`^.*=\ \`\`g")"CA_localityName="$(printf "%s\n" "$CA_SUBJECT" \| grep localityName | sed "s\`^.*=\ \`\`g")"CA_organizationName="$(printf "%s\n" "$CA_SUBJECT" \| grep organizationName | sed "s\`^.*=\ \`\`g")"CA_organizationalUnitName="$(printf "%s\n" "$CA_SUBJECT" \| grep organizationalUnitName | sed "s\`^.*=\ \`\`g")"CA_emailAddress="$(printf "%s\n" "$CA_SUBJECT" \| grep emailAddress | sed "s\`^.*=\ \`\`g")"# Match the current CA elements to the vars file settingsCA_vars_match=1[ "$CA_countryName" = "$KEY_COUNTRY" ] || CA_vars_match=0[ "$CA_stateOrProvinceName" = "$KEY_PROVINCE" ] || CA_vars_match=0[ "$CA_localityName" = "$KEY_CITY" ] || CA_vars_match=0[ "$CA_organizationName" = "$KEY_ORG" ] || CA_vars_match=0[ "$CA_organizationalUnitName" = "$KEY_OU" ] || CA_vars_match=0[ "$CA_emailAddress" = "$KEY_EMAIL" ] || CA_vars_match=0if [ "$CA_vars_match" -eq 1 ]thenCURRENT_CA_IS_VERIFIED="partially"elseup23_fail_upgrade "CA certificate does not match vars file settings"fiopts="-certopt no_pubkey,no_sigdump"if [ ! "$EASYRSA_BATCH" ]thenup23_show_current_caelif [ "$VERBOSE" ]thenup23_show_current_caficonfirm "* Confirm CA shown above is correct: " "yes" \"Found current CA at: $KEY_DIR/ca.crt"CURRENT_CA_IS_VERIFIED="$in_file"} #=> up23_verify_current_ca ()up23_show_current_ca (){printf "%s\n" "-------------------------------------------------------------------------"# $opts is always set here# shellcheck disable=SC2086easyrsa_openssl $format -in "$in_file" -noout -text\-nameopt multiline $opts || die "\OpenSSL failure to process the input CA certificate: $in_file"printf "%s\n" "-------------------------------------------------------------------------"} #=> up23_show_current_ca ()up23_backup_current_pki (){up23_verbose "> Backup current PKI .."mkdir -p "$EASYRSA_SAFE_PKI" \|| up23_fail_upgrade "Failed to create safe PKI dir: $EASYRSA_SAFE_PKI"cp -r "$KEY_DIR" "$EASYRSA_SAFE_PKI" \|| up23_fail_upgrade "Failed to copy $KEY_DIR to $EASYRSA_SAFE_PKI"# EASYRSA_VER2_VARSFILE is either version 2 *nix ./vars or Win vars.batcp "$EASYRSA_VER2_VARSFILE" "$EASYRSA_SAFE_PKI" \|| up23_fail_upgrade "Failed to copy $EASYRSA_VER2_VARSFILE to EASYRSA_SAFE_PKI"up23_verbose "> OK"up23_verbose " Current PKI backup created in: $EASYRSA_SAFE_PKI"} #=> up23_backup_current_pki ()up23_create_new_pki (){# Dirs: renewed and revoked are created when used.up23_verbose "> Create NEW PKI .."up23_verbose ">> Create NEW PKI dirs .."for i in private reqs issued certs_by_serialdomkdir -p "$EASYRSA_PKI/$i" \|| up23_fail_upgrade "Failed to Create NEW PKI dir: $EASYRSA_PKI/$i"doneup23_verbose ">> OK"up23_verbose ">> Copy database to NEW PKI .."# Failure for these is not optional# Files ignored: index.txt.old serial.oldfor i in index.txt serial ca.crt index.txt.attrdocp "$KEY_DIR/$i" "$EASYRSA_PKI" \|| up23_fail_upgrade "Failed to copy $KEY_DIR/$i to $EASYRSA_PKI"doneup23_verbose ">> OK"up23_verbose ">> Copy current PKI to NEW PKI .."for i in "csr.reqs" "pem.certs_by_serial" "crt.issued" "key.private" \"p12.private" "p8.private" "p7b.issued"doFILE_EXT="${i%%.*}"DEST_DIR="${i##*.}"if ls "$KEY_DIR/"*".$FILE_EXT" > /dev/null 2>&1; thencp "$KEY_DIR/"*".$FILE_EXT" "$EASYRSA_PKI/$DEST_DIR" \|| up23_fail_upgrade "Failed to copy .$FILE_EXT"elseup23_verbose " Note: No .$FILE_EXT files found"fidoneup23_verbose ">> OK"up23_verbose "> OK"# Todo: CRL - Or generate a new CRL on completionup23_verbose " New PKI created in: $EASYRSA_PKI"} #=> up23_create_new_pki ()up23_upgrade_ca (){[ -d "$EASYRSA_PKI" ] || return 0up23_verbose "> Confirm that index.txt.attr exists and 'unique_subject = no'"if [ -f "$EASYRSA_PKI/index.txt.attr" ]thenif grep -q 'unique_subject = no' "$EASYRSA_PKI/index.txt.attr"then# If index.txt.attr exists and "unique_suject = no" then do nothingreturn 0fielse# If index.txt.attr does not exists then do nothingreturn 0fi# Otherwise this is required for all easyrsa v3#confirm "Set 'unique_subject = no' in index.txt.attr for your current CA: " \#"yes" "This version of easyrsa requires that 'unique_subject = no' is set correctly"printf "%s\n" "unique_subject = no" > "$EASYRSA_PKI/index.txt.attr"up23_verbose "> OK"up23_verbose " Upgraded index.txt.attr to v306+"} #=> up23_upgrade_index_txt_attr ()up23_create_openssl_cnf (){up23_verbose "> OpenSSL config .."EASYRSA_PKI_SSL_CNFFILE="$EASYRSA_PKI/openssl-easyrsa.cnf"EASYRSA_PKI_SAFE_CNFFILE="$EASYRSA_PKI/safessl-easyrsa.cnf"cp "$EASYRSA_SSL_CNFFILE" "$EASYRSA_PKI_SSL_CNFFILE" \|| up23_fail_upgrade "create $EASYRSA_PKI_SSL_CNFFILE"up23_verbose "> OK"up23_verbose " New OpenSSL config file created in: $EASYRSA_PKI_SSL_CNFFILE"# Create $EASYRSA_PKI/safessl-easyrsa.cnfeasyrsa_openssl makesafeconfif [ -f "$EASYRSA_PKI_SAFE_CNFFILE" ]thenup23_verbose " New SafeSSL config file created in: $EASYRSA_PKI_SAFE_CNFFILE"elseup23_verbose " FAILED to create New SafeSSL config file in: $EASYRSA_PKI_SAFE_CNFFILE"fi} #=> up23_create_openssl_cnf ()up23_move_easyrsa2_programs (){# These files may not exist hereup23_verbose "> Move easyrsa2 programs to SAFE PKI .."for i in build-ca build-dh build-inter build-key build-key-pass \build-key-pkcs12 build-key-server build-req build-req-pass \clean-all inherit-inter list-crl pkitool revoke-full sign-req \whichopensslcnf build-ca-pass build-key-server-pass init-config \make-crl revoke-crt openssl-0.9.6.cnf openssl-0.9.8.cnf \openssl-1.0.0.cnf openssl.cnf README.txt index.txt.start \vars.bat.sample serial.startdo# Although unlikely, both files could exist# EG: ./build-ca and ./build-ca.batNIX_FILE="$EASYRSA/$i"WIN_FILE="$EASYRSA/$i.bat"if [ -f "$NIX_FILE" ]thencp "$NIX_FILE" "$EASYRSA_SAFE_PKI" \|| up23_fail_upgrade "copy $NIX_FILE $EASYRSA_SAFE_PKI"fiif [ -f "$WIN_FILE" ]thencp "$WIN_FILE" "$EASYRSA_SAFE_PKI" \|| up23_fail_upgrade "copy $WIN_FILE $EASYRSA_SAFE_PKI"fiif [ ! -f "$NIX_FILE" ] && [ ! -f "$WIN_FILE" ]thenup23_verbose "File does not exist, ignoring: $i(.bat)"fi# These files are not removed on TEST run[ "$NOSAVE" -eq 1 ] && rm -f "$NIX_FILE" "$WIN_FILE"doneup23_verbose "> OK"up23_verbose " Easyrsa2 programs successfully moved to: $EASYRSA_SAFE_PKI"} #=> up23_move_easyrsa2_programs ()up23_build_v3_vars (){up23_verbose "> Build v3 vars file .."EASYRSA_EXT="easyrsa-upgrade-23"EASYRSA_VARSV2_TMP="$EASYRSA/vars-v2.tmp.$EASYRSA_EXT"rm -f "$EASYRSA_VARSV2_TMP"EASYRSA_VARSV3_TMP="$EASYRSA/vars-v3.tmp.$EASYRSA_EXT"rm -f "$EASYRSA_VARSV3_TMP"EASYRSA_VARSV3_NEW="$EASYRSA/vars-v3.new.$EASYRSA_EXT"rm -f "$EASYRSA_VARSV3_NEW"EASYRSA_VARSV3_WRN="$EASYRSA/vars-v3.wrn.$EASYRSA_EXT"rm -f "$EASYRSA_VARSV3_WRN"printf "%s\n" "\########################++++++++++############################ ###### WARNING: THIS FILE WAS AUTOMATICALLY GENERATED ###### ALL SETTINGS ARE AT THE END OF THE FILE ###### ###########################++++++++++#########################" > "$EASYRSA_VARSV3_WRN" || up23_fail_upgrade "Failed to create $EASYRSA_VARSV3_WRN"# Create vars v3 temp file from sourced vars v2 key variables{printf "%s\n" "set_var EASYRSA_KEY_SIZE $KEY_SIZE"printf "%s\n" "set_var EASYRSA_REQ_COUNTRY \"$KEY_COUNTRY\""printf "%s\n" "set_var EASYRSA_REQ_PROVINCE \"$KEY_PROVINCE\""printf "%s\n" "set_var EASYRSA_REQ_CITY \"$KEY_CITY\""printf "%s\n" "set_var EASYRSA_REQ_ORG \"$KEY_ORG\""printf "%s\n" "set_var EASYRSA_REQ_EMAIL \"$KEY_EMAIL\""printf "%s\n" "set_var EASYRSA_REQ_OU \"$KEY_OU\""printf "%s\n" 'set_var EASYRSA_NS_SUPPORT "yes"'printf "%s\n" 'set_var EASYRSA_DN "org"'printf "%s\n" 'set_var EASYRSA_RAND_SN "no"'printf "%s\n" ""} > "$EASYRSA_VARSV3_TMP" \|| up23_fail_upgrade "Failed to create $EASYRSA_VARSV3_TMP"# cat temp files into new v3 varscat "$EASYRSA_VARSV3_WRN" "$EASYRSA_VARSV3_EXMP" "$EASYRSA_VARSV3_TMP" \> "$EASYRSA_VARSV3_NEW" \|| up23_fail_upgrade "Failed to create $EASYRSA_VARSV3_NEW"# This file must be created and restored at the end of TEST# for the REAL update to to succeedEASYRSA_VARS_LIVEBKP="$EASYRSA_TARGET_VARSFILE.livebackup"cp "$EASYRSA_VER2_VARSFILE" "$EASYRSA_VARS_LIVEBKP" \|| up23_fail_upgrade "Failed to create $EASYRSA_VARS_LIVEBKP"rm -f "$EASYRSA_VER2_VARSFILE"# "$EASYRSA_TARGET_VARSFILE" is always $EASYRSA/varscp "$EASYRSA_VARSV3_NEW" "$EASYRSA_TARGET_VARSFILE" \|| up23_fail_upgrade "copy $EASYRSA_VARSV3_NEW to $EASYRSA_TARGET_VARSFILE"# Delete temp filesrm -f "$EASYRSA_VARSV2_TMP" "$EASYRSA_VARSV3_TMP" \"$EASYRSA_VARSV3_NEW" "$EASYRSA_VARSV3_WRN"up23_verbose "> OK"up23_verbose " New v3 vars file created in: $EASYRSA_TARGET_VARSFILE"} #=> up23_build_v3_vars ()up23_do_upgrade_23 (){up23_verbose "============================================================================"up23_verbose "Begin ** $1 ** upgrade process .."up23_verbose ""up23_verbose "Easyrsa upgrade version: $EASYRSA_UPGRADE_23"up23_verbose ""up23_verify_new_pkiup23_verify_current_pkiup23_verify_current_caup23_backup_current_pkiup23_create_new_pkiup23_upgrade_caup23_move_easyrsa2_programsup23_build_v3_varsup23_create_openssl_cnfif [ "$NOSAVE" -eq 0 ]then# Must stay in this order# New created dirs: EASYRSA_NEW_PKI and EASYRSA_SAFE_PKIrm -rf "$EASYRSA_NEW_PKI"rm -rf "$EASYRSA_SAFE_PKI"# EASYRSA_TARGET_VARSFILE is always the new created v3 vars# Need to know if this failsrm "$EASYRSA_TARGET_VARSFILE" \|| up23_fail_upgrade "remove new vars file: $EASYRSA_TARGET_VARSFILE"# EASYRSA_VER2_VARSFILE is either v2 *nix ./vars or Win vars.bat# Need this dance because v2 vars is same name as v3 vars abovecp "$EASYRSA_VARS_LIVEBKP" "$EASYRSA_VER2_VARSFILE"firm -f "$EASYRSA_VARS_LIVEBKP"} #= up23_do_upgrade_23 ()up23_manage_upgrade_23 (){EASYRSA_UPGRADE_VERSION="v1.0a (2020/01/08)"EASYRSA_UPGRADE_TYPE="$1"# Verify all existing versions of vars/vars.batif [ -f "$vars" ]thenif grep -q 'Complain if a user tries to do this:' "$vars"thenEASYRSA_FOUND_VARS=1EASYRSA_VARS_IS_VER3=1fi# Easyrsa v3 does not use NOR allow use of `export`.if grep -q 'export' "$vars"thenEASYRSA_FOUND_VARS=1EASYRSA_VARS_IS_VER2=1EASYRSA_VER2_VARSFILE="$vars"EASYRSA_TARGET_VARSFILE="$vars"fifiif [ -f "$EASYRSA/vars.bat" ]thenEASYRSA_FOUND_VARS=1EASYRSA_VARS_IS_WIN2=1EASYRSA_VER2_VARSFILE="$EASYRSA/vars.bat"EASYRSA_TARGET_VARSFILE="$EASYRSA/vars"fi[ "$EASYRSA_FOUND_VARS" ] || return 0# Only allow specific vars/vars.bat to existif [ "$EASYRSA_VARS_IS_VER3" ] && [ "$EASYRSA_VARS_IS_VER2" ]thendie "Verify your current vars file, v3 cannot use 'export'."fiif [ "$EASYRSA_VARS_IS_VER3" ] && [ "$EASYRSA_VARS_IS_WIN2" ]thendie "Verify your current vars/vars.bat file, cannot have both."fiif [ "$EASYRSA_VARS_IS_VER2" ] && [ "$EASYRSA_VARS_IS_WIN2" ]thendie "Verify your current vars/vars.bat file, cannot have both."fi# Die on invalid upgrade type or environmentif [ "$EASYRSA_UPGRADE_TYPE" = "ca" ]thenif [ "$EASYRSA_VARS_IS_VER3" ]then# v3 ensure index.txt.attr "unique_subject = no"up23_upgrade_caunset EASYRSA_BATCHnotice "Your CA is fully up to date."return 0elsedie "Only v3 PKI CA can be upgraded."fifiif [ "$EASYRSA_UPGRADE_TYPE" = "pki" ]thenif [ "$EASYRSA_VARS_IS_VER3" ]thenunset EASYRSA_BATCHnotice "Your PKI is fully up to date."return 0fielsedie "upgrade type must be 'pki' or 'ca'."fi# PKI is potentially suitable for upgradewarn "=========================================================================* WARNING *Found settings from EasyRSA-v2 which are not compatible with EasyRSA-v3.Before you can continue, EasyRSA must upgrade your settings and PKI.* Found EASYRSA and vars file:$EASYRSA$EASYRSA_VER2_VARSFILE :Further info:* https://community.openvpn.net/openvpn/wiki/easyrsa-upgradeEasyrsa upgrade version: $EASYRSA_UPGRADE_VERSION========================================================================="# Test upgradeNOSAVE=0confirm "* EasyRSA **TEST** upgrade (Changes will NOT be written): " "yes" "This upgrade will TEST that the upgrade works BEFORE making any changes."up23_do_upgrade_23 "TEST"notice "=========================================================================* NOTICE *EasyRSA upgrade **TEST** has successfully completed."# Upgrade for REALNOSAVE=1confirm "* EasyRSA **REAL** upgrade (Changes WILL be written): " "yes" "=========================================================================* WARNING *Run REAL upgrade: Answer yes (Once completed you will have a version 3 PKI)Terminate upgrade: Answer no (No changes have been made to your current PKI)"confirm "* Confirm **REAL** upgrade (Changes will be written): " "yes" "=========================================================================* SECOND WARNING *This upgrade will permanently write changes to your PKI !(With full backup backout)"up23_do_upgrade_23 "REAL"notice "=========================================================================* NOTICE *Your settings and PKI have been successfully upgraded to EasyRSA version3A backup of your current PKI is here:$EASYRSA_SAFE_PKI* IMPORTANT NOTICE *1. YOU MUST VERIFY THAT YOUR NEW ./vars FILE IS SETUP CORRECTLY2. IF YOU ARE USING WINDOWS YOU MUST ENSURE THAT openssl IS CORRECTLY DEFINEDIN ./vars (example follows)## This sample is in Windows syntax -- edit it for your path if not using PATH:# set_var EASYRSA_OPENSSL \"C:/Program Files/OpenSSL-Win32/bin/openssl.exe\"## Alternate location (Note: Forward slash '/' is correct for Windpws):# set_var EASYRSA_OPENSSL \"C:/Program Files/Openvpn/bin/openssl.exe\"#3. Finally, you can verify that easyrsa works by using these two commands:./easyrsa show-ca (Verify that your CA is intact and correct)./easyrsa gen-crl ((re)-generate a CRL file)Further info:* https://community.openvpn.net/openvpn/wiki/easyrsa-upgrade"up23_verbose "* UPGRADE COMPLETED SUCCESSFULLY *"return 0} # => up23_manage_upgrade_23 ()######################################### Invocation entry point:NL=''# Be secure with a restrictive umask[ -z "$EASYRSA_NO_UMASK" ] && umask 077# Parse optionswhile :; do# Separate option from value:opt="${1%%=*}"val="${1#*=}"empty_ok="" # Empty values are not allowed unless exceptedcase "$opt" in--days)export EASYRSA_CERT_EXPIRE="$val"export EASYRSA_CA_EXPIRE="$val"export EASYRSA_CRL_DAYS="$val";;--pki-dir)export EASYRSA_PKI="$val" ;;--use-algo)export EASYRSA_ALGO="$val" ;;--keysize)export EASYRSA_KEY_SIZE="$val" ;;--curve)export EASYRSA_CURVE="$val" ;;--dn-mode)export EASYRSA_DN="$val" ;;--req-cn)export EASYRSA_REQ_CN="$val" ;;--digest)export EASYRSA_DIGEST="$val" ;;--req-c)empty_ok=1export EASYRSA_REQ_COUNTRY="$val" ;;--req-st)empty_ok=1export EASYRSA_REQ_PROVINCE="$val" ;;--req-city)empty_ok=1export EASYRSA_REQ_CITY="$val" ;;--req-org)empty_ok=1export EASYRSA_REQ_ORG="$val" ;;--req-email)empty_ok=1export EASYRSA_REQ_EMAIL="$val" ;;--req-ou)empty_ok=1export EASYRSA_REQ_OU="$val" ;;--ns-cert)export EASYRSA_NS_SUPPORT="$val" ;;--ns-comment)empty_ok=1export EASYRSA_NS_COMMENT="$val" ;;--batch)empty_ok=1export EASYRSA_BATCH=1 ;;--passin)export EASYRSA_PASSIN="$val";;--passout)export EASYRSA_PASSOUT="$val";;--subca-len)export EASYRSA_SUBCA_LEN="$val" ;;--vars)export EASYRSA_VARS_FILE="$val" ;;--copy-ext)empty_ok=1export EASYRSA_CP_EXT=1 ;;--subject-alt-name)export EASYRSA_EXTRA_EXTS="\$EASYRSA_EXTRA_EXTSsubjectAltName = $val" ;;*)break ;;esac# fatal error when no value was providedif [ ! $empty_ok ] && { [ "$val" = "$1" ] || [ -z "$val" ]; }; thendie "Missing value to option: $opt"fishiftdone# Intelligent env-var detection and auto-loading:vars_setup# Register cleanup on EXITtrap "cleanup" EXIT# When SIGHUP, SIGINT, SIGQUIT, SIGABRT and SIGTERM,# explicitly exit to signal EXIT (non-bash shells)trap "exit 1" 1trap "exit 2" 2trap "exit 3" 3trap "exit 6" 6trap "exit 14" 15# Upgrade: EasyRSA v2.x to EasyRSA v3.x# Upgrade: EasyRSA < v3.0.6 to v3.0.6+#up23_manage_upgrade_23# determine how we were called, then hand off to the function responsiblecmd="$1"[ -n "$1" ] && shift # scrape off commandcase "$cmd" ininit-pki|clean-all)init_pki "$@";;build-ca)build_ca "$@";;gen-dh)gen_dh;;gen-req)gen_req "$@";;sign|sign-req)sign_req "$@";;build-client-full)build_full client "$@";;build-server-full)build_full server "$@";;build-serverClient-full)build_full serverClient "$@";;gen-crl)gen_crl;;revoke)revoke "$@";;renew)renew "$@";;import-req)import_req "$@";;export-p12)export_pkcs p12 "$@";;export-p7)export_pkcs p7 "$@";;set-rsa-pass)set_pass rsa "$@";;set-ec-pass)set_pass ec "$@";;update-db)update_db;;show-req)show req "$@";;show-cert)show cert "$@";;show-ca)show_ca "$@";;upgrade)up23_manage_upgrade_23 "$@";;""|help|-h|--help|--usage)cmd_help "$1"exit 0;;*)die "Unknown command '$cmd'. Run without commands for usage help.";;esac# vim: ft=sh nu ai sw=8 ts=8 noet