Kubernetes Ingress — NGINX

Description 在 Kubernetes 中,Service 是一种抽象的概念,它定义了每一组 Pod 的逻辑集合和访问方式,并提供一个统一的入口,将请求进行负载分发到后端的各个 Pod 上。Service 默认类型是 ClusterIP,集群内部的应用服务可以相互访问,但集群外部的应用服务无法访问。为此 Kubernetes 提供了 NodePorts,LoadBalancer 和 Ingress 三种外部访问 Kubernetes 集群的方式。

Ingress 是 Kubernetes 中的一个 API 对象(在1.19版本GA),它提供路由规则来管理外部用户对 Kubernetes 集群中服务的访问。Ingress Controller 是 Ingress API 的实际实现,通过和 Kubernetes API 交互,动态的去感知集群中 Ingress 规则变化,将外部流量路由到 Kubernetes 集群,同时提供负载平衡,并负责L4-L7网络服务。 Description

目前社区上的 Ingress Controller 有十几种,如 Nginx Ingress、Kong、Traefik、Istio Ingress、APISIX 等,可根据自己的功能需求选型。

Nginx Ingress

Nginx Ingress 是由 Kubernetes SIGs 小组开发的。顾名思义,它基于 nginx,并补充了一组用于实现额外功能的 Lua 插件。由于 nginx 的普及以及在用作控制器时对其进行的最小改动,对于大部分人来说,它可能是最简单,最直接的选择。 Description

Nginx Ingress 安装非常简单,在裸机上部署的 kubernetes 集群,使用 NodePort

1kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v0.42.0/deploy/static/provider/baremetal/deploy.yaml

其他环境查看社区文档。

1[root@k8s-test01 ~]# kubectl get po,svc -n ingress-nginx 2NAME READY STATUS RESTARTS AGE 3pod/ingress-nginx-admission-create-xtjfl 0/1 Completed 0 97m 4pod/ingress-nginx-admission-patch-wx2jt 0/1 Completed 0 97m 5pod/ingress-nginx-controller-848bfcb64d-6spj4 1/1 Running 0 97m 6 7NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE 8service/ingress-nginx-controller NodePort 10.254.14.4 <none> 80:80/TCP,443:443/TCP 97m 9service/ingress-nginx-controller-admission ClusterIP 10.254.94.128 <none> 443/TCP 97m 10[root@k8s-test01 ~]#

创建一个 ingress 示例

1[root@k8s-test01 ~]# cat ingress-nginx-demo.yaml 2--- 3apiVersion: networking.k8s.io/v1 4kind: Ingress 5metadata: 6 name: ingress-wildcard-host 7 annotations: 8 kubernetes.io/ingress.class: nginx 9spec: 10 rules: 11 - host: "foo.bar.com" 12 http: 13 paths: 14 - pathType: Prefix 15 path: "/" 16 backend: 17 service: 18 name: nginx 19 port: 20 number: 80 21[root@k8s-test01 ~]# kubectl describe ingress ingress-wildcard-host 22Name: ingress-wildcard-host 23Namespace: default 24Address: 172.31.9.226 25Default backend: default-http-backend:80 (<error: endpoints "default-http-backend" not found>) 26Rules: 27 Host Path Backends 28 ---- ---- -------- 29 foo.bar.com 30 / nginx:80 (192.168.47.155:80) 31Annotations: kubernetes.io/ingress.class: nginx 32Events: 33 Type Reason Age From Message 34 ---- ------ ---- ---- ------- 35 Normal Sync 103s (x2 over 111s) nginx-ingress-controller Scheduled for sync 36[root@k8s-test01 ~]# kubectl get po -o wide | grep nginx 37nginx-546585459c-zxfmh 1/1 Running 0 94m 192.168.47.155 k8s-test02 <none> <none> 38[root@k8s-test01 ~]#

Description

  1. NGINX Ingress Controller 提供了有三种方式配置 NGINX :
  2. ConfigMap:使用 ConfigMap 在 NGINX 中设置全局配置。
  3. Annotations:在特定 Ingress 规则的特定配置。
  4. Custom template:当需要更具体的设置(如打开文件缓存)时,可以使用自定义 nginx 模板。

配置 SSL

