Commit 02d0b225618503ff14ef20d769a3f3983b24b43a

Authored by Frederik Lindenaar
1 parent 5df78460

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
@@ -24,6 +24,9 @@ This repository contains the following scripts: @@ -24,6 +24,9 @@ This repository contains the following scripts:
24 * [freeipa-dns.py](#freeipadns) 24 * [freeipa-dns.py](#freeipadns)
25 is a script providing functionality not available in FreeIPA itself to 25 is a script providing functionality not available in FreeIPA itself to
26 migrate/synchronize and maintain DNS zones in FreeIPA 26 migrate/synchronize and maintain DNS zones in FreeIPA
  27 + * [freeipa-letsencrypt.sh](#freeipaletsencrypt)
  28 + is a script to setup and configure Certbot and FreeIPA to request and renew
  29 + use publicly verifiable Let's Encrypt certificate(s)
27 30
28 31
29 <a name=users2freeipa>users2freeipa.py</a> 32 <a name=users2freeipa>users2freeipa.py</a>
@@ -60,6 +63,14 @@ This will also install the OpenDirectory-specific schema customization, create @@ -60,6 +63,14 @@ This will also install the OpenDirectory-specific schema customization, create
60 groups and copy group memberships, copy usuable passwords and ensure that all 63 groups and copy group memberships, copy usuable passwords and ensure that all
61 users have a password (storing generated passwords to ```passwords.txt```) 64 users have a password (storing generated passwords to ```passwords.txt```)
62 65
  66 +Please note that migrating existing passwords from LDAP has limitations, see
  67 +[this](https://www.freeipa.org/page/NIS_accounts_migration_preserving_Passwords)
  68 +page on migrating NIS passwords and [this](https://pagure.io/freeipa/issue/4732)
  69 +issue reported with it. Bottom line is that (at this moment) password migration
  70 +is flawed and always will require manual action from the user. For this reason
  71 +the better alternative to set a random password and ask the user to reset the
  72 +password using the FreeIPA portal makes more sense.
  73 +
63 Before running a production user migration, it is important to have FreeIPA 74 Before running a production user migration, it is important to have FreeIPA
64 setup and configured correctly so that the right defaults are used for new 75 setup and configured correctly so that the right defaults are used for new
65 users. Best is to start with a single user and add that as a stage user (please 76 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 @@ -160,6 +171,54 @@ for available commands run ```freeipa-dns.py -h``` and to get an overview of
160 the available options for each commmand run ```freeipa-dns.py <command> -h``` 171 the available options for each commmand run ```freeipa-dns.py <command> -h```
161 172
162 173
  174 +<a name=freeipaletsencrypt>freeipa-letsencrypt.sh</a>
  175 +----------------------------------------------------------------
  176 +This script will ensure the necessary setup is in place so that Certbot (EFF's
  177 +certificate request script for Let's Encrypt) will work with FreeIPA for DNS
  178 +challenges and and instructs it to deploy new certificates for FreeIPA's web
  179 +interface. Before writing this script I looked at available options, especially
  180 +[freeipa-letsencrypt](https://github.com/freeipa/freeipa-letsencrypt) and [antevens'](https://github.com/antevens/letsencrypt-freeipa) implementation but
  181 +decided to take a slightly different approach where Certbot does all the work
  182 +and the setup script will only ensure that the environment is prepared and that
  183 +Certbot is initially instructed correctly. This allows to fully tie-in with how
  184 +Certbot handles renews and use the Certbot package's provided method to schedule
  185 +these.
  186 +
  187 +The following changes will be made for this:
  188 + 1. Add Let's Encrypt Root and Intermediate CAs as trusted CAs
  189 + 2. Create DNS Administrator role in FreeIPA that can edit any DNS Record
  190 + 3. Create host service: ${SERVICE}
  191 + 4. Allow letsencrypt host service to manage DNS entries
  192 + 5. Register with Let's encrypt as: ${EMAIL}
  193 + 6. Request a Let's Encrypt SSL certificate for: ${CERTNAME}
  194 + with DNS Alternative names: ${DNSALTNAMES}
  195 + 7. install the Let's Encrypt certificate in apache as host SSL certificate,
  196 + storing renewal config in: /etc/letsencrypt/renewal/$HOSTNAME.conf
  197 + 8. configure the Fedora Certbot renew timer so that certbot is run daily to
  198 + renew the certificate when needed.
  199 +
  200 +The script is built to auto-configure but many of the defaults can be overridden
  201 +by setting one of the following environment variables:
  202 +
  203 +| Variable | Description | Default value |
  204 +|-------------|------------------------------------|---------------------------|
  205 +| CERTNAME | certificate hostname, | host's canonicalname (*) |
  206 +| DNSALTNAMES | certificate DNS names | host's principalnames (*) |
  207 +| DOMAIN | Let's Encrypt challenge DNS zone | {DNS name's domain} (**) |
  208 +| EMAIL | administrator's e-mail address | hostmaster@{domain} |
  209 +| HOSTNAME | FreeIPA server's hostname | `hostname --fqdn` |
  210 +| KEYTAB | Let'sEncrypt service's keytab file | /etc/letsencrypt/keytab |
  211 +| KRB5CCNAME | Kerberos5 cache to use for tickets | automatically determined |
  212 +| REPLY | when 'y' skip user confirmation | "" |
  213 +| SERVICE | FreeIPA service for Certbot to use | letsencrypt/canonicalname |
  214 +| SUDO | command to become root (if needed) | sudo |
  215 +| TMPDIR | Directory for temporary files | /tmp |
  216 +
  217 +(*) obtained from the FreeIPA server record looked up based on ${HOSTNAME}
  218 +(**) this allows to enforce the DNS zone, e.g. host.subdomain in mydomain.tld
  219 +
  220 +When things change, the script can simply be run again.
  221 +
163 <a name="license">License</a> 222 <a name="license">License</a>
164 ----------------------------- 223 -----------------------------
165 These scripts, documentation & configration examples are free software: you can 224 These scripts, documentation & configration examples are free software: you can
freeipa-dns.py
@@ -378,7 +378,7 @@ def reverseptr(api, args): @@ -378,7 +378,7 @@ def reverseptr(api, args):
378 if currrev == recordname: 378 if currrev == recordname:
379 logger.debug('no update for %s (%s) in %s', 379 logger.debug('no update for %s (%s) in %s',
380 reventry, recordname, revzone) 380 reventry, recordname, revzone)
381 - elif currrev and not args.overwrite: 381 + elif currrev and not args.override:
382 logger.warn('not updating %s (%s) in %s pointing to' 382 logger.warn('not updating %s (%s) in %s pointing to'
383 ' %s', reventry, recordname, revzone, currrev) 383 ' %s', reventry, recordname, revzone, currrev)
384 else: 384 else:
@@ -588,7 +588,7 @@ if __name__ == &#39;__main__&#39;: @@ -588,7 +588,7 @@ if __name__ == &#39;__main__&#39;:
588 args.func(api, args) 588 args.func(api, args)
589 exit(0) 589 exit(0)
590 except DNSException as e: 590 except DNSException as e:
591 - logger.critical("Domain %s cannot be downloaded,%s", domain, e) 591 + logger.critical("Domain cannot be downloaded: %s", e)
592 except AuthenticationError: 592 except AuthenticationError:
593 logger.critical("Unable to authenticate to FreeIPA, make sure you have a valid Kerberos ticket!") 593 logger.critical("Unable to authenticate to FreeIPA, make sure you have a valid Kerberos ticket!")
594 except PublicError as e: 594 except PublicError as e:
freeipa-letsencrypt.sh 0 โ†’ 100755
  1 +#!/bin/bash -e
  2 +#
  3 +# freeipa-letsencrypt.sh - script to setup Let's Encrypt's Certbot for FreeIPA
  4 +#
  5 +# Version 1.0, latest version, documentation and bugtracker available at:
  6 +# https://gitlab.lindenaar.net/scripts/freeipa
  7 +#
  8 +# Copyright (c) 2018 Frederik Lindenaar
  9 +#
  10 +# This script is free software: you can redistribute and/or modify it under the
  11 +# terms of version 3 of the GNU General Public License as published by the Free
  12 +# Software Foundation, or (at your option) any later version of the license.
  13 +#
  14 +# This script is distributed in the hope that it will be useful but WITHOUT ANY
  15 +# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
  16 +# A PARTICULAR PURPOSE. See the GNU General Public License for more details.
  17 +#
  18 +# You should have received a copy of the GNU General Public License along with
  19 +# this program. If not, visit <http://www.gnu.org/licenses/> to download it.
  20 +
  21 +# Sanity checks, ensure we have a valid Kerberos ticket, current host is a
  22 +# FreeIPA server, certbot is installed and that we can run priviliged commands
  23 +die() { echo $* >&2; exit 1; }
  24 +if ! klist -s; then
  25 + die no valid Kerberos ticket, please login to FreeIPA using kinit first
  26 +elif ! ipa server-show ${HOSTNAME:=$(hostname --fqdn)} > /dev/null; then
  27 + die this script should be run on an active IPA server
  28 +elif ! which certbot > /dev/null; then
  29 + die this script requires Certbot to be installed, please install that first
  30 +elif [ $(id -u) == 0 ]; then
  31 + unset SUDO
  32 +elif ! ${SUDO:=sudo} -v; then
  33 + die Error: this script must be run by root and $SUDO does not seem to work
  34 +else
  35 + $SUDO kinit -k
  36 +fi
  37 +
  38 +# Set KRB5CCNAME to ensure the current ticket cache will be used
  39 +KRB5CCNAME=${KRB5CCNAME:-$(klist -l | head -3 | tail -1 | cut -d\ -f2-)}
  40 +
  41 +# Ensure the user consents with changing his system.
  42 +if tty > /dev/null; then
  43 + cat << EOT
  44 +This script modifies this host's FreeIPA setup so that its web interface will
  45 +use a Let's Encrypt certificate and will automatically renew that when needed.
  46 +The following changes will be made for this:
  47 + 1. Add Let's Encrypt Root and Intermediate CAs as trusted CAs
  48 + 2. Create DNS Administrator role in FreeIPA that can edit any DNS Record
  49 + 3. Create host service: ${SERVICE:=letsencrypt/$(
  50 +ipa host-show $HOSTNAME --raw | fgrep "krbcanonicalname: host/" | cut -d/ -f2)}
  51 + 4. Allow letsencrypt host service to manage DNS entries
  52 + 5. Register with Let's encrypt as: ${EMAIL:=hostmaster@${HOSTNAME#*.}}
  53 + 6. Request a Let's Encrypt SSL certificate for: ${CERTNAME:=$(ipa host-show $HOSTNAME --raw | fgrep "krbcanonicalname: host/" | cut -d/ -f2 | cut -d@ -f1)}
  54 + with DNS Alternative names: ${DNSALTNAMES:=$(ipa host-show $HOSTNAME --raw | fgrep "krbprincipalname: host/" | cut -d/ -f2 | cut -d@ -f1 | paste -sd,)}
  55 + 7. install the Let's Encrypt certificate in apache as host SSL certificate,
  56 + storing renewal config in: /etc/letsencrypt/renewal/$HOSTNAME.conf
  57 + 8. configure the Fedora Certbot renew timer so that certbot is run daily to
  58 + renew the certificate when needed.
  59 +EOT
  60 + while ! [[ "${REPLY:-}" =~ ^[YyNn]$ ]]; do
  61 + echo
  62 + read -rp "Please confirm these changes should be applied (y/n): " -n 1
  63 + done
  64 + echo
  65 + [[ "${REPLY}" =~ ^[Nn]$ ]] && die aborted
  66 +fi
  67 +
  68 +echo
  69 +echo Download and importing Let\'s Encrypt certificates, this may take a while
  70 +declare -A LETSENCRYPTCERTS=(
  71 + [ISRGRootCAX1]=https://letsencrypt.org/certs/isrgrootx1.pem
  72 + [LetsEncryptX3]=https://letsencrypt.org/certs/letsencryptauthorityx3.pem
  73 +)
  74 +for certname in ${!LETSENCRYPTCERTS[@]}; do
  75 + certfile=$(mktemp -u ${TMPDIR:-/tmp}/$0.XXXXXXX.cert)
  76 + curl -s "${LETSENCRYPTCERTS[$certname]}" -o $certfile
  77 + $SUDO ipa-cacert-manage install $certfile -n "$certname" -t C,,
  78 + rm -f $certfile
  79 +done
  80 +$SUDO ipa-certupdate
  81 +
  82 +echo
  83 +if ! ipa role-show "DNS Administrator" > /dev/null 2>&1; then
  84 + echo Creating DNS Administrator role
  85 + ipa role-add "DNS Administrator"
  86 + ipa role-add-privilege "DNS Administrator" --privileges="DNS Administrators"
  87 +else
  88 + echo DNS Administrator role already exists
  89 +fi
  90 +
  91 +echo
  92 +if ! ipa service-show "$SERVICE" > /dev/null 2>&1; then
  93 + echo Add service user $SERVICE
  94 + ipa service-add "$SERVICE"
  95 + ipa role-add-member "DNS Administrator" --services="$SERVICE"
  96 +else
  97 + echo Service user $SERVICE already exists
  98 +fi
  99 +
  100 +echo
  101 +if [ ! -f "${KEYTAB:=/etc/letsencrypt/keytab}" ]; then
  102 + echo creating keytab file $KEYTAB
  103 + $SUDO ipa-getkeytab -p "$SERVICE" -k "$KEYTAB"
  104 +else
  105 + echo not touching existing keytab file $KEYTAB
  106 +fi
  107 +
  108 +echo
  109 +echo Requesting Let\'s Encrypt SSL certificate and setup renewals
  110 +$SUDO certbot certonly --expand --manual --preferred-challenges dns \
  111 + --manual-public-ip-logging-ok --agree-tos --email "${EMAIL}" \
  112 + --cert-name ${HOSTNAME} --domains "${DNSALTNAMES}" \
  113 + --pre-hook "kinit -k -t $KEYTAB \"$SERVICE\"" --post-hook "kdestroy" \
  114 + --manual-auth-hook "ipa dnsrecord-add ${DOMAIN:-\${CERTBOT_DOMAIN#*.\}}. _acme-challenge.\${CERTBOT_DOMAIN}. \"--txt-rec=\${CERTBOT_VALIDATION}\"; sleep 3" \
  115 + --manual-cleanup-hook "ipa dnsrecord-del ${DOMAIN:-\${CERTBOT_DOMAIN#*.\}}. _acme-challenge.\${CERTBOT_DOMAIN}. --del-all" \
  116 + --deploy-hook 'echo | ipa-server-certinstall --http "${RENEWED_LINEAGE}/fullchain.pem" "${RENEWED_LINEAGE}/privkey.pem" --dirman-password="" --pin="" && service httpd restart'
  117 +
  118 +echo
  119 +echo Enabling Certbot package\'s renewal timer
  120 +$SUDO systemctl enable --now certbot-renew.timer
  121 +
  122 +cat << EOT
  123 +
  124 +FreeIPA was successfully setup to use a Let\'s Encrypt certificate for its web
  125 +interface. This certificate will be renewed automatically when needed.
  126 +
  127 +EOT
  128 +