{"id":1584,"date":"2017-07-10T11:02:21","date_gmt":"2017-07-10T16:02:21","guid":{"rendered":"http:\/\/lisa.rushworth.us\/?p=1584"},"modified":"2021-04-19T23:33:38","modified_gmt":"2021-04-20T04:33:38","slug":"kerberos-authentication-on-tomcat","status":"publish","type":"post","link":"https:\/\/www.rushworth.us\/lisa\/?p=1584","title":{"rendered":"Kerberos Authentication on Tomcat"},"content":{"rendered":"<p>I finally got around to testing out TomCat 8 and setting up Kerberos authentication for a &#8220;single sign-on&#8221; experience (i.e. it re-uses the domain logon Kerberos token to authenticate users). This was all done in a docker image, so the config files can be stashed and re-used by anyone with Docker.<\/p>\n<p>First you need an account &#8211; on the account properties page, the DES encryption needs to be unchecked and the two AES ones need to be checked. The account then needs to have a service principal name mapped to it. That name will be based on the URL used to access the site. In my case, my site is http:\/\/lisa.example.com:8080 (SPNs don&#8217;t mind http\/https or port numbers) so my SPN is HTTP\/lisa.example.com &#8230; to set the SPN, run<\/p>\n<pre>setspn -A HTTP\/lisa.example.com sAMAccountNameOfMyNewlyCreatedAccount\r\n<\/pre>\n<p>Then generate the keytab:<\/p>\n<pre>ktpass \/out .\\lisa.example.com.keytab \/mapuser sAMAccountNameOfMyNewlyCreatedAccount@EXAMPLE.COM \/princ HTTP\/lisa.rushworth.us@EXAMPLE.COM \/pass P@ssw0rdG03sH3r3\r\n<\/pre>\n<p>** Note about keytabs &#8211; there is a KVNO (key version number) associated with a keytab file. When security-related attributes on the account are changed, the KVNO is incremented. Aaaand you need a new keytab. This means you need to be able to get a new keytab if you plan on changing the account password, but it also means that tweaking account settings\u00a0<em>can<\/em> render your keytab useless. Get the account all sorted (check off password never expires if that&#8217;s what you want, check off user cannot change password, etc) and\u00a0<em>then<\/em> generate the keytab.<\/p>\n<p>While you&#8217;re working on getting the SPN and keytab stuff sorted, get docker installed and running on your box. I use <a href=\"https:\/\/store.docker.com\/editions\/community\/docker-ce-desktop-windows\" target=\"_blank\" rel=\"noopener\">Docker CE<\/a> (free) on my Windows laptop, and I&#8217;ve had to disable the firewall to allow access from external clients. I would\u00a0<em>expect<\/em> a rule (esp one allowing anything to make an inbound connection to 8080\/tcp!) would sort it, but I&#8217;ve always had the port show as filtered until the firewall is turned off. YMMV.<\/p>\n<p>I create a folder for files mapped into docker containers (i.e. c:\\docker) and sub-folders for each specific container. All of the files from\u00a0<a href=\"http:\/\/lisa.rushworth.us\/?attachment_id=1586\" rel=\"attachment wp-att-1586\">TomcatKerberosConfigFiles<\/a>\u00a0are unzipped into that folder. The\u00a0test website is named lisa.rushworth.us and is either set up in DNS or added to c:\\windows\\system32\\drivers\\etc\\hosts on the client(s) that will access the site. And, of course, there&#8217;s a client machine somewhere logged onto the domain. You are going to need to tweak my config files for your domain.<\/p>\n<p>In jaas.conf &#8212; I have debug on. Good for testing and playing around, bad for production use. Also you&#8217;ll need\u00a0<em>your<\/em> SPN and keytab file name<\/p>\n<pre>principal=\"HTTP\/lisa.example.com@EXAMPLE.COM\"\r\nkeyTab=\"\/usr\/local\/tomcat\/conf\/lisa.example.com.keytab\"\r\n<\/pre>\n<p>In krb5.conf &#8212; the encryption is about the only thing you can keep. Use your hostnames and domain name (REALM). If you have multiple domain controllers, you can have more than one &#8220;kdc = &#8221; line in the realms.<\/p>\n<pre>[libdefaults]\r\ndefault_realm = EXAMPLE.COM\r\ndefault_keytab_name = \/usr\/local\/tomcat\/conf\/lisa.rushworth.us.keytab\r\ndefault_tkt_enctypes = aes128-cts rc4-hmac des3-cbc-sha1 des-cbc-md5 des-cbc-crc\r\ndefault_tgs_enctypes = aes128-cts rc4-hmac des3-cbc-sha1 des-cbc-md5 des-cbc-crc\r\npermitted_enctypes = aes128-cts rc4-hmac des3-cbc-sha1 des-cbc-md5 des-cbc-crc\r\nforwardable=true\r\n\r\n[realms]\r\nRUSHWORTH.US = {\r\nkdc = exchange01.example.com:88\r\nmaster_kdc = exchange01.example.com:88\r\nadmin_server = exchange01.example.com:88\r\n}\r\n\r\n[domain_realm]\r\nexample.com= EXAMPLE.COM\r\n.example.com= EXAMPLE.COM\r\n<\/pre>\n<p>In web.xml &#8211; Roles may need to be sorted around (I&#8217;m not much of a TomCat person, LMGTFY if you want to do something with roles). Either way, the realm needs to be changed to yours<\/p>\n<pre>&lt;realm-name&gt;EXAMPLE.COM&lt;\/realm-name&gt;\r\n<\/pre>\n<p>Once Docker is running and the files are updated with your domain info, install the tomcat:8.0 image from the default repository. Start the container mapping all of the custom config files where they go:<\/p>\n<pre>docker run -detach --publish 8080:8080 --name tomcat8 --restart always -v \/c\/docker\/tomcat8\/tomcat-users.xml:\/usr\/local\/tomcat\/conf\/tomcat-users.xml:ro -v \/c\/docker\/tomcat8\/lisa.example.com.keytab:\/usr\/local\/tomcat\/conf\/lisa.example.com.keytab:ro -v \/c\/docker\/tomcat8\/krb5.conf:\/usr\/local\/tomcat\/conf\/krb5.conf:ro -v \/c\/docker\/tomcat8\/jaas.conf:\/usr\/local\/tomcat\/conf\/jaas.conf:ro -v \/c\/docker\/tomcat8\/web.xml:\/usr\/local\/tomcat\/webapps\/examples\/WEB-INF\/web.xml:ro -v \/c\/docker\/tomcat8\/context.xml:\/usr\/local\/tomcat\/webapps\/examples\/WEB-INF\/context.xml:ro -v \/c\/docker\/tomcat8\/logging.properties:\/usr\/local\/tomcat\/conf\/logging.properties:ro -v \/c\/docker\/tomcat8\/spnego-r9.jar:\/usr\/local\/tomcat\/lib\/spnego-r9.jar:ro -v \/c\/docker\/tomcat8\/login.conf:\/usr\/local\/tomcat\/conf\/login.conf:ro -v \/c\/docker\/tomcat8\/testAuth.jsp:\/usr\/local\/tomcat\/webapps\/examples\/testAuth.jsp:ro tomcat:8.0\r\n<\/pre>\n<p>A couple of useful things about Docker &#8212; the container ID is useful<\/p>\n<pre>C:\\docker\\tomcat8&gt;docker ps\r\nCONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES\r\n<strong>4e06b32e1ca8<\/strong> tomcat:8.0 \"catalina.sh run\" 12 minutes ago Up 12 minutes 0.0.0.0:8080-&gt;8080\/tcp, 0.0.0.0:8888-&gt;8080\/tcp tomcat8\r\n<\/pre>\n<p>But most commands seem to let you use the &#8216;friendly&#8217; name you ascribed to the container. Running &#8220;docker inspect&#8221; will give you details about the container &#8211; including its IP address. I&#8217;ve found different images use different settings: some map to localhost on my box, some get an IP address within my DHCP range.<\/p>\n<pre>C:\\docker\\tomcat&gt;docker inspect tomcat8 | grep IPAddress\r\n\"SecondaryIPAddresses\": null,\r\n\"IPAddress\": \"172.17.0.2\",\r\n\"IPAddress\": \"172.17.0.2\",\r\n<\/pre>\n<p>Since this is an image that maps to localhost on my box, I need the lisa.example.com hostname to resolve to my laptop&#8217;s IP address. For simplicity, I did this by editing the c:\\windows\\system32\\drivers\\etc\\hosts file.<\/p>\n<p>Shell into the container:<\/p>\n<pre>docker exec -it tomcat8 bash\r\n<\/pre>\n<p>Update your packages and install the kerberos client utilities:<\/p>\n<pre>root@4e06b32e1ca8:\/usr\/local\/tomcat\/conf# apt-get update\r\nroot@4e06b32e1ca8:\/usr\/local\/tomcat\/conf# apt-get install krb5-user\r\n<\/pre>\n<p>Then test that your keytab is working:<\/p>\n<pre>root@4e06b32e1ca8:\/usr\/local\/tomcat\/conf# kinit -k -t .\/lisa.example.com.keytab HTTP\/lisa.example.com@EXAMPLE.COM\r\nroot@4e06b32e1ca8:\/usr\/local\/tomcat\/conf# klist\r\nTicket cache: FILE:\/tmp\/krb5cc_0\r\nDefault principal: HTTP\/lisa.example.com@EXAMPLE.COM\r\n\r\nValid starting Expires Service principal\r\n07\/08\/2017 18:27:38 07\/09\/2017 04:27:38 krbtgt\/EXAMPLE.COM@EXAMPLE.COM\r\nrenew until 07\/09\/2017 18:27:38\r\n<\/pre>\n<p>Assuming you don&#8217;t get errors authenticating using the Kerberos client utilities, try accessing the TomCat site. I&#8217;ve added a testAuth.jsp file to the examples webapp &#8211; it shows the logon method, user name, and what roles they have:<\/p>\n<p><a href=\"http:\/\/lisa.rushworth.us\/?attachment_id=1587\" rel=\"attachment wp-att-1587\"><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-full wp-image-1587\" src=\"http:\/\/lisa.rushworth.us\/wp-content\/uploads\/2017\/09\/TomcatWithKerberosAuth.png\" alt=\"\" width=\"819\" height=\"421\" srcset=\"https:\/\/www.rushworth.us\/lisa\/wp-content\/uploads\/2017\/09\/TomcatWithKerberosAuth.png 819w, https:\/\/www.rushworth.us\/lisa\/wp-content\/uploads\/2017\/09\/TomcatWithKerberosAuth-300x154.png 300w, https:\/\/www.rushworth.us\/lisa\/wp-content\/uploads\/2017\/09\/TomcatWithKerberosAuth-768x395.png 768w\" sizes=\"auto, (max-width: 819px) 100vw, 819px\" \/><\/a><\/p>\n<ul>\n<li>Log files are in\u00a0\/usr\/local\/tomcat\/logs<\/li>\n<li>If you aren&#8217;t using Integrated Authentication for other stuff, you may need to <a href=\"https:\/\/ping.force.com\/Support\/PingFederate\/Integrations\/How-to-configure-supported-browsers-for-Kerberos-NTLM\" target=\"_blank\" rel=\"noopener\">configure your browser to pass along your Kerberos token<\/a>.<\/li>\n<li>If you are getting an error in your Catalina log that says:<\/li>\n<\/ul>\n<pre>09-Jul-2017 15:42:55.734 FINE [http-apr-8080-exec-1] org.apache.catalina.authenticator.SpnegoAuthenticator.authenticate Unable to login as the service principal\r\njava.security.PrivilegedActionException: GSSException: Defective token detected (Mechanism level: GSSHeader did not find the right tag)\r\n<\/pre>\n<p>Verify that your SPN is set to the same name being used to access the site. I&#8217;m not sure why the configured service principal name doesn&#8217;t supersede the user-entered hostname. But I got nothing but auth failures until I actually entered the hostname into my hosts file and used an address that matches the service principal name.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>I finally got around to testing out TomCat 8 and setting up Kerberos authentication for a &#8220;single sign-on&#8221; experience (i.e. it re-uses the domain logon Kerberos token to authenticate users). This was all done in a docker image, so the config files can be stashed and re-used by anyone with Docker. First you need an &hellip;<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[30],"tags":[231,328,326,327],"class_list":["post-1584","post","type-post","status-publish","format-standard","hentry","category-system-administration","tag-docker","tag-kerberos","tag-sso","tag-tomcat"],"_links":{"self":[{"href":"https:\/\/www.rushworth.us\/lisa\/index.php?rest_route=\/wp\/v2\/posts\/1584","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.rushworth.us\/lisa\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.rushworth.us\/lisa\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.rushworth.us\/lisa\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.rushworth.us\/lisa\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=1584"}],"version-history":[{"count":5,"href":"https:\/\/www.rushworth.us\/lisa\/index.php?rest_route=\/wp\/v2\/posts\/1584\/revisions"}],"predecessor-version":[{"id":7720,"href":"https:\/\/www.rushworth.us\/lisa\/index.php?rest_route=\/wp\/v2\/posts\/1584\/revisions\/7720"}],"wp:attachment":[{"href":"https:\/\/www.rushworth.us\/lisa\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=1584"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.rushworth.us\/lisa\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=1584"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.rushworth.us\/lisa\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=1584"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}