Spring OAuth2 Keycloak Kubernetes内部/外部访问

vnjpjtjt  于 2022-11-02  发布在  Kubernetes
关注(0)|答案(3)|浏览(240)

我在Kubernetes集群中配置了Keycloak(10.0.3)服务器。
keycloak服务器必须处理外部用户的身份验证(使用外部url),还必须处理Spring微服务通信的oauth2令牌。
然后,Web应用程序Spring服务使用oidc提供程序:

spring:
  security:
    oauth2:
      client:
        provider:
          oidc:
            issuer-uri: http://keycloak-cluster-http.keycloak-cluster.svc.cluster.local/auth/realms/myrealm
            authorization-uri: http://keycloak-cluster-http.keycloak-cluster.svc.cluster.local/auth/realms/myrealm/protocol/openid-connect/auth
            jwk-set-uri: http://keycloak-cluster-http.keycloak-cluster.svc.cluster.local/auth/realms/myrealm/protocol/openid-connect/certs
            token-uri: http://keycloak-cluster-http.keycloak-cluster.svc.cluster.local/auth/realms/myrealm/protocol/openid-connect/token
            user-name-attribute: preferred_username

keycloak的外部URL为https://keycloak.localhost,由Traefik v2处理的入口重定向进行管理

apiVersion: traefik.containo.us/v1alpha1
kind: IngressRoute
metadata:
  name: keycloak-https
  namespace: keycloak-cluster
  annotations:
    traefik.frontend.passHostHeader: "true"
spec:
  entryPoints:
    - websecure
  routes:
    - match: Host(`keycloak.localhost`)
      kind: Rule
      services:
        - name: keycloak-cluster-http
          port: 80
  tls:
    options:
      name: mytlsoption
      namespace: traefik
    store:
      name: default

我可以使用https://keycloak.localhost访问Keycloak,没有问题,它工作。
问题是,当我尝试访问我的Web应用程序时,它总是重定向到“http://keycloak-cluster-http.keycloak-cluster.svc.cluster.local/auth/realms/myrealm”,这在k8s之外无法解析。
如果我把issuer-uri改为http://keycloak.localhost,那么它就不能作为keycloak工作。
我尝试将KEYCLOAK_FRONTEND_URL设置为https://keycloak.localhost/auth,但没有更改。
拜托,有没有人有同样的设置,并设法使它工作?
顺祝商祺

xbp102n0

xbp102n01#

使用coredns并添加重写规则设法修复了它...:

重写名称keycloak.本地主机keycloak-cluster-http.keycloak-cluster.svc.cluster.local

apiVersion: v1
data:
  Corefile: |
    .:53 {
        errors
        health
        ready
        kubernetes cluster.local in-addr.arpa ip6.arpa {
           pods insecure
           fallthrough in-addr.arpa ip6.arpa
           ttl 30
        }
        rewrite name keycloak.localhost keycloak-cluster-http.keycloak-cluster.svc.cluster.local
        prometheus :9153
        forward . /etc/resolv.conf
        cache 30
        loop
        reload
        loadbalance
    }
kind: ConfigMap
metadata:
  name: coredns
  namespace: kube-system
e4yzc0pl

e4yzc0pl2#

浏览器需要理解authorization_uri,因为该URI在前向通道中处理,其余URI在后向通道中处理。
因此,authorization_uri应该使用前通道方式来寻址授权服务器:

authorization_uri: https://keycloak.localhost/auth/realms/myrealm/protocol/openid-connect/auth

EDIT根据Joe Grandja在下面输入的内容,似乎也不需要指定issuer-uri属性。issuer-uri属性是指定其他URI的快捷方式,既然您指定了这些URI,就不需要它了。

r9f1avp5

r9f1avp53#

这是帮助我解决此问题的POC。
类似的配置,kubernetes中有keycloak和spring gateway。
外部用户使用keycloak外部主机和https协议

https://external-https/auth/realms/myrealm/protocol/openid-connect/auth?...

入口中断https并将其移动到http +将主机更改为 internal-http
网关使用 internal-http 连接到端口8080上的密钥伪装
为了使颁发者与外部使用相同的协议,配置在user-info-uri和authorization-uri中使用 https,但其余的使用 http,请确保keycloak pod已为https连接打开(8443)

