Skip to content

Commit

Permalink
Integrate Easy-RSA TLS-Key for use with 'init-pki soft'
Browse files Browse the repository at this point in the history
The primary use of 'init-pki soft' is to present a simple way
to renew an expired CA Certificate. The method Easy-RSA uses
is to create a new CA and sign old Requests.

Key(1): pki/private/easyrsa-tls.key - Singular Easy-RSA TLS key.
Key(2): pki/easyrsa-keepsafe-tls.key - Created by 'init-pki soft'.

The Easy-RSA TLS Key(1) in use is not changed by this method.
Also, this TLS Key(1) is private data, therefore, it is not
added to new inline files. This allows the new inline files
to be easily distrubuted, without leaking security data.

All members of the VPN in use have a local copy of the TLS Key(1).
This key(1) can either be added to the OpenVPN config file, using
option '--tls-auth/--tls-crypt <key-file>', or be pasted into
the inline file locally.

This patch integrates the Easy-RSA TLS Key into 'init-pki soft'
by recreating the key(2) after the PKI has been reset. However,
the key(2) is recreated by a different file-name, to ensure that
it is not included with newly generated inline files.

After building a new CA, the TLS key(2) is copied to the original
key(1). However, these TLS keys will be omitted from new inline
files until the key(2), created by 'init-pki soft', is deleted.

All inline files that contain private keys are written to sub-dir
'pki/inline/private'. All inline files that do NOT contain any
private keys are written to 'pki/inline'.

Also, minimise some 'case' statements and minor improvements.

Signed-off-by: Richard T Bonhomme <[email protected]>
TinCanTech committed Aug 30, 2024

Verified

This commit was signed with the committer’s verified signature.
1 parent 294c05b commit 03d9dc2
Showing 2 changed files with 166 additions and 71 deletions.
57 changes: 35 additions & 22 deletions dev/easyrsa-tools.lib
Original file line number Diff line number Diff line change
@@ -24,7 +24,8 @@ verify_openvpn() {
# Try to find openvpn
set_var EASYRSA_OPENVPN "$(which openvpn)"
if [ -f "$EASYRSA_OPENVPN" ]; then
verbose "verify_openvpn - $EASYRSA_OPENVPN"
verbose \
"verify_openvpn - EASYRSA_OPENVPN='$EASYRSA_OPENVPN'"
else
user_error "Cannot find an OpenVPN binary."
fi
@@ -33,41 +34,52 @@ verify_openvpn() {
# OpenVPN TLS Auth/Crypt Key
tls_key_gen() {
case "$1" in
tls-auth)
tls_key_type=TLS-AUTH
;;
tls-crypt)
tls_key_type=TLS-CRYPT
;;
tls-crypt-v2)
print "Unavailable."
cleanup
;;
*)
die "Unknown key type: '$1'"
tls-crypt) tls_key_type=TLS-CRYPT ;;
tls-auth) tls_key_type=TLS-AUTH ;;
*) die "Unknown key type: '$1'"
esac
tls_key_file="$EASYRSA_PKI/private/easyrsa-tls.key"

# Forbid overwrite
# Over write error message
tls_key_error_msg="
If this file is changed then it MUST be redistributed to ALL servers
AND clients, to be in effect. Do NOT change this existing file."

# Assign possible TLS key sources
tls_key_file="$EASYRSA_PKI"/private/easyrsa-tls.key
old_tls_key_file="$EASYRSA_PKI"/easyrsa-keepsafe-tls.key

# Forbid overwrite - default TLS key
if [ -f "$tls_key_file" ]; then
tls_key_data="$(cat "$tls_key_file")"
case "$tls_key_data" in
*'TLS-AUTH'*)
tls_key_type=TLS-AUTH
;;
*'TLS-CRYPT'*)
tls_key_type=TLS-CRYPT
;;
*)
tls_key_type=UNKNOWN
*'TLS-CRYPT'*) tls_key_type=TLS-CRYPT ;;
*'TLS-AUTH'*) tls_key_type=TLS-AUTH ;;
*) tls_key_type=UNKNOWN
esac

user_error "\
Cannot overwrite existing $tls_key_type Key:
* $tls_key_file
$tls_key_error_msg"
fi

If this file is changed then it MUST be redistributed to ALL servers
AND clients, to be in effect. Do NOT change the existing file."
# Forbid overwrite - Old TLS key
if [ -f "$old_tls_key_file" ]; then
old_tls_key_data="$(cat "$old_tls_key_file")"
case "$old_tls_key_data" in
*'TLS-CRYPT'*) tls_key_type=TLS-CRYPT ;;
*'TLS-AUTH'*) tls_key_type=TLS-AUTH ;;
*) tls_key_type=UNKNOWN
esac

user_error "\
Cannot overwrite existing $tls_key_type Key:
* $old_tls_key_file
$tls_key_error_msg"
fi

verify_openvpn
@@ -89,7 +101,8 @@ AND clients, to be in effect. Do NOT change the existing file."