通过 Annotations 来配置某一个 ingress 使用 SSL。

创建secret

1kubectl create secret tls ingress-cert --key=fullchain.com.key --cert=fullchain.cer

创建Ingress

1[root@k8s-test01 cert]# cat ingress-nginx-demo.yaml 2--- 3apiVersion: networking.k8s.io/v1 4kind: Ingress 5metadata: 6 name: ingress-wildcard-host 7 annotations: 8 kubernetes.io/ingress.class: nginx 9 ingress.kubernetes.io/force-ssl-redirect: "true" 10 kubernetes.io/tls-acme: "true" 11spec: 12 rules: 13 - host: "foo.xxx.com" 14 http: 15 paths: 16 - pathType: Prefix 17 path: "/" 18 backend: 19 service: 20 name: nginx 21 port: 22 number: 80 23 tls: 24 - secretName: ingress-cert 25[root@k8s-test01 cert]# kubectl create -f ingress-nginx-demo.yaml 26ingress.networking.k8s.io/ingress-wildcard-host created 27

Description

配置ModSecurity防火墙与OWASP规则

ModSecurity是一个免费、开源的 Apache 模块,用于入侵探测与拦截,目前已经支持 Nginx,可以充当 Web 应用防火墙(WAF),旨在增强 Web 应用程序的安全性和避免遭受来自已知与未知的攻击。而 OWASP 是安全社区开发和维护的一套免费的应用程序保护规则,是 MoodSecurity 的核心规则集。Nginx-ingress 集成了 ModSecurity 模块和 OWASP 规则,默认没有开启。

测试简单 XSS 攻击,没开启 Modsecurity 之前: Description

状态200,没有拦截。

开启 Modsecurity 模块

1[root@k8s-test01 ~]# cat ingress-nginx-demo.yaml 2--- 3apiVersion: networking.k8s.io/v1 4kind: Ingress 5metadata: 6 name: ingress-wildcard-host 7 annotations: 8 kubernetes.io/ingress.class: nginx 9 nginx.ingress.kubernetes.io/enable-modsecurity: "true" 10 nginx.ingress.kubernetes.io/enable-owasp-modsecurity-crs: "true" 11 nginx.ingress.kubernetes.io/modsecurity-snippet: | 12 SecRuleEngine On 13 SecRequestBodyAccess On 14 SecAuditEngine RelevantOnly 15 SecAuditLogParts ABIJDEFHZ 16 SecAuditLog /var/log/nginx/modsec_audit.log 17 Include /etc/nginx/owasp-modsecurity-crs/nginx-modsecurity.conf 18spec: 19 rules: 20 - host: "foo.bar.com" 21 http: 22 paths: 23 - pathType: Prefix 24 path: "/" 25 backend: 26 service: 27 name: nginx 28 port: 29 number: 80 30[root@k8s-test01 ~]#

再进行 XSS 攻击测试 Description

状态403,已拦截。

查看 Nginx 拦截日志

12020/12/27 09:37:56 [error] 3001#3001: *213189 [client 47.242.91.20] ModSecurity: Access denied with code 403 (phase 2). Matched "Operator `Ge' with parameter `5' against variable `TX:ANOMALY_SCORE' (Value: `5' ) [file "/etc/nginx/owasp-modsecurity-crs/rules/REQUEST-949-BLOCKING-EVALUATION.conf"] [line "80"] [id "949110"] [rev ""] [msg "Inbound Anomaly Score Exceeded (Total Score: 5)"] [data ""] [severity "2"] [ver "OWASP_CRS/3.3.0"] [maturity "0"] [accuracy "0"] [tag "application-multi"] [tag "language-multi"] [tag "platform-multi"] [tag "attack-generic"] [hostname "192.168.123.79"] [uri "/"] [unique_id "160906187674.566940"] [ref ""], client: 47.242.91.20, server: foo.bar.com, request: "HEAD /?search=<scritp>alert(xss);</script> HTTP/1.1", host: "foo.bar.com"

获取客户端真实IP

Nginx Ingress 部署使用 nodePort 模式, 在讲获取客户端真实 IP 之前,我们大概了解下 nodePort 模式的链路。从 Kubernetes 1.5 开始,NodePort 类型的 Services 的数据包默认进行源地址 NAT。

