diff --git a/0001-Add-optional-user-and-group-configuration.patch b/0001-Add-optional-user-and-group-configuration.patch new file mode 100644 index 0000000..fb931b7 --- /dev/null +++ b/0001-Add-optional-user-and-group-configuration.patch @@ -0,0 +1,66 @@ +From 700040068e3c08025f206e06ba5cfa76a124d805 Mon Sep 17 00:00:00 2001 +From: Daniel Molkentin +Date: Thu, 21 Sep 2017 19:07:54 +0200 +Subject: [PATCH] Add optional user and group configuration + +when DEHYDRATED_USER is set, dehydrated will refuse to run as root, +and instead launch itself as the user in DEHYDRATED_USER (and +DEHYDRATED_GROUP if set). +--- + dehydrated | 15 +++++++++++++++ + docs/examples/config | 6 ++++++ + 2 files changed, 21 insertions(+) + +diff --git a/dehydrated b/dehydrated +index 8b31ee1..39c717f 100755 +--- a/dehydrated ++++ b/dehydrated +@@ -126,6 +126,8 @@ load_config() { + LOCKFILE= + OCSP_MUST_STAPLE="no" + IP_VERSION= ++ DEHYDRATED_USER= ++ DEHYDRATED_GROUP= + + if [[ -z "${CONFIG:-}" ]]; then + echo "#" >&2 +@@ -159,6 +161,19 @@ load_config() { + done + fi + ++ # Check if we are running & are allowed to run as root ++ if [[ ! -z "$DEHYDRATED_USER" && $EUID == 0 ]]; then ++ if [ ! -z "$DEHYDRATED_GROUP" ]; then ++ group="-g $DEHYDRATED_GROUP" ++ fi ++ echo "# INFO: Running $0 as $DEHYDRATED_USER" ++ su -c "$0" $group "$DEHYDRATED_USER" ++ exit ++ fi ++ ++ # Check for missing dependencies ++ check_dependencies ++ + # Remove slash from end of BASEDIR. Mostly for cleaner outputs, doesn't change functionality. + BASEDIR="${BASEDIR%%/}" + +diff --git a/docs/examples/config b/docs/examples/config +index 1b1b3d8..9a890f4 100644 +--- a/docs/examples/config ++++ b/docs/examples/config +@@ -10,6 +10,12 @@ + # Default values of this config are in comments # + ######################################################## + ++# Which user should dehydrated run as? This will be implictly enforced when running as root ++#DEHYDRATED_USER= ++ ++# Which group should dehydrated run as? This will be implictly enforced when running as root ++#DEHYDRATED_GROUP= ++ + # Resolve names to addresses of IP version only. (curl) + # supported values: 4, 6 + # default: +-- +2.12.3 + diff --git a/0002-use-nullglob-disable-warning-on-empty-CONFIG_D-direc.patch b/0002-use-nullglob-disable-warning-on-empty-CONFIG_D-direc.patch new file mode 100644 index 0000000..47cc609 --- /dev/null +++ b/0002-use-nullglob-disable-warning-on-empty-CONFIG_D-direc.patch @@ -0,0 +1,49 @@ +From 5214632c55c70c6c1f0dabce204a9fb8529c8ca8 Mon Sep 17 00:00:00 2001 +From: Lukas Schauer +Date: Thu, 21 Sep 2017 18:10:01 +0200 +Subject: [PATCH] use nullglob, disable warning on empty CONFIG_D directory + +--- + dehydrated | 12 ++++-------- + 1 file changed, 4 insertions(+), 8 deletions(-) + +diff --git a/dehydrated b/dehydrated +index 8b31ee1..a62b858 100755 +--- a/dehydrated ++++ b/dehydrated +@@ -8,7 +8,9 @@ + set -e + set -u + set -o pipefail +-[[ -n "${ZSH_VERSION:-}" ]] && set -o SH_WORD_SPLIT && set +o FUNCTION_ARGZERO ++[[ -n "${ZSH_VERSION:-}" ]] && set -o SH_WORD_SPLIT && set +o FUNCTION_ARGZERO && set -o NULL_GLOB ++[[ -z "${ZSH_VERSION:-}" ]] && shopt -s nullglob ++ + umask 077 # paranoid umask, we're creating private keys + + # Find directory in which this script is stored by traversing all symbolic links +@@ -146,10 +148,7 @@ load_config() { + fi + + for check_config_d in "${CONFIG_D}"/*.sh; do +- if [[ ! -e "${check_config_d}" ]]; then +- echo "# !! WARNING !! Extra configuration directory ${CONFIG_D} exists, but no configuration found in it." >&2 +- break +- elif [[ -f "${check_config_d}" ]] && [[ -r "${check_config_d}" ]]; then ++ if [[ -f "${check_config_d}" ]] && [[ -r "${check_config_d}" ]]; then + echo "# INFO: Using additional config file ${check_config_d}" + # shellcheck disable=SC1090 + . "${check_config_d}" +@@ -1020,9 +1019,6 @@ command_cleanup() { + + # Loop over all files of this type + for file in "${certdir}/${filebase}-"*".${fileext}"; do +- # Handle case where no files match the wildcard +- [[ -f "${file}" ]] || break +- + # Check if current file is in use, if unused move to archive directory + filename="$(basename "${file}")" + if [[ ! "${filename}" = "${current}" ]]; then +-- +2.12.3 + diff --git a/dehydrated.1 b/dehydrated.1 new file mode 100644 index 0000000..49230ef --- /dev/null +++ b/dehydrated.1 @@ -0,0 +1,155 @@ +.TH DEHYDRATED 1 2017-09-20 "Dehydrated ACME Client" +.SH NAME +dehydrated \- ACME client implemented as a shell-script +.SH SYNOPSIS +.B dehydrated +[\fBcommand\fR [\fBargument\fR]] +[\fBargument\fR [\fBargument\fR]] +.IR ... +.SH DESCRIPTION +A client for ACME-based Certificate Authorities, such as LetsEncrypt. It +allows to request and obtain TLS certificates from an ACME-based +certificate authority. + +Before any certificates can be requested, Dehydrated needs +to acquire an account with the Certificate Authorities. Optionally, an email +address can be provided. It will be used to e.g. notify about expiring +certificates. You will usually need to accept the Terms of Service of the CA. +Dehydrated will notify if no account is configured. Run with \fB--register +--accept-terms\fR to create a new account. + +Next, all domain names must be provided in domains.txt. The format is line +based: If the file contains two lines "example.com" and "example.net", +Dehydrated will request two certificate, one for "example.com" and the other +for "example.net". A single line while "example.com example.net" will request a +single certificate valid for both "example.net" and "example.com" through the \fISubject +Alternative Name\fR (SAN) field. + +For the next step, one way of verifying domain name ownership needs to be +configured. Dehydrated implements \fIhttp-01\fR and \fIdns-01\fR verification. + +The \fIhttp-01\fR verification provides proof of ownership by providing a +challenge token. In order to do that, the directory referenced in the +\fIWELLKNOWN\fR config variable needs to be exposed at +\fIhttp://{domain}/.well-known/acme-challenge/\fR, where {domain} is every +domain name specified in \fIdomains.txt\fR. Dehydrated does not provide its +own challenge responder, but relies on an existing web server to provide the +challenge response. See \fIwellknown.md\fR for configuration examples of +popular web servers. + +The \fIdns-01\fR verification works by providing a challenge token through DNS. +This is especially interesting for hosts that cannot be exposed to the public +Internet. Because adding records to DNS zones is oftentimes highly specific to +the software or the DNS provider at hand, there are many third party hooks +available for dehydrated. See \fIdns-verification.md\fR for hooks for popular +DNS servers and DNS hosters. + +Finally, the certificates need to be requested and updated on a regular basis. +This can happen through a cron job or a timer. Initially, you may enforce this +by invoking \fIdehydrated -c\fR manually. + +After a successful run, certificates are stored in +\fI/etc/dehydrated/certs/{domain}\fR, where {domain} is the domain name in the +first column of \fIdomains.txt\fR. + +.SH OPTIONS + +.BR Commands +.TP +.BR \-\-version ", " \-v +Print version information +.TP +.BR \-\-register +Register account key +.TP +.BR \-\-account +Update account contact information +.TP +.BR \-\-cron ", " \-c +Sign/renew non\-existent/changed/expiring certificates. +.TP +.BR \-\-signcsr ", " \-s " " \fIpath/to/csr.pem\fR +Sign a given CSR, output CRT on stdout (advanced usage) +.TP +.BR \-\-revoke ", " \-r " " \fIpath/to/cert.pem\fR +Revoke specified certificate +.TP +.BR \-\-cleanup ", " \-gc +Move unused certificate files to archive directory +.TP +.BR \-\-help ", " \-h +Show help text +.TP +.BR \-\-env ", " \-e +Output configuration variables for use in other scripts + +.PP +.BR Parameters +.TP +.BR \-\-accept\-terms +Accept CAs terms of service +.TP +.BR \-\-full\-chain ", " \-fc +Print full chain when using \fB\-\-signcsr\fR +.TP +.BR \-\-ipv4 ", " \-4 +Resolve names to IPv4 addresses only +.TP +.BR \-\-ipv6 ", " \-6 +Resolve names to IPv6 addresses only +.TP +.BR \-\-domain ", " \-d " " \fIdomain.tld\fR +Use specified domain name(s) instead of domains.txt entry (one certificate!) +.TP +.BR \-\-keep\-going ", " \-g +Keep going after encountering an error while creating/renewing multiple +certificates in cron mode +.TP +.BR \-\-force ", " \-x +Force renew of certificate even if it is longer valid than value in RENEW_DAYS +.TP +.BR \-\-no\-lock ", " \-n +Don't use lockfile (potentially dangerous!) +.TP +.BR \-\-lock\-suffix " " \fIexample.com\fR +Suffix lockfile name with a string (useful for use with \-d) +.TP +.BR \-\-ocsp +Sets option in CSR indicating OCSP stapling to be mandatory +.TP +.BR \-\-privkey ", " \-p " " \fIpath/to/key.pem\fR +Use specified private key instead of account key (useful for revocation) +.TP +.BR \-\-config ", " \-f " " \fIpath/to/config\fR +Use specified config file +.TP +.BR \-\-hook ", " \-k " " \fIpath/to/hook.sh\fR +Use specified script for hooks +.TP +.BR \-\-out ", " \-o " " \fIcerts/directory\fR +Output certificates into the specified directory +.TP +.BR \-\-challenge ", " \-t " " \fI[http\-01|dns\-01]\fR +Which challenge should be used? Currently http\-01 and dns\-01 are supported +.TP +.BR \-\-algo ", " \-a " " \fI[rsa|prime256v1|secp384r1]\fR +Which public key algorithm should be used? Supported: rsa, prime256v1 and +secp384r1 +.SH DIAGNOSTICS +The program exits 0 if everything was fine, 1 if an error occurred. +.SH BUGS +Please report any bugs that you may encounter at the project web site +.UR https://github.com/lukas2511/dehydrated/issues +.UE . +.SH AUTHOR +Dehydrated was written by Lukas Schauer. This man page was contributed by +Daniel Molkentin. +.SH COPYRIGHT +Copyright 20015-2017 by Lukas Schauer and the respective contributors. +Provided under the MIT License. See the LICENSE file that accompanies the +distribution for licensing information. +.SH SEE ALSO +Full documentation along with configuration examples are provided in the \fIdocs\fR +directory of the distribution, or at +.UR https://github.com/lukas2511/dehydrated/tree/master/docs +.UE . diff --git a/dehydrated.changes b/dehydrated.changes index e11bbd9..2587d19 100644 --- a/dehydrated.changes +++ b/dehydrated.changes @@ -1,3 +1,18 @@ +------------------------------------------------------------------- +Fri Sep 22 11:18:55 UTC 2017 - daniel.molkentin@suse.com + +- Add man page + +- Ensure dehydrated is always run as designated user + * adds 0001-Add-optional-user-and-group-configuration.patch + +- Introduce config.d directory for user configuration + +- Avoid warning about empty config.d directory + * adds 0002-use-nullglob-disable-warning-on-empty-CONFIG_D-direc.patch + +- Fix sed warning about unescaped curly braces in regex + ------------------------------------------------------------------- Tue Sep 19 15:40:46 UTC 2017 - daniel.molkentin@suse.com @@ -16,7 +31,9 @@ Thu Sep 14 13:47:06 UTC 2017 - daniel.molkentin@suse.com ------------------------------------------------------------------- Wed Sep 13 15:27:08 UTC 2017 - daniel.molkentin@suse.com -- Use timer instead of cron for systemd-enabled distros +- Use timer instead of cron for systemd-enabled distros + + Note: Timer must be explicitly enabled! ------------------------------------------------------------------- Tue Feb 21 13:12:19 UTC 2017 - daniel.molkentin@suse.com diff --git a/dehydrated.spec b/dehydrated.spec index 930fa32..216d48e 100644 --- a/dehydrated.spec +++ b/dehydrated.spec @@ -60,6 +60,11 @@ Source4: dehydrated.cron.in Source5: dehydrated.tmpfiles.d Source6: dehydrated-update.service.in Source7: dehydrated-update.timer +Source8: dehydrated.1 +# Patch submitted to upstream +Patch1: 0001-Add-optional-user-and-group-configuration.patch +# Patch from upstream +Patch2: 0002-use-nullglob-disable-warning-on-empty-CONFIG_D-direc.patch BuildRequires: %{_apache} %if %{with lighttpd} BuildRequires: lighttpd @@ -179,6 +184,8 @@ systemd-tmpfiles --create %{_tmpfilesdir}/%{name}.conf ||: %prep %setup -q +%patch1 -p1 +%patch2 -p1 %build @@ -186,6 +193,11 @@ systemd-tmpfiles --create %{_tmpfilesdir}/%{name}.conf ||: # sensitive keys mkdir -p %{buildroot}%{_home}/{accounts,certs} mkdir -p %{buildroot}%{_sbindir} +mkdir -p %{buildroot}%{_mandir}/man1 +mkdir -p %{buildroot}%{_home}/config.d +mkdir -p %{buildroot}%{_home}/hooks # used by hook packages + +cat %{SOURCE8} | gzip > %{buildroot}%{_mandir}/man1/dehydrated.1.gz sed -i "s,#WELLKNOWN=.*,WELLKNOWN=%{_challengedir},g" docs/examples/config install -m 0644 docs/examples/* %{buildroot}%{_home} @@ -225,7 +237,13 @@ install -m 0755 -d %{buildroot}/etc/cron.d sed "s,@USER@,%{_user},g" %{SOURCE4} > dehydrated.cron install -m 0644 dehydrated.cron %{buildroot}/etc/cron.d/dehydrated %endif #with systemd -perl -p -i -e 's|#LOCKFILE="\${BASEDIR}/lock"|LOCKFILE="%{_lock_dir}/lock"|' %{buildroot}%{_home}/config + +# Adjust config file +perl -p -i -e 's|#LOCKFILE="\$\{BASEDIR\}/lock"|LOCKFILE="%{_lock_dir}/lock"|' %{buildroot}%{_home}/config +perl -p -i -e 's|#CONFIG_D=|CONFIG_D="%{_home}/config.d"|' %{buildroot}%{_home}/config +perl -p -i -e 's|#DEHYDRATED_USER=|DEHYDRATED_USER="%{_user}"|' %{buildroot}%{_home}/config +perl -p -i -e 's|#DEHYDRATED_GROUP=|DEHYDRATED_GROUP="%{_user}"|' %{buildroot}%{_home}/config + diff -urN docs/examples/config %{buildroot}%{_home}/config ||: %files @@ -234,10 +252,13 @@ diff -urN docs/examples/config %{buildroot}%{_home}/config ||: %attr(700,%{_user},%{_user}) %dir %{_sysconfdir}/dehydrated/accounts %attr(700,%{_user},%{_user}) %dir %{_sysconfdir}/dehydrated/certs %config(noreplace) %attr(640,root,%{_user}) %{_sysconfdir}/dehydrated/config +%config(noreplace) %attr(750,root,%{_user}) %{_sysconfdir}/dehydrated/config.d %config(noreplace) %attr(640,root,%{_user}) %{_sysconfdir}/dehydrated/domains.txt %config(noreplace) %attr(750,root,%{_user}) %{_sysconfdir}/dehydrated/hook.sh +%config(noreplace) %attr(750,root,%{_user}) %{_sysconfdir}/dehydrated/hooks %{_bindir}/dehydrated %attr(-,%{_user},root) %dir %{_localstatedir}/lib/acme-challenge +%{_mandir}/man1/* %doc LICENSE README.md docs/*.md docs/*.jpg %if %{with systemd} %{_tmpfilesdir}/%{name}.conf