Blame view

README.md 20 KB
Frederik Lindenaar authored
1
2
3
4
5
6
7
8
9
10
11
12
FreeIPA Scripts
===============
This repository contains a small collection of scripts written to migrate my
existing LDAP/DNS setup (MacOS Server) to [FreeIPA](https://www.freeipa.org)
and manage my setup afterwards. These scripts provide functionality unavailable
in the FreeIPA command line tools. They use the FreeIPA API as much as possible
as I didn't like the provided alternatives for migration to directly update the
FreeIPA LDAP database. Most of these script / commands are meant to synchronize
between an existing situation and FreeIPA and are safe to run multiple times.
As side-effect, this also makes them suitable to support a gradual migration
over time (where a source system is still in production until final cut-over)
Frederik Lindenaar authored
13
14
15
16
17
18
Please note that these scripts are intended to run on the FreeIPA server and
require a valid (admin) kerberos ticket, which can be obtained with:
```
kinit admin
```
Frederik Lindenaar authored
19
20
21
The latest versions, documentation and a bug tracker are available on my
[GitLab instance](https://gitlab.lindenaar.net/scripts/freeipa)
Frederik Lindenaar authored
22
23
Copyright (c) 2018 - 2019 Frederik Lindenaar. free for distribution under the
GNU General Public License, see [below](#license)
Frederik Lindenaar authored
24
25
26
27
28
29
30

Contents
========
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
Frederik Lindenaar authored
31
32
33
  * [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)
Frederik Lindenaar authored
34
35
36
37
38
  * [set-dns-source.sh](#setdnssource)
    enforce the source address of outgoing DNS messages using firewalld as work
    around for bind-dyndb-ldap plugin not supporting bind's ```notify-source```
  * [users2freeipa.py](#users2freeipa)
    is a migration script to transfer/synchronize LDAP users to/with FreeIPA
Frederik Lindenaar authored
39
40
41
42
43
44
  * [freeipa-service-password.sh](#freeipaservicepassword)
    is a script to create a service account and (re)set it's password
  * [freeipa-service-ntlm.sh](#freeipaservicentlm)
    is a script to grant a service account access to NTLM password attributes
  * [freeipa-samba-user.sh](freeipasambauser)
    is a script to extend users to a sambaSAMAccount for Samba compatibility
Frederik Lindenaar authored
45
46
47


<a name=freeipadns>freeipa-dns.py</a>
Frederik Lindenaar authored
48
-------------------------------------
Frederik Lindenaar authored
49
50
51
This script provides functionality not provided by FreeIPA to migrate and/or
synchronize / maintain DNS data in FreeIPA. Currently the following commands
are implemented:
Frederik Lindenaar authored
52
53
54
55
56
  * axfr - synchronize DNS zone(s) using a zone-xfer. Contrary to the suggested
    [migration approach](https://docs.pagure.org/bind-dyndb-ldap/Migration.html)
    this uses the FreeIPA API to migrate or synchronize DNS zones with FreeIPA
    so it can also be used for running things in parallel or gradual migrations.
Frederik Lindenaar authored
57
58
    for example, to migrate / synchronize fromain ```domain.tld``` from DNS
    server ```192.168.1.53``` without checking DNS overlap, issue the command:
Frederik Lindenaar authored
59
Frederik Lindenaar authored
60
61
62
    ~~~
    ./freeipa-dns.py -v axfr -T 172.1.2.53 -n -f none 192.168.1.53 domain.tld
    ~~~
Frederik Lindenaar authored
63
Frederik Lindenaar authored
64
65
    in addition, this will ensure zone-xfers are allowed from ```172.1.2.53```
    and disable forwarding in FreeIPA.
Frederik Lindenaar authored
66
Frederik Lindenaar authored
67
68
69
  * copy - copy a DNS record in FreeIPA within or between zones
    for example, to copy ```A``` and ```AAAA``` from host ```wwww.domain.tld```
    to the domain ```domain.tld``` itself, issue the command:
Frederik Lindenaar authored
70
Frederik Lindenaar authored
71
72
73
    ~~~
    ./freeipa-dns.py -v copy -l A AAAA wwww.domain.tld -T domain.tld
    ~~~
Frederik Lindenaar authored
74
Frederik Lindenaar authored
75
76
77
  * move - move a DNS record in FreeIPA from one one to another
    for example, to move ```host1.int``` in zone ```domain.tld``` to ```host```
    in zone ```int.domain.tld``` issue the command:
Frederik Lindenaar authored
78
Frederik Lindenaar authored
79
80
81
    ~~~
    ./freeipa-dns.py -v move -z domain.tld host.int host.int.domain.tld
    ~~~
Frederik Lindenaar authored
82
Frederik Lindenaar authored
83
84
85
86
  * serial - update (set) zone serial(s) in FreeIPA, supporting both RFC1912
    style serials (YYYYMMDD##) based on current date and setting the serial to
    a specific value. To set the serial of a zone to revision 2 of today for
    zones ```zone1.mydomain.tld``` and ```zone2.mydomain.tld```, run:
Frederik Lindenaar authored
87
Frederik Lindenaar authored
88
89
90
    ~~~~
    ./freeipa-dns.py -v serial -t 2 zone1.mydomain.tld zone2.mydomain.tld
    ~~~~
Frederik Lindenaar authored
91
Frederik Lindenaar authored
92
93
    by default this command will set the serial to a larger value (which can be
    overridden with the ```-f```/```--force``` flag)
Frederik Lindenaar authored
94
Frederik Lindenaar authored
95
96
97
98
  * generate - generate number-range DNS records/attributes in FreeIPA
    This is meant to generate series of hosts or attributes, for example, to
    generate hosts ```dhcp-01``` to ```dhcp-10``` in zone ```int.mydomain.tld```
    with ip addresses starting from ```192.168.2.100``` issue to command:
Frederik Lindenaar authored
99
Frederik Lindenaar authored
100
101
102
103
    ~~~
    ./freeipa-dns.py -v generate int.mydomain.tld dhcp-%02d -4 192.168.2.100 \
                     --auto-increment-a -n 5
    ~~~
Frederik Lindenaar authored
104
Frederik Lindenaar authored
105
106
    it can also be used to generate a farm of web servers in different subnets
    with the command:
Frederik Lindenaar authored
107
Frederik Lindenaar authored
108
109
110
    ~~~
    ./freeipa-dns.py -v generate int.mydomain.tld www -4 192.168.%d.80 -n 5
    ~~~
Frederik Lindenaar authored
111
Frederik Lindenaar authored
112
113
114
115
116
  * reverse-ptr - create/update reverse DNS (PTR) entries in FreeIPA
    With this command reverse-zones can be automatically maintained. it scans
    the zones in FreeIPA for ```A``` and ```AAAA``` records and creates the
    corresponding records in the ```in-addr.arpa``` and ```ip6.arpa``` zones.
    The reverse zones must exist, and can also be created with this command by:
Frederik Lindenaar authored
117
Frederik Lindenaar authored
118
119
120
    ~~~
    ./freeipa-dns.py -v reverse-ptr -n -p -c 10. 10.100 192.168 2001:0db8:85a3
    ~~~
Frederik Lindenaar authored
121
Frederik Lindenaar authored
122
123
124
    which will create the reverse zones for prefixes 10.* 10.100.* 192.168.*
    and ipv6 prefix 2001:0db8:85a3. Reverse (PTR) records will automatically be
    created in the correct zone with the following command:
Frederik Lindenaar authored
125
Frederik Lindenaar authored
126
127
128
    ~~~
    ./freeipa-dns.py -v reverse-ptr -a
    ~~~
Frederik Lindenaar authored
129
Frederik Lindenaar authored
130
131
132
    by default, the command will not overwrite existing records, (which can be
    overridden with the ```-o```/```--override``` flag). To force a PTR record
    to point to a specific host, e.g. ```www.mydomain.tld``` run the command:
Frederik Lindenaar authored
133
Frederik Lindenaar authored
134
135
136
137
138
139
140
141
    ~~~
    ./freeipa-dns.py -v reverse-ptr -o -z mydomain.tld -H www
    ~~~

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```
Frederik Lindenaar authored
142
<a name=freeipaletsencrypt>freeipa-letsencrypt.sh</a>
Frederik Lindenaar authored
143
-----------------------------------------------------
Frederik Lindenaar authored
144
145
146
147
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
Frederik Lindenaar authored
148
149
[freeipa-letsencrypt](https://github.com/freeipa/freeipa-letsencrypt) and
[antevens'](https://github.com/antevens/letsencrypt-freeipa) implementation but
Frederik Lindenaar authored
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
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             |
|-------------|------------------------------------|---------------------------|
Frederik Lindenaar authored
174
175
| CERTNAME    | certificate hostname,              | host's canonicalname  (1) |
| DNSALTNAMES | certificate DNS names              | host's principalnames (1) |
Frederik Lindenaar authored
176
| DOMAIN      | Let's Encrypt challenge DNS zone   | {DNS name's domain}  (2)  |
Frederik Lindenaar authored
177
178
179
180
181
182
183
184
185
| 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                      |
Frederik Lindenaar authored
186
187
(1)  obtained from the FreeIPA server record looked up based on ${HOSTNAME}
(2) this allows to enforce the DNS zone, e.g. host.subdomain in mydomain.tld
Frederik Lindenaar authored
188
189
190

When things change, the script can simply be run again.
Frederik Lindenaar authored
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265

<a name=setdnssource>set-dns-source.sh</a>
------------------------------------------
FreeIPA's bind-dyndb-ldap plugin does not support bind's ```notify-source```
settings and simply uses the main address when sending DNS notifications. This
breaks multi-homed setups and, in case of IPv6, even causes bind to send NOTIFY
messages from the temporary IPv6 address, causing slave servers to reject them.
This script will setup Source NAT in firewalld to enforce the IPv4/IPv6 address
of outgoing DNS packets to ensure that DNS NOTIFYs messages will use the correct
source IPv4/IPv6 address. The IPv4/IPv6 address(es) to enforce should either be
provided in the environment or changed at the top of the script. In case an IPv4
address is provided, the script can figure out itself which device to use when
running it, if not this should be provided as well. To install run:
~~~
sudo IPV4ADDR=192.168.0.100 IPV6ADDR=none ./set-dns-source.sh install
~~~
to remove installed rules, run with the same parameters and parameter 'remove':
~~~
sudo IPV4ADDR=192.168.0.100 IPV6ADDR=none ./set-dns-source.sh remove
~~~
and to see custom rules run: ```sudo firewall-cmd --direct --get-all-rules```



<a name=users2freeipa>users2freeipa.py</a>
------------------------------------------
This script uses LDAP to obtain users from a MacOS Server (or other LDAP)
server and synchronizes the results with the users registered in FreeIPA.
Since it synchronizes data it is safe to run multiple times and users can be
imported also as stage users initially.

The intent is to migrate user data and to not drag on a legacy setup. For
this reason, the script will create new user and group IDs and not copy homedir
and shell information by default. For the IDs, the legacy information can be
stored in an FreeIPA ID View so it remains available, other items can be copied
over using command line options. Passwords can be copied-over (if available in
a usable format), and the script also supports having FreeIPA generate random
passwords and store these in a file for further processing/sharing with users.

Users can be copied from an existing (generic) LDAP database and a MacOS Server
OpenDirectory-flavor LDAP server. In this case, additional information (e.g.
Apple's generatedUID) will be copied over as well. Please note that this does
require customizing the FreeIPA LDAP schema, which the script will check for
and can install (option ```-U```). As the setup is modular it should be easy
to tweak or add other migrations.

By default all users will be migrated/synchronized, but it is also possible to
limit this to specific user(s) or group(s) or specifically exclude specific
users or groups. An example to copy all users in the group ```workgroup```
except ```admin``` from an Apple MacOS OpenDirectory server:
~~~
./users2freeipa.py -v -O -U -c "Legacy LDAP" -g workgroup -x admin -G \
                   -P -p passwords.txt ldap://ldap.mydomain.tld
~~~
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
note that this will not yet assign userIDs, group memberships and a password
as FreeIPA does not yet support that) and use an ID View to store legacy data.

For all available command-line options, run ```users2freeipa.py -h```
Frederik Lindenaar authored
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
<a name=freeipaservicepassword>freeipa-service-password.sh</a>
--------------------------------------------------------------
This script sets up a service under a host (creating both if needed) so that it
can use an LDAP simple bind for authentication. Although it is straightforward
to setup a host and service account in FreeIPA using the web interface, this
will not allow it to perform an LDAP simple bind (without requiring Kerberos).
For this, a direct change to the LDAP database is required to extend the service
principal object and make it an ```simpleSecurityObject``` with a password. This
script accepts a hostname and one or more services and should be run like:
~~~
./freeipa-service-password.sh <hostname> <service> [<service>]
~~~

As it always sets the password this script can be used for initial setup as well
as a reset of a service password. It performs the following actions:
  * Creates the host in FreeIPA (if it does not exists)
    When creating a new host it scans its SSH key and stores this in FreeIPA
  * Creates each service under the host in FreeIPA (if it does not exists)
  * (Re)sets each service’s LDAP password to a long generated random password

When done it prints the services bind DN and generated password for later use.


<a name=freeipaservicentlm>freeipa-service-ntlm.sh</a>
------------------------------------------------------
This script grants a service under a host access to the LDAP attributes required
to perform NLTM authentication. It sets up the necessary privilege, permission
and a role to grant the rights (if necessary) and then assigns the role to the
service on the host as specified on the command line. for this to work, Active
Directory domain trust support must have been enabled with the command:
~~~
sudo ipa-adtrust-install --add-sid
~~~
(the ```--addsid``` parameter is required to convert existing users).

Please note that for the necessary attributes to become available, users *must*
change their password after enabling Active Directoy domain support as FreeIPA
only maintains the necessary attributes after the user object has been modified.

Running this command will make the ```ipNTHash``` attribute available with the
necessary hash to perform NTLM authentication. Depending on whether the client
implementation supports mapping the attribute it is sufficient to configure it
to use this attribute or require to migrate users to the Samba schema with
[freeipa-samba-user.sh](freeipasambauser). To use the script execute:
~~~
./freeipa-service-ntlm.sh <hostname> <service> [<service>]
~~~
The specified service principals must already exist (they can be created using
[freeipa-service-password.sh](#freeipaservicepassword) or manually).

The script is built to auto-configure though some settings can be overridden by
setting one of the following environment variables:

| Variable  | Description             | Default value                          |
|-----------|-------------------------|----------------------------------------|
| HOST      | Service host hostname   | 1st command line parameter             |
| HOSTNAME  | FreeIPA server hostname | `hostname --fqdn`                      |
| PERM_NAME | Name of permission      | Read Samba NTLM RC4 Password Hash attribute |
| PRIV_NAME | Name of privilege       | Samba (NTLM) RC4 Password Hash Access  |
| ROLE_NAME | Name of role            | Samba/NTLM Authenticator               |

The description of the privilege / role creates can be changed through:
| Variable       | Default value                                               |
|----------------|-------------------------------------------------------------|
|PRIV_DESCRIPTION|Perform Samba NTLM authentication using the RC4 password Hash|
|ROLE_DESCRIPTION|Perform Samba (NTLM) Authentication using the RC4 Password hash|


<a name=freeipasambauser>freeipa-samba-user.sh</a>
--------------------------------------------------
This script adds the ```sambaSAMAccount``` objectclass to specified users so
that they can with Samba / NTLM. For everything to work, the Samba server must
login with a service account that has a simple password (setup with
[freeipa-service-password.sh](#freeipaservicepassword)) with access to the NTLM
password attributes (setup with [freeipa-service-ntlm.sh](#freeipaservicentlm)).
This script was written to support integration with Synology DSM (see also this
[blog post](https://frederik.lindenaar.nl/2019/07/14/integrating-synology-ds-with-freeipa.html))
but should also work for other Samba servers (please raise an issue in case it
doesn't work). I found that FreeIPA will maintain and expose required attributes
for NTLM authentication (```sambaNTPassword``` and ```sambaPwdLastSet```) when
Active Directory domain trust support has been enabled with the command:
~~~
sudo ipa-adtrust-install --add-sid
~~~
(the ```--addsid``` parameter is required to convert existing users).

To use the script to migrate all users (except admin) run (the backslash is
needed to avoid shell expansion of * as parameter):
~~~
./freeipa-service-password.sh \*
~~~

Besides a single * the script also accepts the login of one or more users to
migrate as parameter. To explicitly migrate admin - excluded with * - run:
~~~
./freeipa-samba-user.sh admin
~~~

The script will only migrate users that are not yet a ```sambaSAMAccount``` so
it can be run safely multiple times or at set intervals from cron (additional
work is required to make that work though as it will need a valid Kerberos
ticket from a keytab file for that).

Please note that for the necessary attributes to become available, users *must*
change their password after being converted ```sambaSAMAccount``` as FreeIPA
only maintains the necessary attributes after the user object has been modified.

The script is built to auto-configure though some settings can be overridden by
setting one of the following environment variables:

| Variable    | Description                        | Default value             |
|-------------|------------------------------------|---------------------------|
| HOST        | Service host hostname              | 1st command line parameter|
| HOSTNAME    | FreeIPA server's hostname          | `hostname --fqdn`         |
| SAMBADOMAIN | Samba (Windows) domain name        | FreeIPA Kerberos realm    |
Frederik Lindenaar authored
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
<a name="license">License</a>
-----------------------------
These scripts, documentation & configration examples are free software: you can
redistribute and/or modify it under the terms of the GNU General Public License
as published by the Free Software Foundation, either version 3 of the License,
or (at your option) any later version.

This script, documenatation and configuration examples are 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, download it from <http://www.gnu.org/licenses/>.