【问题标题】:How to perform custom authentication with kubernetes Ingress如何使用 Kubernetes Ingress 执行自定义身份验证
【发布时间】:2020-10-27 10:59:40
【问题描述】:

我在 kubernetes 中部署了一些服务,并使用 NGINX 入口访问外部。(使用 EC2 实例进行所有集群设置)。能够通过与入口绑定的主机访问服务。现在,我没有直接访问 svc,而是尝试在访问服务之前添加身份验证。并重定向到登录页面,用户输入凭据并应重定向到所询问的页面。到目前为止,我尝试了以下代码片段。请指导寻找解决方案。

我的入口.yml

apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: my-ingress
  namespace: mynamespace  
  annotations:
    nginx.ingress.kubernetes.io/rewrite-target: /
    nginx.ingress.kubernetes.io/affinity: cookie
    nginx.ingress.kubernetes.io/session-cookie-name: JSESSIONID
    nginx.ingress.kubernetes.io/ssl-passthrough: "false"
    nginx.ingress.kubernetes.io/ssl-redirect: "true"
    nginx.ingress.kubernetes.io/force-ssl-redirect: "true"
    kubernetes.io/ingress.class: "nginx"
    kubernetes.io/ingress.allow-http: "false"
    nginx.ingress.kubernetes.io/auth-signin: "https://auth.mysite.domain/api/auth/login"  #will show login page
    nginx.ingress.kubernetes.io/auth-url: "https://auth.mysite.domain/api/auth/token/validate"
    nginx.ingress.kubernetes.io/auth-response-headers: "authorization"    
    
spec:
  tls:
  - hosts: 
    - mysite.domain
    #secretName: ${TLS_TOKEN_NAME}
  rules:
  - host: a.mysite.domain
    http:
      paths:
        - path: /
          backend:
            serviceName: myservice1
            servicePort: 9090 
 

所以首先它会调用“/token/validate”并获得未授权然后进入“auth/login”并显示登录页面 输入凭据后转到“/token/validate”并再次登录页面。实际上应该重定向到被调用的页面。

如何做到这一点?[如果认证成功后,如果我们可以在被调用的 ling 中添加标头,我认为可以解决,但不确定如何解决]

后端:Java Spring

@RequestMapping("login")  
public String login() {  
    return "login.html";  
} 

登录.html

    <form action="validate-user" method="post" enctype="application/x-www-form-urlencoded">  
        <label for="username">Username</label>  
        <input type="text" id="username" value="admin" name="username" autofocus="autofocus" />  <br>
        <label for="password">Password</label>  
        <input type="password" id="password" value="password" name="password" />  <br>
        
        <input id="submit" type="submit" value="Log in" />  
    </form> 

后端:Java Spring

@PostMapping("validate-user")
@ResponseBody
public ResponseEntity<?> validateUser(HttpServletRequest request, HttpServletResponse response) throws Exception {
                ...
                
    HttpStatus httpStatus=HttpStatus.FOUND;
    //calling authentication api and validating
        
    //else
    httpStatus=HttpStatus.UNAUTHORIZED;
    HttpHeaders responseHeaders= new HttpHeaders();
    responseHeaders.set("Authoriztion", token);
                
    //responseHeaders.setLocation(new URI("https://a.mysite.domain")); ALSO TRIED BUT NOT WORKED
    return new ResponseEntity<>(responseHeaders,httpStatus);        
        
        }

UPDATE1: 我正在使用我自己的自定义身份验证 API,如果我使用来自邮递员的自定义标头 "Authorization":"bearer token" 访问 url,那么响应是可以的,但来自浏览器的不是可能,因此仅从上游 svc(成功登录后)标头应包含在重定向页面中,我们该怎么做?
我缺少任何注释吗?

