Spring Security Kerberos統合

1. 概要

このチュートリアルでは、Spring Security Kerberosの概要を説明します。
Kerberos化されたサービスへのアクセスを許可するKerberosクライアントをJavaで作成します。 そして、独自の組み込みKey Distribution Centerを実行して、完全なエンドツーエンドのKerberos認証を実行します。 https://docs.spring.io/spring-security-kerberos/docs/1.0.1.RELEASE/reference/htmlsingle/[Spring Security Kerberos]のおかげで、外部インフラストラクチャは不要です。

2. Kerberosとその利点

Kerberosは、MITが1980年代に作成したネットワーク認証プロトコルであり、特にネットワーク上で認証を集中化するのに役立ちます。
1987年に、MITはhttps://github.com/krb5/krb5 [オープンソース]コミュニティにリリースし、現在も活発に開発中です。 2005年には、** ** https://tools.ietf.org/html/rfc4120 [RFC 4120]でIETF標準として標準化されました。
通常、Kerberosは*企業環境で使用されます*。 そこでは、*ユーザーが各サービスを個別に認証する必要がない*ように環境を保護します*。 このアーキテクチャソリューションは、* _ Single Sign-on_ *と呼ばれます。
簡単に言えば、Kerberosはチケットシステムです。 ユーザーは、*一度認証**し、*チケット許可チケット*(TGT)を受け取ります。 *その後、ネットワークインフラストラクチャはTGTをサービスチケットと交換します。*これらのサービスチケットは、TGTが有効である限り(通常は数時間)、ユーザーがインフラストラクチャサービスと対話できるようにします。
そのため、ユーザーがサインインするのは一度だけであることは素晴らしいことです。 しかし、セキュリティ上の利点もあります。そのような環境では、*ユーザーのパスワードがネットワーク経由で送信されることはありません*。 代わりに、Kerberosはそれを要素として使用して、メッセージの暗号化と復号化に使用される別の秘密鍵を生成します。
もう1つの利点は、* LDAPに支援されているユーザーなど、ユーザーを中央の場所から管理できることです*。 したがって、特定のユーザーの集中データベースでアカウントを無効にすると、インフラストラクチャでのアクセスが取り消されます。 したがって、管理者は各サービスで個別にアクセスを取り消す必要はありません。

3. Kerberos環境

それでは、Kerberosプロトコルで認証するための環境を作成しましょう。 環境は、同時に実行される3つの個別のアプリケーションで構成されます。
まず、認証ポイントとして機能するキー配布センターがあります。 次に、Kerberosプロトコルを使用するように構成するクライアントとサービスアプリケーションを作成します。
現在、Kerberosを実行するには、少しのインストールと構成が必要です。 ただし、https://docs.spring.io/spring-security-kerberos/docs/1.0.1.RELEASE/reference/htmlsingle/ [Spring Security Kerberos]を活用するため、Key Distribution Centerをプログラムで実行します。 、埋め込みモード。 また、以下に示す_MiniKdc_は、Kerberosインフラストラクチャとの統合テストの場合に役立ちます。

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_にプリンシパルのセットと構成ファイルを指定しました。さらに、生成する_https://web.mit.edu/kerberos/krb5-devel/doc/basic/keytab_def.html [keytab] _を何と呼ぶか​​を__MiniKdc_に伝えました。
_MiniKdc_は、クライアントアプリケーションとサービスアプリケーションに提供するhttps://web.mit.edu/kerberos/krb5-1.12/doc/admin/conf_files/krb5_conf.html[_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 ___を使用して外部のREST APIを呼び出すSpring Bootアプリケーションになります。
しかし、代わりに* Kerberos_RestTemplate_を使用します*。 キータブとクライアントのプリンシパルが必要です。
@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 __negotiatesは、Kerberosプロトコルのクライアント側を決定します。
それでは、エンドポイント_app.access-url_でホストされている、Kerberos化されたサービスからいくつかのデータをクエリするクイッククラスを作成しましょう。
@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;
    }
}
https://tools.ietf.org/html/rfc4559[SPNEGO認証]用にSpring Securityを設定していることに注意してください。 これにより、httpsプロトコルを介して認証できるようになりますが、https://docs.oracle.com/javase/8/docs/technotes/guides/security/jgss/lab/part6.html [コア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番目のテストでは、https://web.mit.edu/kerberos/krb5-1.12/doc/basic/ccache_def.html [credential cache] *に既に保存されているチケットを再利用できる可能性があります* 。 これは、_HttpUrlConnection_で使用される自動SPNEGOネゴシエーションが原因で発生します。
その結果、*データが実際に返され、テストが無効になる可能性があります。*ニーズに応じて、システムプロパティ_http.use.global.creds = false._を使用してチケットキャッシュの使用を無効にできます。

5. 結論

このチュートリアルでは、*集中ユーザー管理のためのKerberos *と、Spring SecurityがKerberosプロトコルとSPNEGO認証メカニズムをどのようにサポートするかを調べました。
_MiniKdc_を使用して組み込みKDCを立ち上げ、非常に単純なKerberos化されたクライアントとサーバーも作成しました。 この設定は探索に便利で、特にテストのために統合テストを作成したときに便利でした。
これで、表面をスクラッチしただけです。 さらに詳しく調べるには、Kerberos https://en.wikipedia.org/wiki/Kerberos_(protocol)[wiki page]またはhttps://tools.ietf.org/html/rfc4120[its RFC]を確認してください。 また、https://web.mit.edu/kerberos/krb5-latest/doc/ [公式ドキュメントページ]も役立ちます。 それ以外に、コアJavaで物事がどのように行われるかを確認するには、https://docs.oracle.com/javase/8/docs/technotes/guides/security/jgss/lab/ [Oracleのチュートリアルに従う]に示されています。詳細に。
いつものように、コードはhttps://github.com/eugenp/tutorials/tree/master/spring-security-sso/spring-security-sso-kerberos[GitHub]ページで見つけることができます。