From c64e35229801d5d23037b9d6e2b1d52bddd0c219 Mon Sep 17 00:00:00 2001 From: Marcin Krol Date: Tue, 24 Oct 2017 00:29:18 +0000 Subject: [PATCH] - from PLD, TLDized --- apache.conf | 12 ++++++ crontab | 3 ++ dehydrated.spec | 112 ++++++++++++++++++++++++++++++++++++++++++++++++ domains.txt | 1 + hook.sh | 82 +++++++++++++++++++++++++++++++++++ lighttpd.conf | 3 ++ nginx.conf | 3 ++ tld.patch | 98 ++++++++++++++++++++++++++++++++++++++++++ 8 files changed, 314 insertions(+) create mode 100644 apache.conf create mode 100644 crontab create mode 100644 dehydrated.spec create mode 100644 domains.txt create mode 100755 hook.sh create mode 100644 lighttpd.conf create mode 100644 nginx.conf create mode 100644 tld.patch diff --git a/apache.conf b/apache.conf new file mode 100644 index 0000000..ac93b93 --- /dev/null +++ b/apache.conf @@ -0,0 +1,12 @@ +Alias /.well-known/acme-challenge /var/lib/dehydrated/acme-challenges + + # Apache 2.x + + Order allow,deny + Allow from all + + # Apache 2.4 + + Require all granted + + diff --git a/crontab b/crontab new file mode 100644 index 0000000..dc0c363 --- /dev/null +++ b/crontab @@ -0,0 +1,3 @@ +MAILTO=root + +42 02 * * 2 root /usr/sbin/dehydrated -c > /dev/null diff --git a/dehydrated.spec b/dehydrated.spec new file mode 100644 index 0000000..44ec771 --- /dev/null +++ b/dehydrated.spec @@ -0,0 +1,112 @@ +Summary: letsencrypt/acme client implemented as a shell-script +Name: dehydrated +Version: 0.4.0 +Release: 2 +License: MIT +Group: Applications/Networking +Source0: https://github.com/lukas2511/dehydrated/archive/v%{version}/%{name}-%{version}.tar.gz +# Source0-md5: 8114ba0144a158d5ad1bdf02e6f43195 +Source1: apache.conf +Source2: lighttpd.conf +Source3: nginx.conf +Source4: domains.txt +Source5: hook.sh +Source6: crontab +Patch0: tld.patch +URL: https://github.com/lukas2511/dehydrated +BuildRequires: rpmbuild(macros) >= 1.713 +Requires: ca-certificates +Requires: crondaemon +Requires: curl +Requires: diffutils +Requires: grep +Requires: mktemp +Requires: openssl-tools +Requires: sed +Requires: webapps +Suggests: webserver(access) +Suggests: webserver(alias) +BuildArch: noarch +BuildRoot: %{tmpdir}/%{name}-%{version}-root-%(id -u -n) + +%define _webapps /etc/webapps +%define _webapp %{name} +%define _sysconfdir %{_webapps}/%{_webapp} +%define _appdir %{_datadir}/%{_webapp} + +%description +This is a client for signing certificates with an ACME-server +(currently only provided by letsencrypt) implemented as a relatively +simple bash-script. + +Current features: +- Signing of a list of domains +- Signing of a CSR +- Renewal if a certificate is about to expire or SAN (subdomains) + changed +- Certificate revocation + +%prep +%setup -q +%patch0 -p1 + +%install +rm -rf $RPM_BUILD_ROOT +install -d $RPM_BUILD_ROOT{%{_sbindir},%{_sysconfdir}/certs,/etc/cron.d} \ + $RPM_BUILD_ROOT/var/lib/%{name}/{accounts,acme-challenges,certs} + +install -p %{name} $RPM_BUILD_ROOT%{_sbindir} +cp -p %{SOURCE1} $RPM_BUILD_ROOT%{_sysconfdir}/apache.conf +cp -p %{SOURCE2} $RPM_BUILD_ROOT%{_sysconfdir}/lighttpd.conf +cp -p %{SOURCE3} $RPM_BUILD_ROOT%{_sysconfdir}/nginx.conf +cp -p docs/examples/config $RPM_BUILD_ROOT%{_sysconfdir} +cp -p %{SOURCE4} $RPM_BUILD_ROOT%{_sysconfdir} +cp -p %{SOURCE6} $RPM_BUILD_ROOT/etc/cron.d/%{name} +install -p %{SOURCE5} $RPM_BUILD_ROOT%{_sysconfdir} +cp -p $RPM_BUILD_ROOT%{_sysconfdir}/{apache,httpd}.conf + +%clean +rm -rf $RPM_BUILD_ROOT + +%triggerin -- apache1 < 1.3.37-3, apache1-base +%webapp_register apache %{_webapp} + +%triggerun -- apache1 < 1.3.37-3, apache1-base +%webapp_unregister apache %{_webapp} + +%triggerin -- apache < 2.2.0, apache-base +%webapp_register httpd %{_webapp} + +%triggerun -- apache < 2.2.0, apache-base +%webapp_unregister httpd %{_webapp} + +%triggerin -- lighttpd +%webapp_register lighttpd %{_webapp} + +%triggerun -- lighttpd +%webapp_unregister lighttpd %{_webapp} + +%triggerin -- nginx +%webapp_register nginx %{_webapp} + +%triggerun -- nginx +%webapp_unregister nginx %{_webapp} + +%files +%defattr(644,root,root,755) +%doc README.md CHANGELOG LICENSE +%attr(640,root,root) %config(noreplace) %verify(not md5 mtime size) /etc/cron.d/%{name} +%dir %attr(750,root,http) %{_sysconfdir} +%attr(640,root,root) %config(noreplace) %verify(not md5 mtime size) %{_sysconfdir}/apache.conf +%attr(640,root,root) %config(noreplace) %verify(not md5 mtime size) %{_sysconfdir}/httpd.conf +%attr(640,root,root) %config(noreplace) %verify(not md5 mtime size) %{_sysconfdir}/lighttpd.conf +%attr(640,root,root) %config(noreplace) %verify(not md5 mtime size) %{_sysconfdir}/nginx.conf +%attr(640,root,root) %config(noreplace) %verify(not md5 mtime size) %{_sysconfdir}/config +%attr(640,root,root) %config(noreplace) %verify(not md5 mtime size) %{_sysconfdir}/domains.txt +%attr(750,root,root) %config(noreplace) %verify(not md5 mtime size) %{_sysconfdir}/hook.sh +%attr(755,root,root) %{_sbindir}/%{name} +%dir %attr(751,root,root) /var/lib/%{name} +%dir %attr(700,root,root) /var/lib/%{name}/accounts +%dir %attr(700,root,root) /var/lib/%{name}/certs +# challenges written here, need to be readable by webserver +%dir %attr(751,root,root) /var/lib/%{name}/acme-challenges diff --git a/domains.txt b/domains.txt new file mode 100644 index 0000000..933e883 --- /dev/null +++ b/domains.txt @@ -0,0 +1 @@ +#example.org www.example.org example.net diff --git a/hook.sh b/hook.sh new file mode 100755 index 0000000..d5387a4 --- /dev/null +++ b/hook.sh @@ -0,0 +1,82 @@ +#!/bin/sh + +# concat file atomic way +atomic_concat() { + local file=$1; shift + > $file.new + chmod 600 $file.new + cat "$@" > $file.new + cp -f $file $file.dehydrated~ + mv -f $file.new $file +} + +lighttpd_reload() { + if [ ! -x /usr/sbin/lighttpd ] || [ ! -f /etc/lighttpd/server.pem ]; then + return + fi + + echo " + Hook: Overwritting /etc/lighttpd/server.pem and reloading lighttpd..." + atomic_concat /etc/lighttpd/server.pem "$FULLCHAINCERT" "$PRIVKEY" + /sbin/service lighttpd reload +} + +haproxy_reload() { + if [ ! -x /usr/sbin/haproxy ] || [ ! -f /etc/haproxy/server.pem ]; then + return + fi + + echo " + Hook: Overwritting /etc/haproxy/server.pem and restarting haproxy..." + atomic_concat /etc/haproxy/server.pem "$FULLCHAINCERT" "$PRIVKEY" + /sbin/service haproxy reload +} + +nginx_reload() { + if [ ! -f /etc/nginx/server.crt ] || [ ! -f /etc/nginx/server.key ]; then + return + fi + + echo " + Hook: Overwritting /etc/nginx/server.{crt,key} and reloading nginx..." + atomic_concat /etc/nginx/server.crt "$FULLCHAINCERT" + atomic_concat /etc/nginx/server.key "$PRIVKEY" + /sbin/service nginx reload +} + +httpd_reload() { + if [ ! -x /etc/rc.d/init.d/httpd ]; then + return + fi + + echo " + Hook: Reloading Apache..." + /sbin/service httpd graceful +} + + +case "$1" in +deploy_cert) + DOMAIN="$2" + PRIVKEY="$3" + CERT="$4" + FULLCHAINCERT="$5" + CHAINCERT="$6" + TIMESTAMP="$7" + + lighttpd_reload + nginx_reload + httpd_reload + haproxy_reload + ;; +clean_challenge) + CHALLENGE_TOKEN="$2" + KEYAUTH="$3" + echo " + Hook: $1: Nothing to do..." + ;; +deploy_challenge) + echo " + Hook: $1: Nothing to do..." + ;; +unchanged_cert) + echo " + Hook: $1: Nothing to do..." + ;; +*) + echo " + Hook: $1: Nothing to do..." + ;; +esac diff --git a/lighttpd.conf b/lighttpd.conf new file mode 100644 index 0000000..0eb7625 --- /dev/null +++ b/lighttpd.conf @@ -0,0 +1,3 @@ +alias.url += ( + "/.well-known/acme-challenge" => "/var/lib/dehydrated/acme-challenges", +) diff --git a/nginx.conf b/nginx.conf new file mode 100644 index 0000000..2ba49a4 --- /dev/null +++ b/nginx.conf @@ -0,0 +1,3 @@ +location /.well-known/acme-challenge { + alias /var/lib/dehydrated/acme-challenges; +} diff --git a/tld.patch b/tld.patch new file mode 100644 index 0000000..2c6448e --- /dev/null +++ b/tld.patch @@ -0,0 +1,98 @@ +diff -ur dehydrated-0.4.0.orig/dehydrated dehydrated-0.4.0/dehydrated +--- dehydrated-0.4.0.orig/dehydrated 2017-02-05 14:33:17.000000000 +0000 ++++ dehydrated-0.4.0/dehydrated 2017-10-24 00:24:53.662801025 +0000 +@@ -1,4 +1,4 @@ +-#!/usr/bin/env bash ++#!/bin/bash + + # dehydrated by lukas2511 + # Source: https://github.com/lukas2511/dehydrated +@@ -94,7 +94,7 @@ + load_config() { + # Check for config in various locations + if [[ -z "${CONFIG:-}" ]]; then +- for check_config in "/etc/dehydrated" "/usr/local/etc/dehydrated" "${PWD}" "${SCRIPTDIR}"; do ++ for check_config in "/etc/dehydrated" "/etc/webapps/dehydrated" "/usr/local/etc/dehydrated" "/etc/webapps/letsencrypt.sh" "${PWD}" "${SCRIPTDIR}"; do + if [[ -f "${check_config}/config" ]]; then + BASEDIR="${check_config}" + CONFIG="${check_config}/config" +@@ -115,7 +115,7 @@ + DOMAINS_TXT= + HOOK= + HOOK_CHAIN="no" +- RENEW_DAYS="30" ++ RENEW_DAYS="10" + KEYSIZE="4096" + WELLKNOWN= + PRIVATE_KEY_RENEW="yes" +@@ -166,7 +166,7 @@ + [[ -d "${BASEDIR}" ]] || _exiterr "BASEDIR does not exist: ${BASEDIR}" + + CAHASH="$(echo "${CA}" | urlbase64)" +- [[ -z "${ACCOUNTDIR}" ]] && ACCOUNTDIR="${BASEDIR}/accounts" ++ [[ -z "${ACCOUNTDIR}" ]] && ACCOUNTDIR="/var/lib/dehydrated/accounts" + mkdir -p "${ACCOUNTDIR}/${CAHASH}" + [[ -f "${ACCOUNTDIR}/${CAHASH}/config" ]] && . "${ACCOUNTDIR}/${CAHASH}/config" + ACCOUNT_KEY="${ACCOUNTDIR}/${CAHASH}/account_key.pem" +@@ -181,9 +181,9 @@ + mv "${BASEDIR}/private_key.json" "${ACCOUNT_KEY_JSON}" + fi + +- [[ -z "${CERTDIR}" ]] && CERTDIR="${BASEDIR}/certs" +- [[ -z "${DOMAINS_TXT}" ]] && DOMAINS_TXT="${BASEDIR}/domains.txt" +- [[ -z "${WELLKNOWN}" ]] && WELLKNOWN="/var/www/dehydrated" ++ [[ -z "${CERTDIR}" ]] && CERTDIR="/var/lib/dehydrated/certs" ++ [[ -z "${DOMAINS_TXT}" ]] && DOMAINS_TXT="/etc/webapps/dehydrated/domains.txt" ++ [[ -z "${WELLKNOWN}" ]] && WELLKNOWN="/var/lib/dehydrated/acme-challenges" + [[ -z "${LOCKFILE}" ]] && LOCKFILE="${BASEDIR}/lock" + [[ -n "${PARAM_LOCKFILE_SUFFIX:-}" ]] && LOCKFILE="${LOCKFILE}-${PARAM_LOCKFILE_SUFFIX}" + [[ -n "${PARAM_NO_LOCK:-}" ]] && LOCKFILE="" +diff -ur dehydrated-0.4.0.orig/docs/examples/config dehydrated-0.4.0/docs/examples/config +--- dehydrated-0.4.0.orig/docs/examples/config 2017-02-05 14:33:17.000000000 +0000 ++++ dehydrated-0.4.0/docs/examples/config 2017-10-24 00:23:06.163807433 +0000 +@@ -21,6 +21,7 @@ + # Path to certificate authority license terms redirect (default: https://acme-v01.api.letsencrypt.org/terms) + #CA_TERMS="https://acme-v01.api.letsencrypt.org/terms" + ++ + # Path to license agreement (default: ) + #LICENSE="" + +@@ -37,16 +38,16 @@ + #BASEDIR=$SCRIPTDIR + + # File containing the list of domains to request certificates for (default: $BASEDIR/domains.txt) +-#DOMAINS_TXT="${BASEDIR}/domains.txt" ++#DOMAINS_TXT="/etc/webapps/dehydrated/domains.txt" + + # Output directory for generated certificates +-#CERTDIR="${BASEDIR}/certs" ++#CERTDIR="/var/lib/dehydrated/certs" + + # Directory for account keys and registration information +-#ACCOUNTDIR="${BASEDIR}/accounts" ++#ACCOUNTDIR="/var/lib/dehydrated/accounts" + + # Output directory for challenge-tokens to be served by webserver or deployed in HOOK (default: /var/www/dehydrated) +-#WELLKNOWN="/var/www/dehydrated" ++#WELLKNOWN="/var/lib/dehydrated/acme-challenges" + + # Default keysize for private keys (default: 4096) + #KEYSIZE="4096" +@@ -64,13 +65,13 @@ + # + # BASEDIR and WELLKNOWN variables are exported and can be used in an external program + # default: +-#HOOK= ++#HOOK=/etc/webapps/dehydrated/hook.sh + + # Chain clean_challenge|deploy_challenge arguments together into one hook call per certificate (default: no) + #HOOK_CHAIN="no" + +-# Minimum days before expiration to automatically renew certificate (default: 30) +-#RENEW_DAYS="30" ++# Minimum days before expiration to automatically renew certificate (default: 10) ++#RENEW_DAYS="10" + + # Regenerate private keys instead of just signing new certificates on renewal (default: yes) + #PRIVATE_KEY_RENEW="yes" -- 2.44.0