セキュリティ

Spring Security と Spring Boot は多くのセキュリティ構成をサポートしているため、(物理ネットワークセキュリティから OAuth2 ベアラートークンまで)自分にとって意味のある方法で ConfigServer を保護できます。

HTTP 基本認証

デフォルトの SpringBoot 構成 HTTP 基本セキュリティを使用するには、クラスパスに Spring Security を含めます(たとえば、spring-boot-starter-security を介して)。デフォルトは、user のユーザー名とランダムに生成されたパスワードです。ランダムパスワードは実際には役に立たないため、(spring.security.user.password を設定して)パスワードを構成し、暗号化することをお勧めします(その方法については以下を参照してください)。

application.yml
spring:
  security:
    user:
      name: configserver
      password: "{cipher}ENCRYPTED_PASSWORD_HERE"

クライアント側では、bootstrap.yml (または spring.config.import を使用する場合は application.yml)で認証情報を設定します。

bootstrap.yml (クライアント)
spring:
  cloud:
    config:
      uri: https://config.example.com
      username: configserver
      password: "${CONFIG_SERVER_PASSWORD}"

OAuth2/JWT ベアラートークン認証

多数のクライアントが個別に認証を行う必要がある本番環境のデプロイでは、JWT ベアラートークンを使用した OAuth2 が一般的な選択肢です。Spring Security のリソースサーバーサポートは、有効な JWT を含むリクエストのみが受け入れられるように、コンフィグサーバーのエンドポイントを保護できます。

リソースサーバーの依存関係を追加します。

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-oauth2-resource-server</artifactId>
</dependency>

構成サーバーの application.yml で JWKS URI(または発行者 URI)を設定します。

application.yml (構成サーバー)
spring:
  security:
    oauth2:
      resourceserver:
        jwt:
          issuer-uri: https://idp.example.com/issuer

上記プロパティが設定されている場合、Spring Security は JWT ベアラートークンの検証を自動的に構成します。クライアントは認証サーバーからトークンを取得し、それを Bearer ヘッダーとして渡す必要があります。

構成クライアント側では、カスタム RestTemplate を設定するか、環境 / ブートストラッププロパティを介してトークンを提供することにより、Authorization ヘッダーを設定します。

Spring Cloud Config はクライアント側で OAuth2 トークンを自動的に取得しません。クライアント側でトークンを取得するように設定し(たとえば、client_credentials グラントを使用した spring-security-oauth2-client など)、取得したトークンを構成サーバーのリクエストに添付する必要があります。

アプリケーションごとのアクセス制御

デフォルトでは、認証済みのユーザーはどの {application} に対しても設定をリクエストできます。各クライアントが独自の設定のみにアクセスできるように制限する必要がある場合(たとえば、あるマイクロサービスが別のサービスのシークレットを読み取ることを防ぐ場合)、カスタムの SecurityFilterChain または RequestMatcher に認証ポリシーを実装できます。

一般的なアプローチは、リクエスト URL 内のパスセグメント {application} が認証されたプリンシパルの識別子 (たとえば、JWT のクレームまたは HTTP Basic ユーザー名) と一致することを検証することです。次の例は、各クライアントが自身のプリンシパル名で始まるパスに制限される Spring Security 構成を示しています。

@Configuration
@EnableWebSecurity
public class ConfigServerSecurityConfig {

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http
            .authorizeHttpRequests(auth -> auth
                // Actuator health endpoint is public
                .requestMatchers("/actuator/health").permitAll()
                // Clients may only request their own application's config
                .requestMatchers("/{application}/**").access((authentication, context) -> {
                    String requestedApp = context.getVariables().get("application");
                    String principal = authentication.get().getName();
                    return new AuthorizationDecision(principal.equals(requestedApp));
                })
                .anyRequest().authenticated()
            )
            .httpBasic(Customizer.withDefaults());
        return http.build();
    }
}

アプリケーションごとのアクセス制御がない場合、認証済みのクライアントは、別のアプリケーション名を知っている場合、そのアプリケーション名を {application} パスセグメントとして使用するだけで、そのアプリケーションの設定をリクエストできてしまいます。サービスごとに分離する必要のある機密情報を保存する場合は、上記に示すような認証ポリシーを実装してください。

TLS/HTTPS

本番環境のデプロイでは、認証情報や設定値が平文で送信されないように、必ず設定サーバーで TLS を設定してください。標準の Spring Boot TLS 設定(server.ssl.* プロパティ)に従うか、TLS を終端するリバースプロキシの背後にデプロイしてください。

クライアント側の設定

構成クライアントは認証情報を 構成サーバーに送信します。環境固有のオーバーライドを使用するか、実行時に認証情報を指定することで、認証情報をソース管理にコミットすることを避けてください。

bootstrap.yml (クライアント— HTTP ベーシック)
spring:
  cloud:
    config:
      uri: https://config.example.com
      username: "${CONFIG_SERVER_USER}"
      password: "${CONFIG_SERVER_PASSWORD}"
application.yml (クライアント— spring.config.import with credentials)
spring:
  config:
    import: "configserver:https://${CONFIG_SERVER_USER}:${CONFIG_SERVER_PASSWORD}@config.example.com"

関連事項: アクチュエーターとセキュリティ for important notes about securing actuator endpoints alongside the Config Server API.