假设某一个 Pod 运行在 node1 节点,客户端访问 node2:nodeport,过程如下:

1 client 2 \ ^ 3 \ \ 4 v \ 5 node 1 <--- node 2 6 | ^ SNAT 7 | | ---> 8 v | 9 endpoint

1、客户端发送数据包到 node2:nodePort 2、node2 使用它自己的 IP 地址替换数据包的源 IP 地址(SNAT) 3、node2 使用 pod IP 地址替换数据包的目的 IP 地址 4、数据包被路由到 node1,然后交给 endpoint 5、Pod 的回复被路由回 node2 6、Pod 的回复被发送回给客户端

所以 nodePort 模式下源地址被转换了(SNAT),服务端获取的并不是正确的客户端 IP,它们是集群的内部 IP。为什么会这样呢?原因是为了支持从任一节点IP+NodePort 都可以访问应用,而不得不做的 SNAT。

当然,Kubernetes 也提供了一个特性来保留客户端的源 IP 地址,通过设置 externalTrafficPolicy 的值为 Local,请求就只会被代理到本地 endpoints 而不会被转发到其它节点。这样就保留了最初的源 IP 地址。

1--- 2# Source: ingress-nginx/templates/controller-service.yaml 3apiVersion: v1 4kind: Service 5metadata: 6 annotations: 7 labels: 8 helm.sh/chart: ingress-nginx-3.17.0 9 app.kubernetes.io/name: ingress-nginx 10 app.kubernetes.io/instance: ingress-nginx 11 app.kubernetes.io/version: 0.42.0 12 app.kubernetes.io/managed-by: Helm 13 app.kubernetes.io/component: controller 14 name: ingress-nginx-controller 15 namespace: ingress-nginx 16spec: 17 externalTrafficPolicy: Local 18 type: NodePort 19 ports: 20 - name: http 21 port: 80 22 protocol: TCP 23 targetPort: http 24 nodePort: 80 25 - name: https 26 port: 443 27 protocol: TCP 28 targetPort: https 29 nodePort: 443 30 selector: 31 app.kubernetes.io/name: ingress-nginx 32 app.kubernetes.io/instance: ingress-nginx 33 app.kubernetes.io/component: controller

但是,如果没有本地 endpoints,发送到这个节点的数据包将会被丢弃。即请求 node2:nodePort , 但 node2 上没有运行 Pod , 故本地没有 endpoints ,所以请 node2:nodePort 是失败的,node1:nodePort 正常。

1 client 2 ^ / \ 3 / / \ 4 / v X 5 node 1 node 2 6 ^ | 7 | | 8 | v 9 endpoint

所以 Nginx Ingress如需获取客户端真实 IP 需要设置 externalTrafficPolicy 或设置容器网络使用主机模式 hostNetwork: true ,然后 daemonset 部署,或通过亲和性把 Pod 固定在某些节点,客户端访问 Pod 所在的节点,源 IP 地址便不会 SNAT。

Nginx Ingress 获取客户端真实 IP 的配置如下:

1--- 2# Source: ingress-nginx/templates/controller-configmap.yaml 3apiVersion: v1 4kind: ConfigMap 5metadata: 6 labels: 7 helm.sh/chart: ingress-nginx-3.17.0 8 app.kubernetes.io/name: ingress-nginx 9 app.kubernetes.io/instance: ingress-nginx 10 app.kubernetes.io/version: 0.42.0 11 app.kubernetes.io/managed-by: Helm 12 app.kubernetes.io/component: controller 13 name: ingress-nginx-controller 14 namespace: ingress-nginx 15data: 16 server-tokens: "false" 17 forwarded-for-header: "X-Forwarded-For" 18 use-forwarded-headers: "true" 19 compute-full-forwarded-for: "true"

还可以通过http-snippet 获取 客户端真实 IP

1data: 2 server-tokens: "false" 3 http-snippet: | 4 real_ip_header X-Forwarded-For; 5 set_real_ip_from 0.0.0.0/0;

通过日志可以看到已经获取到真实的IP Description

当 Nginx Ingress 在转发请求时会通过 X-Forwarded-For 和 X-Real-IP 字段来记录客户端源 IP,后端可以通过此字段获得客户端真实源 IP,可以写一个简单的程序来验证下

