I'm new with this authentication through kerberos protocol so I tried to read a lot of howto on it but seems like I can't find any specifics with my constraints. Here is what I have :
An Active Directory Server on which users authenticate to log into their workstations
Each end user uses IE 7 to connect to my intranet application
An Apache server with load balancing
Some Tomcats servers acting as workers for the Apache server.
on each tomcat, I have 2 jakarta servlet running, users connect only on one servlet (further i will call it the servlet as if there is only one)
my tomcats need to run under jdk5. not jdk6 or jdk4. it's jdk5 period.
Now I want one to automatically get logged on my servlet. Basically I just need my servlet to retrieve the client's principal then I can manage the rest.
Based on what I understood, my client has a ticket, he ask the KDC for a special ticket for accessing the apache server, then he tries to connect to the Apache server. Based on his keytab, the apache server then decode the auth data and grant/refuse the access to specified resource.
Am I right? please guide me through this, I've been reading pages for 4 days and still no clue on which solution is the more appropriate. I tried mod_auth_kerberos for Apache but instead of grabbing the user's ticket he ask it like a basic auth. Apparently spgneo
Thanks
Ok I got this working :
Install Kerberos 5 + apache 2 + mod_auth_kerb.
On your AD, generate a keytab with only the principal you will use for Apache, I use HTTP/apache.mydom.com#MYDOM.COM
Put this keytab file on your apache server and make it readable only
by your Apache user.
Then edit your apache conf with these directive for your secure
location
apache.conf:
[…]
ServerName apache.mydom.com:80
[…]
LoadModule auth_kerb_module modules/mod_auth_kerb.so
[…]
<LocationMatch /secure)>
[… some other stuff …]
Order allow,deny
Allow from all
AuthType Kerberos
AuthName "Authentification requise"
KrbAuthRealms MYDOM.COM
#this allows user to be saved in the request
KrbSaveCredentials on
#this one force Negotiate AuthType instead of basic fallback
KrbMethodNegotiate on
#this trim the realm from username saved in the request (request.getRemoteUser() will give you "user" instead of "user#MYDOM.COM"
KrbLocalUserMapping on
KrbAuthoritative on
KrbVerifyKDC on
Krb5Keytab /install/binaries/httpd/apache.keytab
KrbServiceName HTTP
require valid-user
</LocationMatch>
And the one thing I almost failed to find on the web, you have to modify your tomcat server config (tomcat/conf/server.xml) :
<Connector [... AJP connector configuration ...] request.tomcatAuthentication="false"/>
This is really important because without it you tomcat won't retrieve any info from tomcat auth.
Don't forget too, DNS is really really really really important for a Kerberos install. If you have any issue try checking your DNS for all of your servers.
Related
I have a JHipster monolithic application (Angular + Java SpringBoot + Tomcat container, everything together) deployed successfully in a EC2. I could set the security groups in order to enable 8443 incoming requests to the Public DNS and I am able to access it from any browser.
After that, I've requested a public certificate from Amazon for a domain I've already acquired with Route53.
So the idea was to use 443 instead of 8443, and the real domain (instead the Public DNS provided by AWS), so in effect I've created a ELB (all in the same VPC, security group and hosted zone). This ELB is listening in 443 and has a redirect to 8443 as default action.
But.. ERR_CONNECTION_REFUSED is what the browser shows..
It is important to mention that since AWS does not allow us to download the certificate (at least I don't see any option for that in the console) in the JDK of the EC2 where the app runs I've installed a custom certificate (generated with keytools) in order to apply it in Tomcat to listening the already mentioned 8443 port.
I also tried running in 8080 instead of 8443 (and of course updating the security groups) but no change..
Could you give me a clue about what I'm missing? So far the unique way I see is to create a new EC2 with a NGINX to act as a reverse proxy (with a rewrite policy maybe) behind the ELB, but I prefer to avoid additional complexity unless absolutely needed.
Additional data:
Tomcat server configuration:
server:
port: 8443
server.ssl.key-store: keystore.p12
server.ssl.key-store-password: thePassword
server.ssl.keyStoreType: PKCS12
server.ssl.keyAlias: theKeyAlias
Security group inbound rules:
Custom TCP 8443 with 172.31.0.0/16 (the same range of the ELB)
HTTPS TCP 443 with 0.0.0.0/0 and ::/0
Also the AWS Certificate is enabled and already issued (CNAME record set was created in Route53)
**UPDATE 1 - 04 February 2019 22:21 (GMT-3) **
Guys, I finally decided to have a NGINX behind the ELB. Also I've realized that communication between NGINX and App Server could be HTTP, therefore my app is gonna listen in port 8080, simplifying a bit the scheme. I've realized also that I need only one certificate in order to have the "browser padlock" and encrypted all traffic between clients and ELB, so no matter if it is not possible to download it (it is not needed to install also in NGINX nor App. Server).
At the Apache level you should add a listener on port 443 which would proxy pass the requests on port 8443. This will make sure that all incoming requests on port 443 of the domain will be passed to the application running on port 8443 of the server
listen 443;
location /{
proxy_pass http://127.0.0.1:8443;
}
Finally issue RESOLVED I could make work fine the NGINX and also I had to change another things:
I've passed from an Application Load Balancer to a Classic Load Balancer. The final scheme is like I've explained in the UPDATE of this topic, I mean:
User connects via HTTP or HTTPS through Classic LB and then it goes to EC2 NGINX listening on port 80.
Then from NGINX to WebApp I've used a proxy_pass in this way:
location / {
proxy_pass http://172.x.y.z:8080;
}
And finally an HTTP forward in NGINX to use HTTPS exclusively:
proxy_set_header X-Forwarded-Proto $scheme;
if ( $http_x_forwarded_proto != 'https' )
{
return 301 https://$host$request_uri;
}
Lijo Abraham, your answer helped me to have a clear direction and this post shows the exactly solution applied (thats why I will green tick this post).
Many thanks and regards.
**UPDATE 1 - 10 February 2019 17:21 (GMT-3) ** Finally I've remade all again using Application ELB this time instead of Classic ELB (the latter deprecated) and everything works as expected, don't know why in the beginning ELB Classic didn't work (probably some error in security groups rules configuration or something kind of that).
I have embedded Jetty running on port 7000. Also, I have a keycloak server running on same machine on port 8100.
My all clients access goes via Jetty i.e. localhost:7000. So, I have put keycloak as reverse proxy on Jetty i.e localhost:7000/keycloak/auth will redirect to localhost:8100/auth. It is hitting correctly.
Now, there is KeycloakInstalled client to authenticate the user. I have provided auth-url as http://localhost:7000/keycloak/auth. When I run this client, it correctly authenticate the user, but when retruning the token, it gives out the exception that auth-url (localhost:7000/keycloak/auth) given to it does not match the url from keycloak sever (localhost:8100/auth).
I tried out doing following also:
https://www.keycloak.org/docs/1.9/server_installation_guide/topics/clustering/load-balancer.html
But, I am unable to generate X-Forward headers from Jetty.
Am I doing any basic thing wrong here?
Any pointers here would be very helpful.
Thanks.
I workaround it by mapping http://localhost:7000/auth (not localhost:7000/keycloak/auth) to http://localhost:8100/auth via Jetty reverse proxy. It worked perfectly.
P.S. I also need to add proxy-address-forwarding="true" in keycloak standalone.xml
I have a user configured in AD with delegated kerberos ticket:
klist
Ticket cache: FILE:/tmp/krb5cc_527
Default principal: user1#EXAMPLE
Valid starting Expires Service principal 11/27/15 16:28:27 11/28/15 02:28:27 krbtgt/EXAMPLE.com#EXAMPLE.COM
How can I get this 'user1'? On this client domain account (client side jsp? Or server side?) I want to extract this value after button click and pass it back (with backurl) to another java app.
EDIT:
My Apache configuration:
<Location /kerb >
AuthType Kerberos
AuthName "auth-realm"
KrbMethodNegotiate off
KrbMethodK5Passwd off
KrbServiceName HTTP
Krb5Keytab /etc/krb5.keytab
require valid-user
</Location>
ProxyPreserveHost On
ProxyPass /kerb ajp://120.201.131.169:8019/myApp
ProxyPassReverse /kerb ajp://120.201.131.169:8019/myApp
But I received
[Sun Dec 13 18:17:32 2015] [debug] src/mod_auth_kerb.c(1944): [client
126.185.3.202] kerb_authenticate_user entered with user (NULL) and auth_type Kerberos
It depends. In my case I have an Apache server configured with mod_kerb and forwarding http requests to Tomcat by means of AJP.
In such scenario, Tomcat AJP conector is configured with tomcatAuthentication=false and I can get user authenticated from JSP and Servlet using request.getRemoteUser().
Obviously, the user string comes with domain info after # so you have to consider it.
If you are interested in my solution, I can elaborate my answer.
Edit
I edit my answer to give more info about configuring Tomcat to use Kerberos.
Configure NTP
First, it is quite common to have NTP clients configured in every system AD server, Apache server and Tomcat server. If there is no date and time synchronization, it is quite common to get clock skew too great or postdating problems.
Create an AD principal for the server
You need to create a principal into AD to use for server principal authentication. It is necessary to get a keytab file for this principal. I am sorry, I can't tell you how to do this.
Install and configure Kerberos on Apache server
Once you have your server principal and keytab file, it is time to configure Apache server. Install kerberos into that system and configure /etc/krb5.conf. A sample of this file is:
HERE.YOUR.KERB.DOMAIN = {
kdc = your.dns.kerb.domain
admin_server = your.dns.kerb.domain
}
Check with:
kinit -k -t keytab.file HTTP/principal.dns.name#HERE.YOUR.KERB.DOMAIN
klist
that your server is right configured.
Install and configure mod_auth_kerb
Install mod_auth_kerb apache module and configure its use in every location, directory, virtual host or whatever you need, see below. This configuration is very dependent on your kerberos server, you will have to play with some parameters as KDC verification, negotiation, be or not authoritative, ...
The most important parameter is Krb5Keytab, but you can check this page to understand all parameters. Here you are with a sample location:
<Location /sample/>
AuthType Kerberos
AuthName "auth-realm"
KrbMethodNegotiate on
KrbMethodK5Passwd off
Krb5Keytab /your/path/to/keytab.file
require valid-user
</Location>
When you try to access this location http://your.apache.server/sample Apache will try to check user credentials by means of kerberos.
AJP Configuration
For AJP configuration (I already answer this before, but I cut and paste here and adapt for this question) the procedure is the following:
Install Apache module for AJP, usually it is called something like libapache2-mod-jk. (In debian/ubuntu you can run sudo apt-get install libapache2-mod-jk).
Then you will have a new module calledjk or similar. You have to enable it (In debian/ubuntu you can run sudo a2enmod jk).
Default configuration will serve mostly, open it a see where does JkWorkersFile point. This file is needed to configure the workers that manage communication with tomcat apps.
Create workers file (if it does not exists). A workers file is more or less as following.
Sample workers file:
ps=/
worker.list=worker1,worker2,...
# worker1 definition
worker.worker1.port=8009
worker.worker1.host=host or ip
worker.worker1.type=ajp13
# worker2 definition
....
Every worker can point to different tomcat server. Port must be the same that configured into $CATALINA_HOME/conf/server.xml. In this file there is a connector for AJP protocol:
<Connector port="8009" protocol="AJP/1.3"
redirectPort="8443" tomcatAuthentication="false"/>
Every worker has to point to this port.
Finally, you can configure your location (or whatever) using JkMount workerName to indicate Apache that this url has to be forwarded to the proper worker:
<Location /sample/>
JkMount worker1
AuthType Kerberos
AuthName "auth-realm"
KrbMethodNegotiate on
KrbMethodK5Passwd off
Krb5Keytab /your/path/to/keytab.file
require valid-user
</Location>
There are plenty of samples an documentation. Here you are with Tomcat official docs: https://tomcat.apache.org/connectors-doc/webserver_howto/apache.html
Web app authentication
You don't need to configure anything about security constraint in web.xml, with this configuration, Apache will authenticate users instead Tomcat, and Tomcat will receive user's principal name into HTTP request.
Tomcat (and any other servlet container) will encapsulate the user's principal into request.getRemoteUser().
Hope it helps.
I got it! I don't know what exactly was wrong but now It works.
I can simple get principal from HTTP Header with the following configuration (I moved this configuration from /conf.d to main /conf/httpd.conf file). What is imported. On RHEL httpd server apache user should have rights to read /etc/krb5.keytab. In my case:
ps -ef | grep httpd
apache 27537 27535 0 16:18 ? 00:00:00 /usr/sbin/httpd
<VirtualHost myhost.domain.com:80>
ServerName myhost.domain.com
<Location /myApp >
# SSLRequireSSL
AuthType Kerberos
KrbMethodNegotiate On
KrbMethodK5Passwd Off
KrbServiceName HTTP/nmyhost.domain.com#EXAMPLE.COM
KrbAuthRealms EXAMPLE.COM
Krb5KeyTab /etc/krb5.keytab
require valid-user
RewriteEngine On
RewriteCond %{LA-U:REMOTE_USER} (.+)
RewriteRule . - [E=RU:%1]
Header add X-Remote-User "%{RU}e" env=RU
</Location>
ProxyRequests Off
ProxyPreserveHost On
ProxyPass /myApp ajp://126.101.100.169:8029/myApp
ProxyPassReverse /myApp ajp://126.101.100.169:8029/myApp
</VirtualHost>
Try checking your keytab file. I had similar issue and when i ran
cat httpd.keytab
it showed plain text, which isnt normal, keytab file should contain ASCII chars, its a binary file. Had to regenerate it with Domain Admin account "$user.name" and then it worked.
I thought it could be a good idea to use kerberos on Windows 2003 to authenticate a Java server application against active directory. That way, we will not have to put a password in a file. But this does not work.
[2012-09-20 17:42:19,301] ERROR Cannot authenticate server via JAAS
javax.security.auth.login.LoginException: No LoginModules configured for KerberosLogin
at javax.security.auth.login.LoginContext.init(LoginContext.java:273)
at javax.security.auth.login.LoginContext.<init>(LoginContext.java:349)
If I run the exact same code and configuration on Windows XP, then authentication works. In both cases I am using Java 7 u07.
It turned out my TGT that I had aquired via JDK's kinit command had expired.
In Glassfish v2 I secure my JNDI lookups by enabling the "IIOP Client Authentication" checkbox in the admin console under the ORB node.
In my standalone client I then perform a "ProgrammaticLogin", which then allows me to do JNDI lookups.
In Glassfish v3 however, I get this error if want to do any JNDI lookups in the same setup:
18.08.2010 14:31:10 com.sun.enterprise.transaction.JavaEETransactionManagerSimplified
initDelegates INFO: Using
com.sun.enterprise.transaction.jts.JavaEETransactionManagerJTSDelegate
as the delegate
org.omg.CORBA.NO_PERMISSION:
----------BEGIN server-side stack trace----------
org.omg.CORBA.NO_PERMISSION:
vmcid: 0x0
minor code: 0
completed: No
I am pretty sure the "ProgrammaticLogin" works in general, since it allows me to do remote method calls on my secured EJB's (using #RolesAllowed but if the IIOP Client Auth is turned off).
The user I login with is a simple user that I created for the "file" realm.
Any ideas, why the ProgrammticLogin fails to work in Glassfish v3 for authorizing JNDI lookups?
Or what is the correct way to authenticate with the ORB from a standalone client; for this purpose?
Did you make sure appserv-rt.jar is on your classpath? It is part of every glassfish3 installation e.g. at my installation it is located here:
/opt/glassfish3/glassfish/lib
This is very important. It contains some client java classes especially: AppclientIIOPInterceptorFactory.
These add a SecClientRequestInterceptor to the ORB which cares that the username and password is added to the GIOP request sent to the server.
It took me about two days of scanning the source code of glassfish and sniffing the corba packages on the wire with wireshark until I found this.
An example how to secure a bean and write a real Java EE client can be found here:
http://download.oracle.com/docs/cd/E19798-01/821-1841/bnbzk/index.html
Hope this helps
Manuel