Commit 02d0b225618503ff14ef20d769a3f3983b24b43a
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)
Showing
3 changed files
with
189 additions
and
2 deletions
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__ == '__main__': | @@ -588,7 +588,7 @@ if __name__ == '__main__': | ||
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 | + |