notice "\
$tls_key_type Key generated at:
* $tls_key_file"
* $tls_key_file
$tls_key_error_msg"
verbose "tls_key_gen: openvpn --genkey $tls_key_type OK"
} # => tls_key_gen()

180 changes: 131 additions & 49 deletions easyrsa3/easyrsa
Original file line number Diff line number Diff line change
@@ -1336,8 +1336,14 @@ init_pki() {
reset="hard"
while [ "$1" ]; do
case "$1" in
hard-reset|hard) reset="hard" ;;
soft-reset|soft) reset="soft" ;;
hard-reset|hard)
reset="hard"
confirm_msg=
;;
soft-reset|soft)
reset="soft"
confirm_msg='PARTIALLY '
;;
*) warn "Ignoring unknown command option: '$1'"
esac
shift
@@ -1354,7 +1360,7 @@ init_pki() {
confirm "Confirm removal: " "yes" "
WARNING!!!

You are about to remove the EASYRSA_PKI at:
You are about to ${confirm_msg}remove the EASYRSA_PKI at:
* $EASYRSA_PKI

and initialize a fresh PKI here."
@@ -1374,6 +1380,8 @@ and initialize a fresh PKI here."
To keep your current 'pki/vars' settings use 'init-pki soft'.
To keep your current Request files use 'init-pki soft'
The Requests can then be signed by a new CA (Partial CA renewal)
To keep your current Easy-RSA TLS Key use 'init-pki soft'
This private key file is in use by your current VPN.

** USE OF 'init-pki soft' IS RECOMMENDED **${NL}"

@@ -1383,7 +1391,38 @@ and initialize a fresh PKI here."
;;
soft)
# There is no unit test for a soft reset
# Do NOT remove pki/reqs sub-dir, for "renew ca"
# Save existing TLS key
tls_key_file="$EASYRSA_PKI"/private/easyrsa-tls.key
old_tls_key_file="$EASYRSA_PKI"/easyrsa-keepsafe-tls.key

# If both keys exist then they must be the same
if [ -f "$old_tls_key_file" ]; then
if [ -f "$tls_key_file" ]; then
# Match by hash
tls_key_hash="$(
"$EASYRSA_OPENSSL" dgst -sha256 \
"$tls_key_file")"
old_tls_key_hash="$(
"$EASYRSA_OPENSSL" dgst -sha256 \
"$old_tls_key_file")"
[ "$tls_key_hash" = "$old_tls_key_hash" ] || \
user_error "\
Easy-RSA TLS Keys do not match, only ONE of these files is valid:
* $tls_key_file
* $old_tls_key_file

Please delete the key above that is no longer in use."
fi
fi

# Save existing TLS key
if [ -f "$tls_key_file" ]; then
tls_key_data="$(cat "$tls_key_file")"
else
tls_key_data=
fi

# Do NOT remove pki/reqs sub-dir, for "renew ca"
for i in ca.crt crl.pem \
issued private inline revoked renewed expired \
serial serial.old index.txt index.txt.old \
@@ -1410,15 +1449,29 @@ and initialize a fresh PKI here."
easyrsa_mkdir "${EASYRSA_PKI}/$i"
done

# If one existed then recreate old TLS key backup file
if [ "$tls_key_data" ]; then
header="# Easy-RSA TLS Key: $(date)${NL}# DO NOT DELETE"
printf '%s\n\n%s\n' "$header" "$tls_key_data" \
> "$old_tls_key_file"
tls_msg="\
Previous Easy-RSA TLS key saved to:
* $old_tls_key_file"
else
tls_msg="\
Create a TLS-AUTH|TLS-CRYPT-V1 key now: See 'help gen-tls'"
fi

# write pki/vars.example - no temp-file because no session
write_legacy_file_v2 vars "$EASYRSA_PKI"/vars.example || \
warn "init-pki - Failed to create vars.example"
write_legacy_file_v2 \
vars "$EASYRSA_PKI"/vars.example overwrite || \
warn "init-pki - Failed to create vars.example"

# User notice
notice "\
'init-pki' complete; you may now create a CA or requests.

Create a TLS-AUTH|TLS-CRYPT-V1 key now: See 'help gen-tls'
$tls_msg

Your newly created PKI dir is:
* $EASYRSA_PKI"
@@ -1608,6 +1661,14 @@ Unable to create necessary PKI files (permissions?)"
> "$EASYRSA_PKI/serial" || die "$err_msg"
unset -v err_msg

# If one exists then recreate TLS Key
tls_key_file="$EASYRSA_PKI/private/easyrsa-tls.key"
old_tls_key_file="$EASYRSA_PKI"/easyrsa-keepsafe-tls.key
if [ -f "$old_tls_key_file" ]; then
cp "$old_tls_key_file" "$tls_key_file" || \
warn "Failed to install TLS Key!"
fi

# Set ssl batch mode, as required
if [ "$EASYRSA_BATCH" ]; then
ssl_batch=1
@@ -2889,13 +2950,20 @@ inline_file() {
# Source files
crt_source="${EASYRSA_PKI}/issued/${1}.crt"
key_source="${EASYRSA_PKI}/private/${1}.key"
ca_source="${EASYRSA_PKI}/ca.crt"
tls_source="${EASYRSA_PKI}"/private/easyrsa-tls.key
ca_source="$EASYRSA_PKI"/ca.crt
tls_source="$EASYRSA_PKI"/private/easyrsa-tls.key
old_tls_key_file="$EASYRSA_PKI"/easyrsa-keepsafe-tls.key

# output
inline_out="${EASYRSA_PKI}/inline/${1}.inline"
easyrsa_mkdir "${EASYRSA_PKI}/inline"
easyrsa_mkdir "$EASYRSA_PKI"/inline
easyrsa_mkdir "$EASYRSA_PKI"/inline/private
print "\
# Inline files in the 'private' directory contain security keys which
# MUST only be transmitted over a secure connection, such as 'scp'." \
> "$EASYRSA_PKI"/inline/private/README.inline.private
inline_incomplete=
inline_private=

# Generate Inline data
# Certificate
@@ -2942,6 +3010,7 @@ $(cat "$crt_source")

# Private key
if [ -f "$key_source" ]; then
inline_private=1
key_data="\
<key>
$(cat "$key_source")
@@ -2974,45 +3043,59 @@ $(cat "$ca_source")
# </ca>"
fi

# TLS auth|crypt key
# TLS KEY - Set TLS auth|crypt key inline label
if [ -f "$tls_source" ]; then
tls_key_data="$(cat "$tls_source")"
case "$tls_key_data" in
*'TLS-AUTH'*)
tls_key_label=tls-auth
;;
*'TLS-CRYPT'*)
tls_key_label=tls-crypt
;;
*)
tls_key_label=
*'TLS-AUTH'*) tls_key_label=tls-auth ;;
*'TLS-CRYPT'*) tls_key_label=tls-crypt ;;
*) tls_key_label=
esac
fi

if [ "$tls_key_label" ]; then
tls_data="\
# Do NOT add TLS key if OLD TLS key exists
# because this PSK has already been shared.
if [ -f "$old_tls_key_file" ]; then
tls_data="\
# Add the existing TLS AUTH/CRYPT-V1 Key here:
# <${tls_key_label}>
# * Paste The existing pre-shared TLS key here *
# </${tls_key_label}>"

# Add --key-direction for TLS-AUTH
[ "$tls_key_label" = tls-auth ] && \
tls_data="$tls_data
#
# Add the required 'key-direction 0|1' here:
# key-direction 1"
unset -v tls_key_data tls_key_label
else
# Add standard TLS key details
if [ -f "$tls_source" ]; then
inline_private=1
if [ "$tls_key_label" ]; then
tls_data="\
<${tls_key_label}>
${tls_key_data}
</${tls_key_label}>"
else
inline_incomplete=1
tls_data="# Easy-RSA TLS Key not recognised!"
fi
else
inline_incomplete=1
tls_data="# Easy-RSA TLS Key not recognised!"
#inline_incomplete=1
tls_data="# Easy-RSA TLS Key not found!"
fi
else
inline_incomplete=1
tls_data="# Easy-RSA TLS Key not found!"
fi

# Only support inline files for OpenVPN server/client use
# Only support inline TLS keys for OpenVPN server/client use
case "$crt_type" in
server)
key_direction="key-direction 0"
;;
client)
key_direction="key-direction 1"
;;
*)
server) key_direction="key-direction 0" ;;
client) key_direction="key-direction 1" ;;
*)
verbose "inline: Unsupported certificate type: $crt_type"
tls_key_label=
key_direction=
tls_data="# No TLS Key support for cert-type: $crt_type"
esac

@@ -3021,6 +3104,10 @@ ${tls_key_data}
tls_data="${tls_data}${NL}${NL}${key_direction}"
fi

# If inline file has keys then redirect to 'private' dir
[ "$inline_private" ] && \
inline_out="${EASYRSA_PKI}/inline/private/${1}.inline"

# Print data
print "\
# Easy-RSA Inline file
@@ -3038,7 +3125,7 @@ $ca_data
$tls_data
" > "$inline_out"

# interactive feedback
# user info
if [ "$inline_incomplete" ]; then
warn "\
INCOMPLETE Inline file created:
@@ -5069,6 +5156,11 @@ fi
# 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"

# Windows users, to generate OpenVPN TLS Keys the Openvpn binary must be
# defined here.
#
#set_var EASYRSA_OPENVPN "C:\\Program Files\\Openvpn\\bin\\openvpn.exe"

# Define X509 DN mode.
#
# This is used to adjust which elements are included in the Subject field
@@ -5677,20 +5769,10 @@ case "$cmd" in
*)
require_pki=1
case "$cmd" in
gen-req|gen-dh|build-ca|show-req|export-p*)
: # ok
;;
inline)
: # ok
;;
self-sign-*)
: # ok
;;
write)
: # ok
;;
*)
require_ca=1
gen-req|gen-dh|build-ca|show-req|export-p*| \
inline|self-sign-*|write|gen-tls-*)
: ;; # ok
*) require_ca=1
esac
esac

0 comments on commit 03d9dc2

Please sign in to comment.