Samba4 Testing
We started following Samba4's development around rc1, with a hope to have a good grasp of Samba4's configuration and internal parameters for inclusion in the next Karoshi Server and Client. This will be completely replacing Samba3 on installed servers, but there might be an option to set up a server with Samba3 instead of Samba4, or allow the use of Samba3 servers during the migration stage.
Contents
DNS
Samba4 will not easily support our current DNS service, dnsmasq, so we either need to use Samba4's internal DNS server or BIND. Currently it seems to be much easier to just use the internal server, and it is working well in testing so far.
One thing that is required to do to support proper Linux Kerberized logins is to add in a reverse DNS zone. This can be done with
$ samba-tool dns zonecreate <server> <reverse-network-ip-part>.in-addr.arpa -U Administrator
where <server> is usually localhost, and <reverse-network-ip-part> depends on the network IP range. For example, on a 192.168.0.0/255.255.255.0 network, the network part is 192.168.0, so that field should become the reverse which is 0.168.192. Therefore the parameter becomes 0.168.192.in-addr.arpa. This can be demonstrated for an IP address by doing an nslookup on another IP address in your network. The nslookup will fail, but will show which zone it is trying to query. Once the zone has been created, samba needs to be restarted (verify?) and the main DC needs to be added as a PTR record. This uses the command
$ samba-tool dns add <server> <zone> <host-part> PTR <domain-name>
where <zone> is the zone created earlier, <host-part> is just the host part of the IP address (so a server at 192.168.0.2 would be host 2 in zone 0.168.192.in-addr.arpa) that is also reversed, and <domain-name> is the FQDN of the server. This can be tested using nslookup.
Note for reverse DNS with server aliases If your server is called, for example, 'edna', but you want it to serve web pages at 'www', then you should not add a new A record for 'www' pointing to the IP address of 'edna'. This will break Kerberos, as it will try to get a keytab for 'www', and fail, since the keytab should be in the name of 'edna'. Instead, use a CNAME record, linking 'www' to 'edna.FQDN', which allows Kerberos to get a ticket for 'edna' instead. However, do note that this requires proper reverse DNS accessible to both the client and the server, at least as far as we can work out from testing. Authentication would be refused unless both the client and the server had correct reverse DNS information about the server.
DMZ Domain Controllers
One problem we faced when putting a replicating server firewalled off from the main server was replication. Additional domain controllers or read only domain controllers require direct access to the main server to be able to replicate information. In a DMZ scenario, there are two ways for this to work - either every port needed is pinholed into the internal zone, which is messy and requires a LOT of ports to be opened; or a tunnel is set up between the domain controller and the PDC. We chose the latter method, and decided to implement it using OpenVPN.
OpenVPN should be set up in the usual way, with the PDC acting as the server and other domain controllers acting as clients. This has the problem of connection initialization - since the DMZ DC initiates the tunnel, it needs access to the main server on the OpenVPN port. One way of doing this is by pinholing through the firewall, however we came up with a very interesting solution. It is possible to hole punch from the main server to the DMZ server, using a tool such as nmap. With the main server set up as an OpenVPN server and the firewalled server set up as a client pointing at the main server, running:
$ nmap -P0 -sU -p1194 -g1194 <server>
on the main server results in a packet being sent, with a source port of 1194 (the OpenVPN port), through the firewall and to the OpenVPN client server. Since this is a UDP packet (OpenVPN uses UDP), the firewall between the zones will mark anything going to a port of 1194 on the main server from port 1194 on this DMZ server as related traffic on an established connection, thus allowing it through. When the OpenVPN client decides to attempt a connection to the main server, it will find that its traffic can pass without any problems, establishing a tunnel. For this to work, there must be no 'nobind' option in /etc/openvpn/client.conf, to ensure that the local OpenVPN binds on port 1194 also.
The next problem we faced was a problem with Samba4's replication code, or more accurately the DNS code. Since Samba4 is a DNS server in its own right, any process occurring within it (like replication) only looks at the Samba4 DNS records, completely ignoring anything set in /etc/hosts. This was a major pain for us, since we couldn't simply set the main server to resolve to its VPN tunnel IP address to push any traffic down the tunnel. Our fix was to abandon the attempts at getting the main server to resolve to the tunnel IP address, and physically redirect any packets destined for the main server down the tunnel using iptables, running the following on the DMZ server:
$ iptables -t nat -A OUTPUT -d <real-main-server-ip> -j DNAT --to-destination <vpn-main-server-ip> $ iptables -t nat -A POSTROUTING -d <vpn-main-server-ip> -j SNAT --to-source <vpn-local-ip> $ iptables -t nat -A PREROUTING -s <vpn-main-server-ip> -j DNAT --to-destination <real-local-ip> $ iptables -t nat -A INPUT -s <vpn-main-server-ip> -j SNAT --to-source <real-main-server-ip>
4 things to replace there. <real-main-server-ip> and <vpn-main-server-ip> are the real and VPN tunnel IPs for the main server respectively, while <real-local-ip> and <vpn-local-ip> are the real and VPN tunnel IPs for the DMZ server. These rules modify any outgoing packets destined to the main server on its real IP, so that they are actually going to the main server on its VPN IP (line 1), along with modifying the source of such packets to be the VPN IP of the local server (line 2) - this acts much like a router NAT, replacing the source IP with its own IP (in this case, the IP the packet was sent out on - the VPN tunnel IP). Any incoming packets with a source of the VPN IP of the main server have their destination modified to be the real IP of the local server (line 3), and their source modified to be the real IP of the main server (line 4). These rules combine to make the DMZ server think that it is talking directly to the main server, while it actually goes through the tunnel.
One potential drawback which we though we would experience is these rules modifying the packets coming out of OpenVPN, preventing a tunnel from being established in the first place. Interestingly though, stopping and starting OpenVPN on the DMZ server worked fine, with the tunnel coming back up correctly. We assume that OpenVPN traffic bypasses the netfilter firewall in the Linux kernel, but if anyone has a better hypothesis please contact us!
Getting User Information
There are multiple ways of doing this, but this article will focus on LDAP and SSSD, rather than Winbind or other methods.
LDAP
First is the required software:
nslcd libnss-ldapd
Authentication
There are two methods to bind to the LDAP server. Originally it was thought that the only practical way was to authenticate with a special reserved user to LDAP, since we thought that Samba4 LDAP did not allow anonymous binds. Recently however (16/12/12) we discovered that it is possible to bind anonymously. This is a much preferred way since there does not need to be a reserved user, and there is less of a risk that that user will be able to access other things that it shouldn't be able to. The authentication user method is still listed here, for reference.
Reserved User
There are two ways of doing this - using a Kerberos keytab or just doing a simple bind with a password. I used the keytab method, but it is probably easier to do it the simple bind method.
Once you have created the nslcd-service user, you probably want to prevent it from changing its own password in the case of a client becoming compromised. If a client with this configuration becomes compromised, then a malicious individual could change the password of the nslcd-service user and prevent other computers from binding to the LDAP database - in effect, a denial of service attack. At the same time is is convenient to set the user's password to never expire. This can be done by setting the userAccountControl attribute of the user object in the LDAP database - either through a GUI like phpLDAPadmin or CLI tools like ldapmodify. Its default is 512 (normal account) but it should be changed to 66112 (normal account + no expire password + disable password modifications). This will have the desired result of preventing such DoS attacks on the LDAP system.
As of rc6 you will need to set
acl:search = false
This is to allow normal users to see certain attributes such as unixHomeDirectory and loginShell. Note that this is a bug with Samba4 at the moment, and not a workaround for a feature.
Kerberos keytab method
You will need to install some additional software:
libsasl2-modules-gssapi-mit kstart
On the Samba4 server, enter the following commands to create a new user, give it a service principal name (SPN) and extract a keytab for it:
$ samba-tool user create nslcd-service --random-password $ samba-tool spn add ldap/clients nslcd-service $ rm /tmp/krb5.keytab $ samba-tool domain exportkeytab /tmp/krb5.keytab --principal=nslcd-service
That keytab needs to be copied to /etc, as well as to all clients that want to get user information. The resulting file should probably be set to 400 permissions, just to add a bit more resilience against casual hackers :) Remember to delete the copy in /tmp!
For each client, we need to make a slight modification to /etc/init.d/nslcd:
K5START_PRINCIPAL="nslcd-service"
At this stage make sure that /etc/krb5.conf is configured correctly to allow getting tickets.
For the <authentication> in the next section, replace the following:
# Kerberos authentication sasl_mech GSSAPI sasl_realm <realm-here> krb5_ccname /tmp/nslcd.tkt
Simple bind method
No extra software with this method, but you still need to create a user:
$ samba-tool user create nslcd-service <password>
For the <authentication> in the next section, replace the following:
# The DN to bind with for normal lookups. binddn cn=nslcd-service,cn=Users,<base-dn> bindpw <password>
Anonymous Bind
Anonymous binding requires a special attribute to be set within LDAP. This needs to be done as an administrative user, for evident reasons. The attribute that needs modifying (or creating, if it is not there) is in CN=Directory Service,CN=Windows NT,CN=Services,CN=Configuration,<basedn> and the attribute is dsHeuristics. It needs to look like the following:
dsHeuristics: 0000002001001
If the attribute already exists, then you need to modify it so that the 7th number is a '2'. This flag allows anonymous binds to the LDAP directory.
For this method, you can leave the <authentiction> section below blank since it uses anonymous binds by default.
Configuring nslcd
nslcd is the daemon that performs LDAP lookups for the system. All configuration for it is done in /etc/nslcd.conf. It will need to be replaced with the following:
# /etc/nslcd.conf
# nslcd configuration file. See nslcd.conf(5)
# for details.
# The user and group nslcd should run as.
uid nslcd
gid nslcd
# The location at which the LDAP server(s) should be reachable.
uri ldap://<server-ip>/
# The search base that will be used for all queries.
base cn=Users,<base-dn>
# The LDAP protocol version to use.
#ldap_version 3
###################
<authentication>
###################
# The DN used for password modifications by root.
#rootpwmoddn cn=admin,dc=example,dc=com
# SSL options
#ssl off
#tls_reqcert never
# The search scope.
#scope sub
###################
#Custom AD mappings
###################
#Replace objectSid:... with the domain SID
nss_min_uid 1000
pam_authz_search (!(userAccountControl:1.2.840.113556.1.4.803:=2))
filter passwd (&(objectClass=user)(!(objectClass=computer)))
map passwd uid sAMAccountName
map passwd uidNumber objectSid:S-1-5-21-2180207617-2439552426-3592756590
map passwd gidNumber primaryGroupID
map passwd homeDirectory "${unixHomeDirectory:-/home/$sAMAccountName}"
map passwd loginShell "${loginShell:-/bin/bash}"
filter shadow (&(objectClass=user)(!(objectClass=computer)))
map shadow uid sAMAccountName
map shadow shadowLastChange pwdLastSet
filter group (objectClass=group)
map group cn sAMAccountName
map group gidNumber objectSid:S-1-5-21-2180207617-2439552426-3592756590
Where <authentication> is replaced by one of the authentication sections from the method you followed above, and <base-dn> is replaced by the base DN of your server. Remember to replace objectSid:... with your domain's SID - this is printed during the provisioning step, or can be found by running wbinfo --name-to-sid Administrator on the Samba4 server - everything up to the final dash (-) is the domain SID. These maps will set certain attributes required by a Linux client, like the username (mapped to sAMAccountName), uid (mapped to the unique UID from the objectSid stored in AD), gid (mapped to primaryGroupID), homeDirectory (mapped to unixHomeDirectory if it exists, otherwise mapped to /home/[username] - this can be edited to suit your needs) and loginShell (mapped to loginShell if it exists, otherwise to /bin/bash). The filters prevent any objects that do not have the 'user' objectClass or have the 'computer' objectClass. The pam_authz_search line prevents users that have their account disabled from authenticating. The nss_min_uid line prevents users with a UID lower than 1000 from being considered valid users - this is to stop certain built-in AD users like Administrator from showing up.
Next you need to configure /etc/nsswitch.conf if you haven't already:
passwd: files ldap group: files ldap
Now just to test - run service nslcd restart. If you followed the Kerberos keytab authentication method, then you should see some lines about k5start getting a ticket. Next run getent passwd. You should see some users show up - if you haven't added any into Samba4 yet, then you should at least see krbtgt show up. You can also do getent group to see groups - this should show up considerably more information for an empty directory. At this point your system is correctly pulling down user and group information from LDAP, and you can proceed onto another step like Kerberized logins.
SSSD with LDAP
SSSD is a software package that performs a similar job to Winbind, getting user information and authenticating users. However, it has the advantage of being completely configurable and being designed for a Linux-centric Samba4 configuration, rather than Winbind which aims to support all Microsoft AD features. This means, for example, that SSSD can map UIDs and GIDs like nslcd does (see above), while still merging authentication and user information into one software package, and therefore one configuration file.
You will need to install:
sssd
All configuration for SSSD is done in /etc/sssd/sssd.conf. It is divided into sections, making it easy to separate the configuration for each part. This article will mainly talk about the NSS settings.
In the main section, you need to specify the services and the domains:
[sssd] services = nss domains = <domain>
In the NSS section, certain default parameters should be configured:
[nss] filter_groups = root filer_users = root fallback_homedir = /home/%u default_shell = /bin/bash
These settings ignore 'root', which forces the use of the local user/group, along with setting a default home directory and shell for any user accounts that do not specify it.
All of the next parameters are set in a section that is specific to the domain:
[domain/<domain>] enumerate = false id_provider = ldap min_id = 1000 ldap_search_base = <ldap-base> ldap_schema = rfc2307bis ldap_user_name = cn ldap_user_gecos = displayName ldap_user_home_directory = unixHomeDirectory ldap_user_fullname = displayName
These options are similar to the ones set in nslcd.conf for the LDAP configuration above. Note that <ldap-base> should be set to a valid LDAP base that contains all the users and groups on the directory. For testing purposes, setting 'enumerate' to 'true' will let you run 'getent passwd' or 'getent group' and see all users/groups show up.
Authentication
There are multiple methods for authentication, each suited for a different purpose. LDAP authentication is probably more suitable for a server that needs to be able to log in as a user, but does not need a Kerberos ticket. Kerberos on the other hand is more suitable for clients that want the single sign on support. SSSD has the advantages of both LDAP and Kerberos, and merges everything into one software package for easier configuration.
The one most important thing to do in this is to always keep a root shell open, in case you mess up your PAM configuration and cannot authenticate at all! If this happens and you do not have a root shell open, it is practically impossible to recover your system.
LDAP Authentication
You will need to install:
libpam-ldapd
Configuration of this PAM LDAP module is handled in nslcd.conf, which should already be set correctly in the section above. In fact, there really is nothing else to do - just make sure that su'ing in as a different user works, and you are done!
Kerberos Authentication
You will need to install:
libpam-krb5
There is no real configuration file for this PAM module, but you need to ensure that /etc/krb5.conf is configured correctly to allow for kinits using just the username. Test authentication using su, make sure you get a ticket (klist) and you are done.
SSSD Authentication with Kerberos
This authentication method doesn't make much sense unless users are also getting pulled down using SSSD - see #SSSD with LDAP. This article will append to the configuration defined in that article.
For starters, SSSD needs to be configured to use PAM:
[sssd] services = nss, pam
The '[pam]' section unusually contains no important configuration paramters, so it can be excluded. All configuration occurs under the domain-specific section:
[domain/<domain>] cache_credentials = true auth_provider = krb5 chpass_provider = krb5 krb5_realm = <realm> krb5_auth_timeout = 2
The 'cache_credentials' line sets SSSD to cache credentials, so that in the case the machine goes offline it can continue to authenticate users that have logged in at some point when the machine was online. This is particularly useful for laptops that may be taken offline from the network for personal use, but need to be able to authenticate to the network once they are connected again.
'<realm>' needs to be set to the same value as is in /etc/krb5.conf - usually a capitalized string of the domain name.
Services
Many services can be Kerberized - that is to say, made to allow for Kerberos tickets as valid authentication. This section assumes you have Kerberos correctly working on each machine.
To start you will need a user to create SPNs (service principal names) against. In these examples we will be using the user 'karoshi-krb5'. Add the user to Samba4 as normal, and you can probably use --random-password unless you need to use it for other things. Each following section will ask for an SPN to be added to that user and a keytab extracted for it, then that keytab copied to the server it will be used on. It can either be copied to the system-wide keytab in /etc/krb5.keytab or as an application specific keytab:
$ samba-tool spn add <spn>/<server>.<domain> karoshi-krb5 $ samba-tool domain exportkeytab /tmp/krb5.keytab.$$ --principal=<spn>/<server>.<domain> $ scp -p /tmp/krb5.keytab.$$ root@<server>:<keytablocation> $ rm -f /tmp/krb5.keytab.$$
Note that when using $$ all the commands need to be run within a single shell because $$ expands to the process ID.
If you are copying into /etc/krb5.keytab, and there is already an existing keytab there, use the following commands instead of the copying command:
$ scp -p /tmp/krb5.keytab root@<server>:/tmp/krb5.keytab.$$ $ ssh root@<server> 'echo -e "rkt /tmp/krb5.keytab.'$$'\nwkt /etc/krb5.keytab\nexit\n" | ktutil; rm -f /tmp/krb5.keytab.'$$
This will merge the keytabs together to allow the machine to use both principals in authentication. Unless your services run as the root user, their users will also need access to the keytab file. The keytab file by default is owned by root:root and has permissions of 600, denying anyone but root access to the keytab. Perhaps the easiest way to accomplish this is to add a new group, for example keytab-users, then make that group owners of the keytab file, and give the keytab file 640 permissions.
Alternatively, if you are going to use application-specific keytabs, then use the commands above to copy the file into the right place, but you will need to make sure that that keytab can be read by the process user and has permissions of 600 or 400. The service will also need to be configured to specifically use that keytab rather than the system-wide one, and each section will state how to accomplish this.
Server Configuration
Squid
Firefox can authenticate against Squid using Kerberos credentials. You will need to create a SPN with the name 'HTTP' for the Squid server, and make sure that the 'proxy' user can read the keytab file. Then /etc/squid3/squid.conf needs to be edited:
# Kerberos auth_param negotiate program /usr/lib/squid3/squid_kerb_auth -s HTTP/<server>.<domain> auth_param negotiate children 10 auth_param negotiate keep_alive on
If necessary, there can also be an LDAP fallback for users that do not have a Kerberos ticket:
# LDAP Fallback auth_param basic program /usr/lib/squid3/squid_ldap_auth -b "cn=Users,<basedn>" -v 3 -f "uid=%s" -h -D "cn=<ldapuser>,cn=Users,<basedn>" -w <ldapuser_pass> <ldap_server>
If you are using a keytab that is not the system-wide one, then you need to set the KRB5_KTNAME variable for Squid. This can be done with the command:
echo "export KRB5_KTNAME=<keytablocation>" >> /etc/default/squid3
At this point Squid needs to be restarted to read the new settings.
DansGuardian
Although the above settings will allow Squid to authenticate the user properly, a middleman filtering application like DansGuardian will not work using Kerberos. This is because the filtering software extracts the proxy authentication details, mainly the username, from the credentials sent by the client to the proxy. With basic and digest auth this works, as the username is transmitted in cleartext, but with negotiate (Kerberos) the credentials are strongly encrypted and cannot be decrypted apart from at the client or server. Thus DansGuardian has no idea who the user is, and therefore can do no ACL filtering.
One solution is to run multiple instances of Squid, listening on different ports - one running after DansGuardian and acting as a caching proxy, and one running before DansGuardian and doing authentication. However, this requires two init scripts, and two configuration files, not to mention the pain with logging. The solution we have developed is to use a single Squid instance, listening on two ports, redirecting traffic to DansGuardian should it come in on one port, and allowing it straight out on the Internet if it comes in on the internal port. Authentication only occurs for the 'auth' port, so the 'cache' port needs to be accessible only to localhost.
At the top of squid.conf, insert lines defining the ports that Squid should listen on:
http_port 3129 name=auth http_port localhost:8080 name=cache
Notice that we have named the ports - this is to make squid.conf slightly easier to understand. Also, we specifically bind port 8080 to localhost, which stops any access to that port except from localhost.
Next, we need some new ACLs:
acl auth_port myportname auth acl cache_port myportname cache acl authenticated proxy_auth REQUIRED
The two ACLs of interest are 'auth_port' and 'cache_port', which simply match any traffic coming in on the 'auth' port (port 3129 in the example above) or on the 'cache' port (8080) respectively. The 'authenticated' ACL forces the user to be authenticated - this is used for the auth port. Note that for authentication to do something, you need auth_param directives. This example shows how to use both Kerberos negotiate auth and Basic auth:
# Kerberos auth_param negotiate program /usr/lib/squid3/squid_kerb_auth -s HTTP/<server>.<realm> auth_param negotiate children 10 auth_param negotiate keep_alive on # Basic auth_param basic program /usr/lib/squid3/msnt_auth auth_param basic children 20 auth_param basic realm Squid proxy-caching web server auth_param basic credentialsttl 2 hours
Note
Due to the way Kerberos works, the username presented to Squid will be in the format <username>@<REALM>. DansGuardian does not recognize that the Kerberos format is the same as just the username, so it does not perform correct filtering. To get this to work properly, we have had to strip out the realm part of the username with a custom auth program for Squid that acts as a wrapper for squid_kerb_auth. We put it in /usr/lib/squid3/karoshi_kerb_auth:
#!/bin/bash
while read -r result_type result_body1 result_body2; do
if $result_type == AF ; then
result_body2=${result_body2%@*}
fi
echo $result_type $result_body1 $result_body2
done < <(/usr/lib/squid3/squid_kerb_auth "$@")
With the corresponding change in squid.conf:
auth_param negotiate program /usr/lib/squid3/karoshi_kerb_auth -s HTTP/<server>.<realm>
The script acts as a drop-in replacement for squid_kerb_auth, but note that it may not continue to work completely in the future if the specification for communication between Squid and negotiate helpers changes, however this page will be updated if those changes occur.
EDIT: The above is only valid for Squid versions <3.2. Squid 3.2 and above have included an extra option in squid_kerb_auth that removes the realm, so hacking together a script is no longer necessary. Simply change the squid.conf line to:
auth_param negotiate program /usr/lib/squid3/squid_kerb_auth -s HTTP/<server>.<realm> -r
END EDIT
Next, we will configure the directives to point Squid at DansGuardian (on port 3128 here), but only for traffic arriving on the auth port:
cache_peer localhost parent 3128 0 no-query login=*:nopasswd connection-auth=off cache_peer_access localhost deny !auth_port never_direct allow auth_port
This tells Squid that an upstream proxy exists on localhost port 3128, and traffic forwarded to there should be accompanied by a Basic auth string containing the username of the authenticated user and a password of 'nopasswd'. This allows DansGuardian to know who the user is, and since it doesn't care about the password we just use a placeholder. The second line tells Squid that the upstream proxy cannot be accessed by traffic arriving on a port other than the auth port - in other words only traffic coming in on the authentication proxy port is allowed to be passed to DansGuardian. The third line enforces that a little more, preventing traffic coming in on the auth port from going directly to the Internet, instead forcing it to go to DansGuardian.
It is also useful at this point to tell Squid to disable caching for the auth port, and also to prevent logging in access.log for the cache port. This means that the logs will only contain accesses on the auth port (containing the correct IP address and username) and the auth port will not do caching (so that DansGuardian is never skipped due to a cached copy of a web page):
cache deny auth_port access_log none cache_port
Now, we only need to add in some http_access directives to enforce the ACLs defined above, as well as correctly allowing traffic from the cache port while still requiring authentication for the auth port. Add these lines below the default Squid lines, but above your custom lines, so that port filtering is done at the auth proxy level and will not cause unneeded traffic:
http_access allow cache_port http_access deny !authenticated
First of all, this allows all traffic coming from the cache port to go straight out on the Internet. This is desired, since authentication has already been done by this point. The second line is the first of the ACLs that only applies for the auth port - deny any users that have not authenticated. Following these lines go your custom filtering rules.
One more thing to note about logging: using this method DansGuardian and cache Squid will not know what IP your request came in on. However, auth Squid does set an 'X-Forwarded-For' header, and DansGuardian can be configured to trust that:
usexforwardedfor = on
However, this opens up a potential soft vulnerability where the client can insert that header, and DansGuardian will log the request as coming from that address instead. To prevent this, Squid can be configured to remove any existing headers and replace them with its own:
forwarded_for truncate
In summary, when a client connects to the proxy to try and load http://www.google.co.uk:
- The request comes in on the auth port (3129 in this example) to Squid
- Squid runs through the http_access directives, not denying access since HTTP (port 80) is on the list of safe ports
- Squid hits 'http_access allow cache_port', which does not match, since this is not the cache port
- Squid hits 'http_access deny !authenticated', and forces authentication of the user. Now Squid knows who the user is
- Squid runs through any other ACLs you have set, eventually allowing the request (you are allowing access to Google, right?)
- The request, coming in on the auth port, matches the requirements to be sent out to port 3128 to DansGuardian
- The request arrives at DansGuardian
- Filtering is applied, and eventually the request is allowed
- DansGuardian is configured to pass on the request to port 8080 on localhost (Squid)
- The request arrives back at Squid on the cache port
- Squid runs through the http_access directives again, but this time matches 'http_access allow cache_port'
- Squid understands that traffic on the cache port is not allowed to go back to DansGuardian, and since it has matched that http_access rule, it goes out to the Internet
- The request gets to the Internet, and the server replies (hopefully)
- The chain of Squid - DansGuardian - Squid is repeated in reverse for the reply
Apache
You will need to install libapache2-mod-auth-kerb to provide the module for Kerberos authentication, and also create an SPN called 'HTTP', along with making sure the 'www-data' user can read the keytab. The module is configured within a virtual host file in /etc/apache2/sites-*. The directives can then be specified in a Directory or Files directive:
AuthName "<authname>" AuthType Kerberos Krb5Keytab <keytablocation> KrbMethodNegotiate on KrbMethodK5Passwd off KrbAuthoritative on require valid-user
where <authname> is set to whatever you want, for example "Betty's Foods" for a food website run by Betty or whatever. <keytablocation> is of course the path to the keytab, be that in /etc/krb5.keytab or an application-specific keytab. To finish off restart Apache2.
This will provide Kerberos authentication for your website, but you will most likely need to configure your web application to accept the Kerberos credentials as valid authentication.
Warning about custom 401 pages
Using ErrorDocument to customise 401 pages can cause problems with Negotiate authentication. GSSAPI (the Negotiate authentication method over HTTP) uses 401s to start the authentication, and using an incorrectly coded 401 page can break this. To get around this, replace the ErrorDocument directive with one that looks like the following:
ErrorDocument 401 "<html><meta http-equiv=\"refresh\" content=\"0;url=/login.htm\"></html>"
replacing /login.htm with your 401 page URL. This will allow GSSAPI to do its thing, while still redirecting the user to your custom 401 page when authentication fails.$base_dn
Custom Authenticated Website
If your website uses Apache's authentication, such as mod_auth_basic or mod_auth_md5, then you will easily be able to change to using mod_auth_kerb, along with a fallback in case the user has no ticket. Simply replace your current authentication section with the one shown above in the Apache section. If you want a fallback, then change 'KrbMethodK5Passwd' to 'on'. It may be necessary to tweak the 'require' option, for example to do LDAP group lookups to only allow access to one group of users. Note, however, that mod_auth_kerb will output '<username>@<REALM>' instead of just '<username>', which will likely break all lookups of that kind. Fortunately, to make mod_auth_kerb output just the username, use:
KrbLocalUserMapping on
along with the other options.
For a working example, here is what we use for staff access to the web management. Note how we use LDAP group lookups to make sure the user is in the correct group, and 'KrbMethodK5Passwd' is set to 'on' to allow typing a password in case negotiation failed. Also note that we actually use 2 'require' statements - this is because setting a user's primaryGroupID in Samba4 actually removes that user as a 'member' of that group. Thus as a workaround, putting in both 'require' statements allows a user if they are a member of 'cn=Staff', or they have a primaryGroupID (resolved through nslcd) of the staff group GID. This is the whole .htaccess file for the staff area:
Order deny,allow Deny from All AuthName "Karoshi Web Control" AuthType Kerberos Krb5Keytab /etc/keytabs/webmanagement.keytab KrbMethodNegotiate on KrbMethodK5Passwd on KrbAuthoritative on KrbLocalUserMapping on #AuthType Basic #AuthBasicProvider ldap #AuthzLDAPAuthoritative on AuthLDAPUrl ldap://127.0.0.1/CN=personnel,CN=Users,<ldap-base>?CN Require valid-user Require ldap-group CN=staff,CN=Users,<ldap-base> Require ldap-attribute gidNumber=<staff-gid> Satisfy any ErrorDocument 401 "<html><meta http-equiv=\"refresh\" content=\"0;url=/cgi-bin/access_denied.cgi\"></html>" ErrorDocument 403 "<html><meta http-equiv=\"refresh\" content=\"0;url=/cgi-bin/access_forbidden.cgi\"></html>"
Moodle
First follow the instructions for Apache above, putting the directives in the following format in the Moodle virtual host file, replacing [authentication] with the configuration from the Apache section above:
<Directory [webroot]/moodle/auth/ldap/> <Files ntlmsso_magic.php> [authentication] </Files> </Directory>
Next go to the Moodle configuration page on the web interface, by logging in as the administrative user. You then need to go to Site Administration -> Plugins -> Authentication -> LDAP Server. In this page, enable NTLM SSO, set the IP subnet correctly for your local network along with the mask (for example, 172.30.0.0/16) and set the authentication type to Kerberos.
Moodle will now attempt to use Kerberos authentication when it can, and otherwise will fall back to using its default authentication type (most likely LDAP).
Dovecot and Postfix are often used together to provide IMAP and SMTP email access. However, it is possible (and recommended) in this case to run the Postfix authentication off Dovecot's authentication. This means that the keytab needs to be readable by Dovecot only, yet needs to contain SPNs for both IMAP and SMTP. Postfix will need an SPN with the name 'smtp', and Dovecot will need an SPN with the name 'imap'.
First, modify /etc/dovecot/dovecot.conf:
auth_mechanisms = plain gssapi
auth_krb5_keytab = <keytab>
passdb {
driver = pam
}
userdb {
driver = passwd
}
service auth {
unix_listener /var/spool/postfix/private/auth {
mode = 0660
# Assuming the default Postfix user and group
user = postfix
group = postfix
}
}
Ommitting <keytab> will result in /etc/krb5.keytab being used.
Next, you need to edit /etc/postfix/main.cf to contain the following lines:
smtpd_sasl_type = dovecot smtpd_sasl_path = private/auth smtpd_sasl_auth_enable = yes smtpd_recipient_restrictions = permit_mynetworks, permit_sasl_authenticated, reject_unauth_destination
After a restart of both Postfix and Dovecot, you should be able to connect to both IMAP and SMTP using Kerberos credentials.
Groups / mailing lists
There are two ways to implement mail groups. One way is to have the email client expand the group members when you add the group to the email. Unfortunately, we have found that no email clients implement this properly, at least through a server-side address book like LDAP. So the alternate method is for each group to have its own email address, then have the SMTP server expand the groups, sending the email to each group member. This has the additional benefit of the receiving person seeing a 'To' field of the group name, so it is immediately obvious that this email was sent to the whole group at once.
For this, we needed the Postfix LDAP module (postfix-ldap in Ubuntu), and an additional configuration file for Postfix, in our case /etc/postfix/ldap.cf:
search_base = CN=Users,<base-dn> query_filter = cn=%s leaf_result_attribute = mail special_result_attribute = member server_host = <ldap-server>
This configures a search for cn=<username>. This will only work if you have a masquerade_domains property set to your domain name in main.cf, which strips off the domain part of the email address (leaving just the username, thus we search for only the username here). In other setups without masquerade_domains set, use mail=%s instead to match the whole email against the LDAP server. This may also be beneficial in cases where the CN doesn't match the email address.
Finally, a change in main.cf is required:
alias_maps = ldap:/etc/postfix/ldap.cf
If that property already exists, append the LDAP string to the end of it, separated with a comma. This tells Postfix to attempt to match unknown email addresses with the LDAP query for groups as defined above, which will map a group email address to the individual user email addresses, allowing Postfix to correctly forward the messages.
SOGo
SOGo is an open-source groupware solution enabling shared address books, calendar information and more, along with providing a webmail interface. In Karoshi we use it to synchronize information between Thunderbird clients and the webmail application, along with managing settings on the clients themselves.
SOGo is available in a Ubuntu PPA, http://inverse.ca/ubuntu. Unfortunately, we have identified a shortcoming in SOGo's authentication, which makes it very awkward to use single sign-on for certain parts of the application, yet fall back to standard authentication for other parts. For example, using Kerberos authentication for the webmail part will not work, due to the inability to pass on the Kerberos ticket to the IMAP and SMTP servers to get/send email. However, CardDAV, CalDAV and GroupDAV (accessible using external clients like Thunderbird) CAN use single sign-on with Kerberos. In the current mode of operation, either the entire thing uses single sign-on (and subsequently fails at webmail) or the entire thing uses passwords. A pull request on GitHub has been sent with the fixed code, and you can track the progress here.
Apache configuration
SOGo is actually a program in itself, but runs on port 20000 and lacks abilities like HTTPS. Thus, Apache is used as a reverse proxy for SOGo, forwarding requests to /SOGo to the application on port 20000. This is our configuration (using the patched single sign-on system), which should be put in as a configuration file under /etc/apache2/conf.d. For example, this file is at /etc/apache2/conf.d/sogo.conf:
Alias /SOGo.woa/WebServerResources/ \
/usr/lib/GNUstep/SOGo/WebServerResources/
Alias /SOGo/WebServerResources/ \
/usr/lib/GNUstep/SOGo/WebServerResources/
AliasMatch /SOGo/so/ControlPanel/Products/(.*)/Resources/(.*) \
/usr/lib/GNUstep/SOGo/$1.SOGo/Resources/$2
<Directory /usr/lib/GNUstep/SOGo/WebServerResources>
order allow,deny
allow from all
</directory>
<Directory /usr/lib/GNUstep/SOGo/>
AllowOverride None
Order deny,allow
Allow from all
# Explicitly allow caching of static content to avoid browser specific behavior.
# A resource's URL MUST change in order to have the client load the new version.
<IfModule expires_module>
ExpiresActive On
ExpiresDefault "access plus 1 year"
</IfModule>
</Directory>
<LocationMatch "^/SOGo/so/ControlPanel/Products/.*UI/Resources/.*\.(jpg|png|gif|css|js)">
SetHandler default-handler
</LocationMatch>
## Uncomment the following to enable proxy-side authentication, you will then
## need to set the "SOGoTrustProxyAuthentication" SOGo user default to YES and
## adjust the "x-webobjects-remote-user" proxy header in the "Proxy" section
## below.
<Location /SOGo/dav>
AuthType Kerberos
AuthName "Sogo"
Krb5Keytab /etc/keytabs/sogo.keytab
KrbServiceName HTTP/<server-url>
KrbMethodNegotiate on
KrbMethodK5Passwd on
KrbSaveCredentials on
KrbAuthoritative on
KrbLocalUserMapping on
Require valid-user
SetEnv proxy-nokeepalive 1
Allow from all
RewriteEngine on
RewriteRule .* - [E=SOGO_REMOTE_USER:%{REMOTE_USER}]
</Location>
ProxyRequests Off
SetEnv proxy-nokeepalive 1
ProxyPreserveHost On
# When using CAS, you should uncomment this and install cas-proxy-validate.py
# in /usr/lib/cgi-bin to reduce server overloading
#
# ProxyPass /SOGo/casProxy http://localhost/cgi-bin/cas-proxy-validate.py
# <Proxy http://localhost/app/cas-proxy-validate.py>
# Order deny,allow
# Allow from your-cas-host-addr
# </Proxy>
ProxyPass /SOGo http://127.0.0.1:20000/SOGo retry=0
<Proxy http://127.0.0.1:20000/SOGo>
## adjust the following to your configuration
RequestHeader set "x-webobjects-server-port" "443"
RequestHeader set "x-webobjects-server-name" "<server-name>"
RequestHeader set "x-webobjects-server-url" "https://<server-url>"
## When using proxy-side autentication, you need to uncomment and
## adjust the following line:
RequestHeader set "x-webobjects-remote-user" "%{SOGO_REMOTE_USER}e"
RequestHeader set "x-webobjects-remote-host" %{REMOTE_HOST}e env=REMOTE_HOST
RequestHeader set "x-webobjects-server-protocol" "HTTP/1.0"
AddDefaultCharset UTF-8
Order allow,deny
Allow from all
</Proxy>
This depends on some Apache modules to work. First off, mod_proxy, mod_proxy_http, mod_rewrite and mod_headers are needed to enable the base functionality. To use Kerberos authentication, as the example above uses, mod_auth_kerb is also required, along with a valid keytab, in this case put in /etc/keytabs/sogo.keytab.
SOGo configuration
Next is the main SOGo configuration file, located at /etc/sogo/sogo.conf:
{
/* ********************* Main SOGo configuration file **********************
* *
* Since the content of this file is a dictionary in OpenStep plist format, *
* the curly braces enclosing the body of the configuration are mandatory. *
* See the Installation Guide for details on the format. *
* *
* C and C++ style comments are supported. *
* *
* This example configuration contains only a subset of all available *
* configuration parameters. Please see the installation guide more details. *
* *
* ~sogo/GNUstep/Defaults/.GNUstepDefaults has precedence over this file, *
* make sure to move it away to avoid unwanted parameter overrides. *
* *
* **************************************************************************/
/* Database configuration (mysql:// or postgresql://) */
SOGoProfileURL = "mysql://sogo_user:<password>@localhost:5432/sogo/sogo_user_profile";
OCSFolderInfoURL = "mysql://sogo_user:<password>@localhost:5432/sogo/sogo_folder_info";
OCSSessionsFolderURL = "mysql://sogo_user:<password>@localhost:5432/sogo/sogo_sessions_folder";
/* Mail */
SOGoDraftsFolderName = Drafts;
SOGoSentFolderName = Sent;
SOGoTrashFolderName = Trash;
SOGoIMAPServer = imap://localhost;
//SOGoSieveServer = sieve://127.0.0.1:4190;
SOGoSMTPServer = 127.0.0.1;
SOGoMailDomain = <domain>;
SOGoMailingMechanism = smtp;
SOGoForceExternalLoginWithEmail = NO;
SOGoMailSpoolPath = /var/spool/sogo;
NGImap4ConnectionStringSeparator = "/";
SOGoAppointmentSendEMailNotifications = NO;
SOGoACLsSendEMailNotifications = NO;
//Set to NO to disable single sign-on from Apache
SOGoTrustProxyAuthentication = YES;
/* Authentication */
//SOGoPasswordChangeEnabled = YES;
/* LDAP authentication example */
SOGoUserSources = (
{
type = ldap;
CNFieldName = displayName;
IDFieldName = cn;
UIDFieldName = CN;
bindFields = "CN";
baseDN = "CN=Users,<base-dn>";
SearchFieldNames = (sn,displayName);
scope = SUB;
canAuthenticate = YES;
displayName = "Active Directory";
hostname = ldap://<ldap-server>:389;
id = directory;
isAddressBook = YES;
}
);
/* SQL authentication example */
/* These database columns MUST be present in the view/table:
* c_uid - will be used for authentication - it's the username or username@domain.tld)
* c_name - which can be identical to c_uid - will be used to uniquely identify entries
* c_password - password of the user, plain-text, md5 or sha encoded for now
* c_cn - the user's common name - such as "John Doe"
* mail - the user's mail address
* See the installation guide for more details
*/
//SOGoUserSources =
// (
// {
// type = sql;
// id = directory;
// viewURL = "postgresql://sogo:sogo@127.0.0.1:5432/sogo/sogo_view";
// canAuthenticate = YES;
// isAddressBook = YES;
// userPasswordAlgorithm = md5;
// }
// );
/* Web Interface */
SOGoPageTitle = SOGo;
SOGoVacationEnabled = YES;
SOGoForwardEnabled = YES;
SOGoSieveScriptsEnabled = YES;
/* General */
SOGoLanguage = English;
SOGoTimeZone = Europe/London;
SOGoCalendarDefaultRoles = (
PublicDAndTViewer,
ConfidentialDAndTViewer
);
//SOGoSuperUsernames = (sogo1, sogo2); //This is an array - keep the parens!
/* Debug */
//SoDebugBaseURL = YES;
//ImapDebugEnabled = YES;
//LDAPDebugEnabled = YES;
//SOGoDebugRequests = YES;
//PGDebugEnabled = YES;
//MySQL4DebugEnabled = YES;
//SOGoUIxDebugEnabled = YES;
//WODontZipResponse = YES;
//WOLogFile = /var/log/sogo/sogo.log;
}
Note that SOGo requires a MySQL database - one that we will configure in a minute. Set the password to something secure, but keep it for use in the next step. Set the various LDAP settings to match your configuration, and make sure to set SOGoMailDomain. If you are not using single sign-on, set SOGoTrustProxyAuthentication to NO. It is also possible to change the 'id' of the LDAP user source to whatever you wish, but keep the value handy for configuring the client later on.
MySQL
SOGo can use either a MySQL database or a PostgreSQL database. In this case, we are using MySQL. Set up a new user, called sogo_user, and assign it the password you set in the previous step. Next, create a database called 'sogo', and remember to assign the correct permissions to the database.
mysqladmin --password=<root-password> create sogo echo "GRANT ALL PRIVILEGES ON sogo.* TO 'sogo_user'@'localhost' IDENTIFIED BY '<sogo_user-password>';" | mysql --password=<root-password>
Client configuration
The main parts on the client that deal with Kerberos are logins, web access and email access. This section will assume you already have Kerberos logins working using libpam-krb5, as described in the Authentication section above.
Unlike the server, however, applications on the client can often be configured further than just using Kerberos. For example, Thunderbird can be configured to use contacts from the Samba4 LDAP server.
Firefox
Proxy, web servers, Moodle and other Kerberized web applications can use Kerberos for single sign-on authentication. If you have correctly configured the desired application as described in the Server Configuration section above, you now need to configure Firefox correctly. This can be done by going to 'about:config' in the address bar (and ignoring the warning about there being DRAGONS!) then searching for 'network.negotiate'. Then you need to make this modification:
network.negotiate-auth.trusted-uris | string | <server>.<domain>
For multiple servers, this field can be comma separated with the server names. Note that it must be the FQDN of the servers, unless 'network.negotiate-auth.allow-non-fqdn' has been set to 'true'. However, for a domain where each server is addressed using a static DNS domain (for example, a domain called testing.com with servers such as moodle.testing.com, www.testing.com etc), it is possible to just add that domain name into the list of trusted URIs, and Firefox will allow any server with that domain name to perform negotiate authentication. This modification can be put into prefs.js or user.js for real world applications.
Thunderbird
If Dovecot and Postfix are configured to use Kerberos credentials as described above, Thunderbird can authenticate against them. Similar to the Firefox configuration, 'network.negotiate-auth.trusted-uris' needs to be set up correctly to permit Thunderbird to perform negotiation.
Using the GUI, Thunderbird should pick up on the fact that your servers are using Kerberos authentication and will configure itself automatically based on that. Scripting it, however, isn't such an easy task. Add the following to a prefs.js or user.js:
user_pref('mail.identity.id1.useremail', '<email>');
user_pref('mail.identity.id1.valid', true);
user_pref('mail.identity.id1.fullName', '<full-name>');
user_pref('mail.identity.id1.smtpServer', 'smtp1');
user_pref('mail.identity.id1.archive_enabled', false);
user_pref('mail.identity.id1.draft_folder', 'imap://<username>@<mail-server>/Drafts');
user_pref('mail.identity.id1.drafts_folder_picker_mode', '0');
user_pref('mail.identity.id1.fcc_folder', 'imap://<username>@<mail-server>/Sent');
user_pref('mail.identity.id1.fcc_folder_picker_mode', '0');
user_pref('mail.identity.id1.stationery_folder', 'imap://<username>@<mail-server>/Templates');
user_pref('mail.identity.id1.tmpl_folder_picker_mode', '0');
user_pref('mail.server.server1.authMethod', 5);
user_pref('mail.server.server1.check_new_mail', true);
user_pref('mail.server.server1.hostname', '<mail-server>');
user_pref('mail.server.server1.login_at_startup', true);
user_pref('mail.server.server1.name', '<email>');
user_pref('mail.server.server1.socketType', 2);
user_pref('mail.server.server1.storeContractID', '@mozilla.org/msgstore/berkeleystore;1');
user_pref('mail.server.server1.type', 'imap');
user_pref('mail.server.server1.userName', '<username>');
user_pref('mail.server.server1.using_subscription', false);
user_pref('mail.smtpserver.smtp1.authMethod', 5);
user_pref('mail.smtpserver.smtp1.hostname', '<mail-server>');
user_pref('mail.smtpserver.smtp1.port', 25);
user_pref('mail.smtpserver.smtp1.try_ssl', 2);
user_pref('mail.smtpserver.smtp1.username', '<username>');
user_pref('mail.smtpservers', 'smtp1');
The parts to be replaced are fairly self-explanantory, such as <email>, <full-name>, <username> and <mail-server>. Note that <username>@<mail-server> is not the same as <email>, and must use the FQDN of the mail server itself. <full-name> can be configured to whatever you want your user to be identified as, but the full name is usually sufficient.
Also note that this configuration uses STARTTLS for both IMAP and SMTP. To change it, 'mail.server.server1.socketType' and 'mail.smtpserver.smtp1.try_ssl' can be modified to switch between no encrpytion (0), STARTTLS (2) and SSL (3). I *think* a value of '1' is something along the lines of try STARTTLS if available.
LDAP Contacts
Apart from Kerberos authentication, Thunderbird can also use an LDAP server for contacts. Unfortunately the defualt mappings in Thunderbird are not suitable for Samba4's LDAP schemas (or at least, not the way we have set it up), so a little bit of fine-tuning is required to make everything work.
Into prefs.js or user.js, append:
user_pref('ldap_2.autoComplete.directoryServer', 'ldap_2.servers.Karoshi');
user_pref('ldap_2.autoComplete.useDirectory', true);
user_pref('ldap_2.servers.Karoshi.auth.dn', );
user_pref('ldap_2.servers.Karoshi.auth.saslmech', );
user_pref('ldap_2.servers.Karoshi.description', '<name>');
user_pref('ldap_2.servers.Karoshi.filename', 'ldap.mab');
user_pref('ldap_2.servers.Karoshi.maxHits', 100);
user_pref('ldap_2.servers.Karoshi.uri', 'ldap://<server>/<base-dn>??sub?(&(mail=*)(displayName=*))');
user_pref('ldap_2.servers.Karoshi.autoComplete.commentFormat', '[displayName]');
user_pref('ldap_2.servers.Karoshi.autoComplete.nameFormat', '[displayName]');
user_pref('ldap_2.servers.Karoshi.autoComplete.filterFormat', '(|(mail=*%v*)(displayName=*%v*)(givenName=*%v*)(sn=*%v*)(cn=*%v*))');
user_pref('ldap_2.servers.default.attrmap.DisplayName', 'displayname,cn,commonname');
Replace <name> with the canonical name that you want to show the user - a good name could be the domain name itself, such as 'new.test.com'. It makes it very clear this is the address book for the domain. The last 4 lines tune the attribute mappings that Thunderbird uses. Thunderbird by default tries to match the 'cn' of an entry in LDAP to the user's full name, but very often that is the username instead of the full name (to prevent problems with two users with the same name). Thus 'commentFormat', 'nameFormat' and 'attrmap.DisplayName' are all changed to use 'displayName' instead, which is generally filled with the user's full name in Samba4.
SOGo
Instead of configuring LDAP contacts and server settings manually, SOGo can do it all. First, download the Lightning extension and install it into Thunderbird. You will need to download the SOGo Connector and Integrator from [1]. Install the Connector directly, but a bit of work needs to be done on the Integrator. Extract it (it is a standard ZIP file with a funny extension) so that you can change some files.
First on the list is chrome/content/extensions.rdf. Edit it so that it points to your server:
<Seq about="http://inverse.ca/sogo-integrator/extensions" isi:updateURL="https://<server>/SOGo.plugins/updates.php?plugin=%ITEM_ID%&version=%ITEM_VERSION%&platform=%PLATFORM%">
Note that you should change SOGo.plugins based upon where you want this magical updates.php script to reside. At this current time we haven't quite worked out how to configure automatic updating, so just set it to something that you know won't conflict. This settings should match the 'x-webobjects-server-url' setting on the server side in the Apache configuration.
Next, modify defaults/preferences/site.js:
pref("sogo-integrator.autocomplete.server.urlid", "directory");
This should match the 'id' that you configured in sogo.conf on the server. If you left it as the example shows, then it is using 'directory'. It may also be beneficial at this point to modify the calendar timezone:
pref("calendar.timezone.local", "/mozilla.org/20070129_1/Europe/London");
Now we are done with modifying the Integrator, you need to package it back up into an XPI file. Remember to package the root files - that is: chrome, custom, defaults, chrome.manifest etc., not the directory that you extracted the files into. Again, just ZIP the files up, then if necessary rename the archive to a .xpi.
Install your new custom Integrator into Thunderbird, then restart Thunderbird, make sure your email account is configured correctly, and watch as the Integrator configures your address book and calendar automatically.
Client host keytab authentication
Some some applications, it may be required to get the underlying client to authenticate to the server. If a client is joined to the domain, and has a keytab, then it can do this.
Server
In this example we will use a CGI page using Apache Kerberos authentication, very similar to #Apache, with the Kerberos settings defined in a .htaccess file. The server will require mod_auth_kerb and mod_authnz_ldap.
Order deny,allow Deny from All AuthName "<authname>" AuthType Kerberos Krb5Keytab <keytab-location> KrbMethodNegotiate on KrbMethodK5Passwd off KrbAuthoritative on KrbLocalUserMapping on AuthLDAPUrl ldap://<server>/CN=Computers,<ldap-base>?sAMAccountName Require valid-user Require ldap-attribute objectClass=computer Satisfy any
With the required variables replaced with the values suitable for your network, this will authenticate any 'users' (in Active Directory computers are treated as users) that are in CN=Computers and have the objectClass of 'computer'. It matches principal names in the format '<HOSTNAME>$', so for example a computer with the hostname 'lxtest' will have a principal name of 'LXTEST$'. This is why sAMAccountName is used to match the account. In a CGI script, the $REMOTE_USER variable will be set to this principal name:
#!/bin/bash echo "Content-type: text/plain" echo echo "$REMOTE_USER @ $REMOTE_ADDR was granted access"
Client
On the client side, a valid keytab is required, containing the principal name '<hostname>$'. This can be generated by the 'net ads join' command, if a specific option is defined in smb.conf:
keytab method = system keytab
When the system is joined to the domain, a keytab containing the required principal name (along with the host SPNs for that machine, perhaps useful?) will be generated as /etc/krb5.keytab. We can use this to authenticate the client to the server and get a ticket-granting ticket, which will let us authenticate to any other machines using Kerberos.
As a user with permission to access the keytab file (usually only root), run the following to get a ticket:
kinit -kt /etc/krb5.keytab '<HOSTNAME>$'
It is important to note that the hostname part must be capitalised. The result of this command is a ticket-granting ticket, exactly the same as if a user typed in their password into kinit to start using Kerberos, or if a user authenticated via pam_krb5. This ticket can be used to authenticate to any Kerberos-enabled services on the network.
One example use of this is to send data to a CGI script, from the command line or a script. After the 'kinit' command has been run without errors, a 'curl' command can be used to 'get' the CGI page on a server:
curl --negotiate -u : <page>
In the case of the web management, 'curl' will complain about the self-signed SSL certificate being used (we will add the CA to curl's configuration when this is implemented), so the option '-k' can be used to ignore SSL errors. Hopefully you will see the CGI page without any problems.
SSH on Clients
It is possible to get a client set up with an SSH server to accept Kerberos tickets as an authentication source. For this to work, the 'server' client needs to have a keytab produced when the machine joins the domain, using the line
keytab method = system keytab
in smb.conf. On the client, the following option needs to be set in /etc/ssh/sshd_config:
GSSAPIAuthentication yes
With the existence of a valid host keytab in /etc/krb5.keytab, this will allow any client with a valid Kerberos ticket to authenticate to this client using Kerberos:
ssh -K <host>
'<host>' must be the FQDN of the machine, and the reverse DNS must either be correct or non-existent (as with all Kerberos interactions).
Note however that this will usually by default give you an SSH session with no Kerberos ticket-granting ticket, so the user will not be able to authenticate using Kerberos to any other services from that machine. This can be fixed by ensuring that the 'client' client has a forwardable Kerberos ticket, and that SSH correctly tries to forward the ticket. In /etc/krb5.conf, the following needs to be added:
[libdefaults]
forwardable = true
Any 'kinit's or authentications using a Kerberos PAM module will now get a forwardable Kerberos ticket, that can be passed on to the 'server' client with Kerberos delegation. If the connection is started with '-K' then delegation is automatically attempted, but options can be set for this to happen automatically. However, SSH will attempt to delegate the Kerberos ticket to any machine that asks for it, so the following configuration limits Kerberos ticket delegation to hosts on the same DNS domain. It can be put in /etc/ssh/ssh_config for global settings, or ~/.ssh/config for one user. Note that it must be above any defaults, since the first configuration option that is set is used:
Host *.<domain>
GSSAPIDelegateCredentials yes
Unfortunately this means that this will only work when connecting to a machine using the FQDN - non-FQDNs will not match the 'Host' part and won't trigger these settings. It is not recommended to set this option for all hosts, since there is a chance that SSH will delegate your ticket to a malicious machine.