I need to set a server that creates self-signed certificate when a user register in. So i thought to create a new AD account every time a new users register to the server. BUT, I need to store the user information into a sql server and i can't find a way to do this.
Any idea?
Based on what you describe and your comment:
My problem is that i think that store "public users" (that can register from the web) information into AD is insicure, so i'm trying to find another way to do that "mapping", – Stefano
What you seem to need is an AD domain with a one-way trust:
Your public users are in domain A.
Domain A trusts your internal private domain B.
Your app does AD authentication against domain domain A, and your internal users can authenticate using their full domain credentials (the request gets passed to domain B, which says yay or nay).
Note that this is coming from a guy who hasn't used Windows in a very long time.
I could be giving you terrible advice (and if I am I'm sure one of our Windows folks will clobber me for it).
If you're going to be storing external users for an application, you should be using AD LDS (formerly ADAM) instead of real AD. Or any other generic LDAP, really, but AD LDS is a lot like AD and might fit your needs better.
Related
[I'm fairly new to Kerberos Protocol]
We have a customer, who back in 2020 was using a domain let's call it customdom.itm, which has a user account krb-test-cd setup for Kerberos delegation and this domain is part of a domain Active Directory forest itm.
Since they're a large corporation with many users across different countries, they also have another huge domain AD forest with many child domains (and domain controllers) as part of this forest let's call it top.abc. Here the domain relevant to us is aust.top.abc, which has krb-test-aust user account setup for Kerberos.
Since the forests itm and top.abc are different, the same servicePrincipalName for both krb users is safely set to HTTP/testloadbalancer.com, while their userPrincipalName is of course different, i.e.:
krb-test-cd uPN is HTTP/testloadbalancer.com#CUSTOMDOM.ITM
krb-test-aust uPN is HTTP/testloadbalancer.com#AUST.TOP.ABC
And since https://testloadbalancer.com is part of the intranet sites at the customer, their browsers do not challenge the users to enter their AD credentials.
Now here's the problem:
Last month the customer decided to migrate the users in customdom.itm to a new domain can.top.abc which is part of forest top.abc. The user krb-test-cd and some other accounts were not migrated, however, and customdom.itm still exists in its own forest.
Due to the migration, these users are now challenged everytime to enter their AD credentials and are granted access only with the old domain name, i.e.
customdom\michael and password
I have setup a new user account krb-test-can in the domain can.top.abc for Kerberos delegation with setspn and the SPN HTTP/testloadbalancer.com, and the first time, I got this error:
The operation failed because SPN value provided for addition/modification is not unique forest-wide.
Next, I tried ktpass with SPN HTTP/testloadbalancer.com#CAN.TOP.ABC, and I get another error:
Failed to set property 'servicePrincipalName' to 'host/<host name>' on
Dn 'CN=<CN Name>,CN=Users,DC=<DC Name>,DC=<DC Name>,DC=abc': 0x13.
WARNING: Unable to set SPN mapping data.
Later, I finally understood that the SPN is already set to user krb-test-aust.
My question is:
How can I still successfully assign the SPN HTTP/testloadbalancer.com and eventually UPN HTTP/testloadbalancer.com#CAN.TOP.ABC to the user krb-test-can without affecting Kerberos delegation to user krb-test-aust?
Or is there a workaround on how I can use only the user krb-test-aust to delegate Kerberos authentications to the users now residing in domain can.top.abc without the need for user krb-test-can at all?
Any help is highly appreciated.
Thanks in advance!
[Some background]
We have an Access Management software on our side where we have configured many Identity Providers, 2 Policy Enforcement Points and Kerberos authentication for SingleSignOn for each of the above 2 domains.
We only need to inject the uPN and the password of the krb users into the respective PEPs and the software doesn't require a keytab file.
We are identity providers and the customer uses some links like https://testloadbalancer.com/xyz/efg_idp/entityid to make an IdP initiated login and is redirected to the target application.
Taking a hint from Steve, I finally found the solution to my own problem:
Since the domains can.top.abc and aust.top.abc and other domains are part of the same forest top.abc, they SHOULD have a cross-domain trust (I'm not sure if the trust is default or needs to be setup separately).
Hence, I only need the user account krb-test-aust to delegate Kerberos authentications to the users residing in all domains under top.abc.
The uPN of krb-test-aust remains HTTP/testloadbalancer.com#AUST.TOP.ABC.
What works
I developed this against our company's AD:
#Bean
public AuthenticationProvider adProvider() {
ActiveDirectoryLdapAuthenticationProvider adProvider = new ActiveDirectoryLdapAuthenticationProvider(
adConfig.getDomain(), adConfig.getUrl(), adConfig.getRootDn());
adProvider.setSearchFilter(adConfig.getSearchFilter());
adProvider.setUseAuthenticationRequestCredentials(true);
adProvider.setConvertSubErrorCodesToExceptions(true);
adProvider.setAuthoritiesMapper(authorities -> List.of(new FooAuthority("*")));
return adProvider;
}
This does work; I can log in using my company credentials. Important: I can use my sAMAccountName (which happens to be my uid as well) to log in.
Goal
Now I want to have some automated tests for certain edge cases, using a local AD. I chose ApacheDS for its cross platform availability, plus it has some Docker containers available. I use openmicroscopy/apacheds
, because it seemed active, documented and configurable, important for a rookie like me.
Problem
The problem is, I cannot log in. I traced it down to two lines in o.s.security.ldap.authentication.ad.ActiveDirectoryLdapAuthenticationProvider: while searchForUser(ctx, username) in doAuthentication(...) (line 148 in 5.0.12.RELEASE) works with my sAMAccountName (like foobar), contextFactory.createContext(env) in bindAsUser(...) (line 204 in 5.0.12.RELEASE) requires a fully qualified DN (like cn=foobar,ou=people,dc=acme,dc=com) to work.
So it seems there is some misconfiguration on my side, probably because of my misunderstanding... Seems I do need some different user to authenticate than to search afterwards? How do I configure this, and/but why does our company's AD works just fine? PS: I read about anonymous authentication, maybe our company allows such? But using Apache Directory Studio against the company's AD requires me to log in (as far as I can tell)...
LDAP based simple BIND operation always requires the distinguished name (DN) of the entry and password. Only AD allows to perform the BIND operation with samAccountName. AD is somewhat special.
We have an application where we store users login name in the format domain\username. We authenticate via windows and then get additional info from our database by matching the domain\username we get from the user to our database.
Now they want to move to the cloud. We authenticate users via apps in Azure AD. However, the user identifier we get back is first.last#domain.com.
I have fiddled around with https://graph.microsoft.com/v1.0/users/email and the select command to try and get the 'old' name. Howev,er I have not yet found out how to get it.
The reason they move to the cloud is that they are merging two ADs. So some users will be DomainA and some DomainB, but in the same tenant. So my first thought was to try and convert the mail to the other format. However, the two different ADs have different naming standards. One has DOMAINA\fila (two first letters from the first name and two first letters from the last name) and the other one has DOMAINB\firlas. Also it feels really ugly to try and solve it that way.
Is it possible to fetch the users loginname formatted as domain\username via Microsoft Graph?
Using the beta edition of Graph, you can obtain the user's domain and username from the onPremisesDomainName and onPremisesSamAccountName properties:
/beta/users?$select=userPrincipalName,onPremisesDomainName,onPremisesSamAccountName
The domain is stored as a FQDN so you'll need to do some translation. For example, domainName.ad.contoso.com might translate to domainName\).
This will give you a workaround so you can match up users with your internal databases. It is however only a temporary solution. Long-term, you really want to migrate to using the userPrincipalName. This is the primary user identifier and guaranteed to be unique within a given tenant.
Azure AD is a little different than the legacy Active Directory. Certain concepts from legacy AD such as Organizational Units (OUs), Group Policy Objects (GPOs), Kerberos Authentication, Lightweight Directory Access Protocol (LDAP), Domain trusts between multiple domains, and several others simply do not exist in the cloud.
Our application (referred to as "XYZ_App" below) is a multi-tenant SaaS application. We are in the process of making it available for Microsoft AppSource as a multi-tenanted "Web app / API" (referred to as "AppSourceXYZ_App" below).
We started our OpenID Connect implementation with endpoints pointing to “common” as per stated in the documentation when multi-tenancy is desired/required.
In XYZ_App, we added information in the system to know what AAD instance each XYZ_App tenant is associated with (using the GUID Microsoft assigned to this AAD instance, we are NOT using the "rename-safe.onmicrosoft.com" representation).
When using the “common” endpoints, we had to manually validate the issuer from the JWT to make sure it was the expected one: a user could access XYZ_App requesting access to XYZ_App’s tenant associated with contoso.onmicrosoft.com, get directed to “login.microsoftonline.com/common” to authenticate and then decide to authenticate with a user from another AAD instance (referred to as "anotherAADInstance.onmicrosoft.com" below). In this scenario, even though a user could successfully authenticate on anotherAADInstance.onmicrosoft.com, XYZ_App’s redirect URI must make sure the JWT issuer is the one from contoso.onmicrosoft.com. I’ll refer to this setup as Scenario_1.
With that scenario in mind, we thought about NOT using “common” and customize the requests going to login.microsoftonline.com on the fly; attempting to “jail” requests to be forced to authenticate against a specific AAD instance. We would still need to perform our validation in the redirect URI to make sure the issuer is the appropriate one, but we thought this approach might make our lives easier. I’ll refer to this setup as Scenario_2.
Do you envision Scenario_2 is viable in the long run or is it too short-sighted ? Based on my current knowledge of OpenID Connect, one limitation I can see with Scenario_2 is that it would become problematic to support “broker accounts” into our app.
Explanation of “broker accounts”: in our industry, some external users are allowed access to the system. Let’s say I have a company called “BrokerCo” (with their own brokerco.onmicrosoft.com AAD instance) who has 2 employees: Broker1 and Broker2. BOTH anotherAADInstance and contoso hired Broker1 and Broker2 to get broker services to perform tasks in XYZ_App; requiring XYZApp to grant them access. What is the ideal way for authentication from an OpenID Connect standpoint ? If XYZ_App were to use “login.microsoftonline.com/common” for authentication (like in Scenario_1; as opposed to “jailed” access like in Scenario_2), Broker1 and Broker2 could authenticate with brokerco.onmicrosoft.com (no AAD "External users" for anotherAADInstance nor contoso), but they would then get to redirect URI with an issuer that is different than what XYZ_App’s anotherAADInstance and contoso tenants are configured for... I feel like I’m back to square 1...
Do you have suggestions or pointers to solve this issue ?
Background context:
While playing with OpenID Connect issuers, I got the following error message:
AADSTS50020: User account 'testuser#anotherAADInstance.onmicrosoft.com' from identity provider 'https://sts.windows.net/XXXXXXXX-fake-GUID-9bZZ-XXXXxxxxXXXX/' does not exist in tenant 'XYZ Publisher' and cannot access the application 'YYYYYYYY-fake0-GUID-YYYYyyyyYYYY' in that tenant. The account needs to be added as an external user in the tenant first. Sign out and sign in again with a different Azure Active Directory user account.
Thanks in advance !
Your question has multiple layers, trying to address most of them:
AppSource is about trial experiences for new users: this mean that any corporate account around the globe can potentially be an user of your SaaS application - or at least to the trial experience of your application, therefore the first thing you need to think when integrating with AppSource is how easy it has to be for a potential user to experience your app for the first time.
With that in mind, AppSource recommends that the trial of application is build on such a way that allows any user from any organization to sign-in, and therefore a multi-tenant approach for your application is the recommended setup for any application.
The single-tenant approach requires more control on your side, and for a trial experience - it means that the user cannot try your application right away because the operation you have to do on your single-tenant application is to add the user to an Azure Active Directory tenant as a guest user. This guest account will need then to wait receiving an email to accept the invitation to join to this tenant you are adding the user to then sign-in to your application.
Therefore your scenario 1 is the best scenario thinking on a trial experience in general, and also in general require less management (as you'd not need to create/ manage each individual account that needs to access your application as guest users of your Azure AD instance).
However some concerns you listed - that this scenario bringing are valid: Because you are accepting the common endpoint, you are saying basically that any user can sign-in to any tenant to your application, and this may not be desirable. In addition, the scenario you listed that a user can generate a token for any application is also valid, however, you can add additional checks to make this more secure and that any token generated by another authentication is blocked:
You can validate the 'audience' claim to guarantee that the token was issued to your application
You can eventually check the 'tid'/'iss' claims against of a list of tenant Ids in your database to see if that the user's organization is a valid organization in your application -- this would be valid for non-trial users/ organizations.
More information in this article.
About scenario '2' and broker accounts:
This scenario could be interpreted in two different ways:
Broker accounts are guest accounts of a customers' Azure AD tenant
Broker accounts are third party accounts but are not actually added as a user of anotherAADInstance or contoso AD
If your case is '1' then you're right: if your application needs to authenticate guest users that belong to another Azure AD tenant, then common endpoint could not be used directly.
If your case is '2' then what you'd need to do is to continue using the common endpoint and somewhat after the user is authenticated ask them to choose the company. I am describing this on generic terms without fully understanding this scenario. Perhaps this is not simple as you want the remote company to control this and not the user - so some additional complexities may need to be handled here.
A note is that if your scenario is scenario 1. - in theory - you can still use an hybrid approach, where you'd ask user to type the username inside the application and the company that they want to sign-in, then check if you need to validate the user against common or tenant-id endpoint, preparing the request and then sending a login_hint argument when authenticating. More information here
Deploying the same HTTP based application on several web servers (srv1, srv2, etc). Protecting the application with SPNEGO auth. The servers are Linux and AD doesn't know of their existence, i.e. they are not joined to the domain. I've got the whole SPNEGO working smoothly on a single host. Now moving on to the subsequent hosts.
Most guides I've found will tell you that you need
An account in AD
A SPN
A keytab (generated on the AD server and then
moved to the Linux host)
While I believe that (2) + (3) will always need to be per-server, I'm somewhat uncertain about (1). Can I do with only one account? I would really like to not having all these accounts in AD if I can do with only one.
This blog has a good recipe for how it can be done: The first invocation of ktpass (for srv1) should be as described in the all the guides you find on the internet, however subsequent invocations (for srv2, srv3, etc) should be using the -setpass and -setupn options.
However I've found that when one uses the ktpass.exe tool the account's userPrincipalName attribute changes to become as given by princ argument from the last invocation of ktpass. So the name of the srv, e.g. srv3 is coded into the name and the name of the account will therefore basically change with each invocation of ktpass. When the web server performs the final step in the SPNEGO chain of events, which is to contact AD using the keytab as credentials, it will look for an account in AD with a userPrincipalName equal to the SPN and this step will therefore fail. (source, scroll to last post, list item 3). Contradicting this is that I'm using Tomcat and thereby JAAS and as far as I understand I can hardcode the principal name to use in my jaas.conf file thereby effectively ignoring the principal name from the keytab.
Can multiple app servers + single account in AD ever work and if so how?
In short, yes it will work and I will tell you how. First of all let's clarify some things and some statements not properly described in your question or the comments:
You have three machines which serve the same DNS name, this means that you either have a DNS round-robin: service.example.com will returned a shuffled list if IPs or a load-balancer (hard of sort) will only one IP for the A record depending on the load. For Kerberos, both setups are equal in the outcome.
Now, you cannot say that the AD does not know the existence of a service or a server if you require Kerberos authentication. It will and must know otherwise it cannot create service tickets for your clients which they pass on to the server. Additionally, Tomcat will not contact the KDC to accept the security context because the service ticket is encrypted with the account's long-term key.
Here is the approach: You have already figured out that one SPN can be bound to one machine, multiple bindings are not allowed. This is the case when you have the machine name bound to the machine account (srv1$, etc.). You need a service account. The service account is a regular account without password expiration, e.g., my-service#EXAMPLE.COM. For this account, you will bind your CNAME or A record. Have you Tomcat authenticator to accept all securty contexts with this service account and it will work.
How to create this magical service account on a Unix-like OS?
Use mskutil to
create the service account,
create a keytab for that service account,
bind your SPN to that service account and have the keytab updated.
After that you will have a keytab suitable for your use. Verify with an LDAP query (e.g., with Softerra's LDAP browser or else) that the account exists, the SPN (servicePrincipalName) is bound to that account and you are done.
Important: if any of your clients use MIT Kerberos or Heimdal, you must set rdns = false your your krb5.conf.
Godspeed!