<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Apache &#8211; Jeffrey Bostoen</title>
	<atom:link href="https://jeffreybostoen.be/tag/apache/feed/" rel="self" type="application/rss+xml" />
	<link>https://jeffreybostoen.be</link>
	<description>Freelance iTop consultant and developer - IT Consulting - Official iTop Partner</description>
	<lastBuildDate>Sun, 11 Feb 2024 20:47:42 +0000</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=6.9.4</generator>

<image>
	<url>https://jeffreybostoen.be/wp-content/uploads/2023/01/cropped-android-chrome-512x512-1-32x32.png</url>
	<title>Apache &#8211; Jeffrey Bostoen</title>
	<link>https://jeffreybostoen.be</link>
	<width>32</width>
	<height>32</height>
</image> 
	<item>
		<title>Kerberos authentication against Apache2</title>
		<link>https://jeffreybostoen.be/kerberos-authentication-against-apache2/</link>
		
		<dc:creator><![CDATA[Jeffrey Bostoen]]></dc:creator>
		<pubDate>Sun, 11 Feb 2024 20:47:41 +0000</pubDate>
				<category><![CDATA[Uncategorized]]></category>
		<category><![CDATA[Apache]]></category>
		<category><![CDATA[GSSAPI]]></category>
		<category><![CDATA[itop]]></category>
		<category><![CDATA[Kerberos]]></category>
		<category><![CDATA[keytab]]></category>
		<category><![CDATA[kinit]]></category>
		<category><![CDATA[krb5]]></category>
		<category><![CDATA[ktpass]]></category>
		<category><![CDATA[Single Sign-On]]></category>
		<category><![CDATA[SSO]]></category>
		<guid isPermaLink="false">https://jeffreybostoen.be/?p=1084</guid>

					<description><![CDATA[It&#8217;s possible to require Kerberos authentication before allowing access to a web application, let&#8217;s say served by an Apache2 web server. In practice, for one of my favorite web applications (iTop); it means internal domain users could be automatically authenticated. They would not need to provide their credentials and there&#8217;s no need to configure an [&#8230;]]]></description>
										<content:encoded><![CDATA[
<p>It&#8217;s possible to require <strong>Kerberos </strong>authentication before allowing access to a web application, let&#8217;s say served by an <strong>Apache2 </strong>web server.</p>



<p>In practice, for one of my favorite web applications (iTop); it means <strong>internal domain users</strong> could be <strong>automatically authenticated</strong>. They would not need to provide their credentials and there&#8217;s no need to configure an external Identity Provider using SAML or OpenID.</p>



<p>I&#8217;ve initially first set it up for a WordPress plugin called &#8220;Next Active Directory integration&#8221;, to allow single-sign on to the WordPress. Later on, I also configured iTop to work this way.</p>



<h2 class="wp-block-heading">Prerequisites</h2>



<p>For this post, we make the following assumptions about the infrastructure:</p>



<ul class="wp-block-list">
<li>There is an <strong>Active Directory</strong> in place in a typical <strong>Microsoft Windows</strong> enterprise environment.</li>



<li>There is an <strong>Apache2</strong> web server &#8211; using a <strong>Debian-based</strong> operating system (I&#8217;m personally using <strong>Ubuntu</strong>).<br>This webserver can connect to the primary domain controller of the Active Directory environment. <br>This domain controller should allow <strong>inbound</strong> connections coming from the webserver on both <strong>UDP and TCP port 88.</strong></li>
</ul>



<p>Note that I try to follow best-practices and security standards which are in place when writing this post, in February 2024. If you come here after several years, the instructions or recommendations may be slightly different.</p>



<h2 class="wp-block-heading">Reference</h2>



<p>In this post, we&#8217;ll be using:</p>



<figure class="wp-block-table"><table><tbody><tr><td>YOURDOMAIN.ORG</td><td>This is the Fully Qualified Domain Name (FQDN) of your Windows domain.</td></tr><tr><td>webserver.yourdomain.org</td><td>This is the name of your webserver. Note: We did not domain-join this webserver in any specific way.</td></tr><tr><td>krb5.webserver01</td><td>The user name of the Active Directory user account that will be used for the service.</td></tr><tr><td>DontJustCopyThis*1234</td><td>The password of the Active Directory user account that will be used for the service.</td></tr></tbody></table></figure>



<h2 class="wp-block-heading">Active Directory configuration</h2>



<p>Let&#8217;s prepare the Active Directory side. What we need here, is a <strong>regular</strong> user account. This user does not need to belong to any privileged groups. In fact, I recommend against it. Ideally, this should be a <strong>dedicated</strong> account.</p>



<ol class="wp-block-list">
<li>Open Active Directory Users and Computers.</li>



<li>Create a new user. Give it a decent name ( e.g. krb5.webserver01 ) and secure password.<br>In the rest of this post, we&#8217;ll be using <strong>krb5.webserver01</strong> and <strong>DontJustCopyThis*1234 </strong>as password.</li>



<li>Set the password to never expire.</li>



<li>In the properties, under &#8220;Account&#8221;, check these boxes:
<ul class="wp-block-list">
<li>User cannot change password.</li>



<li>Password never expires.</li>



<li>This account supports Kerberos AES 256 bit encryption. (Scroll down a bit, it&#8217;s also in the list of Account options).</li>
</ul>
</li>
</ol>



<figure class="wp-block-image size-full is-resized"><img fetchpriority="high" decoding="async" width="616" height="835" src="https://jeffreybostoen.be/wp-content/uploads/2024/02/image.png" alt="" class="wp-image-1089" style="width:375px;height:auto" srcset="https://jeffreybostoen.be/wp-content/uploads/2024/02/image.png 616w, https://jeffreybostoen.be/wp-content/uploads/2024/02/image-221x300.png 221w" sizes="(max-width: 616px) 100vw, 616px" /></figure>



<p>We&#8217;ll also need to generate a so-called <strong>keytab</strong> using the <strong>ktpass</strong> command. There are some variants of this instruction on the Internet, with some for example allowing all cryptography algorithms. I want to keep it as secure as possible, and explicitly want to restrict it to only support AES-256. This may lead to an additional challenge which gets covered below.</p>



<p>I suggest executing this in an <strong>elevated (Run as administrator) command prompt</strong> on a domain controller:</p>



<pre class="wp-block-code"><code>ktpass -princ HTTP/webserver.yourdomain.org@YOURDOMAIN.ORG -mapuser krb5.webserver01@YOURDOMAIN.ORG -pass DontJustCopyThis*1234 -ptype KRB5_NT_PRINCIPAL -kvno 0 -crypto AES256-SHA1 -out krb5.webserver01.keytab</code></pre>



<p><strong>Q: What is a keytab file?</strong><br>A: A keytab is a file containing pairs of Kerberos principals and encrypted keys that are derived from the Kerberos password. You can use this file to log on to Kerberos without being prompted for a password.</p>



<p><strong>Q: Can I use a &#8220;service account&#8221; for this?</strong><br>A:  I looked into <strong>service accounts</strong> (introduced in Microsoft Windows Server 2016),<strong> </strong> but that was a dead end for me.</p>



<p>You can verify if the proper SPN (Service Principal Name) is set. In Active Directory Users and Computers, under View, make sure Advanced features is checked.</p>



<p>Then, when viewing the properties of the user account, you should see the HTTP/webserver.yourdomain.org@YOURDOMAIN.ORG value in the <strong>Attribute Editor</strong> tab for <strong>servicePrincipalName</strong>.</p>



<figure class="wp-block-image size-full is-resized"><img decoding="async" width="616" height="833" src="https://jeffreybostoen.be/wp-content/uploads/2024/02/image-1.png" alt="" class="wp-image-1090" style="width:369px;height:auto" srcset="https://jeffreybostoen.be/wp-content/uploads/2024/02/image-1.png 616w, https://jeffreybostoen.be/wp-content/uploads/2024/02/image-1-222x300.png 222w" sizes="(max-width: 616px) 100vw, 616px" /></figure>



<h2 class="wp-block-heading">Kerberos configuration (webserver)</h2>



<p>Time to configure the server.</p>



<pre class="wp-block-code"><code>apt install krb5-user</code></pre>



<p>Now, take your favorite text editor to change the configuration file <strong>/etc/krb5.conf </strong>.<br>Under the <strong>[realms]</strong> section, you&#8217;ll want to add your own Active Directory domain in uppercase.</p>



<pre class="wp-block-code"><code>&#91;realms]
YOURDOMAIN.ORG = {

	# Specify at least one Kerberos Domain Controller (KDC).
	# Usually, your primary domain controller acts as a Kerberos Domain Controller.
	# If you want to specify additional KDCs, just enter one line like this for each KDC:
	kdc = 10.1.10.1
	
	# Admin server. In most setups, just point to the primary Domain Controller.
	admin_server = 10.1.10.1

	# AES256 made things a bit more tricky. To avoid trying deprecated cryptography such as RC4-HMAC and only use AES256:
	default_tkt_enctypes = aes256-cts-hmac-sha1-96 rc4-hmac
	default_tgs_enctypes = aes256-cts-hmac-sha1-96  rc4-hmac
	permitted_enctypes = aes256-cts-hmac-sha1-96 rc4-hmac
}
</code></pre>



<p>Let&#8217;s put this configuration to the test. On the Linux webserver, you can now try the command below. Note that this is <strong>NOT</strong> our krb5.webserver01 user; but just another regular user account!</p>



<pre class="wp-block-code"><code>kinit your-user@YOURDOMAIN.ORG</code></pre>



<p>You won&#8217;t see much if everything goes right. If something goes wrong, you&#8217;d see an error message.<br>For example:</p>



<ul class="wp-block-list">
<li><strong>kinit: Client &#8216;nonexistinguser@YOURDOMAIN.ORG&#8217; not found in Kerberos database while getting initial credentials</strong>. &#8211; After entering an incorrect username.</li>



<li><strong>kinit: Password incorrect while getting initial credentials.</strong> &#8211; After entering an incorrect password.</li>
</ul>



<p>To truly validate, run:</p>



<pre class="wp-block-code"><code>klist</code></pre>



<p>This should output info about the ticket cache such as the default principal, two timestamps to indicate during which period the ticket will be valid, and a service principal such as <strong>krbtgt/YOURDOMAIN.ORG@YOURDOMAIN.ORG</strong> .</p>



<p><strong>Q: What is this krbtgt string?</strong><br>A: KRB = Kerberos, TGT = Ticket Granting Ticket. KRBTGT is a default account that exists in all Windows domains. It is meant to act as a service account (specifically for the KDC = Key Distribution Center) for domain controllers.</p>



<p>To clear the credentials, run:</p>



<pre class="wp-block-code"><code>kdestroy</code></pre>



<h2 class="wp-block-heading">Apache2 configuration (webserver)</h2>



<p>We&#8217;ll assume you already have your Apache2 up and running; with SSL enabled.</p>



<p>We&#8217;ll be using this module: <a href="https://github.com/gssapi/mod_auth_gssapi">GitHub &#8211; gssapi/mod_auth_gssapi: GSSAPI Negotiate module for Apache</a> . <br>Let&#8217;s first get this concept working. I do recommend reading some of the documentation after we manage to pull this off, as it also contains some other options (I recommend reading up on GssapiUseSessions ).</p>



<p>Anyhow, we&#8217;ll need to install this module:</p>



<pre class="wp-block-code"><code>apt install libapache2-mod-auth-gssapi</code></pre>



<p>Now, for the sake of my implementation, I personally had a virtual host configured on port 443. The server name (host header) was webserver.yourdomain.org .</p>



<p>I put these settings in my location block (I only had one):</p>



<pre class="wp-block-code"><code>&lt;Location/>

	AuthType GSSAPI
	AuthName "Kerberos Authentication"

	# The keytab file that was generated before.
	GssapiCredStore keytab:/privkeytab/krb5.webserver01.keytab
	GssapiAllowedMech krb5
	GssapiBasicAuth Off
	
	# Play around with this setting as needed.
	# "On" means the remote user would be identified as "someuser", with "Off" it would be "someuser@YOURDOMAIN.ORG".
	GssapiLocalName Off

	# Users should not be able to transmit sensitive data over non-SSL/TLS connections.
	GssapiSSLonly On

	# Only accept users who are able to authenticate.
	require valid-user

&lt;/Location></code></pre>



<p>As you see, we need to transfer the krb5.webserver01.keytab file that we generated on the Windows domain controller to our Linux webserver.</p>



<p>In this example, I&#8217;ve transferred it to the <strong>/privkeytab</strong> folder. Now, make sure the user account linked to Apache 2.4 (often <strong>www-data</strong>) has permissions to use this:</p>



<pre class="wp-block-code"><code>chown -R www-data:www-data /privkeytab</code></pre>



<p>Additionally, don&#8217;t forget to at least reload the Apache configuration. Although, if possible, I always perform a restart instead for this.</p>



<pre class="wp-block-code"><code>service apache2 restart &amp;&amp; service apache2 status</code></pre>



<p>Hint: If you have PHP installed, just make a simple PHP file which can echo our &#8220;remote user&#8221; variable later on. <strong>$_SERVER[&#8216;REMOTE_USER&#8217;]</strong> is the variable where the web application will be able to grab the username from.</p>



<pre class="wp-block-code"><code>&lt;?php
	echo 'Identified remote user: "'.$_SERVER&#91;'REMOTE_USER'].'"';</code></pre>



<h2 class="wp-block-heading">Browser configuration</h2>



<p>Finally, the browser configuration. It&#8217;s commonly referred to as <strong>SPNEGO</strong> (Simple and Protected GSSAPI Negotiation Mechanism).</p>



<p>Below I&#8217;ll describe the steps to enable it on an individual machines. If you&#8217;re a system administrator; consider using <strong>group policies</strong> to deploy these settings to all your domain users and machines.</p>



<p>If you want to perform a quick test, navigate to the PHP file you created earlier. You should <strong>NOT</strong> be prompted for authentication. You should be automatically signed in. Your username should automatically be shown.</p>



<h3 class="wp-block-heading">Microsoft Edge and Google Chrome</h3>



<p>Open good old <strong>iexplore.exe </strong>. </p>



<ol class="wp-block-list">
<li>Go to Tools > Internet Options.</li>



<li>In the <strong>Security</strong> tab, select <strong>Local Intranet</strong>.
<ol class="wp-block-list">
<li>Click <strong>[Sites].</strong> </li>



<li>Add: <strong>https://webserver01.yourdomain.org</strong> (or https://*.yourdomain.org if you want to be more lenient ).</li>



<li>Close the window.</li>



<li>Click <strong>[Custom level&#8230;]</strong>.</li>



<li>Scroll to the bottom, find <strong>User Authentication > Logon</strong>.</li>



<li>Select <strong>Automatic logon with current user name and password.</strong></li>
</ol>
</li>



<li>Save and close all.<strong> </strong></li>
</ol>



<h3 class="wp-block-heading">Mozilla Firefox</h3>



<ol class="wp-block-list">
<li>In the address bar, type <strong>about:config</strong> and press [Enter].</li>



<li>In the filter/search field, enter <strong>negotiate.</strong></li>



<li>Find the setting named <strong>network.negotiate-auth.trusted-uris</strong>.</li>



<li>Make sure it includes https://webserver01.yourdomain.org and save.</li>



<li>Restart Mozilla Firefox.</li>
</ol>



<p></p>



<h2 class="wp-block-heading">iTop configuration</h2>



<p>As a bonus, I&#8217;ll show you how to configure iTop to accept these kind of logons. If you want additional security, you can still check my &#8220;pro&#8221; extensions and use the Multi-Factor Authentication.</p>



<p>We&#8217;ll be configuring &#8220;<strong>external</strong>&#8221; authentication.</p>



<p>Open the iTop configuration. We&#8217;ll need to update two settings.</p>



<pre class="wp-block-code"><code>'allowed_login_types' =&gt; '...',</code></pre>



<p>This setting lists the allowed login modes, seperated by the pipe ( | ) character. &#8220;External&#8221; needs to be present here. By default, it&#8217;s added at the end, according to the &#8220;Configuration parameters&#8221; documentation. However, another page suggests to put it <strong>first</strong>:</p>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p>In order to ensure that the external authentication is used first (preventing iTop from prompting the already authenticated user a second time), make sure that in the iTop configuration file, the order for allowed_login_types specifies “external” as the first login mode</p>
<cite><a href="https://www.itophub.io/wiki/page?id=latest:admin:user_authentication_options#logon_types">User Authentication Options [iTop Documentation] (itophub.io)</a></cite></blockquote>



<p>Do put it first!</p>



<p>The second setting is this one; which you should set to match the $_SERVER[&#8216;REMOTE_USER&#8217;] variable:</p>



<pre class="wp-block-code"><code>'ext_auth_variable' =&gt; '$_SERVER&#91;\'REMOTE_USER\']',</code></pre>



<p>Now save.</p>



<p>Make sure in iTop you do have a <strong>User account</strong> created.</p>



<p>If you&#8217;re going to use short login names (&#8220;someuser&#8221;), set GssapiLocalName in the Apache configuration to &#8220;On&#8221;.<br>Otherwise, with GssapiLocalName set to &#8220;Off&#8221;, your user accounts would be named &#8220;someuser@YOURDOMAIN.ORG&#8221; instead.</p>



<h2 class="wp-block-heading">Some common issues</h2>



<p>Often, posts don&#8217;t include how to troubleshoot. I&#8217;ll share some issues I&#8217;ve ran into myself, and some solutions. You may want to consider setting a specific <strong>error_log</strong> directive in the Apache configuration; so you can easily see the Kerberos-related issues</p>



<p><strong>Incorrect service principal name</strong></p>



<pre class="wp-block-code"><code>&#91;Sun Feb 11 13:29:16.940771 2024] &#91;auth_gssapi:error] &#91;pid 5428:tid 139654099572288] &#91;client 192.168.0.210:64532] GSS ERROR In Negotiate Auth: gss_accept_sec_context() failed: &#91;Unspecified GSS failure.  Minor code may provide more information (Request ticket server HTTP/webserver01.yourdomain.org@YOURDOMAIN.ORG not found in keytab (ticket kvno 1))]
</code></pre>



<p>Solution: Double-check whether the keytab actually does contain this entry.</p>



<p><strong>Enctype issues</strong></p>



<pre class="wp-block-code"><code>&#91;Sun Feb 11 13:42:42.501763 2024] &#91;auth_gssapi:error] &#91;pid 5852:tid 139767178040896] &#91;client 192.168.0.210:63466] GSS ERROR In Negotiate Auth: gss_accept_sec_context() failed: &#91;Unspecified GSS failure.  Minor code may provide more information (Request ticket server HTTP/webserver01.yourdomain.org@YOURDOMAIN.ORG kvno 4 found in keytab but not with enctype rc4-hmac)]</code></pre>



<p>Solution: In krb5.conf, make sure to specify the encryption types. In the keytab, only AES256 was included; so <strong>/etc/krb5.conf</strong> had to be adjusted. Otherwise, it still tried to authenticate with the insecure RC4-HMAC.</p>



<p>Afterward, I also learned that I might have seen even more info when creating a trace file I could check after running this command:</p>



<pre class="wp-block-code"><code>KRB5_TRACE=/tmp/krb5_trace.log kinit -Vkt /privkeytab/krb5.webserver01.keytab HTTP/webserver01.yourdomain.org@YOURDOMAIN.ORG</code></pre>



<p>Then, not only check the output of the above command, but also of the trace log <strong>/tmp/krb5_trace.log </strong>.</p>



<p><strong>Ticket out of date</strong></p>



<pre class="wp-block-code"><code>&#91;Sun Feb 11 13:57:52.894981 2024] &#91;auth_gssapi:error] &#91;pid 6349:tid 140421321233984] &#91;client 192.168.0.210:63692] GSS ERROR In Negotiate Auth: gss_accept_sec_context() failed: &#91;Unspecified GSS failure.  Minor code may provide more information (Request ticket server HTTP/webserver01.yourdomain.org@YOURDOMAIN.ORG kvno 4 not found in keytab; ticket is likely out of date)]</code></pre>



<p>To be honest, this one was only resolved after I rebooted the machine I used to navigate to the web application. I think I just should have purged the cache there. Running <strong>klist purge</strong> on this Windows machine may have spared me from a reboot.</p>



<p>When running <strong>klist</strong> on Microsoft Windows, you&#8217;ll also get an overview of the cached tickets. You&#8217;ll see the same <strong>krbtgt/YOURDOMAIN.ORG@YOURDOMAIN.ORG</strong> ; and also a ticket where the server is listed as <strong>HTTP/webserver01.yourdomain.org@YOURDOMAIN.ORG</strong> . It also includes some extra info such as the validity period and session key type (encryption). It also lists the KDC (Kerberos Domain Controller) that was used.</p>



<p><strong>SPNEGO cannot find mechanisms to negotiate</strong></p>



<pre class="wp-block-code"><code>Sun Feb 11 14:41:02.838029 2024] &#91;auth_gssapi:error] &#91;pid 14397] &#91;client 192.168.0.210:61579] GSS ERROR In Negotiate Auth: gss_accept_sec_context() failed: &#91;Unspecified GSS failure.  Minor code may provide more information ( SPNEGO cannot find mechanisms to negotiate)]
</code></pre>



<p>It means the SPNEGO authentication fails.</p>



<p>In my case, I was just messing around with settings in the Apache configuration that led to the error above.</p>



<p><strong>No credentials were supplied, or the credentials were unavailable or inaccessible (Unknown error)</strong></p>



<pre class="wp-block-code"><code>&#91;Sun Feb 11 20:40:55.691852 2024] &#91;auth_gssapi:error] &#91;pid 4492] &#91;client 192.168.0.210:57991] GSS ERROR In Negotiate Auth: gss_accept_sec_context() failed: &#91;No credentials were supplied, or the credentials were unavailable or inaccessible (Unknown error)]</code></pre>



<p>The above error occurred if I used Google Chrome to navigate to the web application, without it being in the list of local Intranet websites. It then shows a prompt instead to enter credentials. Trying do dismiss it and not entering credentials, leads to the above. However, when manually entering correct credentials; it won&#8217;t display this.</p>



<h2 class="wp-block-heading">Background info</h2>



<h3 class="wp-block-heading">Useful links</h3>



<ul class="wp-block-list">
<li><a href="https://en.wikipedia.org/wiki/Generic_Security_Services_Application_Program_Interface">Generic Security Services Application Program Interface &#8211; Wikipedia</a> &#8211; Info about GSSAPI.</li>



<li><a href="https://web.mit.edu/kerberos/krb5-devel/doc/basic/keytab_def.html">keytab — MIT Kerberos Documentation</a> &#8211; Info about keytab.</li>



<li><a href="https://web.mit.edu/kerberos/krb5-1.12/doc/admin/conf_files/krb5_conf.html">krb5.conf — MIT Kerberos Documentation</a> &#8211; Info about krb5.conf and its parameters.</li>



<li><a href="https://learn.microsoft.com/en-us/windows-server/identity/ad-ds/manage/understand-service-accounts">Service Accounts | Microsoft Learn</a> &#8211; Info about service accounts in the Active Direcotry.</li>



<li><a href="https://forum.kaspersky.com/topic/kwts-kerberos-ldap-sso-proxy-authentication-problems-kaspersky-web-traffic-security-37974/">KWTS Kerberos LDAP, SSO, Proxy authentication problems [Kaspersky Web Traffic Security] &#8211; Advice and solutions for Kaspersky Security for Internet Gateway &#8211; Kaspersky Support Forum</a> &#8211; Some additional help in troubleshooting.</li>
</ul>



<h3 class="wp-block-heading">Used versions</h3>



<p>While writing this tutorial, my environment looked like this:</p>



<ul class="wp-block-list">
<li>Microsoft Windows Server 2022.</li>



<li>Ubuntu 22.04 LTS</li>



<li>krb5-user : 1.19.2</li>



<li>Apache 2.4: 2.4.52</li>



<li>libapache2-mod-auth-gssapi: 1.6.3</li>
</ul>
]]></content:encoded>
					
		
		
			</item>
	</channel>
</rss>
