1. 概要

このチュートリアルでは、SpringSecurityKerberosの概要を説明します。

Javaで、Kerberos化されたサービスへのアクセスを許可するKerberosクライアントを作成します。 また、独自の組み込みKey Distribution Centerを実行して、完全なエンドツーエンドのKerberos認証を実行します。 Spring Security Kerberos のおかげで、外部インフラストラクチャは必要ありません。

2. Kerberosとその利点

Kerberosは、MITが1980年代に作成したネットワーク認証プロトコルであり、ネットワークでの認証の集中化に特に役立ちます。

1987年に、MITはそれをオープンソースコミュニティにリリースし、現在も活発に開発されています。 2005年には、 RFC4120の下でIETF標準として標準化されました。

通常、Kerberosは企業環境で使用されます。  そこでは、ユーザーが各サービスに対して個別に認証する必要がないように環境を保護します。 このアーキテクチャソリューションは、シングルサインオンとして知られています。

簡単に言えば、Kerberosはチケットシステムです。 ユーザーは一度を認証し、はチケット許可チケット(TGT)を受け取ります。 次に、ネットワークインフラストラクチャはそのTGTをサービスチケットと交換します。これらのサービスチケットを使用すると、TGTが有効である限り(通常は数時間)、ユーザーはインフラストラクチャサービスを操作できます。

したがって、ユーザーが一度だけサインインするのは素晴らしいことです。 ただし、セキュリティ上の利点もあります。このような環境では、ユーザーのパスワードがネットワークを介して送信されることはありません。 代わりに、Kerberosはそれを要素として使用して、メッセージの暗号化と復号化に使用される別の秘密鍵を生成します。

もう1つの利点は、中央の場所からユーザーを管理できることです。たとえば、LDAPに支えられている場所です。 したがって、特定のユーザーの集中型データベースでアカウントを無効にすると、インフラストラクチャでのそのユーザーのアクセスが取り消されます。 したがって、管理者は各サービスで個別にアクセスを取り消す必要はありません。

SpringでのSPNEGO/Kerberos認証の概要は、テクノロジーの詳細な概要を提供します。

3. ケルバ化された環境

それでは、Kerberosプロトコルで認証するための環境を作成しましょう。 環境は、同時に実行される3つの別個のアプリケーションで構成されます。

まず、認証ポイントとして機能するキー配布センターがあります。 次に、Kerberosプロトコルを使用するように構成するクライアントとサービスアプリケーションを作成します。

現在、Kerberosを実行するには、少しのインストールと構成が必要です。 ただし、 Spring Security Kerberos を利用するため、プログラムで組み込みモードでKeyDistributionCenterを実行します。 また、以下に示す 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. クライアントアプリケーション

クライアントは、RestTemplateを使用して外部のRESTAPIを呼び出すSpring Bootアプリケーションになります。

ただし、代わりにKerberosRestTemplateを使用します。 キータブとクライアントのプリンシパルが必要です。

@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プロトコルのクライアント側をネゴシエートします。

それでは、エンドポイントapp.access-urlでホストされているKerberizedサービスからいくつかのデータをクエリするクイッククラスを作成しましょう。

@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を使用して、適切なKerberos固有のBeanで構成します。

また、サービスにはプリンシパルがあり、キータブも使用することに注意してください。

@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;
    }
}

イントロ記事には上記のすべての実装が含まれているため、簡潔にするためにここでは完全なメソッドを省略しています。

SPNEGO認証用にSpringSecurityを構成したことに注意してください。 このようにして、HTTPプロトコルを介して認証できるようになりますが、コアJavaを使用してSPNEGO認証を実現することもできます。

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);
}

ちなみに、2番目のテストでクレデンシャルキャッシュにすでに保存されているチケットを再利用できる可能性があります。 これは、HttpUrlConnectionで使用される自動SPNEGOネゴシエーションが原因で発生します。

その結果、データが実際に返され、テストが無効になる可能性があります。必要に応じて、システムプロパティ http.use.global.creds=falseを使用してチケットキャッシュの使用を無効にできます。

5. 結論

このチュートリアルでは、集中ユーザー管理のためのKerberos と、SpringSecurityがKerberosプロトコルとSPNEGO認証メカニズムをサポートする方法について説明しました。

MiniKdc を使用して組み込みKDCを立ち上げ、非常に単純なKerberizedクライアントとサーバーも作成しました。 この設定は、探索に便利であり、統合テストを作成してテストするときに特に便利でした。

さて、表面を引っかいたところです。 詳細については、KerberoswikiページまたはそのRFCを確認してください。 また、公式ドキュメントページも参考になります。 それ以外に、コアjavaでどのように処理できるかを確認するために、Oracleのチュートリアルに続くで詳細を示しています。

いつものように、コードはGitHubページにあります。