成功最有效的方法就是向有经验的人学习!

ingress-nginx配置Basic Auth

我们先创建一个示例

apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-nginx
spec:
  selector:
    matchLabels:
      app: my-nginx
  template:
    metadata:
      labels:
        app: my-nginx
    spec:
      containers:
      - name: my-nginx
        image: nginx
        ports:
        - containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
  name: my-nginx
  labels:
    app: my-nginx
spec:
  ports:
  - port: 80
    protocol: TCP
    name: http
  selector:
    app: my-nginx
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: my-nginx
  namespace: default
spec:
  ingressClassName: nginx  # 使用 nginx 的 IngressClass(关联的 ingress-nginx 控制器)
  rules:
  - host: demo1.gl.sh.cn  # 将域名映射到 my-nginx 服务
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:  # 将所有请求发送到 my-nginx 服务的 80 端口
            name: my-nginx
            port:
              number: 80
# 不过需要注意大部分Ingress控制器都不是直接转发到Service
# 而是只是通过Service来获取后端的Endpoints列表,直接转发到Pod,这样可以减少网络跳转,提高性能

我们可以在 Ingress 对象上配置一些基本的 Auth 认证,比如 Basic Auth,可以用 htpasswd 生成一个密码文件来验证身份验证。

htpasswd -c auth foo
New password:
Re-type new password:
Adding password for user foo

然后根据上面的 auth 文件创建一个 secret 对象:

kubectl create secret generic basic-auth --from-file=auth
secret/basic-auth created
kubectl get secret basic-auth -o yaml
apiVersion: v1
data:
  auth: Zm9vOiRhcHIxJFUxYlFZTFVoJHdIZUZQQ1dyZTlGRFZONTQ0dXVQdC4K
kind: Secret
metadata:
  name: basic-auth
  namespace: default
type: Opaque

然后对上面的 my-nginx 应用创建一个具有 Basic Auth 的 Ingress 对象:

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: ingress-with-auth
  namespace: default
  annotations:
    nginx.ingress.kubernetes.io/auth-type: basic  # 认证类型
    nginx.ingress.kubernetes.io/auth-secret: basic-auth  # 包含 user/password 定义的 secret 对象名
    nginx.ingress.kubernetes.io/auth-realm: 'Authentication Required - foo'  # 要显示的带有适当上下文的消息,说明需要身份验证的原因
spec:
  ingressClassName: nginx  # 使用 nginx 的 IngressClass(关联的 ingress-nginx 控制器)
  rules:
  - host: demo1-auth.gl.sh.cn  # 将域名映射到 my-nginx 服务
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:  # 将所有请求发送到 my-nginx 服务的 80 端口
            name: my-nginx
            port:
              number: 80

直接创建上面的资源对象,然后通过下面的命令或者在浏览器中直接打开配置的域名:

kubectl get ingress
NAME                CLASS    HOSTS                        ADDRESS         PORTS   AGE
ingress-with-auth   nginx    demo1-auth.gl.sh.cn            172.135.252.222   80      6m55s

