[TOC]
0x00 Kubernetes中Ingress跨域设置 描述: 在您在kubernetes
搭建ingress并通过其访问集群内部部署的项目时,有些功能可能会存在如下报错:Access to XMLHttpRequest at ... has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
上述错误提示这是一个跨域问题,在传统项目中我们更改Nginx配置即可,然后在kubernetes中或者ingress中,我们应该如何处理这种问题呢?
解决方式 我们可以在kubernetes
中的跨域设置在Ingress中进行配置,要在Ingress规则中启用跨域资源共享(CORS)只需添加如下注释: nginx.ingress.kubernetes.io/enable-cors: "true"
, 除此之外我们还可以使用使用以下注释来控制CORS。
nginx.ingress.kubernetes.io/cors-allow-methods
: 控制接受哪些方法。这是一个多值字段,以”,”分隔,仅接受字母(大写和小写),默认GET, PUT, POST, DELETE, PATCH, OPTIONS
。
nginx.ingress.kubernetes.io/cors-allow-headers
: 控制接受哪些Header请求头。这是一个多值字段,以”,”分隔,并接受字母,数字,_和-。默认: DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Authorization
nginx.ingress.kubernetes.io/cors-expose-headers
: 控制哪些响应头暴露给客户端。这是一个多值字段,以”,”分隔,并接受字母,数字,_,-和。默认值:空。例:nginx.ingress.kubernetes.io/cors-expose-headers: "Version, X-CustomResponseHeader"
nginx.ingress.kubernetes.io/cors-allow-origin
: 控制CORS接受的原产地。这是一个单字段值,格式如下:http(s)://origin-site.com或http(s)://origin-site.com:port
,默认: *,例: nginx.ingress.kubernetes.io/cors-allow-origin: "https://origin-site.com:4443"
nginx.ingress.kubernetes.io/cors-allow-credentials
: 控制在CORS操作期间是否可以传递凭据。默认: true,例: nginx.ingress.kubernetes.io/cors-allow-credentials: "false"
nginx.ingress.kubernetes.io/cors-max-age
: 控制可以将预检请求缓存多长时间。默认值:1728000 示例:nginx.ingress.kubernetes.io/cors-max-age: 600
示例演示 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: front-web namespace: web labels: app: front-web ref: front annotations: kubernetes.io/ingress.class: "nginx" nginx.ingress.kubernetes.io/enable-cors: "true" nginx.ingress.kubernetes.io/cors-allow-methods: "PUT, GET, POST, DELETE, PATCH, OPTIONS" nginx.ingress.kubernetes.io/cors-allow-headers: "Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control" nginx.ingress.kubernetes.io/cors-expose-headers: "Version, X-CustomResponseHeader" nginx.ingress.kubernetes.io/cors-allow-credentials: "true" nginx.ingress.kubernetes.io/cors-allow-origin: "https://*.weiyigeek.top" nginx.ingress.kubernetes.io/cors-max-age: 600
温馨提示: cors-allow-origin 还支持一个通配符如下 http(s)://*.foo.bar, http(s)://*.bar.foo:8080 or http(s)://*.abc.bar.foo:9000
1 2 nginx.ingress.kubernetes.io/cors-allow-origin: "https://*.weiyigeek.top:8443, http://*.weiyigeek.top, https://wygk.eu.org:1199"
Ingress CORS官方文档: https://kubernetes.github.io/ingress-nginx/user-guide/nginx-configuration/annotations/#enable-cors
0x01 Kubernetes中ingress-nginx文件上传代理访问超时设置 描述: 早上开发一张 504 gateway time-out
界面截图给我, 说是在后台导出1数据一分钟后显示此错误页面,由于我们的业务是通过K8s和ingress提供外部访问的,
错误原因: 后台应用界面为使用ingress方式访问, 所以问题点在ingress-nginx-controller
有关,通过查询发现ingress报504 gateway time-out
错误,通常与proxy timeout
有关。 由于默认的ingress-nginx-controller
配置的nginx.conf
的几个timeout
参数都是60 此处修改为300,请根据应用实际情况修改。
解决办法: 描述: 修改该应用域名所对应的的ingress资源,在metadata-annotations
下面增加如下几行。1 2 3 nginx.ingress.kubernetes.io/proxy-connect-timeout: "30" nginx.ingress.kubernetes.io/proxy-send-timeout: "120" nginx.ingress.kubernetes.io/proxy-read-timeout: "120"
扩展知识1.文件太大报413:Request Entity Too Large 1 2 3 4 5 metadata: annotations: nginx.ingress.kubernetes.io/proxy-body-size: 1024m
扩展知识2.当http 的URI太长或者request header过大时会报414 Request URI too large或400 bad request错误解决办法 1 2 3 4 client_header_buffer_size 128 k; large_client_header_buffers 4 128 k;
0x02 Kubernetes中ingress-nginx获取真实客户端IP 描述: 最近将部分业务通过Ingress进行发布管理, 从而实现应用灰蓝发布、金丝雀发布,更贴近当下自动化运维技术的发展,并为了进行实现七层自定义负载转发, 将不同应用程序配置到指定业务域名下不同的目录,并减少业务管理复杂化,同时节约域名资源,即多个业务可以通过一个域名出去提供服务。
但是在实际环境中却发现一个小问题,在通过ingress-nginx访问后端应用时,无法无法获取真实的客户端IP,因为通常用户ip的传递依靠的是X-Forwarded-*参数,但是默认情况下ingress是没有开启的,其中又由于Ingress-Nginx
前端代理是采用硬件负载将真实IP记录在自定义Header中,所以经过一天的资料查找与实践,最终将该问题进行解决,下面将记一波解决思路流程和配置实践。
环境说明
1.逻辑请求访问流程: 客户端 -> 边界防火墙 -> A10(硬件)负载均衡 -> Ingrees-Nginx -> statefulsets.apps (应用配置、扩容及其生命管理) -> Pod (应用程序)。
2.A10硬件负载均衡: 在硬件负载均衡上设置该业务域名的SSL,即实现外网访问https->转内网->http
。
3.ingress-nginx部署说明: 由helm部署的ingress名称空间 kube-base。
4.ingress-nginx-controller 的 Rule 及其 ConfigMap 配置。1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 $ kubectl get ingress -n app weiyigeek-app spec: ingressClassName: ingress-nginx rules: - host: app.weiyigeek.top http: paths: - backend: service: name: app-shangbao port: number: 80 path: /shangbao/ pathType: ImplementationSpecific $ kubectl get svc -n kube-base ingress-nginx-controller $ kubectl get svc -n kube-base ingress-nginx-controller apiVersion: v1 kind: Service metadata: annotations: k8s.kuboard.cn/workload: ingress-nginx-controller meta.helm.sh/release-name: ingress-nginx meta.helm.sh/release-namespace: kube-base creationTimestamp: "2021-02-15T04:57:22Z" labels: app.kubernetes.io/component: controller app.kubernetes.io/instance: ingress-nginx app.kubernetes.io/name: ingress-nginx name: ingress-nginx-controller namespace: kube-base resourceVersion: "82785800" uid: 79ac32ad-82a6-4a77-b5ad-71d30ebb07c5 spec: clusterIP: 11.20.7.61 clusterIPs: - 11.20.7.61 externalTrafficPolicy: Cluster ports: - name: http nodePort: 30080 port: 80 protocol: TCP targetPort: 80 - name: https nodePort: 30443 port: 443 protocol: TCP targetPort: 443 selector: app.kubernetes.io/component: controller app.kubernetes.io/instance: ingress-nginx app.kubernetes.io/name: ingress-nginx sessionAffinity: None type : NodePort status: loadBalancer: {}
Step 01.通过业务后端日志发现获取的地址为K8S的master节点地址,而非真实的IP地址1 2 3 4 5 6 7 8 9 10 11 12 13 14 2022-01-13 09:59:19.365 [http-nio-80-exec-10] INFO com.******.aop.AspectHandler-Access of com.******.controller.PageController.toPage?index [IP]10.41.40.26,10.41.40.26,172.20.170.128, 2022-01-13 09:59:19.367 [http-nio-80-exec-10] INFO com.******.aop.AspectHandler-Return of com.******.controller.PageController.toPage:shangbao/index [IP]10.41.40.26,10.41.40.26,172.20.170.128, String ip0 = request.getHeader("X-Real-IP" ); String ip1 = request.getHeader("X_FORWARDED_FOR" ); String ip2 = request.getHeader("X-Forwarded-For" ); String ip3 = request.getHeader("Proxy-Client-IP" ); String ip4 = request.getHeader("WL-Proxy-Client-IP" ); String ip5 = request.getHeader("HTTP_CLIENT_IP" ); String ip6 = request.getHeader("HTTP_X_FORWARDED_FOR" ); String ip7 = request.getHeader("x-Original-Forwarded-For" ); String ip8 = request.getRemoteAddr();
step 02.通过抓取A10负载均衡流量及其A10访问ingress-nginx的流量,发现硬件负载均衡真实IP带入记录的Header
字段是 X_FORWARDED_FOR
, 这样做的好初是防止客户端请求时伪造真实IP。
weiyigeek.top-A10访问ingress-nginx的流量
Step 03.想要Ingress-Nginx传递自定义的X_FORWARDED_FOR
字段,我们需要在 ingress-nginx 配置字典中加入如下配置.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 $ kubectl get cm -n kube-base ingress-nginx-controller -o yaml $ kubectl edit cm -n kube-base ingress-nginx-controller apiVersion: v1 data: enable -underscores-in-headers: "true" use-forwarded-headers: "true" forwarded-for-header: X_FORWARDED_FOR compute-full-forwarded-for: "true" proxy-real-ip-cidr: 192.168.4.11,192.168.10.11 tee ingress-nginx-controller-cm.yaml <<'EOF' apiVersion: v1 data: allow-snippet-annotations: "true" compute-full-forwarded-for: "true" enable -real-ip: "true" enable -underscores-in-headers: "true" forwarded-for-header: X_FORWARDED_FOR proxy-real-ip-cidr: 192.168.10.1/24,10.41.40.21/28,172.20.0.0/16 proxy-set-headers: X_FORWARDED_FOR use-forwarded-headers: "true" kind: ConfigMap metadata: labels: app.kubernetes.io/component: controller app.kubernetes.io/instance: ingress-nginx app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: ingress-nginx app.kubernetes.io/version: 1.2.0 name: ingress-nginx-controller namespace: ingress-nginx EOF
参数解析:
enable-underscores-in-headers
: 是否在标题名称中启用下划线, 缺省默认为off,表示如果请求中header name
中包含下划线,则忽略掉不会传递到后端代理或者应用程序,即获取不到该Header,所以此处为了不丢弃A10传递过来的 X_FORWARDED_FOR
Header 需要将该参数设置为True。
use-forwarded-headers
: 如果设置为True时,则将设定的X-Forwarded-*
Header传递给后端, 当Ingress在L7 代理/负载均衡器
之后使用此选项。 如果设置为 false 时,则会忽略传入的X-Forwarded-*
Header, 当 Ingress 直接暴露在互联网或者 L3/数据包的负载均衡器后面,并且不会更改数据包中的源 IP请使用此选项。
forwarded-for-header
: 设置用于标识客户端的原始 IP 地址的 Header 字段。默认值X-Forwarded-For
,此处由于A10带入的是自定义记录IP的Header,所以此处填入是X_FORWARDED_FOR
.
compute-full-forwarded-for
: 将 remote address 附加到 X-Forwarded-For Header而不是替换它。当启用此选项后端应用程序负责根据自己的受信任代理列表排除并提取客户端 IP。
proxy-real-ip-cidr
: 如果启用 use-forwarded-headers
或 use-proxy-protocol
,则可以使用该参数其定义了外部负载衡器 、网络代理、CDN等地址,多个地址可以以逗号分隔的 CIDR 。默认值: “0.0.0.0/0”
Tips: 为了统一可移植性,在程序设置或者硬件负载转发中,转发、设置的 header 里不建议采用"_"下划线
,可以用驼峰命名
或者其他的符号(如减号-)
进行代替,上述的X_FORWARDED_FOR
字段把我是坑得,欲哭无泪。
上述参数配置后在ingress-nginx-control
中的/etc/nginx/nginx.conf
结果如下:1 2 3 4 5 6 7 8 9 10 11 12 real_ip_header X_FORWARDED_FOR; real_ip_recursive on; set_real_ip_from 192.168.10.11; set_real_ip_from 192.168.4.11; map $http_x_forwarded_for $full_x_forwarded_for { default "$http_x_forwarded_for , $realip_remote_addr " ; '' "$realip_remote_addr " ; }
Step 04.为了在后端演示ingress-nginx
传递过来的参数, 创建一个nginx应用在log_format参数设置如下"$http_X_FORWARDED_FOR" - "$http_X_Real_IP"'
1 2 3 4 5 6 7 8 log_format main '$remote_addr - $remote_user [$time_local] "$request" ' '$status $body_bytes_sent "$http_referer" ' '"$http_user_agent" "$http_x_forwarded_for" - "$http_X_FORWARDED_FOR" - "$http_X_Real_IP"' ; proxy_set_header X-Real-IP $remote_addr ; proxy_set_header X-Forwarded-For $full_x_forwarded_for ;
Tips: remote_addr
: 代表客户端的IP,但它的值不是由客户端提供的,而是服务端根据客户端的ip指定的,如果你使用了代理则该字段记录的的是代理IP。 Tips: X-Forwarded-For
: 简称 XFF 它是一个头部扩展,TTP协议并没有对它的定义,它最开始是由 Squid 这个缓存代理软件引入用来表示 HTTP 请求端真实 IP,当前被各大 HTTP 代理、负载均衡等转发服务广泛使用并被写入 RFC 7239(Forwarded HTTP Extension)标准之中,格式为:X-Forwarded-For: client, proxy1, proxy2
(注意:如果未经严格处理可以被伪造),例如如果一个 HTTP 请求到达服务器之前,经过了三个代理 Proxy1、Proxy2、Proxy3,IP 分别为 IP1、IP2、IP3,用户真实 IP 为 IP0,而服务端X-Forwarded-For 输出的是IP0, IP1, IP2,Proxy3 直连服务器,它会给 XFF 追加 IP2,表示它是在帮 Proxy2 转发请求,所以说列表中并没有 IP3,IP3 可以在服务端通过 Remote Address 字段获得 Tips: X-Real-IP
: 自定义头部字段,通常被 HTTP 代理用来表示与它产生 TCP 连接的设备 IP,该设备可能是其他代理,也可能是真正的请求端,关键要看经过代理的层级次数或是是否始终将真实IP一路传下来(注意:如果未经严格处理,可以被伪造)。
Step 05.在应用 Pod 中进行利用tcpdump抓包,日志记录真实IP和效果如下所示:1 2 3 4 5 6 $ kubectl exec -it -n app shangbao-text sh 2022-01-13 22:31:00.178 INFO 1 --- [p-nio-80-exec-4] com.******.aop.AspectHandler: Access of com.******.controller.PageController.toPage?index [IP]183.222.192.169,183.222.192.169,10.41.40.21,172.20.35.192, 2022-01-13 22:31:00.180 INFO 1 --- [p-nio-80-exec-4] com.******.aop.AspectHandler: Return of com.******.controller.PageController.toPage:shangbao/index [IP]183.222.192.169,183.222.192.169,10.41.40.21,172.20.35.192,
weiyigeek.top-在Pod中抓包使用wirshake打开
官方文档地址: https://kubernetes.github.io/ingress-nginx/user-guide/nginx-configuration/configmap/#proxy-real-ip-cidr
知识补充: Ingress Pod中无法保留源IP 问题现象: Ingress Pod中无法保留真实客户端IP,显示为节点IP或100.XX.XX.XX网段或其它地址。
问题原因: Ingress所使用的Service中externalTrafficPolicy
设为了Cluster, SLB上使用了七层代理, 使用了WAF接入或WAF透明接入服务。 解决方案:
对于设置externalTrafficPolicy为Cluster,且前端使用了四层SLB的情况。可以将externalTrafficPolicy改为Local。但可能会导致集群内部使用SLB IP访问Ingress不通,具体解决方案,请参见集群内访问集群LoadBalancer暴露的SLB地址不通。
对于使用了七层代理(七层SLB、WAF、透明WAF)的情况,可以按以下步骤解决: 确保使用的七层代理且开启了X-Forwarded-For请求头。 在Ingress Controller的ConfigMap中(默认为kube-system命名空间下的nginx-configuration)添加enable-real-ip: “true”。 观察日志,验证是否可以获取到源IP。 对于链路较长,存在多次转发的情况(例如在Ingress Controller前额外配置了反向代理服务),可以在开启enable-real-ip时通过观察日志中remote_addr的值,来确定真实IP是否是以X-Forwarded-For请求头传递到Ingress容器中。若不是,请在请求到达Ingress Controller之前利用X-Forwarded-For等方式携带客户端真实IP。
总结说明: 描述: 由上述可知并不是所有的场景都能通过X-Forwarded-For来获取用户正式ip, 例如当服务器前端使用了CDN的时候,X-Forwarded-For 方式获取到的可能就是CDN的来源ip了,此种情况可以和CDN配置管理后台约定一个字段名来记录用户真实ip, 然后代理将这个字段逐层传递最后到应用服务端。
各种方式在CDN环境下,获取真实IP地址的优缺点:
CDN自定义header头 优点:获取到最真实的用户IP地址,用户绝对不可能伪装IP。 缺点:需要CDN厂商提供。
获取forwarded-for头 优点:可以获取到用户的IP地址。 缺点:程序需要改动,以及用户IP有可能是伪装的。
使用realip获取 优点:程序不需要改动,直接使用remote_addr即可获取IP地址。 缺点:ip地址有可能被伪装,而且需要知道所有CDN节点的ip地址或者ip段。
至此完毕!
0x03 Kubernetes中ingress-nginx 如何在外部设置自定义nginx指令snippet 描述: 我们可以在ingress-nginx的configMap和ingress域名规则中,利用ConfigMap和Annotations进行自定义配置 snippet 并注入nginx.conf中。
ConfigMap: 使用ConfigMap在NGINX中设置全局配置。
Annotations: 如果需要特定入口规则的特定配置,请使用此选项。
1.在ConfigMap中配置 描述: 我们可以在Ingress-nginx中的ConfigMap进行配置全局生效的Nginx指令片段,其中主要位置的指令片段嵌入位置参数如下:
main-snippet
: 将自定义配置添加到 nginx 配置的主要部分。
http-snippet
: 将自定义配置添加到 nginx 配置的 http 部分。
server-snippet
: 将自定义配置添加到 nginx 配置中的所有服务器。
location-snippet
: 将自定义配置添加到 nginx 配置中的所有位置。您不能使用它来添加代理到 Kubernetes pod 的新位置,因为该片段无法访问 Go 模板函数。如果要添加自定义位置,则必须提供自己的 nginx.tmpl。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 $ kubectl edit cm -n kube-base ingress-nginx-controller apiVersion: v1 data: allow-snippet-annotations: "true" main-snippet: | ....指令.... http-snippet: | ....指令.... server-snippet: | ....指令.... location-snippet: | ....指令.... kind: ConfigMap metadata: annotations: meta.helm.sh/release-name: ingress-nginx meta.helm.sh/release-namespace: ingress-nginx creationTimestamp: "2022-06-24T05:20:53Z" labels: app.kubernetes.io/component: controller app.kubernetes.io/instance: ingress-nginx app.kubernetes.io/managed-by: Helm app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: ingress-nginx app.kubernetes.io/version: 1.2.1 helm.sh/chart: ingress-nginx-4.1.4 name: ingress-nginx-controller namespace: ingress-nginx resourceVersion: "1501009" uid: 0611906f-d100-4907-b29c-d4f55fc269ad
2.在ingress域名规则中配置 温馨提示: 如果要在ingress站点配置清单中在 annotations 加入 Nginx 指令片段 snippet , 则我们需要在 ingress-nginx 的 ConfigMap data 对象中加入allow-snippet-annotations: "true"
键值对即可启用,然后我们便可在 Ingress 配置站点资源清单里的annotations对象下使用。
nginx.ingress.kubernetes.io/server-snippet
: 使用注释可以在服务器配置块中添加自定义配置。(每个虚拟主机只能配置一次) 更改影响局部server站点。nginx.ingress.kubernetes.io/configuration-snippet
: 使用此注释您可以向NGINX位置添加其他配置。(但多租户集群中慎用) 更改影响全局。nginx.ingress.kubernetes.io/stream-snippet
: 使用注释可以添加自定义流配置。
1 2 3 4 5 6 7 8 9 10 11 $ kubectl edit ingress -n app weiyigeek-app apiVersion: networking.k8s.io/v1 kind: Ingress metadata: annotations: nginx.ingress.kubernetes.io/configuration-snippet: | ....指令片段..... nginx.ingress.kubernetes.io/stream-snippet: | ....指令片段..... nginx.ingress.kubernetes.io/server-snippet: | ....指令片段.....
实践示例: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 metadata: annotations: nginx.ingress.kubernetes.io/configuration-snippet: | proxy_set_header X-FORWARDED-FOR $http_X_FORWARDED_FOR ; server_tokens off; location /itwork/res/css { deny all;return 403;} nginx.ingress.kubernetes.io/stream-snippet: | server { listen 8000; proxy_pass 127.0.0.1:80; } nginx.ingress.kubernetes.io/server-snippet: | add_header X-Frame-Options SAMEORIGIN; set $flag 0; set $childdomain "" ; if ($http_user_agent ~* "(Mobile)" ){ set $flag "${flag} 1" ; } if ( $host ~* "(.*).weiyigeek.top" ) { set $childdomain $1 ; set $flag "${flag} 2" ; } if ( $flag = "012" ) { return 301 http://m.weiyigeek.top/$1 $request_uri ; }
0x04 Kubernetes中ingress-nginx指定node亲和性和nginx-ingress-controller修改缺省端口 描述: 在我们需要指定ingress-nginx-controller应用Pod允许运行在那些工作节点时可以对其进行Node和Pod亲和性设置, 与此同时我们还可以更改 nginx-ingress-controller
的服务端口, 但注意修改后需要更改对应的Service服务发现。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 $ kubectl get deployments.apps -n kube-base ingress-nginx-controller -o wide NAME READY UP-TO-DATE AVAILABLE AGE CONTAINERS IMAGES SELECTOR ingress-nginx-controller 9/9 9 9 329d controller harbor.cloud/weiyigeek/ingress-nginx-controller:v0.44.0 app.kubernetes.io/component=controller,app.kubernetes.io/instance=ingress-nginx,app.kubernetes.io/name=ingress-nginx $ kubectl get deployments.apps -n kube-base ingress-nginx-controller spec: affinity: nodeAffinity: requiredDuringSchedulingIgnoredDuringExecution: nodeSelectorTerms: - matchExpressions: - key: node operator: In values: - app - work podAntiAffinity: preferredDuringSchedulingIgnoredDuringExecution: - podAffinityTerm: labelSelector: matchExpressions: - key: app.kubernetes.io/name operator: In values: - ingress-nginx topologyKey: kubernetes.io/hostname weight: 100 containers: - args: - /nginx-ingress-controller - --default-backend-service=$(POD_NAMESPACE)/default-backend - --publish-service=$(POD_NAMESPACE)/ingress-nginx-controller - --election-id=ingress-controller-leader - --ingress-class=ingress-nginx - --configmap=$(POD_NAMESPACE)/ingress-nginx-controller - --validating-webhook=:6443 - --validating-webhook-certificate=/usr/local /certificates/cert - --validating-webhook-key=/usr/local /certificates/key - --http-port=8080 - --https-port=8443 name: controller ports: - containerPort: 8080 hostPort: 8080 name: http protocol: TCP - containerPort: 8443 hostPort: 8443 name: https protocol: TCP - containerPort: 6443 hostPort: 6443 name: webhook protocol: TCP
0x05 Kubernetes中ingress-nginx如何为项目配置子路径 方式1.创建带有app-root注释的Ingress规则 描述: 将 approot.weiyigeek.top 请求重定向到 approot.weiyigeek.top/app1 子目录上。1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 apiVersion: extensions/v1beta1 kind: Ingress metadata: annotations: nginx.ingress.kubernetes.io/app-root: /app1 name: approot namespace: default spec: rules: - host: approot.weiyigeek.top http: paths: - backend: serviceName: http-svc servicePort: 80 path: / curl -I -k approot.weiyigeek.top HTTP/1.1 302 Moved Temporarily
方式2.创建带有rewrite注释的Ingress规则 描述: 在这个Ingress定义中元组(.*)
捕获的所有字符都将分配给占位符 $2,然后将其用作重写目标注释中的参数。
例如,上面的入口定义将进行以下重写:
weiyigeek.top/demo 重写为 weiyigeek.top/
weiyigeek.top/demo/ 重写为 weiyigeek.top/
weiyigeek.top/demo/issues 重写为 weiyigeek.top/issues
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 apiVersion: extensions/v1beta1 kind: Ingress metadata: annotations: nginx.ingress.kubernetes.io/rewrite-target: /$2 name: rewrite namespace: default spec: rules: - host: rewrite.bar.com http: paths: - backend: serviceName: http-svc servicePort: 80 path: /demo(/|$)(.*)
方式3.通过注释server-snippet片段脚本进行Ingress规则重写 描述: 例如将访问 web.weiyigeek.top/index
的请求重定向到http://m.weiyigeek.top/web/index
.1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 apiVersion: extensions/v1beta1 kind: Ingress metadata: annotations: nginx.ingress.kubernetes.io/server-snippet: | if ($host ~* "(.*).weiyigeek.top") { return 301 http://m.weiyigeek.top/$1$request_uri ; } name: web namespace: default spec: rules: - host: '*.weiyigeek.top' http: paths: - backend: serviceName: web-nginx servicePort: 80 path: / apiVersion: extensions/v1beta1 kind: Ingress metadata: name: example-ingress annotations: nginx.ingress.kubernetes.io/configuration-snippet: | rewrite /service/(.*) /s1/$1 break; spec: rules: - host: test.weiyigeek.top http: paths: - path: /service/ backend: serviceName: app1 servicePort: 8080
0x06 Kubernetes 中 ingress-nginx 上的 HTTP 的速率限制请求 描述: 在某些情况我们可以使用ingress-nginx
针对请求速率进行请求限制。
通常我们使用如下两种方式管理通过配置映射
和注释调整
来完成。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 $ kubectl get cm -n ingress-nginx ingress-nginx-controller -o yaml data: http-snippet: | limit_req_zone $http_authorization zone=my-zone:20m rate=5r/s; limit_req_zone $binary_remote_addr zone=my-zone:20m rate=10r/s; limit_req_zone $http_someheader zone=my-zone:20m rate=20r/s; metadata: annotations: nginx.ingress.kubernetes.io/configuration-snippet: | limit_req zone=my-zone-1 burst=10 nodelay; limit_req_log_level notice; limit_req_status 429; metadata: annotations: nginx.ingress.kubernetes.io/server-snippet: | location /content/images/ { limit_req zone=my-zone-2 burst=50 nodelay; } location /content/texts/ { limit_req zone=my-zone-3 burst=50 nodelay; } nginx.ingress.kubernetes.io/configuration-snippet: | limit_req zone=my-zone-1 burst=10 nodelay; limit_req_log_level notice; limit_req_status 429;
温馨提示: 请注意 http-snippet 不允许作为注释,而只能在ConfigMap中进行映射配置!
0x07 Kubernetes中ingress-nginx优化配置 描述: 在K8s集群中部署安装 ingress-nginx 后默认并未进行相应的优化配置,本小结将针对于生产环境的中的 ingress-nginx 控制器进行优化。
我们可以从 ingress-nginx-controller 资源的 Pod 、ConfigMap 、以及业务的 ingress 规则入手。
ingress-nginx-controller Pod 温馨提示: 我们需要针对承载 ingress-nginx 的相关 Pod 容器进行内核参数优化。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 $ kubectl get deployments.apps -n ingress-nginx ingress-nginx-controller NAME READY UP-TO-DATE AVAILABLE AGE ingress-nginx-controller 9/9 9 9 5d20h $ kubectl get deployments.apps -n ingress-nginx ingress-nginx-controller -o yaml initContainers: - name: sysctl image: alpine:3.10 imagePullPolicy: IfNotPresent command : - sh - -c - | mount -o remount rw /proc/sys sysctl -w net.core.somaxconn=65535 sysctl -w net.ipv4.tcp_tw_reuse=1 sysctl -w net.ipv4.ip_local_port_range="1024 65535" sysctl -w fs.file-max=1048576 sysctl -w fs.inotify.max_user_instances=16384 sysctl -w fs.inotify.max_user_watches=524288 sysctl -w fs.inotify.max_queued_events=16384 securityContext: privileged: true
ingress-nginx-controller ConfigMap 温馨提示: 我们需要按照需要将下述K/V配置项插入到 ingress-nginx 的 configMap 里的 data 对象下。
ingress-nginx 资源查看 1 2 3 4 5 kubectl get cm -n ingress-nginx nginx-ingress-controller -o yaml kubectl edit cm -n ingress-nginx nginx-ingress-controller
优化配置 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 load-balance: "round_robin" error-log-level: "notice" use-gzip: "true" gzip-level: "2" gzip-min-length: "3072" gzip-types: "text/html text/plain text/css text/javascript application/javascript application/x-javascript application/xml application/x-httpd-php application/x-font-ttf application/json image/x-icon image/svg+xml image/avif image/webp font/ttf font/opentype" enable -brotli: "true" brotli-level: 5 brotli-min-length: 3072 brotli-types: "text/plain text/css text/javascript application/javascript application/x-javascript application/xml application/x-httpd-php application/x-font-ttf image/x-icon image/svg+xml image/avif image/webp font/ttf font/opentype" use-http2: "true" ssl_session_cache: "shared:SSL:10m;" ssl-session-timeout: "10m" worker-processes: "auto" max-worker-open-files: "10240" max-worker-connections: "32767" enable -multi-accept: "true" keep-alive: "75" keep-alive-requests: "10000" upstream-keepalive-time: "30m" upstream-keepalive-timeout: "60" upstream-keepalive-requests: "10000" upstream-keepalive-connections: "512" proxy-connect-timeout: "30" proxy-send-timeout: "120" proxy-read-timeout: "120"
开发测试: 1 2 enable -opentracing: true
温馨提示:
在 Nginx 中的 upstream
主要是配置均衡池和调度方法.
在 Nginx 中的 proxy_pass
主要是配置代理服务器ip或服务器组的名字.
温馨提示: 在高并发场景下,我们需配置upstream-keepalive-*
相关参数, 使得 nginx 尽可能快速处理 HTTP 请求(尽量少释放并重建 TCP 连接),同时控制 nginx 内存使用量。
温馨提示: ingress nginx 与 upstream pod 建立 TCP 连接并进行通信,其中涉及 3 个超时配置我们需要重点关注。
proxy-read-timeout 选项 设置 nginx 与 upstream pod 之间读操作的超时时间,默认设置为 60s,当业务方服务异常导致响应耗时飙涨时,异常请求会长时间夯住 ingress 网关,我们在拉取所有服务正常请求的 P99.99 耗时之后,将网关与 upstream pod 之间读写超时均缩短到 3s,使得 nginx 可以及时掐断异常请求,避免长时间被夯住。
proxy-connect-timeout 选项 设置 nginx 与 upstream pod 连接建立的超时时间,默认设置为 5s,由于在 nginx 和业务均在内网同机房通信,我们将此超时时间缩短到 1s。
ingress Rule 描述: 通常我们需要为单个业务进行相应配置, 此时我们便需要再业务的ingress部署清单中进行修正。
例如: 编辑 blog.weiyigeek.top 虚拟主机站点的 ingress 规则 (kubectl edit ingress -n weiyigeek blog.weiyigeek.top
)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 apiVersion: networking.k8s.io/v1 kind: Ingress metadata: annotations: ingress.kubernetes.io/proxy-body-size: "128m" nginx.ingress.kubernetes.io/client-body-buffer-size: 64m nginx.ingress.kubernetes.io/proxy-max-temp-file-size: 128m nginx.ingress.kubernetes.io/proxy-buffer-size: 64m nginx.ingress.kubernetes.io/proxy-buffering: "on" nginx.ingress.kubernetes.io/proxy-buffers-number: "4" nginx.ingress.kubernetes.io/proxy-connect-timeout: 60 nginx.ingress.kubernetes.io/proxy-read-timeout: 180s nginx.ingress.kubernetes.io/proxy-send-timeout: 60s nginx.ingress.kubernetes.io/server-snippet: | location ~ /fastfile { client_max_body_size 1024m; client_header_timeout 60; client_body_timeout 60; client_body_buffer_size 10m; proxy_connect_timeout 60; proxy_read_timeout 180; proxy_send_timeout 30; proxy_buffer_size 1024k; proxy_buffers 6 500k; proxy_busy_buffers_size 1024k; proxy_temp_file_write_size 1024k; }
0x08 Kubernetes中ingress-nginx安全配置 描述: 在 K8s 集群中部署安装 ingress-nginx 后默认并未根据应用安全需要进行相应的安全配置,本小结将针对于生产环境的中的 ingress-nginx 控制器以及应用常见进行安全安全配置。
1.配置指定的 Ingress Class 描述: 如果一个K8S集群中部署了多个ingress controller时,我们可以在创建ingress的时候,通过ingressClassName指定ingress class,如下所示:1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 $ kubectl get ingress -n web www-weiyigeek -o yaml apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: blog-weiyigeek-top annotations: nginx.ingress.kubernetes.io/rewrite-target: / labels: app: blog ref: blog.weiyigeek.top spec: ingressClassName: nginx rules: - host: weiyigeek.top http: paths: - backend: service: name: blog port: number: 8080 path: / pathType: ImplementationSpecific
2.安全配置之强制跳转HTTPS 描述: 通过这个annotation可以强制 https,如果是http请求,会通过308 redirect 到 https.
示例:1 2 3 4 5 6 7 8 9 metadata: annotations: nginx.ingress.kubernetes.io/ssl-redirect: "true" nginx.ingress.kubernetes.io/force-ssl-redirect: "true" redirectnginx.ingress.kubernetes.io/preserve-trailing-slash: "true"
3.安全配置之跨域访问cors 描述: 当将Ingress-Nginx作为API网关,必须进行跨域配置否则会对业务造成影响,例如我们公司的CDN业务必须设置跨域方面配置。
示例:1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 metadata: annotations: nginx.ingress.kubernetes.io/enable -cors: "true" nginx.ingress.kubernetes.io/cors-allow-origin:"https://*.weiyigeek.top" nginx.ingress.kubernetes.io/cors-allow-methods: "GET, PUT, POST, OPTIONS" nginx.ingress.kubernetes.io/cors-allow-headers: "X-Forwarded-For, X-App-Weiyigeek" nginx.ingress.kubernetes.io/cors-expose-headers: "*, X-CustomResponseHeader" nginx.ingress.kubernetes.io/cors-allow-credentials: "false" nginx.ingress.kubernetes.io/cors-max-age: 600
4.安全配置之防止DDOS请求限流 描述: 通常针对于文件下载服务器我们需要进行一系列的配置, 我们可以通过 rps 限制每秒请求数,rpm 限制每分钟请求数,connections限制连接数, 如若超过将返回 503 。
示例:1 2 3 4 5 6 7 8 9 10 11 12 metadata: annotations: nginx.ingress.kubernetes.io/limit -rps: "5" nginx.ingress.kubernetes.io/limit -rpm: "300" nginx.ingress.kubernetes.io/limit -connections: "10" nginx.ingress.kubernetes.io/limit -burst-multiplier:"5" nginx.ingress.kubernetes.io/limit -whitelist: "10.0.0.0/24,172.10.0.1"
5.安全配置之请求访问白名单
描述: 配置白名单比黑名单更加安全,与最小所需权限一样,主要是用于安全限制,只允许特定的客户端请求,但由于现在网络中NAT的广泛应用,但是参数使用的场景比较有限,例如针对于内网采集监控服务调用,即将其访问请求IP加入到白名单中,才不受安全策略限制影响。
示例:1 2 3 4 metadata: annotations: ingress.kubernetes.io/whitelist-source-range: "10.0.0.0/24,172.10.0.1"
6.安全配置之请求访问日志记录 描述: 为了等保合规通常需要将各类日志存储 180 天及以上,所以ingress也是非常重要,当业务被攻击时我们可以快速溯源追踪,以及其行为分析。
示例:1 2 3 4 5 6 metadata: annotations: nginx.ingress.kubernetes.io/enable -access-log: "true" nginx.ingress.kubernetes.io/enable -rewrite-log: "true"
7.安全配置之Nginx指定代理响应标头 描述: 在使用 ingress-nginx 场景中免不了配置使用代理或响应表头,例如常见的X-Frame-Options
规定了允许那些站点嵌入配置目标iframe站点。
示例:1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 metadata: annotations: nginx.ingress.kubernetes.io/server-snippet: | server_tokens off; add_header X-Frame-Options SAMEORIGIN; more_set_headers 'x-content-type-options: nosniff ' 'x-xss-protection: 1; mode=block' 'X-Robots-Tag: none;' ; nginx.ingress.kubernetes.io/configuration-snippet: | proxy_set_header My-Custom-Header $http_my_custom_header ; proxy_set_header X-FORWARDED-FOR $http_X_FORWARDED_FOR ;
8.安全配置之Nginx禁止访问某一目录 描述: 在使用 ingress-nginx 场景中,如何禁止客户端访问 ingress 站点某一目录下的所有资源,我们可以配置 server-snippet 注释,例如。
示例:1 2 3 4 5 metadata: annotations: nginx.ingress.kubernetes.io/server-snippet: | location /itwork/ { deny all;return 403;}
9.安全配置之请求访问认证
描述: 针对于某些未有认证的API接口应用,可以通过在 Ingress 规则中添加额外的注释来添加身份验证。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 metadata: annotations: nginx.ingress.kubernetes.io/auth-type: nginx.ingress.kubernetes.io/auth-secret: basic-auth nginx.ingress.kubernetes.io/auth-secret-type:auth-file nginx.ingress.kubernetes.io/auth-realm: "身份认证"
10.安全配置之启用SSL握手加密套件 描述: 通常为了应用安全我们会为其添加证书,但是一些已知脆弱性的SSL加密方式会影响到应用信息安全,例如 RC4 与 MD5 等
示例:1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 $ kubectl edit cm -n ingress-nginx ingress-nginx-controller apiVersion: v1 data: ssl-ciphers: "ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE:ECDH:AES:HIGH:EECDH+CHACHA20:EECDH+CHACHA20-draft:EECDH+AES128:RSA+AES128:EECDH+AES256:RSA+AES256:EECDH+3DES:RSA+3DES:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:!NULL:!aNULL:!eNULL:!EXPORT:!PSK:!ADH:!DH:!DES:!MD5:!RC4" ssl-protocols: "TLSv1.1 TLSv1.2 TLSv1.3" ssl_session_cache: "shared:SSL:10m;" ssl-session-timeout: "10m" metadata: annotations: nginx.ingress.kubernetes.io/ssl-prefer-server-ciphers: "true" nginx.ingress.kubernetes.io/ssl-ciphers: "ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE:ECDH:AES:HIGH:EECDH+CHACHA20:EECDH+CHACHA20-draft:EECDH+AES128:RSA+AES128:EECDH+AES256:RSA+AES256:EECDH+3DES:RSA+3DES:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:!NULL:!aNULL:!eNULL:!EXPORT:!PSK:!ADH:!DH:!DES:!MD5:!RC4;"
11.安全配置之启用 modsecurity waf模块 描述: ModSecurity (http://modsecurity.org/ ) 是一个开源的Web Application防火墙, 可以为一组特定的入口位置启用它。必须首先通过在 ConfigMap 中启用 ModSecurity 来启用 ModSecurity 模块。
请注意,这将为所有路径启用 ModSecurity,并且必须手动禁用每个路径。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 $ kubectl edit cm -n ingress-nginx ingress-nginx-controller apiVersion: v1 data: enable -modsecurity: "true" metadata: annotations: nginx.ingress.kubernetes.io/enable -modsecurity: "true" nginx.ingress.kubernetes.io/enable -owasp-core-rules: "true" nginx.ingress.kubernetes.io/modsecurity-transaction-id: "$request_id " nginx.ingress.kubernetes.io/modsecurity-snippet: | Include /etc/nginx/owasp-modsecurity-crs/nginx-modsecurity.conf SecRuleEngine On SecDebugLog /tmp/modsec_debug.log
OWASP 核心规则集 :https://www.modsecurity.org/CRS/Documentation/ 建议的配置参考 :https://github.com/SpiderLabs/ModSecurity/blob/v3/master/modsecurity.conf-recommended