1
0
mirror of synced 2026-08-05 09:47:05 +00:00

Add Spring Security Kerberos

Move the Spring Security Kerberos Extension into Spring Security

Closes gh-17879
This commit is contained in:
Rob Winch
2025-09-12 14:23:19 -05:00
parent e8bf470582
commit f5fb127c8c
69 changed files with 6173 additions and 0 deletions
Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

@@ -0,0 +1,118 @@
/*
* Copyright 2015 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.kerberos.docs;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.FileSystemResource;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.ProviderManager;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.kerberos.authentication.KerberosAuthenticationProvider;
import org.springframework.security.kerberos.authentication.KerberosServiceAuthenticationProvider;
import org.springframework.security.kerberos.authentication.sun.SunJaasKerberosClient;
import org.springframework.security.kerberos.authentication.sun.SunJaasKerberosTicketValidator;
import org.springframework.security.kerberos.web.authentication.SpnegoAuthenticationProcessingFilter;
import org.springframework.security.kerberos.web.authentication.SpnegoEntryPoint;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;
//tag::snippetA[]
@Configuration
@EnableWebSecurity
public class WebSecurityConfig {
@Value("${app.service-principal}")
private String servicePrincipal;
@Value("${app.keytab-location}")
private String keytabLocation;
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
KerberosAuthenticationProvider kerberosAuthenticationProvider = kerberosAuthenticationProvider();
KerberosServiceAuthenticationProvider kerberosServiceAuthenticationProvider = kerberosServiceAuthenticationProvider();
ProviderManager providerManager = new ProviderManager(kerberosAuthenticationProvider,
kerberosServiceAuthenticationProvider);
http
.authorizeHttpRequests((authz) -> authz
.requestMatchers("/", "/home").permitAll()
.anyRequest().authenticated()
)
.exceptionHandling()
.authenticationEntryPoint(spnegoEntryPoint())
.and()
.formLogin()
.loginPage("/login").permitAll()
.and()
.logout()
.permitAll()
.and()
.authenticationProvider(kerberosAuthenticationProvider())
.authenticationProvider(kerberosServiceAuthenticationProvider())
.addFilterBefore(spnegoAuthenticationProcessingFilter(providerManager),
BasicAuthenticationFilter.class);
return http.build();
}
@Bean
public KerberosAuthenticationProvider kerberosAuthenticationProvider() {
KerberosAuthenticationProvider provider = new KerberosAuthenticationProvider();
SunJaasKerberosClient client = new SunJaasKerberosClient();
client.setDebug(true);
provider.setKerberosClient(client);
provider.setUserDetailsService(dummyUserDetailsService());
return provider;
}
@Bean
public SpnegoEntryPoint spnegoEntryPoint() {
return new SpnegoEntryPoint("/login");
}
public SpnegoAuthenticationProcessingFilter spnegoAuthenticationProcessingFilter(
AuthenticationManager authenticationManager) {
SpnegoAuthenticationProcessingFilter filter = new SpnegoAuthenticationProcessingFilter();
filter.setAuthenticationManager(authenticationManager);
return filter;
}
@Bean
public KerberosServiceAuthenticationProvider kerberosServiceAuthenticationProvider() {
KerberosServiceAuthenticationProvider provider = new KerberosServiceAuthenticationProvider();
provider.setTicketValidator(sunJaasKerberosTicketValidator());
provider.setUserDetailsService(dummyUserDetailsService());
return provider;
}
@Bean
public SunJaasKerberosTicketValidator sunJaasKerberosTicketValidator() {
SunJaasKerberosTicketValidator ticketValidator = new SunJaasKerberosTicketValidator();
ticketValidator.setServicePrincipal(servicePrincipal);
ticketValidator.setKeyTabLocation(new FileSystemResource(keytabLocation));
ticketValidator.setDebug(true);
return ticketValidator;
}
@Bean
public DummyUserDetailsService dummyUserDetailsService() {
return new DummyUserDetailsService();
}
}
//end::snippetA[]
@@ -0,0 +1,27 @@
/*
* Copyright 2002-2015 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this
* file except in compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under
* the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/
package org.springframework.security.kerberos.docs;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations= {"AuthProviderConfig.xml"})
public class AuthProviderConfigTest {
@Test
public void configLoads() {}
}
@@ -0,0 +1,35 @@
/*
* Copyright 2015 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.kerberos.docs;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
//tag::snippetA[]
public class DummyUserDetailsService implements UserDetailsService {
@Override
public UserDetails loadUserByUsername(String username)
throws UsernameNotFoundException {
return new User(username, "notUsed", true, true, true, true,
AuthorityUtils.createAuthorityList("ROLE_USER"));
}
}
//end::snippetA[]
@@ -0,0 +1,67 @@
/*
* Copyright 2015 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.kerberos.client.docs;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.core.io.FileSystemResource;
import org.springframework.security.kerberos.client.config.SunJaasKrb5LoginConfig;
import org.springframework.security.kerberos.client.ldap.KerberosLdapContextSource;
import org.springframework.security.ldap.search.FilterBasedLdapUserSearch;
import org.springframework.security.ldap.userdetails.LdapUserDetailsMapper;
import org.springframework.security.ldap.userdetails.LdapUserDetailsService;
public class KerberosLdapContextSourceConfig {
//tag::snippetA[]
@Value("${app.ad-server}")
private String adServer;
@Value("${app.service-principal}")
private String servicePrincipal;
@Value("${app.keytab-location}")
private String keytabLocation;
@Value("${app.ldap-search-base}")
private String ldapSearchBase;
@Value("${app.ldap-search-filter}")
private String ldapSearchFilter;
@Bean
public KerberosLdapContextSource kerberosLdapContextSource() {
KerberosLdapContextSource contextSource = new KerberosLdapContextSource(adServer);
SunJaasKrb5LoginConfig loginConfig = new SunJaasKrb5LoginConfig();
loginConfig.setKeyTabLocation(new FileSystemResource(keytabLocation));
loginConfig.setServicePrincipal(servicePrincipal);
loginConfig.setDebug(true);
loginConfig.setIsInitiator(true);
contextSource.setLoginConfig(loginConfig);
return contextSource;
}
@Bean
public LdapUserDetailsService ldapUserDetailsService() {
FilterBasedLdapUserSearch userSearch =
new FilterBasedLdapUserSearch(ldapSearchBase, ldapSearchFilter, kerberosLdapContextSource());
LdapUserDetailsService service = new LdapUserDetailsService(userSearch);
service.setUserDetailsMapper(new LdapUserDetailsMapper());
return service;
}
//end::snippetA[]
}
@@ -0,0 +1,38 @@
/*
* Copyright 2015 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.kerberos.client.docs;
import org.springframework.security.kerberos.client.KerberosRestTemplate;
public class KerberosRestTemplateConfig {
//tag::snippetA[]
public void doWithTicketCache() {
KerberosRestTemplate restTemplate =
new KerberosRestTemplate();
restTemplate.getForObject("http://neo.example.org:8080/hello", String.class);
}
//end::snippetA[]
//tag::snippetB[]
public void doWithKeytabFile() {
KerberosRestTemplate restTemplate =
new KerberosRestTemplate("/tmp/user2.keytab", "user2@EXAMPLE.ORG");
restTemplate.getForObject("http://neo.example.org:8080/hello", String.class);
}
//end::snippetB[]
}
@@ -0,0 +1,151 @@
/*
* Copyright 2015 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.kerberos.docs;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.FileSystemResource;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.ProviderManager;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.kerberos.authentication.KerberosServiceAuthenticationProvider;
import org.springframework.security.kerberos.authentication.sun.SunJaasKerberosTicketValidator;
import org.springframework.security.kerberos.client.config.SunJaasKrb5LoginConfig;
import org.springframework.security.kerberos.client.ldap.KerberosLdapContextSource;
import org.springframework.security.kerberos.web.authentication.SpnegoAuthenticationProcessingFilter;
import org.springframework.security.kerberos.web.authentication.SpnegoEntryPoint;
import org.springframework.security.ldap.authentication.ad.ActiveDirectoryLdapAuthenticationProvider;
import org.springframework.security.ldap.search.FilterBasedLdapUserSearch;
import org.springframework.security.ldap.userdetails.LdapUserDetailsMapper;
import org.springframework.security.ldap.userdetails.LdapUserDetailsService;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;
//tag::snippetA[]
@Configuration
@EnableWebSecurity
public class WebSecurityConfig {
@Value("${app.ad-domain}")
private String adDomain;
@Value("${app.ad-server}")
private String adServer;
@Value("${app.service-principal}")
private String servicePrincipal;
@Value("${app.keytab-location}")
private String keytabLocation;
@Value("${app.ldap-search-base}")
private String ldapSearchBase;
@Value("${app.ldap-search-filter}")
private String ldapSearchFilter;
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
KerberosServiceAuthenticationProvider kerberosServiceAuthenticationProvider = kerberosServiceAuthenticationProvider();
ActiveDirectoryLdapAuthenticationProvider activeDirectoryLdapAuthenticationProvider = activeDirectoryLdapAuthenticationProvider();
ProviderManager providerManager = new ProviderManager(kerberosServiceAuthenticationProvider,
activeDirectoryLdapAuthenticationProvider);
http
.authorizeHttpRequests((authz) -> authz
.requestMatchers("/", "/home").permitAll()
.anyRequest().authenticated()
)
.exceptionHandling()
.authenticationEntryPoint(spnegoEntryPoint())
.and()
.formLogin()
.loginPage("/login").permitAll()
.and()
.logout()
.permitAll()
.and()
.authenticationProvider(activeDirectoryLdapAuthenticationProvider())
.authenticationProvider(kerberosServiceAuthenticationProvider())
.addFilterBefore(spnegoAuthenticationProcessingFilter(providerManager),
BasicAuthenticationFilter.class);
return http.build();
}
@Bean
public ActiveDirectoryLdapAuthenticationProvider activeDirectoryLdapAuthenticationProvider() {
return new ActiveDirectoryLdapAuthenticationProvider(adDomain, adServer);
}
@Bean
public SpnegoEntryPoint spnegoEntryPoint() {
return new SpnegoEntryPoint("/login");
}
public SpnegoAuthenticationProcessingFilter spnegoAuthenticationProcessingFilter(
AuthenticationManager authenticationManager) {
SpnegoAuthenticationProcessingFilter filter = new SpnegoAuthenticationProcessingFilter();
filter.setAuthenticationManager(authenticationManager);
return filter;
}
public KerberosServiceAuthenticationProvider kerberosServiceAuthenticationProvider() throws Exception {
KerberosServiceAuthenticationProvider provider = new KerberosServiceAuthenticationProvider();
provider.setTicketValidator(sunJaasKerberosTicketValidator());
provider.setUserDetailsService(ldapUserDetailsService());
return provider;
}
@Bean
public SunJaasKerberosTicketValidator sunJaasKerberosTicketValidator() {
SunJaasKerberosTicketValidator ticketValidator = new SunJaasKerberosTicketValidator();
ticketValidator.setServicePrincipal(servicePrincipal);
ticketValidator.setKeyTabLocation(new FileSystemResource(keytabLocation));
ticketValidator.setDebug(true);
return ticketValidator;
}
@Bean
public KerberosLdapContextSource kerberosLdapContextSource() throws Exception {
KerberosLdapContextSource contextSource = new KerberosLdapContextSource(adServer);
contextSource.setLoginConfig(loginConfig());
return contextSource;
}
public SunJaasKrb5LoginConfig loginConfig() throws Exception {
SunJaasKrb5LoginConfig loginConfig = new SunJaasKrb5LoginConfig();
loginConfig.setKeyTabLocation(new FileSystemResource(keytabLocation));
loginConfig.setServicePrincipal(servicePrincipal);
loginConfig.setDebug(true);
loginConfig.setIsInitiator(true);
loginConfig.afterPropertiesSet();
return loginConfig;
}
@Bean
public LdapUserDetailsService ldapUserDetailsService() throws Exception {
FilterBasedLdapUserSearch userSearch =
new FilterBasedLdapUserSearch(ldapSearchBase, ldapSearchFilter, kerberosLdapContextSource());
LdapUserDetailsService service =
new LdapUserDetailsService(userSearch, new ActiveDirectoryLdapAuthoritiesPopulator());
service.setUserDetailsMapper(new LdapUserDetailsMapper());
return service;
}
}
//end::snippetA[]
+5
View File
@@ -62,6 +62,11 @@
*** xref:servlet/authentication/runas.adoc[Run-As]
*** xref:servlet/authentication/logout.adoc[Logout]
*** xref:servlet/authentication/events.adoc[Authentication Events]
** xref:servlet/authentication/kerberos/index.adoc[Kerberos]
*** xref:servlet/authentication/kerberos/introduction.adoc[Introduction]
*** xref:servlet/authentication/kerberos/ssk.adoc[Reference]
*** xref:servlet/authentication/kerberos/samples.adoc[Samples]
*** xref:servlet/authentication/kerberos/appendix.adoc[Appendices]
** xref:servlet/authorization/index.adoc[Authorization]
*** xref:servlet/authorization/architecture.adoc[Authorization Architecture]
*** xref:servlet/authorization/authorize-http-requests.adoc[Authorize HTTP Requests]
@@ -0,0 +1,473 @@
[[appendices]]
= Appendices
:figures: servlet/authentication/kerberos
:numbered!:
[appendix]
== Material Used in this Document
Dummy UserDetailsService used in samples because we don't have a real
user source.
[source,java,indent=0]
----
include::example$kerberos/DummyUserDetailsService.java[tags=snippetA]
----
[appendix]
== Crash Course to Kerberos
In any authentication process there are usually a three parties
involved.
image::{figures}/drawio-kerb-cc1.png[]
First is a `client` which sometimes is a client computer but in most
of the scenarios it is the actual user sitting on a computer and
trying to access resources. Then there is the `resource` user is trying
to access. In this example it is a web server.
Then there is a `Key Distribution Center` or `KDC`. In a case of
Windows environment this would be a `Domain Controller`. `KDC` is the
one which really brings everything together and thus is the most
critical component in your environment. Because of this it is also
considered as a single point of failure.
Initially when `Kerberos` environment is setup and domain user
principals created into a database, encryption keys are also
created. These encryption keys are based on shared secrets(i.e. user
password) and actual passwords are never kept in a clear text.
Effectively `KDC` has its own key and other keys for domain users.
Interestingly there is no communication between a `resource` and a
`KDC` during the authentication process.
image::{figures}/drawio-kerb-cc2.png[]
When client wants to authenticate itself with a `resource` it first
needs to communicate with a `KDC`. `Client` will craft a special package
which contains encrypted and unencrypted parts. Unencrypted part
contains i.e. information about a user and encrypted part other
information which is part of a protocol. `Client` will encrypt package
data with its own key.
When `KDC` receives this authentication package from a client it
checks who this `client` claims to be from an unencrypted part and based
on that information it uses `client` decryption key it already have in
its database. If this decryption is succesfull `KDC` knows that this
`client` is the one it claims to be.
What KDC returns to a client is a ticket called `Ticket Granting
Ticket` which is signed by a KDC's own private key. Later when
`client` sends back this ticket it can try to decrypt it and if that
operation is succesfull it knows that it was a ticket it itself
originally signed and gave to a `client`.
image::{figures}/drawio-kerb-cc3.png[]
When client wants to get a ticket which it can use to authenticate
with a service, `TGT` is sent to `KDC` which then signs a service ticket
with service's own key. This a moment when a trust between
`client` and `service` is created. This service ticket contains data
which only `service` itself is able to decrypt.
image::{figures}/drawio-kerb-cc4.png[]
When `client` is authenticating with a service it sends previously
received service ticket to a service which then thinks that I don't
know anything about this guy but he gave me an authentication ticket.
What `service` can do next is try to decrypt that ticket and if that
operation is succesfull it knows that only other party who knows my
credentials is the `KDC` and because I trust him I can also trust that
this client is a one he claims to be.
[appendix]
== Setup Kerberos Environments
Doing a production setup of Kerberos environment is out of scope of
this document but this appendix provides some help to get you
started for setting up needed components for development.
[[setupmitkerberos]]
=== Setup MIT Kerberos
First action is to setup a new realm and a database.
[source,text,indent=0]
----
# kdb5_util create -s -r EXAMPLE.ORG
Loading random data
Initializing database '/var/lib/krb5kdc/principal' for realm 'EXAMPLE.ORG',
master key name 'K/M@EXAMPLE.ORG'
You will be prompted for the database Master Password.
It is important that you NOT FORGET this password.
Enter KDC database master key:
Re-enter KDC database master key to verify:
----
`kadmin` command can be used to administer Kerberos environment but
you can't yet use it because there are no admin users in a database.
[source,text,indent=0]
----
root@neo:/etc/krb5kdc# kadmin
Authenticating as principal root/admin@EXAMPLE.ORG with password.
kadmin: Client not found in Kerberos database while initializing
kadmin interface
----
Lets use `kadmin.local` command to create one.
[source,text,indent=0]
----
root@neo:/etc/krb5kdc# kadmin.local
Authenticating as principal root/admin@EXAMPLE.ORG with password.
kadmin.local: listprincs
K/M@EXAMPLE.ORG
kadmin/admin@EXAMPLE.ORG
kadmin/changepw@EXAMPLE.ORG
kadmin/cypher@EXAMPLE.ORG
krbtgt/EXAMPLE.ORG@EXAMPLE.ORG
kadmin.local: addprinc root/admin@EXAMPLE.ORG
WARNING: no policy specified for root/admin@EXAMPLE.ORG; defaulting to
no policy
Enter password for principal "root/admin@EXAMPLE.ORG":
Re-enter password for principal "root/admin@EXAMPLE.ORG":
Principal "root/admin@EXAMPLE.ORG" created.
----
Then enable admins by modifying `kadm5.acl` file and restart Kerberos
services.
[source,text,indent=0]
----
# cat /etc/krb5kdc/kadm5.acl
# This file Is the access control list for krb5 administration.
*/admin *
----
Now you can use `kadmin` with previously created `root/admin`
principal. Lets create our first user `user1`.
[source,text,indent=0]
----
kadmin: addprinc user1
WARNING: no policy specified for user1@EXAMPLE.ORG; defaulting to no
policy
Enter password for principal "user1@EXAMPLE.ORG":
Re-enter password for principal "user1@EXAMPLE.ORG":
Principal "user1@EXAMPLE.ORG" created.
----
Lets create our second user `user2` and export a keytab file.
[source,text,indent=0]
----
kadmin: addprinc user2
WARNING: no policy specified for user2@EXAMPLE.ORG; defaulting to no
policy
Enter password for principal "user2@EXAMPLE.ORG":
Re-enter password for principal "user2@EXAMPLE.ORG":
Principal "user2@EXAMPLE.ORG" created.
kadmin: ktadd -k /tmp/user2.keytab user2@EXAMPLE.ORG
Entry for principal user2@EXAMPLE.ORG with kvno 2, encryption type aes256-cts-hmac-sha1-96 added to keytab WRFILE:/tmp/user2.keytab.
Entry for principal user2@EXAMPLE.ORG with kvno 2, encryption type arcfour-hmac added to keytab WRFILE:/tmp/user2.keytab.
Entry for principal user2@EXAMPLE.ORG with kvno 2, encryption type des3-cbc-sha1 added to keytab WRFILE:/tmp/user2.keytab.
Entry for principal user2@EXAMPLE.ORG with kvno 2, encryption type des-cbc-crc added to keytab WRFILE:/tmp/user2.keytab.
----
Lets create a service ticket for tomcat and export credentials to a
keytab file named `tomcat.keytab`.
[source,text,indent=0]
----
kadmin: addprinc -randkey HTTP/neo.example.org@EXAMPLE.ORG
WARNING: no policy specified for HTTP/neo.example.org@EXAMPLE.ORG;
defaulting to no policy
Principal "HTTP/neo.example.org@EXAMPLE.ORG" created.
kadmin: ktadd -k /tmp/tomcat.keytab HTTP/neo.example.org@EXAMPLE.ORG
Entry for principal HTTP/neo.example.org@EXAMPLE.ORG with kvno 2, encryption type aes256-cts-hmac-sha1-96 added to keytab WRFILE:/tmp/tomcat2.keytab.
Entry for principal HTTP/neo.example.org@EXAMPLE.ORG with kvno 2, encryption type arcfour-hmac added to keytab WRFILE:/tmp/tomcat2.keytab.
Entry for principal HTTP/neo.example.org@EXAMPLE.ORG with kvno 2, encryption type des3-cbc-sha1 added to keytab WRFILE:/tmp/tomcat2.keytab.
Entry for principal HTTP/neo.example.org@EXAMPLE.ORG with kvno 2, encryption type des-cbc-crc added to keytab WRFILE:/tmp/tomcat2.keytab.
----
[[setupwinkerberos]]
=== Setup Windows Domain Controller
This was tested using `Windows Server 2012 R2`
[TIP]
====
Internet is full of good articles and videos how to setup Windows AD
but these two are quite usefull
http://www.rackspace.com/knowledge_center/article/installing-active-directory-on-windows-server-2012[Rackspace] and
http://social.technet.microsoft.com/wiki/contents/articles/12370.windows-server-2012-set-up-your-first-domain-controller-step-by-step.aspx[Microsoft
Technet].
====
- Normal domain controller and active directory setup was done.
- Used dns domain `example.org` and windows domain `EXAMPLE`.
- I created various domain users like `user1`, `user2`, `user3`,
`tomcat` and set passwords to `Password#`.
I eventually also added all ip's of my vm's to AD's dns server for
that not to cause any trouble.
[source,text]
----
Name: WIN-EKBO0EQ7TS7.example.org
Address: 172.16.101.135
Name: win8vm.example.org
Address: 172.16.101.136
Name: neo.example.org
Address: 172.16.101.1
----
Service Principal Name(SPN) needs to be setup with `HTTP` and a
server name `neo.example.org` where tomcat servlet container is run. This
is used with `tomcat` domain user and its `keytab` is then used as a
service credential.
[source,text]
----
PS C:\> setspn -A HTTP/neo.example.org tomcat
----
I exported keytab file which is copied to linux server running tomcat.
[source,text]
----
PS C:\> ktpass /out c:\tomcat.keytab /mapuser tomcat@EXAMPLE.ORG /princ HTTP/neo.example.org@EXAMPLE.ORG /pass Password# /ptype KRB5_NT_PRINCIPAL /crypto All
Targeting domain controller: WIN-EKBO0EQ7TS7.example.org
Using legacy password setting method
Successfully mapped HTTP/neo.example.org to tomcat.
----
[appendix]
== Troubleshooting
This appendix provides generic information about troubleshooting
errors and problems.
[IMPORTANT]
====
If you think environment and configuration is correctly setup, do
double check and ask other person to check possible obvious mistakes
or typos. Kerberos setup is generally very brittle and it is not
always very easy to debug where the problem lies.
====
.Cannot find key of appropriate type to decrypt
[source,text]
----
GSSException: Failure unspecified at GSS-API level (Mechanism level:
Invalid argument (400) - Cannot find key of appropriate type to
decrypt AP REP - RC4 with HMAC)
----
If you see abore error indicating missing key type, this will happen
with two different use cases. Firstly your JVM may not support
appropriate encryption type or it is disabled in your `krb5.conf`
file.
[source,text]
----
default_tkt_enctypes = rc4-hmac
default_tgs_enctypes = rc4-hmac
----
Second case is less obvious and hard to track because it will lead
into same error. This specific `GSSException` is throws also if you
simply don't have a required encryption key which then may be caused
by a misconfiguration in your kerberos server or a simply typo in your
principal.
.Using wrong kerberos configuration
{zwsp} +
In most system all commands and libraries will search kerberos
configuration either from a default locations or special locations
like JDKs. It's easy to get mixed up especially if working from unix
systems, which already may have default settings to work with MIT
kerberos, towards Windows domains.
This is a specific example what happens with `ldapsearch` trying to
query Windows AD using kerberos authentication.
[source,text]
----
$ ldapsearch -H ldap://WIN-EKBO0EQ7TS7.example.org -b "dc=example,dc=org"
SASL/GSSAPI authentication started
ldap_sasl_interactive_bind_s: Local error (-2)
additional info: SASL(-1): generic failure: GSSAPI Error:
Unspecified GSS failure. Minor code may provide more information
(No Kerberos credentials available)
----
Well that doesn't look good and is a simple indication that I don't
have a valid kerberos tickets as shown below.
[source,text]
----
$ klist
klist: Credentials cache file '/tmp/krb5cc_1000' not found
----
We already have a keytab file we exported from Windows AD to be used
with tomcat running on Linux. Lets try to use that to authenticate
with Windows AD.
You can have a dedicated config file which usually can be used with
native Linux commands and JVMs via system propertys.
[source,text]
----
$ cat krb5.ini
[libdefaults]
default_realm = EXAMPLE.ORG
default_keytab_name = /tmp/tomcat.keytab
forwardable=true
[realms]
EXAMPLE.ORG = {
kdc = WIN-EKBO0EQ7TS7.example.org:88
}
[domain_realm]
example.org=EXAMPLE.ORG
.example.org=EXAMPLE.ORG
----
Lets use that config and a keytab to get initial credentials.
[source,text]
----
$ env KRB5_CONFIG=/path/to/krb5.ini kinit -kt tomcat.keytab HTTP/neo.example.org@EXAMPLE.ORG
$ klist
Ticket cache: FILE:/tmp/krb5cc_1000
Default principal: HTTP/neo.example.org@EXAMPLE.ORG
Valid starting Expires Service principal
26/03/15 09:04:37 26/03/15 19:04:37 krbtgt/EXAMPLE.ORG@EXAMPLE.ORG
renew until 27/03/15 09:04:37
----
Lets see what happens if we now try to do a simple query against
Windows AD.
[source,text]
----
$ ldapsearch -H ldap://WIN-EKBO0EQ7TS7.example.org -b "dc=example,dc=org"
SASL/GSSAPI authentication started
ldap_sasl_interactive_bind_s: Local error (-2)
additional info: SASL(-1): generic failure: GSSAPI Error:
Unspecified GSS failure. Minor code may provide more information
(KDC returned error string: PROCESS_TGS)
----
This may be simply because `ldapsearch` is getting confused and simply
using wrong configuration. You can tell `ldapsearch` to use a
different configuration via `KRB5_CONFIG` env variable just like we
did with `kinit`. You can also use `KRB5_TRACE=/dev/stderr` to get
more verbose output of what native libraries are doing.
[source,text]
----
$ env KRB5_CONFIG=/path/to/krb5.ini ldapsearch -H ldap://WIN-EKBO0EQ7TS7.example.org -b "dc=example,dc=org"
$ klist
Ticket cache: FILE:/tmp/krb5cc_1000
Default principal: HTTP/neo.example.org@EXAMPLE.ORG
Valid starting Expires Service principal
26/03/15 09:11:03 26/03/15 19:11:03 krbtgt/EXAMPLE.ORG@EXAMPLE.ORG
renew until 27/03/15 09:11:03
26/03/15 09:11:44 26/03/15 19:11:03
ldap/win-ekbo0eq7ts7.example.org@EXAMPLE.ORG
renew until 27/03/15 09:11:03
----
Above you can see what happened if query was successful by looking
kerberos tickets. Now you can experiment with further query commands
i.e. if you working with `KerberosLdapContextSource`.
[source,text]
----
$ ldapsearch -H ldap://WIN-EKBO0EQ7TS7.example.org \
-b "dc=example,dc=org" \
"(| (userPrincipalName=user2@EXAMPLE.ORG)
(sAMAccountName=user2@EXAMPLE.ORG))" \
dn
...
# test user, example.org
dn: CN=test user,DC=example,DC=org
----
[appendix]
[[browserspnegoconfig]]
== Configure Browsers for Spnego Negotiation
=== Firefox
Complete following steps to ensure that your Firefox browser is
enabled to perform Spnego authentication.
- Open Firefox.
- At address field, type *about:config*.
- In filter/search, type *negotiate*.
- Parameter *network.negotiate-auth.trusted-uris* may be set to
default *https://* which doesn't work for you. Generally speaking
this parameter has to replaced with the server address if Kerberos
delegation is required.
- It is recommended to use `https` for all communication.
=== Chrome
With Google Chrome you generally need to set command-line parameters
order to white list servers with Chrome will negotiate.
- on Windows machines (clients): Chrome shares the configuration with
Internet Explorer so if all changes were applied to IE (as described
in E.3), nothing has to be passed via command-line parameters.
- on Linux/Mac OS machines (clients): the command-line parameter
`--auth-negotiate-delegate-whitelist` should only used if Kerberos
delegation is required (otherwise do not set this parameter).
- It is recommended to use `https` for all communication.
[source,text]
----
--auth-server-whitelist="*.example.com"
--auth-negotiate-delegate-whitelist="*.example.com"
----
You can see which policies are enable by typing *chrome://policy/*
into Chrome's address bar.
With Linux Chrome will also read policy files from
`/etc/opt/chrome/policies/managed` directory.
.mypolicy.json
[source,json]
----
{
"AuthServerWhitelist" : "*.example.org",
"AuthNegotiateDelegateWhitelist" : "*.example.org",
"DisableAuthNegotiateCnameLookup" : true,
"EnableAuthNegotiatePort" : true
}
----
=== Internet Explorer
Complete following steps to ensure that your Internet Explorer browser
is enabled to perform Spnego authentication.
- Open Internet Explorer.
- Click *Tools > Intenet Options > Security* tab.
- In *Local intranet* section make sure your server is trusted by i.e.
adding it into a list.
@@ -0,0 +1,3 @@
= Spring Security Kerberos
Spring Security Kerberos adds the ability to work with Kerberos and Spring applications.
@@ -0,0 +1,5 @@
[[introduction]]
= Introduction
Spring Security Kerberos {spring-security-version} is built and tested with JDK 17,
Spring Security {spring-security-version} and Spring Framework {spring-core-version}.
@@ -0,0 +1,225 @@
[[springsecuritykerberossamples]]
= Spring Security Kerberos Samples
:figures: servlet/authentication/kerberos
This part of the reference documentation is introducing samples
projects. Samples can be compiled manually by building main
distribution from
https://github.com/spring-projects/spring-security-kerberos.
[IMPORTANT]
====
If you run sample as is it will not work until a correct configuration
is applied. See notes below for specific samples.
====
<<samples-sec-server-win-auth>> sample for Windows environment
<<samples-sec-server-client-auth>> sample using server side authenticator
<<samples-sec-server-spnego-form-auth>> sample using ticket validation
with spnego and form
<<samples-sec-client-rest-template>> sample for KerberosRestTemplate
[[samples-sec-server-win-auth]]
== Security Server Windows Auth Sample
Goals of this sample:
- In windows environment, User will be able to logon to application
with Windows Active directory Credential which has been entered
during log on to windows. There should not be any ask for
userid/password credentials.
- In non-windows environment, User will be presented with a screen
to provide Active directory credentials.
[source,yaml,indent=0]
----
server:
port: 8080
app:
ad-domain: EXAMPLE.ORG
ad-server: ldap://WIN-EKBO0EQ7TS7.example.org/
service-principal: HTTP/neo.example.org@EXAMPLE.ORG
keytab-location: /tmp/tomcat.keytab
ldap-search-base: dc=example,dc=org
ldap-search-filter: "(| (userPrincipalName={0}) (sAMAccountName={0}))"
----
In above you can see the default configuration for this sample. You
can override these settings using a normal Spring Boot tricks like
using command-line options or custom `application.yml` file.
Run a server.
[source,text,subs="attributes"]
----
$ java -jar sec-server-win-auth-{spring-security-version}.jar
----
[IMPORTANT]
====
You may need to use custom kerberos config with Linux either by using
`-Djava.security.krb5.conf=/path/to/krb5.ini` or
`GlobalSunJaasKerberosConfig` bean.
====
[NOTE]
====
See xref:servlet/authentication/kerberos/appendix.adoc#setupwinkerberos[Setup Windows Domain Controller]
for more instructions how to work with windows kerberos environment.
====
Login to `Windows 8.1` using domain credentials and access sample
image::{figures}/ie1.png[]
image::{figures}/ie2.png[]
Access sample application from a non windows vm and use domain
credentials manually.
image::{figures}/ff1.png[]
image::{figures}/ff2.png[]
image::{figures}/ff3.png[]
[[samples-sec-server-client-auth]]
== Security Server Side Auth Sample
This sample demonstrates how server is able to authenticate user
against kerberos environment using his credentials passed in via a
form login.
Run a server.
[source,text,subs="attributes"]
----
$ java -jar sec-server-client-auth-{spring-security-version}.jar
----
[source,yaml,indent=0]
----
server:
port: 8080
----
[[samples-sec-server-spnego-form-auth]]
== Security Server Spnego and Form Auth Sample
This sample demonstrates how a server can be configured to accept a
Spnego based negotiation from a browser while still being able to fall
back to a form based authentication.
Using a `user1` principal xref:servlet/authentication/kerberos/appendix.adoc#setupmitkerberos[Setup MIT Kerberos],
do a kerberos login manually using credentials.
[source,text]
----
$ kinit user1
Password for user1@EXAMPLE.ORG:
$ klist
Ticket cache: FILE:/tmp/krb5cc_1000
Default principal: user1@EXAMPLE.ORG
Valid starting Expires Service principal
10/03/15 17:18:45 11/03/15 03:18:45 krbtgt/EXAMPLE.ORG@EXAMPLE.ORG
renew until 11/03/15 17:18:40
----
or using a keytab file.
[source,text]
----
$ kinit -kt user2.keytab user1
$ klist
Ticket cache: FILE:/tmp/krb5cc_1000
Default principal: user2@EXAMPLE.ORG
Valid starting Expires Service principal
10/03/15 17:25:03 11/03/15 03:25:03 krbtgt/EXAMPLE.ORG@EXAMPLE.ORG
renew until 11/03/15 17:25:03
----
Run a server.
[source,text,subs="attributes"]
----
$ java -jar sec-server-spnego-form-auth-{spring-security-version}.jar
----
Now you should be able to open your browser and let it do Spnego
authentication with existing ticket.
[NOTE]
====
See xref:servlet/authentication/kerberos/appendix.adoc#browserspnegoconfig[Configure Browsers for Spnego Negotiation]
for more instructions for configuring browsers to use Spnego.
====
[source,yaml,indent=0]
----
server:
port: 8080
app:
service-principal: HTTP/neo.example.org@EXAMPLE.ORG
keytab-location: /tmp/tomcat.keytab
----
[[samples-sec-client-rest-template]]
== Security Client KerberosRestTemplate Sample
This is a sample using a Spring RestTemplate to access Kerberos
protected resource. You can use this together with
<<samples-sec-server-spnego-form-auth>>.
Default application is configured as shown below.
[source,yaml,indent=0]
----
app:
user-principal: user2@EXAMPLE.ORG
keytab-location: /tmp/user2.keytab
access-url: http://neo.example.org:8080/hello
----
Using a `user1` principal xref:servlet/authentication/kerberos/appendix.adoc#setupmitkerberos[Setup MIT Kerberos],
do a kerberos login manually using credentials.
[source,text,subs="attributes"]
----
$ java -jar sec-client-rest-template-{spring-security-version}.jar --app.user-principal --app.keytab-location
----
[NOTE]
====
In above we simply set `app.user-principal` and `app.keytab-location`
to empty values which disables a use of keytab file.
====
If operation is succesfull you should see below output with `user1@EXAMPLE.ORG`.
[source,text]
----
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity3">
<head>
<title>Spring Security Kerberos Example</title>
</head>
<body>
<h1>Hello user1@EXAMPLE.ORG!</h1>
</body>
</html>
----
Or use a `user2` with a keytab file.
[source,text,subs="attributes"]
----
$ java -jar sec-client-rest-template-{spring-security-version}.jar
----
If operation is succesfull you should see below output with `user2@EXAMPLE.ORG`.
[source,text]
----
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity3">
<head>
<title>Spring Security Kerberos Example</title>
</head>
<body>
<h1>Hello user2@EXAMPLE.ORG!</h1>
</body>
</html>
----
@@ -0,0 +1,85 @@
[[springsecuritykerberos]]
= Spring and Spring Security Kerberos
:figures: servlet/authentication/kerberos
This part of the reference documentation explains the core functionality
that Spring Security Kerberos provides to any Spring based application.
<<ssk-authprovider>> describes the authentication provider support.
<<ssk-spnego>> describes the spnego negotiate support.
<<ssk-resttemplate>> describes the RestTemplate support.
[[ssk-authprovider]]
== Authentication Provider
Provider configuration using JavaConfig.
[source,java,indent=0]
----
include::example$kerberos/AuthProviderConfig.java[tags=snippetA]
----
[[ssk-spnego]]
== Spnego Negotiate
Spnego configuration using JavaConfig.
[source,java,indent=0]
----
include::example$kerberos/SpnegoConfig.java[tags=snippetA]
----
[[ssk-resttemplate]]
== Using KerberosRestTemplate
If there is a need to access Kerberos protected web resources
programmatically we have `KerberosRestTemplate` which extends
`RestTemplate` and does necessary login actions prior to delegating to
actual RestTemplate methods. You basically have few options to
configure this template.
- Leave keyTabLocation and userPrincipal empty if you want to
use cached ticket.
- Use keyTabLocation and userPrincipal if you want to use
keytab file.
- Use loginOptions if you want to customise Krb5LoginModule options.
- Use a customised httpClient.
With ticket cache.
[source,java,indent=0]
----
include::example$kerberos/KerberosRestTemplateConfig.java[tags=snippetA]
----
With keytab file.
[source,java,indent=0]
----
include::example$kerberos/KerberosRestTemplateConfig.java[tags=snippetB]
----
[[ssk-kerberosldap]]
== Authentication with LDAP Services
With most of your samples we're using `DummyUserDetailsService`
because there is not necessarily need to query a real user details
once kerberos authentication is successful and we can use kerberos
principal info to create that dummy user. However there is a way to
access kerberized LDAP services in a say way and query user details
from there.
`KerberosLdapContextSource` can be used to bind into LDAP via kerberos
which is at least proven to work well with Windows AD services.
[source,java,indent=0]
----
include::example$kerberos/KerberosLdapContextSourceConfig.java[tags=snippetA]
----
[TIP]
====
Sample xref:servlet/authentication/kerberos/samples.adoc#samples-sec-server-win-auth[Security Server Windows Auth Sample]
is currently configured to query user details from AD if authentication happen via kerberos.
====
+4
View File
@@ -9,6 +9,10 @@ Below are the highlights of the release, or you can view https://github.com/spri
Being a major release, there are a number of deprecated APIs that are removed in Spring Security 7.
Each section that follows will indicate the more notable removals as well as the new features in that module
== Modules
* The https://github.com/spring-projects/spring-security-kerberos[Spring Security Kerberos Extension] is now part of Spring Security. See the xref:servlet/authentication/kerberos/index.adoc[Kerberos] section of the reference for details.
== Core
* Removed `AuthorizationManager#check` in favor of `AuthorizationManager#authorize`