I'm working on an independent java application inside an Active Directory environment and the usual way for authentication is SingleSignOn.
But there I have some interfaces, which is not supporting SSO. And now I'm thinking of using LDAP for authenticate Users for the applications.
My question is: is it possible to implement LDAP inside an ActiveDirectory environment?
If yes: how does it work together and what do I have to do exactly?
If no: are there any other alternatives except for SSO?
Active Directory Domain Controllers function as LDAP servers. Servers will have clear-text LDAP bound on port 389, and servers that can auto-enroll for a certificate will have ssl encrypted LDAP bound on port 636.
Active Directory as an LDAP server is basically like any other LDAP server, BUT the schema names change. Where you would normally see uid in LDAP, Microsoft uses sAMAccountName or userPrincipalName. The domain is based on domain components (dc=something) instead of organizations (o=something). But the basic concepts remain the same.
You can look at the SRV record for the domain to return a list of candidate domain controllers to use as the LDAP server, you can configure a load balancer VIP to front the directory and add domain controllers that are intended to handle LDAP-sourced authentication traffic, or you can have a primary ldap host with a secondary host to use if that one fails to connect.
If you are using LDAP-SSL (and that's the right thing to do), you'll need to add the public key(s) for the CA chain to the cacerts file unless the domain admins have gone through the process of getting DC certs signed by a public CA. Connect to the AD servers via LDAP, bind with system creds, search for user -- something like (&(sAMAccountName=WhatUserEntered)) -- at the base DN for the organization, return the object fully qualified DN, bind with the fully qualified DN and the password the user provided. Check the result code to determine if authentication passed.
Related
I purchased the domain name 'jimtough.org' directly from Amazon so I could use it with a Cloudfront distribution. Now I have a very simple vanity site at https://jimtough.org/ that uses the certificate. I also played around with AWS API Gateway and confirmed that I can use the same certificate to provide HTTPS secured URLs to work with API Gateway.
I am also running a Java/Spring Boot web application on an EC2 server. I have already associated a Route53 DNS name with the EC2 server that hosts my Spring Boot application, like this: http://instance-b.jimtough.org/. This works, but it is using unsecured HTTP. I get a bunch of warnings when I try to do basic authentication in the application, since it would be sending my username/password via an insecure connection. Fair enough.
So my next step is to enable HTTPS in Spring Security and force secure connections to the application. In order to do this, I first need to provide a certificate for the Java runtime on the EC2 host to use. I found examples of how to do so with a self-signed certificate:
https://mkyong.com/spring-boot/spring-boot-ssl-https-examples/
https://www.baeldung.com/spring-boot-https-self-signed-certificate
Unfortunately, using a self-signed certificate is not what I'm looking for. I want the user to be able to browse my static content vanity site first (https://jimtough.org/), and then follow a link to my web app and keep using the same site certificate (my AWS-issued cert) for my Spring web app.
QUESTION: How can I use my AWS-issued certificate with my Java-based web application?
Inside the AWS Certificate Manager (https://console.aws.amazon.com/acm/home?region=us-east-1#/), I don't see any way to 'export' or save my certificate. Am I missing something? Maybe Amazon doesn't want me to use this certificate outside of their own services?
Note that I did originally set up the certificate with the domain name as 'jimtough.org' and the Additional Names field set to '*.jimtough.org' so I can use the certificate on sub-domains as I'm trying to do here.
EDIT
The accepted answer from julien-b is correct. I did some more research and found that SSL certificates aren't cheap, and come in different flavors. The cheapest is the 'DV' (Domain Validation) type, which only verifies that the SSL certificate is controlled by someone who also controls the DNS record for the associated domain (such as 'mydomain.com'). There are much more thorough (and expensive) certificate types that can be issued, where the issuer has to do background checks on the owning organization. Those are meant for sites that handle e-commerce, financial transactions, etc. Not at all what I need.
There are also multiple types of multi-site certificates to choose from. The very cheapest single-domain certificates only cover your primary domain and the 'www' subdomain (mysite.com and www.mysite.com). If you want all the subdomains of your primary domain covered (app.mysite.com, ftp.mysite.com, etc), then you'll need a 'wildcard' certificate. Those are significantly more expensive. The more exotic certificate types can cover multiple different domains. These seem to be aimed at making certificate management easier for organizations that manage a lot of different domains and don't need a different certificate for each. Not what I need, so I didn't investigate further.
I decided to go with a 'single-domain with subdomain wildcard' certificate from Comodo (recently renamed to Sectigo?), who appears to be the most affordable certificate vendor right now.
REFERENCE: https://www.techradar.com/news/best-ssl-certificate-provider
It seems like a missed opportunity for Amazon that they don't get in on this game and issue their own SSL certificates for a fee. AWS already has all the infrastructure in place to do so, at least for the DV-level certificates.
You cannot export the private key of a publicly trusted ACM certificate. You can use ACM certificates with some managed services, but it doesn't work for all use cases.
https://docs.aws.amazon.com/acm/latest/userguide/export-private.html
AWS Certificate Manager is integrated with other AWS services, so you
can provision an SSL/TLS certificate and deploy it with your Elastic
Load Balancer, Amazon CloudFront distribution or API in Amazon API
Gateway. AWS Certificate Manager also works with AWS Elastic Beanstalk
and AWS CloudFormation for public email-validated certificates to help
you manage public certificates and use them with your applications in
the AWS Cloud.
https://aws.amazon.com/certificate-manager/features/?nc=sn&loc=2
Should you want to use a certificate on a service that is not integrated with ACM or even on-premise, you should get your certificate from another source.
As for the ACM Private CA, it is meant to be used within an organization thus not matching your use case.
https://docs.aws.amazon.com/acm-pca/latest/userguide/PcaWelcome.html
Warning: This is an extremly long post, simply because I don't know how to explain our specific problems any other way (sorry).
Short version:
We are building a very modular suite of Java applications (both client and server side). Some clients have to authenticate against the servers, and the servers can authenticate against different types of user stores. Servers can also call other servers on behalf of their authenticated user, i.e. using their credentials. This has worked fine so far using simple username/password credentials, but now we have to optionally support SSO via Kerberos (and later other authentication systems). So far, we're failing (partly miserably).
Long version:
Our central Java library for handling principals is called User Access Layer (UAL). This library is used by both client and server applications and provides three types of funcationality:
Authenticate users based on their credentials (results in a failed authentication or basic information about the user, i.e. at least a login name or ID)
Perform principal queries
Perform principal modifications in the user store, if the backend supports it
(2) and (3) can be performed either using credentials specified by the caller or (if the backend supports it) as a technical user.
Actual access to the user store is handled by the configured backend. We provide a number of backends (LDAP, WebDAV, a custom JDBC database, a custom XML file) which can all be configured through a unified configuration file, usually named useraccess.xml. This file defines which backend (or backends) should be used and how it is configured, e.g. LDAP server and structure data for the LDAP backend or the database URL and database user credentials for the database backend. All backends implement the same interfaces so the application code is independent from the backends configured for a particular installation.
In our product, UAL is used by two different types of applications:
Clients (both command line/desktop clients and web frontend applications opened by a user in a browser). These applications use UAL to perform principal queries (e.g. our file browser application when modifying the ACLs of a WebDAV resource) or principal modifications (we have a web based user management application). Depending on the application, they get the credentials used for their UAL operations in one of the following ways:
a. User provides the credentials on the command line when calling the application
b. Application opens a dialogue and prompts the user to input credentials when a server access requires them
c. A login screen displayed when the user first accesses a web frontend application
Servers. which use UAL to:
a. Authenticate a user based on the credentials provided via the used protocol (e.g. HTTP/WebDAV or SOAP/WSSE)
b. Perform authorization for a user based on the user's (or the user's groups') attributes
c. Perform UAL operations (queries/modifications) on behalf of the client
d. Call other servers on behalf of the client, i.e. passing another server the user's credentials (e.g. via HTTP/WebDAV or SOAP/WSSE)
Until now, all our credentials were user name/password pairs, which worked fine as long as we made sure to keep these credentials in the user's session where necessary to later use them for accessing another server. We could do that every call that retrieved credentials or passed them to a server went through some of our code which could store/provide the necessary credentials.
Everything has become much more complicated with the requirement to support SSO via Kerberos. We've tried several approaches and modified our code base several times, but every time we believe to be on the right track, we realise that there's one spot we overlooked which cannot work the way we intended.
What makes things so confusing is that we have to handle credentials in several different ways. These are some of the ways we have to provide credentials to a server:
Via Apache HttpClient when accessing a HTTP/WebDAV server
Via SOAP/WSSE when accessing a web service
Via JNDI when accessing the LDAP server (in UAL)
And some of the ways we have to receive and verify credentials from a client:
As a login module in Apache Jackrabbit
When receiving a SOAP/WSSE message in one of our JAX-WS web services
A very common use case for us is the following:
Client calls server A via SOAP, providing credentials
Server A retrieves the credentials from the SOAP message and verifies them (responds with an error if they are invalid (authentication error) or the user is not authorized to perform the desired operation (authorization error))
Server A then calls WebDAV server B on behalf of the user, passing the user's credentials so that the WebDAV operation can be carried out with that user's permissions (and using that user's name and other attributes)
Server B retrieves the credentials from the HTTP message and verifies them
Server B then performs a principal query on the user store C, passing the user's credentials to the user store (depending on the configured UAL backend, this may simply compare the user's name and password to those in the user store XML file, or use them to establish an LDAP connection and query the LDAP store as that user)
And the problem is: There seems to be very little information on the internet to help with our specific problems. For starters, most resources simply describe how to setup a JAAS configuration file for a container to let its web applications perform user authentications. But our code has to run on both clients and servers and use one configuration file to specify the user store config for both authentication and principal queries/modifications. Furthermore, this has to work, with the same code, with user name/password credentials against a variety of user stores (some of them custom written) and with Kerberos (and later other) tickets against an LDAP server. And finally, it's not enough to have an authentication library which reliably tells us that user has provided the correct credentials (as many JAAS login modules seem to do), since we actually have to keep the user's credentials for further calls.
Since Apache Jackrabbit, which is the base for one of our core components, needs us to configure a JAAS login module, and there already are JAAS login modules for LDAP and Kerberos authentication, we have successfully modified UAL to perform all its authentication tasks via JAAS. For this we had two write login modules for our custom backends, and I had to implement my own LDAP login module since the default JAAS one would successfully authenticate the user against the LDAP server, but then throw away both the user's credentials and the LDAP context, so we couldn't perform further LDAP query using the same credentials. All our own login modules store the credentials in the authenticated subject's private credentials set, which is what JAAS's default Kerberos login module does as well. Using the resulting subject, we can then perform user queries. This works with all our backends and with both passwords and Kerberos tickets.
We were also able to modify our SOAP services to extract the credentials from the SOAP message. In the case of password credentials, we can simply pass them to JAAS when the authentication callback asks for credentials. However, there doesn't seem to be a way to do the same with a Kerberos ticket. Instead, our SOAP services currently handle those on their own, passing them through the necessary GSS API calls to verify the ticket, retrieve a matching ticket for the SOAP service's configured service user, and create a Subject containing the credentials and user information. Using this subject, we can then perform queries/modifications through UAL. However, this not only means that our SOAP services completely bypass UAL when authenticating Kerberos tickets, they also need some Kerberos configuration data (the service user name, the realm and the keytab file) in their own configuration, in addition to the useraccess.xml which already contains the same data (but not directly accessible to a generic UAL client because these settings are specific to the UAL LDAP/Kerberos backend). Obviously, things will only get worse when we add support for other ticket based authentication methods and they also have to be manually implemented in each SOAP service in addition to the UAL backend that actually handles the user store access.
Worst of all, we're still unsure how to get all this into our Jackrabbit based WebDAV server. Jackrabbit needs a login module, which should be fine for handling user name/password credentials but (as far as we can tell) fail for Kerberos tickets. We could probably fetch those manually from the HTTP headers, but that won't stop Jackrabbit from calling the login module and the login module from failing because it will still ask for a password and then fail to authenticate against Kerberos without one.
I can't shake the feeling that either our approach or (quite possibly) our understanding of how all these pieces should fit together is fundamentally flawed, but nothing we can find on the net spans enough of our requirements to indicate what we're doing wrong (or, more importantly, how it should be done to make it right). Because of the complexity of even describing our problems I so far shied away from posting this as a question, but if you've read this far and can give us any pointers on how to resolve this, you could save us from weeks of frustration.
You can remove everything after
Until now, all our credentials were user name/password pairs, which worked fine as long as we made sure to keep these credentials in the user's session where necessary to later use them for accessing another server. We could do that every call that retrieved credentials or passed them to a server went through some of our code which could store/provide the necessary credentials.
You problem is plain simple: you need credential delegation with Kerberos. The basic technique is quite simple. Since you have multiple problem areas here, I would recommend to break them up to have you problem solved:
OS/environment configuration for Kerberos credential delegation
How to request a delegable service token
How to retrieve the delegated TGT from the client
How to reuse the TGT from the client and request another service token
Since your inbound channel is HTTP, here are the answers:
If you are in an Active Directory environment, request your admin to set "trusted for delegation" on the machine account which will accept the GSS security context.
This is a bit tricky because it depends on the client language and library. In Java it is as simple as setting GSSContext.requestCredDeleg(true). Eloborate on the rest.
Inspect the code of my Tomcat SPNEGO/AD Authenticator library, I am extracting the client's TGT and storing it in the Principal implementation served by HttpServletRequest#getPrincipal method.
Assuming that your backend client library supports GSS-API properly, there are basically two ways to do it: (1) explicit credential usage: pass the delegated GSSCredential instance to the client lib and it should the rest. (2) implicit: Wrap your client action in a PrivilegedAction, construct a Subject with the private GSSCredential and invoke Subject.doAs with both. JAAS will use the implicit credential from the thread's subject and perform the operation on behalf of your user.
It seems like you haven't even reached point 2 or 3 yet.
Say, I have a Java web app inside a war file that is hosted on cloudfoundry at the url mycoolapp.cfapps.io, which works perfectly. I now need to host it on a custom domain mycoolapp.com and I have purchased the domain.
What is process to host it on my own domain? Can I do it via Cloudfoundry?
My app needs ssl. Currently https://mycoolapp.cfapps.io works. But I need it to work on my custom domain. What will be involved in this? (I think I need to get a certificate for my domain, but what next?)
In the app some confidential information is embedded in urls (this cannot be changed), so I'd also need to ensure that the provider cannot know the urls accessed (apart from the base url). Can this be done? If not, what are the alternatives?
It could be done by creating a CNAME record for your app (see Azure example here). Unfortunately, it seems that Cloud Foudry (CF) does not support it yet. As I understand, it is caused by the fact that CF router determines the exact Virtual Machine (and, hence, IP) by parsing URL and determining the route according to the host name (mycoolapp in your case). Ideally there would be an interface in CF where you could register all CNAME aliases for your app (as implemented for Azure websites)
If CNAME record would be enabled, that it would also work for HTTPS, as it basically resolves IP address. And definitely there would be an interface for you to upload a certificate for your domain. This leads to problems mentioned below about SSL termination. But, again, as far as I know, it is not supported by CF yet.
That it a question to the internal structure of run.pivotal.io deployment of CF. Conceptually HTTPS will do the trick as it encrypts URL parameters. However I suppose that SSL terminates on the router (as certificate is issued for *.cfapps.io - single cert for all apps - you could check it in browser connection to your app by HTTPS). That likely means that internally CF has access to ALL data of your request, and leads to my question about SSL termination in CF, which currently has no answer. Hope CF will provide a way to terminate SSL on the final server processing the request.
UPDATE:
Cloud Foundry has proposed its own way to support custom domains - through using CloudFlare proxy. If the fact of using proxy that decrypts your data is Ok for you, it could be used.
I want to fetch data from an Active Directory using Java and JNDI from my EJB.
Doing this search I need to define a user and a password.
I was thinking of creating a service account (for my server) in the AD.
I will also be using Kerberos protocol and WebLogic Server.
As I understand it now, I need to create a keytab file that will contain this service account's credentials. This keytab file will then be configured in the WebLogic Server?
So, this means that I will have to state the username/password both for the keytab file and in my EJB (to fetch the data from the AD using JNDI). What is the best approach for this? Can the keytab file be defined dynamically? Which would simplify changing the password and only doing this at one place.
Have your machine join the domain, start you WebLogic server with the machine account or give access to the machine keytab, create a JDNI resource with the DirContextSourceFactory and you are able to access the AD as you wish.
we are using JAAS to enable Single Sign On in a Java application using the Windows Kerberos ticket cache. Our jaas.conf config file looks like this:
LoginJaas {
com.sun.security.auth.module.Krb5LoginModule required
useTicketCache=true
doNotPrompt=true
debug=true;
};
With this, we can create a Jaas LoginContext and successfully get the user's Kerberos ticket. We send this ticket to out server application using JMI. What we don't manage to do though is to verify on the server that the Kerberos ticket was in fact created by our Active Directory.
At the moment, we do a very insecure validation of the ticket by simply checking if the Server Principal (KerberosTicket.getServer()) name has our domain name in the realm part. But of course, anyone could set up an own Kerberos server with the same realm name and use that ticket to start the application.
One idea I have found was to authenticate against the Active Directory LDAP using the Kerberos ticket. Unfortunately, we use Windows 7 and re-using the Kerberos ticket to authenticate against the LDAP only works when setting a Registry entry (see http://java.sun.com/j2se/1.5.0/docs/guide/security/jgss/tutorials/Troubleshooting.html, search for allowtgtsessionkey). This is unacceptable for our users.
Is there any way to validate the ticket against our Active Directory server? I suspect there is a way to check if the KerberosTicket.getServer() ticket equals the ticket of our server, but I have no idea how to do that. UPDATE: KerberosTicket().getServer() only returns a KerberosPrincipal that contains nothing but the server ticket name and realm, so it is not suitable for validation.
Thanks for your help,
memminger
As you mentioned, the proper way to solve this is by kerberizing your service, which is the whole point of the Kerberos protocol (authenticating clients against servers). Ticket reuse doesn't work exactly because it'd be a security problem if it did.
A Kerberos service does not need to "log into Active Directory", it just needs to have a shared key with AD.
BTW, to get SSO using JAAS requires having that allowtgtsessionkey set, there's no way around that on Windows.
As no one seems to really know an answer to this, I suppose we have to make a proper Kerberos service out of our server application. One that logs in to the Active Directory itself and that has the ServicePrincipalName attribute set properly. Kind of like SPNEGO does for HTTP. A good starting point how to do that will be the SPNEGO Servlet filter on SourceForge (http://spnego.sourceforge.net/). http://thejavamonkey.blogspot.com/2008/04/clientserver-hello-world-in-kerberos.html is also a very good example of how to do Service logon.
Unfortunately, this leads to the same problem with the registry key, so I posted a new question on Is there a way in Java or a command-line util to obtain a Kerberos ticket for a service using the native SSPI API?.