From 3470ed2dcf8c31dc3103a271e828522a36a85b61 Mon Sep 17 00:00:00 2001 From: Frederik Lindenaar <frederik@lindenaar.nl> Date: Fri, 19 Jul 2019 18:18:02 +0200 Subject: [PATCH] Added 3 additional scripts (refer to README.md for what they do) - freeipa-samba-user.sh - freeipa-service-ntlm.sh - freeipa-service-password.sh --- README.md | 138 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---- freeipa-samba-user.sh | 75 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ freeipa-service-ntlm.sh | 83 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ freeipa-service-password.sh | 81 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 373 insertions(+), 4 deletions(-) create mode 100755 freeipa-samba-user.sh create mode 100755 freeipa-service-ntlm.sh create mode 100755 freeipa-service-password.sh diff --git a/README.md b/README.md index 2461157..1ae22a4 100644 --- a/README.md +++ b/README.md @@ -10,11 +10,17 @@ 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) +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 +``` + The latest versions, documentation and a bug tracker are available on my [GitLab instance](https://gitlab.lindenaar.net/scripts/freeipa) -Copyright (c) 2018 Frederik Lindenaar. free for distribution under the GNU -General Public License, see [below](#license) +Copyright (c) 2018 - 2019 Frederik Lindenaar. free for distribution under the +GNU General Public License, see [below](#license) Contents ======== @@ -30,6 +36,12 @@ This repository contains the following scripts: 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 + * [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 <a name=freeipadns>freeipa-dns.py</a> @@ -133,7 +145,8 @@ 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 +[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 @@ -160,7 +173,7 @@ by setting one of the following environment variables: |-------------|------------------------------------|---------------------------| | CERTNAME | certificate hostname, | host's canonicalname (1) | | DNSALTNAMES | certificate DNS names | host's principalnames (1) | -| DOMAIN | Let's Encrypt challenge DNS zone | {DNS name's domain} (2) | +| DOMAIN | Let's Encrypt challenge DNS zone | {DNS name's domain} (2) | | 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 | @@ -250,6 +263,123 @@ 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``` +<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 | + + <a name="license">License</a> ----------------------------- These scripts, documentation & configration examples are free software: you can diff --git a/freeipa-samba-user.sh b/freeipa-samba-user.sh new file mode 100755 index 0000000..3929364 --- /dev/null +++ b/freeipa-samba-user.sh @@ -0,0 +1,75 @@ +#!/bin/bash -e +# +# freeipa-samba-user.sh - extend existing user(s) with sambaSAMAccount +# +# Version 1.0, latest version, documentation and bugtracker available at: +# https://gitlab.lindenaar.net/scripts/freeipa +# +# Copyright (c) 2019 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. + +die() { echo $* >&2; exit 1; } + +# Exit if hostname not provided +if [ $# -lt 1 ]; then + die "Usage: `basename $0` <user> [<user> ...]" +fi + +# Sanity checks, ensure we have a valid Kerberos ticket and run on FreeIPA server +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" +fi + +# Generate the LDAP User filter, !admin if parameter is * else a list of users +if [ $# == 1 -a "$1" == "*" ]; then + USERFILTER='(!(uid=admin))' +else + USERS="$*" + USERFILTER="(|(uid=${USERS// /)(uid=}))" +fi + +# Lookup the Samba Domain - equal to the Kerberos REALM by default +: ${SAMBADOMAIN:=$(ipa host-show $HOSTNAME --raw | fgrep "krbcanonicalname: host/" | cut -d@ -f2)} + +# Lookup the users not yet converted and process each of them +declare -A params=( ) +ldapsearch -QLLL "(&${USERFILTER}(objectClass=ipantuserattrs)(!(objectClass=sambaSamAccount)))" dn uid ipaNTSecurityIdentifier | while read key value; do + # If we're at an empty line it's the end of the record, perform the change + if [ -z "$key" ]; then + if ldapmodify -Q > /dev/null 2>&1 <<EOLDIF; then +dn: ${params[dn]} +changetype: modify +add: objectClass +objectClass: sambaSamAccount +- +add: sambaSID +sambaSID: ${params[ipaNTSecurityIdentifier]} +- +add: sambaAcctFlags +sambaAcctFlags: [U ] +- +add: sambaDomainName +sambaDomainName: ${SAMBADOMAIN} +EOLDIF + echo "successfully updated user ${params[uid]}" + else + die "failed to update user ${params[uid]}, aborting!" + fi + declare -A params=( ) + else # we got another attibute, store it for later processing + params[${key/:/}]="$value" + fi +done + diff --git a/freeipa-service-ntlm.sh b/freeipa-service-ntlm.sh new file mode 100755 index 0000000..df2e136 --- /dev/null +++ b/freeipa-service-ntlm.sh @@ -0,0 +1,83 @@ +#!/bin/bash -e +# +# freeipa-service-ntlm.sh - grant host service access to NTLM Password Hash +# +# Version 1.0, latest version, documentation and bugtracker available at: +# https://gitlab.lindenaar.net/scripts/freeipa +# +# Copyright (c) 2019 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. + +die() { echo $* >&2; exit 1; } + +# Exit if hostname not provided +if [ $# -lt 2 ]; then + die "Usage: `basename $0` <hostname> <service> [<service> ...]" +fi + +# Sanity checks, ensure we have a valid Kerberos ticket and run on FreeIPA server +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" +fi + +# Set parameters +: ${HOST:=$1} +shift +: ${ROLE_NAME:=Samba/NTLM Authenticator} +: ${ROLE_DESCRIPTION:=Perform Samba (NTLM) Authentication using the RC4 Password hash} +: ${PRIV_NAME:=Samba (NTLM) RC4 Password Hash Access} +: ${PRIV_DESCRIPTION:=Perform Samba NTLM authentication using the RC4 password Hash} +: ${PERM_NAME:=Read Samba NTLM RC4 Password Hash attribute} + + +if ! ipa host-show "$HOST" > /dev/null 2>&1; then + die "host $HOST does not exist, aborting!" +fi + + +if ipa role-add "$ROLE_NAME" --desc="$ROLE_DESCRIPTION" > /dev/null 2>&1; then + echo created role $ROLE_NAME + if ipa privilege-add "$PRIV_NAME" --desc="$PRIV_DESCRIPTION" > /dev/null 2>&1; then + echo created privilege $PRIV_NAME + if ipa permission-add "$PERM_NAME" --attrs=sambaNTPassword --attrs=sambaPwdLastSet --attrs=sambaSID --attrs=sambaAcctFlags --attrs=sambaDomainName --type=user --right=read --right=compare > /dev/null 2>&1; then + echo created permission $PERM_NAME + else + echo permission $PERM_NAME exists + fi + if ! ipa privilege-add-permission "$PRIV_NAME" --permissions="$PERM_NAME" > /dev/null 2>&1; then + die "adding permission to privileges failed, aborting!" + fi + else + echo privilege $PRIV_NAME exists + fi + if ! ipa role-add-privilege "$ROLE_NAME" --privileges="$PRIV_NAME" > /dev/null 2>&1; then + die "adding privilege to role failed, aborting!" + fi +fi + + +for service in $* +do + if ipa service-show "$service/$HOST" > /dev/null 2>&1; then + if ipa role-add-member "$ROLE_NAME" --services="$service/$HOST" > /dev/null 2>&1; then + echo granted service $service/$HOST the role $ROLE_NAME + else + echo service $service/$HOST already had role $ROLE_NAME + fi + else + echo "service $service/$HOST does not exist, skipping" + fi +done + diff --git a/freeipa-service-password.sh b/freeipa-service-password.sh new file mode 100755 index 0000000..293eafd --- /dev/null +++ b/freeipa-service-password.sh @@ -0,0 +1,81 @@ +#!/bin/bash -e +# +# freeipa-service-password.sh - add/set host service login password +# +# Version 1.0, latest version, documentation and bugtracker available at: +# https://gitlab.lindenaar.net/scripts/freeipa +# +# Copyright (c) 2019 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. + +die() { echo $* >&2; exit 1; } + +# Exit if hostname not provided +if [ $# -lt 2 ]; then + die "Usage: `basename $0` <hostname> <service> [<service> ...]" +fi + +# Sanity checks, ensure we have a valid Kerberos ticket and run on FreeIPA server +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" +fi + +# Set parameters from command line +: ${HOST:=$1} +shift + +if ! ipa host-show "$HOST" > /dev/null 2>&1; then + echo Fetching information for $HOST + SSHKEYS=($(ssh-keyscan $HOST 2>/dev/null | cut -f2- -d\ | sed "s/\(.*\)/--sshpubkey='\1'/")) + echo Creating host $HOST + eval ipa host-add "$HOST" ${SSHKEYS[@]} + eval ipa host-add-principal "$HOST" $HOSTALIASES +else + echo host $HOST exists +fi + + +for service in $* +do + if ipa service-add "$service/$HOST" > /dev/null 2>&1; then + echo Created service $service/$HOST + else + echo service $service/$HOST exists + fi + service_binddn=$(ipa service-show "$service/$HOST" --raw --all | fgrep " dn: " | cut -f2 -d: | tr -d \ ) + echo Service Bind DN: $service_binddn + service_bindpw=$(pwmake 128) + if ipa service-show "$service/$HOST" --all --raw | fgrep "objectClass:" | fgrep -q "simpleSecurityObject" > /dev/null 2>&1; then + echo resetting password to generated password: $service_bindpw + ldapmodify -Q > /dev/null 2>&1 <<EOLDIF +dn: $service_binddn +changetype: modify +replace: userPassword +userPassword: $service_bindpw +EOLDIF + else + echo Enabled login with generated password: $service_bindpw + ldapmodify -Q > /dev/null 2>&1 <<EOLDIF +dn: $service_binddn +changetype: modify +add: objectClass +objectClass: simpleSecurityObject +- +add: userPassword +userPassword: $service_bindpw +EOLDIF + fi +done + -- libgit2 0.22.2