authorization-uri: https://internal-http:8443/auth/realms/myrealm/protocol/openid-connect/auth
user-info-uri: https://internal-http:8443/auth/realms/myrealm/protocol/openid-connect/userinfo
issuer-uri: http://internal-http:8080/auth/realms/myrealm

修复发行方的主机部分
在网关代码中,我根据www.example.com更新了以下内容https://github.com/spring-projects/spring-security/issues/8882#user-content-oauth2-client

@SneakyThrows
private WebClient webClient() {
    SslContext sslContext = SslContextBuilder
            .forClient()
            .trustManager(InsecureTrustManagerFactory.INSTANCE)
            .build();
    HttpClient httpClient = HttpClient.create()
            .secure(t -> t.sslContext(sslContext))
            .wiretap(true)
            ;
    ReactorClientHttpConnector conn = new ReactorClientHttpConnector(httpClient);
    return WebClient.builder()
            .defaultHeader("HOST", "external-https")
            .clientConnector(conn)
            .build();
}
@Bean
WebClientReactiveAuthorizationCodeTokenResponseClient webClientReactiveAuthorizationCodeTokenResponseClient() {
    final WebClientReactiveAuthorizationCodeTokenResponseClient webClientReactiveAuthorizationCodeTokenResponseClient = new WebClientReactiveAuthorizationCodeTokenResponseClient();
    final WebClient webClient = webClient();
    webClientReactiveAuthorizationCodeTokenResponseClient.setWebClient(webClient);
    return webClientReactiveAuthorizationCodeTokenResponseClient;
}

@Bean
WebClientReactiveClientCredentialsTokenResponseClient webClientReactiveClientCredentialsTokenResponseClient() {
    final WebClientReactiveClientCredentialsTokenResponseClient webClientReactiveClientCredentialsTokenResponseClient = new WebClientReactiveClientCredentialsTokenResponseClient();
    final WebClient webClient = webClient();
    webClientReactiveClientCredentialsTokenResponseClient.setWebClient(webClient);
    return webClientReactiveClientCredentialsTokenResponseClient;
}

@Bean
WebClientReactiveRefreshTokenTokenResponseClient webClientReactiveRefreshTokenTokenResponseClient() {
    final WebClientReactiveRefreshTokenTokenResponseClient webClientReactiveRefreshTokenTokenResponseClient = new WebClientReactiveRefreshTokenTokenResponseClient();
    final WebClient webClient = webClient();
    webClientReactiveRefreshTokenTokenResponseClient.setWebClient(webClient);
    return webClientReactiveRefreshTokenTokenResponseClient;
}

@Bean
WebClientReactivePasswordTokenResponseClient webClientReactivePasswordTokenResponseClient() {
    final var client = new WebClientReactivePasswordTokenResponseClient();
    final WebClient webClient = webClient();
    client.setWebClient(webClient);
    return client;
}

@Bean
DefaultReactiveOAuth2UserService reactiveOAuth2UserService() {
    final DefaultReactiveOAuth2UserService userService = new DefaultReactiveOAuth2UserService();
    final WebClient webClient = webClient();
    userService.setWebClient(webClient);
    return userService;
}
  • 已禁用证书验证-连接仅在keycloak和网关之间,两者都在kubernetes中,否则将使用http连接,如果没有此问题
  • 主机部分告诉keyclock什么是主机用于发行者

遇到的另一个问题是,重定向到身份验证时返回的位置包含内部url,而不是外部世界不知道的外部url
为此,请更新从网关返回的位置

@Bean
public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http)
...
oauth2Login(oAuth2LoginSpec -> oAuth2LoginSpec
     ...
     .addFilterAfter(new LoginLocationFilter("external-https"), SecurityWebFiltersOrder.LAST)
     ...

 public class LoginLocationFilter implements WebFilter {
     private final String externalUrl;

    public LoginLocationFilter(String externalUrl) {
        this.externalUrl = externalUrl;

    }
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
        //before commit ,otherwise the headers will be read only
        exchange.getResponse().beforeCommit(() -> {
            fixLocation(exchange);
            return Mono.empty();
        });
        return chain.filter(exchange);
    } 
...

相关问题