1package main 2 3import ( 4 "log" 5 "net" 6 "net/http" 7 "strings" 8) 9 10func myHandle(w http.ResponseWriter, r *http.Request) { 11 _, _ = w.Write([]byte(remoteIP(r))) 12} 13 14func main() { 15 16 serveMux := http.NewServeMux() 17 serveMux.HandleFunc("/", myHandle) 18 19 err := http.ListenAndServe("0.0.0.0:80", serveMux) 20 if err != nil { 21 log.Printf("http.ListenAndServe():%v\n", err) 22 return 23 } 24} 25func remoteIP(r *http.Request) string { 26 ip := r.Header.Get("X-Original-Forwarded-For") 27 log.Printf("X-Original-Forwarded-For : %s", r.Header.Get("X-Original-Forwarded-For")) 28 if ip != "" { 29 return ip 30 } 31 32 ip = r.Header.Get("X-Forwarded-For") 33 log.Printf("X-Forwarded-For: %s", r.Header.Get("X-Forwarded-For")) 34 if ip != "" { 35 return ip 36 } 37 38 ip = r.Header.Get("X-Real-Ip") 39 log.Printf("X-Real-Ip : %s", r.Header.Get("X-Real-Ip")) 40 if ip != "" { 41 return ip 42 } 43 44 if ip, _, err := net.SplitHostPort(strings.TrimSpace(r.RemoteAddr)); err == nil { 45 return ip 46 } 47 48 return "" 49}

程序运行日志

1[root@k8s-test01 ~]# kubectl cp app nginx-d8d5f47c9-n9bnm:/ 2[root@k8s-test01 ~]# kubectl exec -it nginx-d8d5f47c9-n9bnm -- bash 3root@nginx-d8d5f47c9-n9bnm:/# ./app 42020/12/27 10:40:57 X-Original-Forwarded-For : 52020/12/27 10:40:57 X-Forwarded-For: 47.112.119.36

路由配置

通过 iris 框架写一个简单的测试程序