# 测试
curl -v http://172.135.252.222 -H 'Host: demo1-auth.gl.sh.cn'
*   Trying 172.135.252.222...
* TCP_NODELAY set
* Connected to 172.135.252.222 (172.135.252.222) port 80 (#0)
> GET / HTTP/1.1
> Host: demo1-auth.gl.sh.cn
> User-Agent: curl/7.64.1
> Accept: */*
>
< HTTP/1.1 401 Unauthorized
< Date: Thu, 16 Dec 2021 10:49:03 GMT
< Content-Type: text/html
< Content-Length: 172
< Connection: keep-alive
< WWW-Authenticate: Basic realm="Authentication Required - foo"
<
<html>
<head><title>401 Authorization Required</title></head>
<body>
<center><h1>401 Authorization Required</h1></center>
<hr><center>nginx</center>
</body>
</html>
* Connection #0 to host 172.135.252.222 left intact
* Closing connection 0

我们可以看到出现了 401 认证失败错误,然后带上我们配置的用户名和密码进行认证:

curl -v http://172.135.252.222 -H 'Host: demo1-auth.gl.sh.cn' -u 'foo:foo'
*   Trying 172.135.252.222...
* TCP_NODELAY set
* Connected to 172.135.252.222 (172.135.252.222) port 80 (#0)
* Server auth using Basic with user 'foo'
> GET / HTTP/1.1
> Host: demo1-auth.gl.sh.cn
> Authorization: Basic Zm9vOmZvbw==
> User-Agent: curl/7.64.1
> Accept: */*
>
< HTTP/1.1 200 OK
< Date: Thu, 16 Dec 2021 10:49:38 GMT
< Content-Type: text/html
< Content-Length: 615
< Connection: keep-alive
< Last-Modified: Tue, 02 Nov 2021 14:49:22 GMT
< ETag: "61814ff2-267"
< Accept-Ranges: bytes
<
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
html { color-scheme: light dark; }
body { width: 35em; margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif; }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>

<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>

<p><em>Thank you for using nginx.</em></p>
</body>
</html>
* Connection #0 to host 172.135.252.222 left intact
* Closing connection 0

可以看到已经认证成功了。除了可以使用我们自己在本地集群创建的 Auth 信息之外,还可以使用外部的 Basic Auth 认证信息,比如我们使用 https://httpbin.org 的外部 Basic Auth 认证,创建如下所示的 Ingress 资源对象:

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  annotations:
    # 配置外部认证服务地址
    nginx.ingress.kubernetes.io/auth-url: https://httpbin.org/basic-auth/user/passwd
  name: external-auth
  namespace: default
spec:
  ingressClassName: nginx
  rules:
  - host: external-demo1-auth.gl.sh.cn
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: my-nginx
            port:
              number: 80

上面的资源对象创建完成后,再进行简单的测试:

kubectl get ingress
NAME                CLASS    HOSTS                        ADDRESS         PORTS   AGE
external-auth       <none>   external-demo1-auth.gl.sh.cn                   80      72s

#测试
curl -k http://172.135.252.222 -v -H 'Host: external-demo1-auth.gl.sh.cn'
*   Trying 172.135.252.222...
* TCP_NODELAY set
* Connected to 172.135.252.222 (172.135.252.222) port 80 (#0)
> GET / HTTP/1.1
> Host: external-demo1-auth.gl.sh.cn
> User-Agent: curl/7.64.1
> Accept: */*
>
< HTTP/1.1 401 Unauthorized
< Date: Thu, 16 Dec 2021 10:57:25 GMT
< Content-Type: text/html
< Content-Length: 172
< Connection: keep-alive
< WWW-Authenticate: Basic realm="Fake Realm"
<
<html>
<head><title>401 Authorization Required</title></head>
<body>
<center><h1>401 Authorization Required</h1></center>
<hr><center>nginx</center>
</body>
</html>
* Connection #0 to host 172.135.252.222 left intact
* Closing connection 0

然后使用正确的用户名和密码测试:

curl -k http://172.135.252.222 -v -H 'Host: external-demo1-auth.gl.sh.cn' -u 'user:passwd'
*   Trying 172.135.252.222...
* TCP_NODELAY set
* Connected to 172.135.252.222 (172.135.252.222) port 80 (#0)
* Server auth using Basic with user 'user'
> GET / HTTP/1.1
> Host: external-demo1-auth.gl.sh.cn
> Authorization: Basic dXNlcjpwYXNzd2Q=
> User-Agent: curl/7.64.1
> Accept: */*
>
< HTTP/1.1 200 OK
< Date: Thu, 16 Dec 2021 10:58:31 GMT
< Content-Type: text/html
< Content-Length: 615
< Connection: keep-alive
< Last-Modified: Tue, 02 Nov 2021 14:49:22 GMT
< ETag: "61814ff2-267"
< Accept-Ranges: bytes
<
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
html { color-scheme: light dark; }
body { width: 35em; margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif; }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>

<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>

<p><em>Thank you for using nginx.</em></p>
</body>
</html>
* Connection #0 to host 172.135.252.222 left intact
* Closing connection 0

如果用户名或者密码错误则同样会出现401的状态码:

curl -k http://172.135.252.222 -v -H 'Host: external-demo1-auth.gl.sh.cn' -u 'user:passwd123'
*   Trying 172.135.252.222...
* TCP_NODELAY set
* Connected to 172.135.252.222 (172.135.252.222) port 80 (#0)
* Server auth using Basic with user 'user'
> GET / HTTP/1.1
> Host: external-demo1-auth.gl.sh.cn
> Authorization: Basic dXNlcjpwYXNzd2QxMjM=
> User-Agent: curl/7.64.1
> Accept: */*
>
< HTTP/1.1 401 Unauthorized
< Date: Thu, 16 Dec 2021 10:59:18 GMT
< Content-Type: text/html
< Content-Length: 172
< Connection: keep-alive
* Authentication problem. Ignoring this.
< WWW-Authenticate: Basic realm="Fake Realm"
<
<html>
<head><title>401 Authorization Required</title></head>
<body>
<center><h1>401 Authorization Required</h1></center>
<hr><center>nginx</center>
</body>
</html>
* Connection #0 to host 172.135.252.222 left intact
* Closing connection 0

当然除了 Basic Auth 这一种简单的认证方式之外,ingress-nginx 还支持一些其他高级的认证,比如我们可以使用 Gitlab OAuth 来认证 Kubernetes 的 Dashboard。

简单介绍一下关于oauth2 proxy介绍

oauth2 proxy是一个反向代理和静态文件服务器,使用提供程序(Google,GitHub和其他提供商)提供身份验证,以通过电子邮件,域或组验证帐户。

项目地址:https://github.com/oauth2-proxy/oauth2-proxy

认证过程的流程如下

在Gitlab配置OpenID应用
登录到Gitlab—>管理中心—>应用,创建一个应用
file
参数:

回调URL:指GitLab在用户通过身份验证后应将其发送到的端点,对于oauth2-proxy应该是https://<应用域名>/oauth2/callback
范围:应用程序对GitLab用户配置文件的访问级别。对于大多数应用程序,选择openid,profile和email即可。
创建完应用后,会生成一对ID和密钥,这个在后面会用到。

4.2 生成Cookie密钥
生成Cookie密钥。该Cookie密钥作为种子字符串以产生安全的cookie。

参考官方说明,使用base64编码,可利用以下的python脚本生成字符串。

import secrets
import base64

print(base64.b64encode(base64.b64encode(secrets.token_bytes(16))))

部署oauth2-proxy
在k8s中部署oauth-proxy,资源清单oauth2-gitlab.yaml和相关参数说明如下。更多的配置参数,可以参考官方文档

apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    k8s-app: oauth2-proxy
  name: oauth2-proxy
  namespace: kube-system
spec:
  replicas: 1
  selector:
    matchLabels:
      k8s-app: oauth2-proxy
  template:
    metadata:
      labels:
        k8s-app: oauth2-proxy
    spec:
      containers:
      - name: oauth2-proxy
        image: quay.io/oauth2-proxy/oauth2-proxy:latest
        imagePullPolicy: IfNotPresent
        ports:
        - containerPort: 4180
          protocol: TCP
        args:
        # OAuth提供者
        - --provider=gitlab
        # 上游端点的http网址
        - --upstream=file:///dev/null
        # 对具有指定域的电子邮件进行身份验证,可以多次给出,使用*验证任何电子邮件
        - --email-domain=*
        # 监听的地址
        - --http-address=0.0.0.0:4180
        # 设置安全(仅HTTPS)cookie标志
        - --cookie-secure=false
        # OAuth重定向URL
        - --redirect-url=https://nginx-test.gl.sh.cn/oauth2/callback
        # 跳过登录页面直接进入下一步
        - --skip-provider-button=false
        # 设置X-Auth-Request-User,X-Auth-Request-Email和X-Auth-Request-Preferred-Username响应头(在Nginx auth_request模式下有用)。与结合使用时--pass-access-token,会将X-Auth-Request-Access-Token添加到响应标头中
        - --set-xauthrequest=true
        # 跳过OPTIONS请求的身份验证
        - --skip-auth-preflight=false
        # 绕过OIDC端点发现
        - --skip-oidc-discovery
        # OpenID Connect发行者url,这里是gitlab的url
        - --oidc-issuer-url=https://gitlab.gl.sh.cn
        # 认证url
        - --login-url=https://gitlab.gl.sh.cn/oauth/authorize
        # token url
        - --redeem-url=https://gitlab.gl.sh.cn/oauth/token
        # 用于令牌验证的url
        - --oidc-jwks-url=https://gitlab.gl.sh.cn/oauth/discovery/keys
        env:
        - name: OAUTH2_PROXY_CLIENT_ID
          value: '85945b7195ab109377183837b9221bd299bc64b31fe272304a1c777e8e241d83'
        - name: OAUTH2_PROXY_CLIENT_SECRET
          value: '2f9782928b493686f387d18db9138e92607448cef045c81319967cc3e5ce4ba1'
         # 安全cookie的种子字符串,可通过python脚本生成
        - name: OAUTH2_PROXY_COOKIE_SECRET
          value: 'VGlYNVBVOGw4UFgyRURzbERxVTRiZz09'

---
apiVersion: v1
kind: Service
metadata:
  labels:
    k8s-app: oauth2-proxy
  name: oauth2-proxy
  namespace: kube-system
spec:
  type: NodePort
  ports:
  - name: http
    port: 4180
    protocol: TCP
    targetPort: 4180
    nodePort: 30020
  selector:
    k8s-app: oauth2-proxy

应用上面的资源清单,创建deployment和service

kubectl apply -f oauth2-gitlab.yaml
kubectl -n kube-system get pods -l k8s-app=oauth2-proxy 
NAME                           READY   STATUS    RESTARTS   AGE
oauth2-proxy-884695869-bkwns   1/1     Running   0          113s

上面通过nodeport单独暴露了oauth2-proxy应用,可以访问检查以确保浏览器可以正常打开

创建测试应用并配置Ingress
资源清单文件nginx.yaml如下,其中为该nginx应用配置了https证书

apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: nginx
  namespace: kube-system
spec:
  replicas: 1
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - image: nginx:1.15
        imagePullPolicy: IfNotPresent
        name: nginx

---
apiVersion: v1
kind: Service
metadata:
  name: nginx
  namespace: kube-system
spec:
  selector:
    app: nginx
  ports:
  - name: nginx
    port: 80
    targetPort: 80

---
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: nginx
  namespace: kube-system
  annotations:
    kubernetes.io/ingress.class: "nginx"
    nginx.ingress.kubernetes.io/rewrite-target: /
    # 指定外部认证url
    nginx.ingress.kubernetes.io/auth-url: "https://$host/oauth2/auth"
    # 指定外部认证重定向的地址
    nginx.ingress.kubernetes.io/auth-signin: "https://$host/oauth2/start?rd=$escaped_request_uri"
    nginx.ingress.kubernetes.io/force-ssl-redirect: "true"
    nginx.ingress.kubernetes.io/secure-backends: "true"
    nginx.ingress.kubernetes.io/ssl-passthrough: "true"
    nginx.ingress.kubernetes.io/ssl-redirect: "true"
spec:
  tls:
  - hosts:
    - nginx-test.gl.sh.cn
    secretName: nginx-test
  rules:
    - host: nginx-test.gl.sh.cn
      http:
        paths:
        - path: /
          backend:
            serviceName: nginx
            servicePort: 80

---
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  annotations:
    kubernetes.io/ingress.class: "nginx"
    # 将nginx应用的访问请求跳转到oauth2-proxy组件url
    nginx.ingress.kubernetes.io/rewrite-target: "/oauth2"
    nginx.ingress.kubernetes.io/force-ssl-redirect: "true"
    nginx.ingress.kubernetes.io/secure-backends: "true"
    nginx.ingress.kubernetes.io/ssl-passthrough: "true"
    nginx.ingress.kubernetes.io/ssl-redirect: "true"
  name: nginx-oauth2
  namespace: kube-system
spec:
  tls:
  - hosts:
    - nginx-test.gl.sh.cn
    secretName: nginx-test
  rules:
  - host: nginx-test.gl.sh.cn
    http:
      paths:
      - path: /oauth2
        backend:
          serviceName: oauth2-proxy
          servicePort: 4180

应用上面的资源清单,创建相应资源

kubectl apply -f other/nginx.yaml
deployment.extensions/nginx unchanged
service/nginx unchanged
ingress.extensions/nginx unchanged
ingress.extensions/nginx-oauth2 unchanged
kubectl -n kube-system get po,svc,ing |grep nginx                        
pod/nginx-5ddcc6cb74-rnjlx                    1/1     Running   0          3m

service/nginx                     ClusterIP   172.135.252.222    <none>        80/TCP                      3m

ingress.extensions/nginx               nginx-test.gl.sh.cn             80, 443   3m
ingress.extensions/nginx-oauth2        nginx-test.gl.sh.cn             80, 443   3m

测试外部认证
通过访问上面部署的nginx应用,在浏览器中进行测试,会被重定向到Gitlab登录页面;

输入账号,正确登录后,会被重定向回nginx应用。

流程分析
在请求登录外部认证的过程中查看oauth2-proxy的日志如下

172.16.1.110:49976 - - [2021/01/23 17:28:23] nginx-test.gl.sh.cn GET - "/oauth2/auth" HTTP/1.1 "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_6) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.0.2 Safari/605.1.15" 401 21 0.000
172.16.1.110:9991 - - [2021/01/23 17:28:23] nginx-test.gl.sh.cn GET - "/oauth2/start?rd=%2F" HTTP/1.1 "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_6) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.0.2 Safari/605.1.15" 302 341 0.000
172.16.1.110:9991 - admin@example.com [2021/01/23 17:28:32] [AuthSuccess] Authenticated via OAuth2: Session{email:admin@example.com user:root PreferredUsername: token:true id_token:true created:2021-01-23 17:28:32.440915913 +0000 UTC m=+2248.944621207 expires:2021-01-23 17:30:32 +0000 UTC refresh_token:true}
172.16.1.110:9991 - - [2021/01/23 17:28:32] nginx-test.gl.sh.cn GET - "/oauth2/callback?code=9b7f5425d48a41f213065a4ff5b3d20d76e6d241ba753c5cd63d1a405f48818e&state=5d868b96b1f539a28da2291404152b7c%3A%2F" HTTP/1.1 "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_6) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.0.2 Safari/605.1.15" 302 24 0.381
172.16.1.110:5610 - admin@example.com [2021/01/23 17:28:32] nginx-test.gl.sh.cn GET - "/oauth2/auth" HTTP/1.1 "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_6) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.0.2 Safari/605.1.15" 202 0 0.000

访问nginx应用的时候,Ingress nginx controller会向定义的auth-url发起认证,该认证由Ingress nginx controller发起,所以Ingress nginx controller对应的pod必须能够访问auth-url。

如果认证没有通过,Ingress nginx controller将客户端重定向到auth-signin。auth-signin是目标应用的 oauth2登录页面即oauth2-proxy。

客户端被重定向到oauth2登录页面后,自动进入Gitlab的登录页面,

用户登录Gitlab后,Gitlab再将客户端重定向到在Gitlab中配置的应用回调地址。

客户端访问回调地址后,oauth2_proxy在客户端设置cookie,并将客户端重定向到最初的访问地址。

带有cookie的客户端再次访问目标应用时,通过了auth-url的认证,成功访问到目标服务即nginx应用。

5、总结
本文以基于k8s部署的nginx服务为例,记录如何通过ingress和oauth2 proxy对接gitlab实现对应用没有代码侵入的外部认证。

最后,还要提到的一点是,我这里一开始使用的Gitlab是已有的10.8.4版本,调试了关于Oauth2-proxy的很多参数一直不成功,也没有找到解决办法,但是按照官方的配置与github对接时却没有报任何异常。最终通过提交issue得到了可能原因,即Gitlab的API版本可能不兼容,oauth2-proxy的开发测试成功版本的Gitlab在12.x版本以上。

赞(2) 打赏
未经允许不得转载:陈桂林博客 » ingress-nginx配置Basic Auth
分享到

大佬们的评论 抢沙发

全新“一站式”建站,高质量、高售后的一条龙服务

微信 抖音 支付宝 百度 头条 快手全平台打通信息流

橙子建站.极速智能建站8折购买虚拟主机

觉得文章有用就打赏一下文章作者

非常感谢你的打赏,我们将继续给力更多优质内容,让我们一起创建更加美好的网络世界!

支付宝扫一扫打赏

微信扫一扫打赏

登录

找回密码

注册