UPDATE2:在成功验证后重定向时,我将令牌作为查询字符串(如responseHeaders.setLocation(new URI("https://a.mysite.domain/?access_token="+token))传递,并在重定向后进行验证。成功验证后转到下游 svc [预期]。但是当那个 svc 路由说 a.mysite.domain/route1 时,查询字符串就消失了,auth svc 无法获取令牌,因此再次 401。它应该像a.mysite.domain/route1/?access_token=token。有什么办法可以做到吗?如果每条路线都有相同的查询字符串,那么将起作用。[这是我的 PLAN-B...但仍然 passwing 令牌是标题是我的优先级]

UPDATE3:我尝试使用如下注释:

nginx.ingress.kubernetes.io/auth-signin: 'https://auth.example.com/api/auth-service-ui/login'
nginx.ingress.kubernetes.io/auth-response-headers: 'UserID, Authorization, authorization'
nginx.ingress.kubernetes.io/auth-snippet: |
      auth_request_set $token $upstream_http_authorization;
      proxy_set_header Foo-Header1 $token; //not showing as request header AND this value only need LOOKS $token val is missed
      proxy_set_header Foo-Header headerfoo1; //showing as request header OK
      more_set_input_headers  'Authorization: $token';//not showing as request header AND this value only need LOOKS $token val is missed

nginx.ingress.kubernetes.io/configuration-snippet: |
      auth_request_set $token1 $upstream_http_authorization;
      add_header  authorization2 QQQAAQ1; //showing as response header OK no use
      add_header  authorization $token; //showing as response header OK how to send as request header on next call
      more_set_input_headers  'Authorization11: uuu1';//showing as request header in next call
      more_set_input_headers  'Authorization: $token1';//not showing as request header and need this val ONLY

**我错过了什么注释?

更新4 PLAN-C:现在尝试将 jwt 令牌存储在 cookie 中。

 nginx.ingress.kubernetes.io/configuration-snippet: |
      auth_request_set $token5 $upstream_http_authorization;    
      add_header Set-Cookie "JWT_TOKEN=$token5";

在每个请求中设置相同的 cookie,但在浏览器中每次都存储它。即多个相同的cookie。如何只设置一次?

【问题讨论】:

  • 您尝试使用哪种外部身份验证,基本身份验证还是 OAuth2?另外,请提供您的 nginx-ingress 版本。
  • @KoopaKiller image: quay.io/kubernetes-ingress-controller/nginx-ingress-controller:0.30.0 /ingress-nginx/nginx-0.30.0 并使用jwt令牌验证(自定义身份验证api )
  • 有没有人知道这个概念并且可以分享一些关于它的知识?
  • 我会尝试重现该问题。如果您可以分享您正在使用的图像或一些示例,那就太好了。
  • 所有资源都在专用网络中,因此无法提供。要检查我只是设置了一个集群并部署了两个 svc,一个 auth-svc(简单和自定义)另一个是我要访问的 svc。从 github 和 nginx-ingress nginx-0.30.0 安装 kubedash。所以请尝试自己复制。我尝试过,但是在重定向到页面时无法设置身份验证标头。如果这样做,那么将工作正常。

标签: spring-boot kubernetes oauth kubernetes-ingress nginx-ingress


【解决方案1】:

将 Oauth2-proxy 与 nginx 入口控制器集成,以启用对入口后面运行的服务的自定义身份验证。

点击链接 --> https://github.com/oauth2-proxy/oauth2-proxy

【讨论】:

  • 感谢您的建议,我正在使用我自己的自定义身份验证 api,如果我从邮递员那里点击带有标题“授权”:“不记名令牌”的 url,那么响应是可以的,但来自浏览器不是可能,所以对于上游 svc,只有标头应该包含在重定向页面中,我们该怎么做?
【解决方案2】:

朝着你的计划-B

在标头中传递令牌是我的首要任务。

我认为仅使用以下注释没有任何问题:

nginx.ingress.kubernetes.io/auth-url: "https://auth.mysite.domain/api/auth/token/validate"
nginx.ingress.kubernetes.io/auth-response-headers: "authorization 

跟进官方external-auth-headers例子:

  1. 修改他们的external-auth-service'scode,使其引入成功的身份验证。响应所需的标头 ("Authorization": "Bearer &lt;token&gt;"):

       w.Header().Add("UserRole", "admin")
            w.Header().Add("Other", "not used")
            w.Header().Add("Authorization", "Bearer foobar" ) <--- here
            fmt.Fprint(w, "ok")
    
  2. 在 Deployment 中重建 docker 镜像并更新 Pod 规范以应用您的自定义镜像

  3. 确保您的 Ingress 定义看起来相似:

    apiVersion: extensions/v1beta1
    kind: Ingress
    metadata:
      annotations:
        nginx.ingress.kubernetes.io/auth-response-headers: UserID, authorization
        nginx.ingress.kubernetes.io/auth-url: http://demo-auth- 
    service.default.svc.cluster.local?code=200
    
  4. 检查是否将 auth-service 自定义标头注入到对其后端的 Ingress 调用(我正在使用httpbin.org/headers 的集群部署版本)

  5. 从客户端和 Ingress 的后端角度看到的请求内容:

客户请求:

     > GET /headers HTTP/1.1
     > Host: custom.example.com
     > User-Agent: curl/7.58.0
     > Accept: */*
     > User: centrino
     > 
     < HTTP/1.1 200 OK
     < Server: nginx/1.17.8
     < Date: Tue, 14 Jul 2020 11:55:43 GMT
     < Content-Type: application/json
     < Content-Length: 327
     < Connection: keep-alive
     < Vary: Accept-Encoding
     < Access-Control-Allow-Origin: *
     < Access-Control-Allow-Credentials: true
     < 
   

在后端看到的客户端请求丰富了 ext-auth-svc 标头:

     {
      "headers": {
        "Accept": "*/*", 
        "Authorization": "Bearer foobar", <---is this what you are looking for ?
        "Host": "custom.example.com", 
        "User": "centrino", 
        "User-Agent": "curl/7.58.0", 
        "Userid": "8674665223082153551", 
        "X-Forwarded-Host": "custom.example.com", 
        "X-Scheme": "http", 
        "X-Using-Nginx-Controller": "true"
      }
    } 
 

 

【讨论】:

  • 我试过这个并且也使用了 add_header 但工作方式符合预期...请参阅我使用的更新 3!...您已经提供了可以验证和发送 auth resp 和此标头的请求到 svc ......但如果它无效,那么我将使用 'auth-signin' 注释发送到登录页面,然后一旦用户登录重定向到所询问的页面,这里只有 Auth 标头丢失!
  • 您可以尝试在您的配置-sn-p 中使用$http_token 而不是$token,或者只是(add_header 授权$token1;),请参阅nginx.ordg doc 以更好地理解此语法
  • 都试过了都没有结果...见 UPDATE4
  • @UPDATE4 - 我不是 Spring Boot 专家,但您可能需要在成功登录后存储和维护客户端会话 ID(user.validated/token.issued)。现在,您的应用程序的行为就像基本身份验证服务背后的那个一样 = 每次用户需要指定凭据时。看看这个code example...
  • ...此外,您还需要基于 cookie 启用会话 affinity,以避免客户端在后端的不同副本之间弹跳。
【解决方案3】:

以下内容对我有用

输入你的价值观 yaml:

nginx.ingress.kubernetes.io/auth-url: "url service here"

那么对于这个 url,你必须实现一个 GET 服务,如果授权成功则返回 200,否则返回 401。

我在烧瓶中实现,具有基本授权,但你可以使用任何你想要的

 def auth():
  request_path = request.headers.get("X-Auth-Request-Redirect")
  authorization_header = request.headers.get('Authorization')
          
  if ServiceImp.auth(authorization_header,request_path):
   return Response(
                response="authorized",
                status=200, 
                mimetype="application/json"
   )
  else:
   resp = Response()
   resp.headers['WWW-Authenticate'] = 'Basic'
   return resp, 401

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2020-04-24
    • 1970-01-01
    • 1970-01-01
    • 2021-05-03
    • 1970-01-01
    • 2017-01-01
    • 2016-07-09
    • 2020-04-01
    相关资源
    最近更新 更多