1func NewAPP() *iris.Application { 2 // 创建app结构体对象 3 app := iris.New() 4 // 配置字符编码 5 app.Configure(iris.WithConfiguration(iris.Configuration{ 6 Charset: "UTF-8", 7 })) 8 9 // 配置日志 10 customLogger := logger.New(logger.Config{ 11 //状态显示状态代码 12 Status: true, 13 // IP显示请求的远程地址 14 IP: true, 15 //显示http方法 16 Method: true, 17 // Path显示请求路径 18 Path: true, 19 // Query将url查询附加到Path。 20 Query: true, 21 //Columns:true, 22 // 如果不为空然后它的内容来自`ctx.Values(),Get("logger_message") 23 //将添加到日志中。 24 MessageContextKeys: []string{"logger_message"}, 25 //如果不为空然后它的内容来自`ctx.GetHeader(“User-Agent”) 26 MessageHeaderKeys: []string{"User-Agent"}, 27 }) 28 // 捕获所有http错误: 29 app.OnAnyErrorCode(customLogger, func(ctx iris.Context) { 30 switch ctx.GetStatusCode() { 31 case 404: 32 ctx.Values().Set("logger_message", "a dynamic message passed to the logs") 33 ctx.Writef("My Custom 404 error page") 34 default: 35 ctx.Values().Set("logger_message", "a dynamic message passed to the logs") 36 ctx.Writef("%v unknown error page", ctx.GetStatusCode()) 37 } 38 }) 39 app.Use(customLogger) 40 41 // favicons 42 app.Favicon("./static/favicons/favicon.ico") 43 44 // static 45 app.HandleDir("/", "static") 46 47 return app 48}

入口程序

1func main() { 2 app := config.NewAPP() 3 resAPI := app.Party("/api/v1") 4 resAPI.Get("/namespaces",handle.GetNameSpace) 5 resAPI.Get("/ip",handle.GetIP) 6 resAPI.Get("/hostname",handle.GetHostname) 7 app.Run(iris.Addr("0.0.0.0:8080"), iris.WithoutServerError(iris.ErrServerClosed), iris.WithOptimizations) 8}

创建Ingress

1--- 2apiVersion: networking.k8s.io/v1 3kind: Ingress 4metadata: 5 name: demo 6 annotations: 7 kubernetes.io/ingress.class: nginx 8 nginx.ingress.kubernetes.io/enable-modsecurity: "true" 9 nginx.ingress.kubernetes.io/enable-owasp-modsecurity-crs: "true" 10 nginx.ingress.kubernetes.io/modsecurity-snippet: | 11 SecRuleEngine On 12 SecRequestBodyAccess On 13 SecAuditEngine RelevantOnly 14 SecAuditLogParts ABIJDEFHZ 15 SecAuditLog /var/log/nginx/modsec_audit.log 16 Include /etc/nginx/owasp-modsecurity-crs/nginx-modsecurity.conf 17spec: 18 rules: 19 - host: "demo.bar.com" 20 http: 21 paths: 22 - pathType: Prefix 23 path: "/api/v1" 24 backend: 25 service: 26 name: demo 27 port: 28 number: 8080 29 - pathType: Prefix 30 path: "/" 31 backend: 32 service: 33 name: demo 34 port: 35 number: 8080

Description

静态资源加一级路由测试,重写路径

1--- 2apiVersion: networking.k8s.io/v1 3kind: Ingress 4metadata: 5 name: demo 6 annotations: 7 kubernetes.io/ingress.class: nginx 8 nginx.ingress.kubernetes.io/enable-modsecurity: "true" 9 nginx.ingress.kubernetes.io/enable-owasp-modsecurity-crs: "true" 10 nginx.ingress.kubernetes.io/modsecurity-snippet: | 11 SecRuleEngine On 12 SecRequestBodyAccess On 13 SecAuditEngine RelevantOnly 14 SecAuditLogParts ABIJDEFHZ 15 SecAuditLog /var/log/nginx/modsec_audit.log 16 Include /etc/nginx/owasp-modsecurity-crs/nginx-modsecurity.conf 17 nginx.ingress.kubernetes.io/app-root: /test/ 18 nginx.ingress.kubernetes.io/rewrite-target: /$2 19 nginx.ingress.kubernetes.io/configuration-snippet: | 20 rewrite ^/css/(.*)$ /test/css/$1 redirect; 21 rewrite ^/js/(.*)$ /test/js/$1 redirect; 22 rewrite ^/img/(.*)$ /test/img/$1 redirect; 23spec: 24 rules: 25 - host: "demo.bar.com" 26 http: 27 paths: 28 - pathType: Prefix 29 path: "/api/v1" 30 backend: 31 service: 32 name: demo 33 port: 34 number: 8080 35 - pathType: Prefix 36 path: "/test(/|$)(.*)" 37 backend: 38 service: 39 name: demo 40 port: 41 number: 8080

显示正常。(这里的前端样式文件用的是相对路径) Description

开启压缩

开启压缩前 Description

开启压缩

1 nginx.ingress.kubernetes.io/server-snippet: | 2 gzip on; 3 gzip_disable "MSIE [1-6]\."; 4 gzip_vary on; 5 gzip_proxied any; 6 gzip_comp_level 5; 7 gzip_min_length 512; 8 gzip_buffers 16 128k; 9 gzip_http_version 1.1; 10 gzip_types 11 application/json 12 application/javascript 13 application/xml 14 application/x-javascript 15 application/vnd.api+json 16 application/json 17 application/x-font-ttf 18 text/javascrip 19 text/css 20 text/plain 21 image/jpeg 22 image/png 23 image/jpg 24 image/svg+xml 25 image/x-icon;

Description

其他

Nginx Ingress 的配置参数与 Nginx 相差无几,更多配置请参考官方文档:

https://kubernetes.github.io/ingress-nginx/user-guide/nginx-configuration/configmap/

感兴趣的读者可以关注下微信号 Description

点赞
收藏

评论区

加载中...

相关推荐

MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1

文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s

Oracle 分组与拼接字符串同时使用

SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(

MySQL部分从库上面因为大量的临时表tmp_table造成慢查询

背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_

皕杰报表之UUID

​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为

手写Java HashMap源码

HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22

2020年前端实用代码段,为你的工作保驾护航

有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )