aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorThomas Lange <code@nerdmind.de>2016-12-19 02:30:15 +0100
committerThomas Lange <code@nerdmind.de>2016-12-19 02:30:15 +0100
commit401a7b31e348814b4529d342d2fe920d28d63bb4 (patch)
tree64fd54b48d8247679859a61d35853e3c6353df6c
downloadpainlessle-401a7b31e348814b4529d342d2fe920d28d63bb4.tar.gz
painlessle-401a7b31e348814b4529d342d2fe920d28d63bb4.tar.xz
painlessle-401a7b31e348814b4529d342d2fe920d28d63bb4.zip
Initial commit
-rw-r--r--licence.md9
-rwxr-xr-xpainless-le.sh83
-rw-r--r--readme.md30
3 files changed, 122 insertions, 0 deletions
diff --git a/licence.md b/licence.md
new file mode 100644
index 0000000..d130d18
--- /dev/null
+++ b/licence.md
@@ -0,0 +1,9 @@
+# The MIT License (MIT)
+
+**Copyright (c) 2016** Thomas Lange [<code@nerdmind.de>]
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+
+*THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.* \ No newline at end of file
diff --git a/painless-le.sh b/painless-le.sh
new file mode 100755
index 0000000..31dc897
--- /dev/null
+++ b/painless-le.sh
@@ -0,0 +1,83 @@
+#!/bin/bash
+#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%#
+# Painless Let's Encrypt Certificate Issuing [Thomas Lange <code@nerdmind.de>] #
+#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%#
+# #
+# PainlessLE makes it easy to issue a X.509 certificate from the Let's Encrypt #
+# Certification Authority (CA) for a bunch of hostnames without already having #
+# a HTTP server installed. PainlessLE assumes that there is already a manually #
+# created RSA private which is used for the Certificate-Signing-Request (CSR). #
+# The location for the RSA private key is defined within "${CONFIDENTIAL}". #
+# #
+# ARGUMENT_DIRECTORY: Full path to the install directory for the certificates #
+# ARGUMENT_HOSTNAMES: Hostnames for CSR: DNS:example.org,DNS:blog.example.org #
+# #
+#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%#
+
+[ -z "$1" ] && echo 'Missing argument $1' && exit 1 || ARGUMENT_DIRECTORY="$1"
+[ -z "$2" ] && echo 'Missing argument $2' && exit 1 || ARGUMENT_HOSTNAMES="$2"
+
+#===============================================================================
+# Information about the Let's encrypt account
+#===============================================================================
+LETSENCRYPT_MAILADDR="john.doe@example.org"
+LETSENCRYPT_ENDPOINT="https://acme-v01.api.letsencrypt.org/directory"
+#LETSENCRYPT_ENDPOINT="https://acme-staging.api.letsencrypt.org/directory"
+
+#===============================================================================
+# Define commands who are executed BEFORE and AFTER the ACME challenge
+#===============================================================================
+#LETSENCRYPT_COMMAND_BEFORE="systemctl stop apache2"
+#LETSENCRYPT_COMMAND_AFTER="systemctl start apache2"
+
+#===============================================================================
+# Define required paths
+#===============================================================================
+ REQUESTFILE=`mktemp -u /tmp/letsencrypt.XXXX.csr`
+ CONFIDENTIAL="${ARGUMENT_DIRECTORY%/}/confidential.pem"
+ INTERMEDIATE="${ARGUMENT_DIRECTORY%/}/intermediate.pem"
+CERTIFICATE_ONLY="${ARGUMENT_DIRECTORY%/}/certificate_only.pem"
+CERTIFICATE_FULL="${ARGUMENT_DIRECTORY%/}/certificate_full.pem"
+
+#===============================================================================
+# Generate Certificate-Signing-Request (CSR)
+#===============================================================================
+openssl req -config <(cat /etc/ssl/openssl.cnf <(printf "[SAN]\nsubjectAltName=${ARGUMENT_HOSTNAMES}")) \
+-new -sha256 -key "${CONFIDENTIAL}" -out "${REQUESTFILE}" -outform der -reqexts SAN -subj "/"
+
+#===============================================================================
+# Checking if Certificate-Signing-Request (CSR) was successfully created
+#===============================================================================
+if [ $? != 0 ]; then
+ echo "[ABORTING]: Certificate-Signing-Request (CSR) could not be created!"
+ exit 1
+fi
+
+#===============================================================================
+# Delete previous certificates from the install directory
+#===============================================================================
+[ -f "${INTERMEDIATE}" ] && rm "${INTERMEDIATE}"
+[ -f "${CERTIFICATE_ONLY}" ] && rm "${CERTIFICATE_ONLY}"
+[ -f "${CERTIFICATE_FULL}" ] && rm "${CERTIFICATE_FULL}"
+
+#===============================================================================
+# Execute defined command BEFORE the ACME challenge is started
+#===============================================================================
+[ ! -z "${LETSENCRYPT_COMMAND_BEFORE}" ] && $($LETSENCRYPT_COMMAND_BEFORE)
+
+#===============================================================================
+# Execute Let's Encrypt and accomplish the ACME challenge to get the certificate
+#===============================================================================
+certbot certonly --authenticator standalone --text --server "${LETSENCRYPT_ENDPOINT}" --email "${LETSENCRYPT_MAILADDR}" \
+--csr "${REQUESTFILE}" --cert-path "${CERTIFICATE_ONLY}" --fullchain-path "${CERTIFICATE_FULL}" --chain-path "${INTERMEDIATE}"
+
+#===============================================================================
+# Adjust the UNIX permissions with owner and group for the new created files
+#===============================================================================
+chmod --reference "${CONFIDENTIAL}" "${INTERMEDIATE}" "${CERTIFICATE_ONLY}" "${CERTIFICATE_FULL}"
+chown --reference "${CONFIDENTIAL}" "${INTERMEDIATE}" "${CERTIFICATE_ONLY}" "${CERTIFICATE_FULL}"
+
+#===============================================================================
+# Execute defined command AFTER the ACME challenge is completed
+#===============================================================================
+[ ! -z "${LETSENCRYPT_COMMAND_AFTER}" ] && $($LETSENCRYPT_COMMAND_AFTER) \ No newline at end of file
diff --git a/readme.md b/readme.md
new file mode 100644
index 0000000..c2f6498
--- /dev/null
+++ b/readme.md
@@ -0,0 +1,30 @@
+# PainlessLE: Let's Encrypt Certificate Issuing
+Painless issuing a single [X.509 certificate](https://tools.ietf.org/html/rfc5280) for a bunch of hostnames from the **Let's Encrypt** Certification Authority (CA) without having a HTTP server installed (or for those people who do not want to touch their HTTP web directories and place a specific file to accomplish the ACME challenge). PainlessLE assumes that there is already a manually created RSA private key which is used for the Certificate-Signing-Request (CSR) by OpenSSL. The location for the RSA private key is defined within the `"CONFIDENTIAL"` variable and the path should exist with the correct UNIX file permissions.
+
+## Configuration
+The email address which is used for the **Let's Encrypt** account is defined within `LETSENCRYPT_MAILADDR`. It is possible to change the `LETSENCRYPT_ENDPOINT` to the address of the ACME staging API for testing purposes. You also can define a command within `LETSENCRYPT_COMMAND_BEFORE` to shutting down a running webserver to release the HTTP(S) port for the standalone webserver before certbot runs the ACME challenge. You can restart your webserver after the ACME challenge is completed within `LETSENCRYPT_COMMAND_AFTER`.
+
+## Arguments
+1. `ARGUMENT_DIRECTORY` contains a string with the directory path where the certificates should be installed. This directory should already contain a manually created RSA private key for the Certificate-Signing-Request (CSR). It's always a good idea to handle the RSA private keys manually because you may use [HTTP Public-Key-Pinning (HPKP)](https://tools.ietf.org/html/rfc7469) so that you must ensure, that the RSA private key does not change.
+
+2. `ARGUMENT_HOSTNAMES` contains a string with the hostnames to include within the certificate. The string must be formatted as follows because he get injected directly into to OpenSSL command to generate the Certificate-Signing-Request: `DNS:example.org,DNS:blog.example.org,DNS:shop.example.org`
+
+## Example
+Lets assume that you want to get a single X.509 certificate from the Let's Encrypt CA which includes three hostnames of your domain `example.org` (main domain, blog subdomain and shop subdomain). You already have a RSA private key with the correct UNIX file permissions stored within the following example directory with the name `confidential.pem`:
+
+ /etc/painless-le/example.org/
+ └── [-rw-r----- user group ] confidential.pem
+
+The next step is to execute `painless-le.sh` and providing the only two command-line arguments which are described above. In this example, the complete command-line string with the desired install directory `/etc/painless-le/example.org` and the desired hostnames `example.org`, `blog.example.org` and `shop.example.org` looks as follows:
+
+ painless-le.sh /etc/painless-le/example.org/ "DNS:example.org,DNS:blog.example.org,DNS:shop.example.org"
+
+The certbot client will now contacting the ACME challenge servers and runs a temporary standalone webserver on your machine to accomplish the ACME challenge. If all works fine, you have nothing to intervene. After the command was successfully executed, you will see your certificates within your desired install directory (the certificates inherit the permissions of the `confidential.pem` file) and you're done:
+
+ /etc/painless-le/example.org/
+ ├── [-rw-r----- user group ] certificate_full.pem
+ ├── [-rw-r----- user group ] certificate_only.pem
+ ├── [-rw-r----- user group ] confidential.pem
+ └── [-rw-r----- user group ] intermediate.pem
+
+**Note:** The new certificates inherit the UNIX file permissions (**chmod** and **chown**) of the RSA private key `confidential.pem`! \ No newline at end of file