From 02d0b225618503ff14ef20d769a3f3983b24b43a Mon Sep 17 00:00:00 2001 From: Frederik Lindenaar <frederik@lindenaar.nl> Date: Sun, 30 Sep 2018 00:43:02 +0200 Subject: [PATCH] fixed small issue in freeipa-dns (override check not working) updated README - added clarification on password migration being limited adding first version of freeipa-letsencrypt.sh (fixes #1) --- README.md | 59 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ freeipa-dns.py | 4 ++-- freeipa-letsencrypt.sh | 128 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 189 insertions(+), 2 deletions(-) create mode 100755 freeipa-letsencrypt.sh diff --git a/README.md b/README.md index 8ae4edf..55830fb 100644 --- a/README.md +++ b/README.md @@ -24,6 +24,9 @@ This repository contains the following scripts: * [freeipa-dns.py](#freeipadns) is a script providing functionality not available in FreeIPA itself to migrate/synchronize and maintain DNS zones in FreeIPA + * [freeipa-letsencrypt.sh](#freeipaletsencrypt) + is a script to setup and configure Certbot and FreeIPA to request and renew + use publicly verifiable Let's Encrypt certificate(s) <a name=users2freeipa>users2freeipa.py</a> @@ -60,6 +63,14 @@ This will also install the OpenDirectory-specific schema customization, create groups and copy group memberships, copy usuable passwords and ensure that all users have a password (storing generated passwords to ```passwords.txt```) +Please note that migrating existing passwords from LDAP has limitations, see +[this](https://www.freeipa.org/page/NIS_accounts_migration_preserving_Passwords) +page on migrating NIS passwords and [this](https://pagure.io/freeipa/issue/4732) +issue reported with it. Bottom line is that (at this moment) password migration +is flawed and always will require manual action from the user. For this reason +the better alternative to set a random password and ask the user to reset the +password using the FreeIPA portal makes more sense. + Before running a production user migration, it is important to have FreeIPA setup and configured correctly so that the right defaults are used for new users. Best is to start with a single user and add that as a stage user (please @@ -160,6 +171,54 @@ for available commands run ```freeipa-dns.py -h``` and to get an overview of the available options for each commmand run ```freeipa-dns.py <command> -h``` +<a name=freeipaletsencrypt>freeipa-letsencrypt.sh</a> +---------------------------------------------------------------- +This script will ensure the necessary setup is in place so that Certbot (EFF's +certificate request script for Let's Encrypt) will work with FreeIPA for DNS +challenges and and instructs it to deploy new certificates for FreeIPA's web +interface. Before writing this script I looked at available options, especially +[freeipa-letsencrypt](https://github.com/freeipa/freeipa-letsencrypt) and [antevens'](https://github.com/antevens/letsencrypt-freeipa) implementation but +decided to take a slightly different approach where Certbot does all the work +and the setup script will only ensure that the environment is prepared and that +Certbot is initially instructed correctly. This allows to fully tie-in with how +Certbot handles renews and use the Certbot package's provided method to schedule +these. + +The following changes will be made for this: + 1. Add Let's Encrypt Root and Intermediate CAs as trusted CAs + 2. Create DNS Administrator role in FreeIPA that can edit any DNS Record + 3. Create host service: ${SERVICE} + 4. Allow letsencrypt host service to manage DNS entries + 5. Register with Let's encrypt as: ${EMAIL} + 6. Request a Let's Encrypt SSL certificate for: ${CERTNAME} + with DNS Alternative names: ${DNSALTNAMES} + 7. install the Let's Encrypt certificate in apache as host SSL certificate, + storing renewal config in: /etc/letsencrypt/renewal/$HOSTNAME.conf + 8. configure the Fedora Certbot renew timer so that certbot is run daily to + renew the certificate when needed. + +The script is built to auto-configure but many of the defaults can be overridden +by setting one of the following environment variables: + +| Variable | Description | Default value | +|-------------|------------------------------------|---------------------------| +| CERTNAME | certificate hostname, | host's canonicalname (*) | +| DNSALTNAMES | certificate DNS names | host's principalnames (*) | +| DOMAIN | Let's Encrypt challenge DNS zone | {DNS name's domain} (**) | +| EMAIL | administrator's e-mail address | hostmaster@{domain} | +| HOSTNAME | FreeIPA server's hostname | `hostname --fqdn` | +| KEYTAB | Let'sEncrypt service's keytab file | /etc/letsencrypt/keytab | +| KRB5CCNAME | Kerberos5 cache to use for tickets | automatically determined | +| REPLY | when 'y' skip user confirmation | "" | +| SERVICE | FreeIPA service for Certbot to use | letsencrypt/canonicalname | +| SUDO | command to become root (if needed) | sudo | +| TMPDIR | Directory for temporary files | /tmp | + +(*) obtained from the FreeIPA server record looked up based on ${HOSTNAME} +(**) this allows to enforce the DNS zone, e.g. host.subdomain in mydomain.tld + +When things change, the script can simply be run again. + <a name="license">License</a> ----------------------------- These scripts, documentation & configration examples are free software: you can diff --git a/freeipa-dns.py b/freeipa-dns.py index ed777a0..fe69658 100755 --- a/freeipa-dns.py +++ b/freeipa-dns.py @@ -378,7 +378,7 @@ def reverseptr(api, args): if currrev == recordname: logger.debug('no update for %s (%s) in %s', reventry, recordname, revzone) - elif currrev and not args.overwrite: + elif currrev and not args.override: logger.warn('not updating %s (%s) in %s pointing to' ' %s', reventry, recordname, revzone, currrev) else: @@ -588,7 +588,7 @@ if __name__ == '__main__': args.func(api, args) exit(0) except DNSException as e: - logger.critical("Domain %s cannot be downloaded,%s", domain, e) + logger.critical("Domain cannot be downloaded: %s", e) except AuthenticationError: logger.critical("Unable to authenticate to FreeIPA, make sure you have a valid Kerberos ticket!") except PublicError as e: diff --git a/freeipa-letsencrypt.sh b/freeipa-letsencrypt.sh new file mode 100755 index 0000000..b7b610c --- /dev/null +++ b/freeipa-letsencrypt.sh @@ -0,0 +1,128 @@ +#!/bin/bash -e +# +# freeipa-letsencrypt.sh - script to setup Let's Encrypt's Certbot for FreeIPA +# +# Version 1.0, latest version, documentation and bugtracker available at: +# https://gitlab.lindenaar.net/scripts/freeipa +# +# Copyright (c) 2018 Frederik Lindenaar +# +# This script is free software: you can redistribute and/or modify it under the +# terms of version 3 of the GNU General Public License as published by the Free +# Software Foundation, or (at your option) any later version of the license. +# +# This script is distributed in the hope that it will be useful but WITHOUT ANY +# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR +# A PARTICULAR PURPOSE. See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with +# this program. If not, visit <http://www.gnu.org/licenses/> to download it. + +# Sanity checks, ensure we have a valid Kerberos ticket, current host is a +# FreeIPA server, certbot is installed and that we can run priviliged commands +die() { echo $* >&2; exit 1; } +if ! klist -s; then + die no valid Kerberos ticket, please login to FreeIPA using kinit first +elif ! ipa server-show ${HOSTNAME:=$(hostname --fqdn)} > /dev/null; then + die this script should be run on an active IPA server +elif ! which certbot > /dev/null; then + die this script requires Certbot to be installed, please install that first +elif [ $(id -u) == 0 ]; then + unset SUDO +elif ! ${SUDO:=sudo} -v; then + die Error: this script must be run by root and $SUDO does not seem to work +else + $SUDO kinit -k +fi + +# Set KRB5CCNAME to ensure the current ticket cache will be used +KRB5CCNAME=${KRB5CCNAME:-$(klist -l | head -3 | tail -1 | cut -d\ -f2-)} + +# Ensure the user consents with changing his system. +if tty > /dev/null; then + cat << EOT +This script modifies this host's FreeIPA setup so that its web interface will +use a Let's Encrypt certificate and will automatically renew that when needed. +The following changes will be made for this: + 1. Add Let's Encrypt Root and Intermediate CAs as trusted CAs + 2. Create DNS Administrator role in FreeIPA that can edit any DNS Record + 3. Create host service: ${SERVICE:=letsencrypt/$( +ipa host-show $HOSTNAME --raw | fgrep "krbcanonicalname: host/" | cut -d/ -f2)} + 4. Allow letsencrypt host service to manage DNS entries + 5. Register with Let's encrypt as: ${EMAIL:=hostmaster@${HOSTNAME#*.}} + 6. Request a Let's Encrypt SSL certificate for: ${CERTNAME:=$(ipa host-show $HOSTNAME --raw | fgrep "krbcanonicalname: host/" | cut -d/ -f2 | cut -d@ -f1)} + with DNS Alternative names: ${DNSALTNAMES:=$(ipa host-show $HOSTNAME --raw | fgrep "krbprincipalname: host/" | cut -d/ -f2 | cut -d@ -f1 | paste -sd,)} + 7. install the Let's Encrypt certificate in apache as host SSL certificate, + storing renewal config in: /etc/letsencrypt/renewal/$HOSTNAME.conf + 8. configure the Fedora Certbot renew timer so that certbot is run daily to + renew the certificate when needed. +EOT + while ! [[ "${REPLY:-}" =~ ^[YyNn]$ ]]; do + echo + read -rp "Please confirm these changes should be applied (y/n): " -n 1 + done + echo + [[ "${REPLY}" =~ ^[Nn]$ ]] && die aborted +fi + +echo +echo Download and importing Let\'s Encrypt certificates, this may take a while +declare -A LETSENCRYPTCERTS=( + [ISRGRootCAX1]=https://letsencrypt.org/certs/isrgrootx1.pem + [LetsEncryptX3]=https://letsencrypt.org/certs/letsencryptauthorityx3.pem +) +for certname in ${!LETSENCRYPTCERTS[@]}; do + certfile=$(mktemp -u ${TMPDIR:-/tmp}/$0.XXXXXXX.cert) + curl -s "${LETSENCRYPTCERTS[$certname]}" -o $certfile + $SUDO ipa-cacert-manage install $certfile -n "$certname" -t C,, + rm -f $certfile +done +$SUDO ipa-certupdate + +echo +if ! ipa role-show "DNS Administrator" > /dev/null 2>&1; then + echo Creating DNS Administrator role + ipa role-add "DNS Administrator" + ipa role-add-privilege "DNS Administrator" --privileges="DNS Administrators" +else + echo DNS Administrator role already exists +fi + +echo +if ! ipa service-show "$SERVICE" > /dev/null 2>&1; then + echo Add service user $SERVICE + ipa service-add "$SERVICE" + ipa role-add-member "DNS Administrator" --services="$SERVICE" +else + echo Service user $SERVICE already exists +fi + +echo +if [ ! -f "${KEYTAB:=/etc/letsencrypt/keytab}" ]; then + echo creating keytab file $KEYTAB + $SUDO ipa-getkeytab -p "$SERVICE" -k "$KEYTAB" +else + echo not touching existing keytab file $KEYTAB +fi + +echo +echo Requesting Let\'s Encrypt SSL certificate and setup renewals +$SUDO certbot certonly --expand --manual --preferred-challenges dns \ + --manual-public-ip-logging-ok --agree-tos --email "${EMAIL}" \ + --cert-name ${HOSTNAME} --domains "${DNSALTNAMES}" \ + --pre-hook "kinit -k -t $KEYTAB \"$SERVICE\"" --post-hook "kdestroy" \ + --manual-auth-hook "ipa dnsrecord-add ${DOMAIN:-\${CERTBOT_DOMAIN#*.\}}. _acme-challenge.\${CERTBOT_DOMAIN}. \"--txt-rec=\${CERTBOT_VALIDATION}\"; sleep 3" \ + --manual-cleanup-hook "ipa dnsrecord-del ${DOMAIN:-\${CERTBOT_DOMAIN#*.\}}. _acme-challenge.\${CERTBOT_DOMAIN}. --del-all" \ + --deploy-hook 'echo | ipa-server-certinstall --http "${RENEWED_LINEAGE}/fullchain.pem" "${RENEWED_LINEAGE}/privkey.pem" --dirman-password="" --pin="" && service httpd restart' + +echo +echo Enabling Certbot package\'s renewal timer +$SUDO systemctl enable --now certbot-renew.timer + +cat << EOT + +FreeIPA was successfully setup to use a Let\'s Encrypt certificate for its web +interface. This certificate will be renewed automatically when needed. + +EOT + -- libgit2 0.22.2