blob: d970ef7f21c46ecffe092c71e37f915296fc7e1c (
plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
|
#!/bin/bash
#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%#
# PainlessLE – A wrapper script for Certbot [Thomas Lange <code@nerdmind.de>] #
#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%#
# #
# Easily get an X.509 certificate from the Let's Encrypt Certificate Authority #
# without the need of having a dedicated web server installed. With PainlessLE #
# you'll manage your certificate and private key files by yourself. Instead of #
# Certbot, you're responsible for the periodic renewal of the certificates. #
# #
# USAGE: #
# painless-le [OPTIONS] TARGET_DIR DNS_DOMAIN [DNS_DOMAIN ...] #
# #
# TARGET_DIR: Path to the target directory for the certificate files. #
# DNS_DOMAIN: One or more DNS hostnames to include in the certficate. #
# #
# OPTION [-K]: Filename of the existing private key in target directory. #
# OPTION [-I]: Filename for the intermediate certificate in target directory. #
# OPTION [-C]: Filename for the standalone certificate in target directory. #
# OPTION [-F]: Filename for the certificate+intermediate in target directory. #
# #
#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%#
#===============================================================================
# Define ACME endpoint address and BEFORE/AFTER commands
#===============================================================================
LETSENCRYPT_ENDPOINT="https://acme-v02.api.letsencrypt.org/directory"
#LETSENCRYPT_ENDPOINT="https://acme-staging-v02.api.letsencrypt.org/directory"
#LETSENCRYPT_COMMAND_BEFORE="systemctl stop apache2"
#LETSENCRYPT_COMMAND_AFTER="systemctl start apache2"
#===============================================================================
# Normalize command-line arguments with GNU getopt
#===============================================================================
set -- $(getopt -uo K:I:C:F: -- "$@")
#===============================================================================
# Parse command-line options with getopts
#===============================================================================
while getopts :K:I:C:F: option
do
case $option in
K) OPT_CONFIDENTIAL="$OPTARG" ;;
I) OPT_INTERMEDIATE="$OPTARG" ;;
C) OPT_CERTIFICATE_ONLY="$OPTARG" ;;
F) OPT_CERTIFICATE_FULL="$OPTARG" ;;
esac
done; shift $((OPTIND-1))
#===============================================================================
# Set positional argument variables
#===============================================================================
TARGET_DIR="$1"
DNS_DOMAIN="${@:2}"
#===============================================================================
# Check if required positional arguments are missing
#===============================================================================
[ -z "${TARGET_DIR}" ] && echo "$0: Missing argument: TARGET_DIR" >&2
[ -z "${DNS_DOMAIN}" ] && echo "$0: Missing argument: DNS_DOMAIN" >&2
[ -z "${TARGET_DIR}" ] || [ -z "${DNS_DOMAIN}" ] && exit 1
#===============================================================================
# Define filename variables
#===============================================================================
REQUESTFILE="$(mktemp /tmp/painless-le.XXXXXX.csr)"
CONFIDENTIAL="${TARGET_DIR%/}/${OPT_CONFIDENTIAL:-confidential.pem}"
INTERMEDIATE="${TARGET_DIR%/}/${OPT_INTERMEDIATE:-intermediate.pem}"
CERTIFICATE_ONLY="${TARGET_DIR%/}/${OPT_CERTIFICATE_ONLY:-certificate_only.pem}"
CERTIFICATE_FULL="${TARGET_DIR%/}/${OPT_CERTIFICATE_FULL:-certificate_full.pem}"
#===============================================================================
# Ensure the Certificate-Signing-Request file is deleted on exit
#===============================================================================
trap 'rm ${REQUESTFILE}' EXIT
#===============================================================================
# Assemble OpenSSL configuration for CSR generation
#===============================================================================
SUBJECT_ALT_NAME="DNS:$(echo ${DNS_DOMAIN} | sed "s/ /,DNS:/g")"
OPENSSL_CONFIG="[req]
distinguished_name = req_distinguished_name
[req_distinguished_name]
[SAN]
subjectAltName=${SUBJECT_ALT_NAME}"
#===============================================================================
# Create Certificate-Signing-Request
#===============================================================================
openssl req -config <(echo "$OPENSSL_CONFIG") -new -sha256 -reqexts SAN \
-subj "/" -key "${CONFIDENTIAL}" -out "${REQUESTFILE}"
#===============================================================================
# Check if Certificate-Signing-Request creation failed
#===============================================================================
if [ $? != 0 ]; then
echo "$0: Certificate-Signing-Request (CSR) could not be created!" >&2
exit 1
fi
#===============================================================================
# Run defined "BEFORE" command
#===============================================================================
if [ ! -z "${LETSENCRYPT_COMMAND_BEFORE}" ]; then
eval $LETSENCRYPT_COMMAND_BEFORE
fi
#===============================================================================
# Run Certbot to accomplish the ACME challenge to get the certificate
#===============================================================================
certbot certonly --authenticator standalone --server "${LETSENCRYPT_ENDPOINT}" --csr "${REQUESTFILE}" \
--cert-path "${CERTIFICATE_ONLY}.$$" --fullchain-path "${CERTIFICATE_FULL}.$$" --chain-path "${INTERMEDIATE}.$$"
#===============================================================================
# Check if Certbot failed to obtain a certificate
#===============================================================================
if [ $? != 0 ]; then
echo "$0: Certbot could not successfully accomplish the ACME challenge." >&2
exit 1
fi
#===============================================================================
# Replace old certificate files (if any) with the newly obtained ones
#===============================================================================
for file in "${INTERMEDIATE}" "${CERTIFICATE_ONLY}" "${CERTIFICATE_FULL}"; do
if [ -f "${file}.$$" ]; then
mv "${file}.$$" "${file}"
fi
done
#===============================================================================
# Inherit permissions of private key to new certificate files
#===============================================================================
for command in "chmod" "chown"; do
$command --reference "${CONFIDENTIAL}" \
"${INTERMEDIATE}" "${CERTIFICATE_ONLY}" "${CERTIFICATE_FULL}"
done
#===============================================================================
# Run defined "AFTER" command
#===============================================================================
if [ ! -z "${LETSENCRYPT_COMMAND_AFTER}" ]; then
eval $LETSENCRYPT_COMMAND_AFTER
fi
|