Перейти к основному содержимому

Интеграция Spring Security Kerberos с MiniKdc

· 6 мин. чтения

1. Обзор

В этом руководстве мы представим обзор Spring Security Kerberos.

Мы напишем клиент Kerberos на Java, который авторизует себя для доступа к нашей службе Kerberized. И мы запустим собственный встроенный центр распространения ключей для выполнения полной сквозной проверки подлинности Kerberos. Все это без какой-либо внешней инфраструктуры благодаря Spring Security Kerberos .

2. Kerberos и его преимущества

Kerberos — это сетевой протокол аутентификации, созданный Массачусетским технологическим институтом в 1980-х годах и особенно полезный для централизации аутентификации в сети.

В 1987 году Массачусетский технологический институт выпустил его для сообщества открытого исходного кода , и он все еще находится в стадии активной разработки. В 2005 году он был канонизирован как стандарт IETF в соответствии с RFC 4120 . ** **

Обычно Kerberos используется в корпоративной среде . Там он защищает среду таким образом, что пользователю не нужно проходить аутентификацию для каждой службы отдельно . Это архитектурное решение известно как единый вход . ``

Проще говоря, Kerberos — это система продажи билетов. Пользователь проходит аутентификацию один раз и получает Билет на предоставление Билета (TGT). Затем сетевая инфраструктура обменивает этот TGT на сервисные билеты. Эти служебные билеты позволяют пользователю взаимодействовать с инфраструктурными службами до тех пор, пока действует TGT, что обычно составляет пару часов.

Поэтому здорово, что пользователь подписывает только один раз. Но есть и преимущество в плане безопасности: в такой среде пароль пользователя никогда не передается по сети . Вместо этого Kerberos использует его как фактор для создания другого секретного ключа, который будет использоваться для шифрования и дешифрования сообщений.

Еще одним преимуществом является то, что мы можем управлять пользователями из центрального места, скажем, с помощью LDAP. Поэтому, если мы отключим учетную запись в нашей централизованной базе данных для данного пользователя, мы лишим его доступа в нашей инфраструктуре. Таким образом, администраторам не нужно отзывать доступ отдельно в каждом сервисе.

Введение в аутентификацию SPNEGO/Kerberos в Spring содержит подробный обзор технологии.

3. Керберизированная среда

Итак, давайте создадим среду для аутентификации по протоколу Kerberos. Среда будет состоять из трех отдельных приложений, которые будут работать одновременно.

Во- первых, у нас будет центр распространения ключей , который будет действовать как точка аутентификации. Далее мы напишем клиентское и сервисное приложения, которые настроим для использования протокола Kerberos.

Теперь для запуска Kerberos требуется небольшая установка и настройка. Однако мы будем использовать Spring Security Kerberos , поэтому запустим Центр распространения ключей программно во встроенном режиме. Кроме того, MiniKdc , показанный ниже, полезен в случае интеграционного тестирования с инфраструктурой Kerberized.

3.1. Запуск центра распространения ключей

Во-первых, мы запустим наш Центр распространения ключей, который будет выдавать нам TGT:

String[] config = MiniKdcConfigBuilder.builder()
.workDir(prepareWorkDir())
.principals("client/localhost", "HTTP/localhost")
.confDir("minikdc-krb5.conf")
.keytabName("example.keytab")
.build();

MiniKdc.main(config);

По сути, мы предоставили MiniKdc набор принципалов и файл конфигурации; кроме того, мы указали MiniKdc , как следует называть созданную им таблицу ключей .

MiniKdc сгенерирует файл krb5.conf , который мы предоставим нашим клиентским и сервисным приложениям. Этот файл содержит информацию о том, где найти наш KDC — хост и порт для данной области.

MiniKdc.main запускает KDC и должен вывести что-то вроде:

Standalone MiniKdc Running
---------------------------------------------------
Realm : EXAMPLE.COM
Running at : localhost:localhost
krb5conf : .\spring-security-sso\spring-security-sso-kerberos\krb-test-workdir\krb5.conf

created keytab : .\spring-security-sso\spring-security-sso-kerberos\krb-test-workdir\example.keytab
with principals : [client/localhost, HTTP/localhost]

3.2. Клиентское приложение

Нашим клиентом будет приложение Spring Boot, использующее RestTemplate для выполнения вызовов внешнего API REST.

Но вместо этого мы собираемся использовать KerberosRestTemplate . Ему понадобится keytab и принципал клиента:

@Configuration
public class KerberosConfig {

@Value("${app.user-principal:client/localhost}")
private String principal;

@Value("${app.keytab-location}")
private String keytabLocation;

@Bean
public RestTemplate restTemplate() {
return new KerberosRestTemplate(keytabLocation, principal);
}
}

Вот и все! KerberosRestTemplate согласовывает для нас клиентскую часть протокола Kerberos.

Итак, давайте создадим быстрый класс, который будет запрашивать некоторые данные из службы Kerberized, размещенной в конечной точке app.access-url :

@Service
class SampleClient {

@Value("${app.access-url}")
private String endpoint;

private RestTemplate restTemplate;

// constructor, getter, setter

String getData() {
return restTemplate.getForObject(endpoint, String.class);
}
}

Итак, давайте сейчас создадим наше сервисное приложение, чтобы этому классу было что вызывать!

3.3. Сервисное приложение

Мы будем использовать Spring Security, настроив его с помощью соответствующих bean-компонентов, специфичных для Kerberos.

Также обратите внимание, что служба будет иметь своего принципала и также будет использовать keytab:

@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

@Value("${app.service-principal:HTTP/localhost}")
private String servicePrincipal;

@Value("${app.keytab-location}")
private String keytabLocation;

@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/", "/home").permitAll()
.anyRequest().authenticated()
.and()
.exceptionHandling()
.authenticationEntryPoint(spnegoEntryPoint())
.and()
.formLogin()
.loginPage("/login").permitAll()
.and()
.logout().permitAll()
.and()
.addFilterBefore(spnegoAuthenticationProcessingFilter(authenticationManagerBean()),
BasicAuthenticationFilter.class);
}

@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth
.authenticationProvider(kerberosAuthenticationProvider())
.authenticationProvider(kerberosServiceAuthenticationProvider());
}

@Bean
public KerberosAuthenticationProvider kerberosAuthenticationProvider() {
KerberosAuthenticationProvider provider = new KerberosAuthenticationProvider();
// provider configuration
return provider;
}

@Bean
public SpnegoEntryPoint spnegoEntryPoint() {
return new SpnegoEntryPoint("/login");
}

@Bean
public SpnegoAuthenticationProcessingFilter spnegoAuthenticationProcessingFilter(
AuthenticationManager authenticationManager) {
SpnegoAuthenticationProcessingFilter filter = new SpnegoAuthenticationProcessingFilter();
// filter configuration
return filter;
}

@Bean
public KerberosServiceAuthenticationProvider kerberosServiceAuthenticationProvider() {
KerberosServiceAuthenticationProvider provider = new KerberosServiceAuthenticationProvider();
// auth provider configuration
return provider;
}

@Bean
public SunJaasKerberosTicketValidator sunJaasKerberosTicketValidator() {
SunJaasKerberosTicketValidator ticketValidator = new SunJaasKerberosTicketValidator();
// validator configuration
return ticketValidator;
}
}

Вступительная статья содержит всю описанную выше реализацию, поэтому для краткости мы опускаем здесь полные методы.

Обратите внимание, что мы настроили Spring Security для аутентификации SPNEGO . Таким образом, мы сможем проходить аутентификацию по протоколу HTTP, хотя мы также можем добиться аутентификации SPNEGO с помощью ядра Java .

4. Тестирование

Теперь мы запустим интеграционный тест, чтобы показать, что наш клиент успешно извлекает данные с внешнего сервера по протоколу Kerberos . Чтобы запустить этот тест, нам нужно, чтобы наша инфраструктура работала, поэтому MiniKdc и наше сервисное приложение должны быть запущены.

По сути, мы будем использовать наш SampleClient из клиентского приложения, чтобы сделать запрос к нашему сервисному приложению. Давайте проверим это:

@Autowired
private SampleClient sampleClient;

@Test
public void givenKerberizedRestTemplate_whenServiceCall_thenSuccess() {
assertEquals("data from kerberized server", sampleClient.getData());
}

Обратите внимание, что мы также можем доказать важность KerberizedRestTemplate , запустив службу без него:

@Test
public void givenRestTemplate_whenServiceCall_thenFail() {
sampleClient.setRestTemplate(new RestTemplate());
assertThrows(RestClientException.class, sampleClient::getData);
}

В качестве примечания: есть шанс, что наш второй тест может повторно использовать билет, уже сохраненный в кэше учетных данных . Это может произойти из-за автоматического согласования SPNEGO, используемого в HttpUrlConnection .

В результате данные могут действительно вернуться, что сделает наш тест недействительным. Таким образом, в зависимости от наших потребностей, мы можем отключить использование кэша билетов через системное свойство http.use.global.creds=false.

5. Вывод

В этом руководстве мы изучили Kerberos для централизованного управления пользователями и то, как Spring Security поддерживает протокол Kerberos и механизм аутентификации SPNEGO.

Мы использовали MiniKdc для установки встроенного KDC, а также создали очень простой клиент и сервер с поддержкой Kerberos. Эта настройка была удобна для исследования и особенно удобна, когда мы создали интеграционный тест для проверки.

Теперь мы только что поцарапали поверхность. Чтобы углубиться, посетите вики-страницу Kerberos или RFC . Также будет полезна официальная страница документации . Помимо этого, чтобы увидеть, как это можно сделать в основной Java, в следующем руководстве Oracle это подробно показано.

Как обычно, код можно найти на нашей странице GitHub .