Ingress-Nginx进阶学习实践扩充配置记录
作者:快盘下载 人气:[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示例演示
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: "GET, POST" 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: {{ .Values.ingress.allowOrigin }} nginx.ingress.kubernetes.io/cors-max-age: 600
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下面增加如下几行。
nginx.ingress.kubernetes.io/proxy-connect-timeout: "30" # 注意,此值不能超过75. nginx.ingress.kubernetes.io/proxy-send-timeout: "120" nginx.ingress.kubernetes.io/proxy-read-timeout: "120"
扩展知识1.文件太大报413:Request Entity Too Large
# 解决办法:创建 ingress 时添加 annotations(注释) metadata: annotations: nginx.ingress.kubernetes.io/proxy-body-size: 1024m
扩展知识2.当http 的URI太长或者request header过大时会报414 Request URI too large或400 bad request错误解决办法
# 客户端请求头缓冲区大小 client_header_buffer_size 128k; # 请求头总长度大于128k时使用如下设置的缓存区 large_client_header_buffers 4 128k;
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)自定义ingress管理域名后端映射配置 $ kubectl get ingress -n app weiyigeek-app # NAME CLASS HOSTS ADDRESS PORTS AGE # weiyigeek-app ingress-nginx app.weiyigeek.top 11.20.7.61 80 41d # 关键配置 spec: ingressClassName: ingress-nginx rules: - host: app.weiyigeek.top http: paths: - backend: service: name: app-shangbao port: number: 80 path: /shangbao/ pathType: ImplementationSpecific # (2)ingress-nginx-controller 控制器 services 服务查看 $ kubectl get svc -n kube-base ingress-nginx-controller # NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE # ingress-nginx-controller nodePort 11.20.7.61 <none> 80:30080/TCP,443:30443/TCP 332d $ 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地址
# 日志 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, # 程序中获取真实IP地址的请求头几种Header. 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.A10访问ingress-nginx的流量
Step 03.想要Ingress-Nginx传递自定义的X_FORWARDED_FOR字段,我们需要在 ingress-nginx 配置字典中加入如下配置.# 配置 ingress-nginx 字典更改后会自动更新进ingress-nginx的POD中/etc/nginx/nginx.conf的文件里。 $ 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 # 具体的 ingres-nginx 的 configMap 配置 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结果如下:
# use-forwarded-headers 参数设置后 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; # We can't use $proxy_add_x_forwarded_for because the realip module replaces the remote_addr too soon # 我们不能使用 `$proxy_add_x_forwarded_for`, 因为 realip 模块过早地替换了远程 remote_addr。 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"'
# /etc/nginx/nginx.conf 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"'; # 输出结果 "10.41.40.22" -- "10.41.40.22" -- "10.20.172.103" proxy_set_header X-Real-IP $remote_addr; # realip 模块 已经将 X_FORWARDED_FOR 字段赋值给 $remote_addr, 所以该字段也记录了真实IP. 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和效果如下所示:
$ kubectl exec -it -n app shangbao-text sh # tcpdump -nnX port 80 -vv -w test.pcap # 日志效果: 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.在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。$ 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 : 使用注释可以添加自定义流配置。
$ 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: | ....指令片段.....
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_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服务发现。
$ 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 # 编辑查看 ingress-nginx-controller 缺省的80、443端口 $ 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 # nginx-ingress-controller 启动参数 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端口,一个是https端口) - --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 子目录上。
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: / # 检查rewrite是否正常 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/issuesapiVersion: 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(/|$)(.*) # 元组1 $1 = (/|$) # 元组2 $2 = (.*)
方式3.通过注释server-snippet片段脚本进行Ingress规则重写 描述: 例如将访问 web.weiyigeek.top/index 的请求重定向到http://m.weiyigeek.top/web/index.
# 方式1 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: / # 方式2.访问test.weiyigeek.top/service/index重定向到test.weiyigeek.top/s1/index地址 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针对请求速率进行请求限制。
通常我们使用如下两种方式管理通过配置映射和注释调整来完成。
# 配置映射-ConfigMap $ 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; # 配置注释-annotations # - 入口资源中的注释: 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 容器进行内核参数优化。
$ 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 # 在 spec.template.spec 对象下添加一个初始化 initContainers 容器 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 资源查看
# 查看 Ingress-nginx 全局配置参数: kubectl get cm -n ingress-nginx nginx-ingress-controller -o yaml # 修改 Ingress-nginx 全局配置参数: kubectl edit cm -n ingress-nginx nginx-ingress-controller
优化配置
# 负载工作机制,轮询 load-balance: "round_robin" # 错误日志等级设置 (debug, info, notice, warn, error, crit, alert, or emerg) error-log-level: "notice" # 启用Gzip资源压缩 (3k以上) 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" # 不建议进行照片压缩 image/jpeg image/gif image/png 可能反而会增加其体积 # 启用Brotli资源压缩(同等条件下优于Gzip,任选一个) 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" # 不建议进行照片压缩 image/jpeg image/gif image/png 可能反而会增加其体积 # 启用http2支持(实际上默认是开启的,如果过关闭请将其设置为true) use-http2: "true" # ssl 会话复用 ssl_session_cache: "shared:SSL:10m;" ssl-session-timeout: "10m" # worker 每个工作进程可以打开的最大文件数与同时打开最大连接数设置 worker-processes: "auto" max-worker-open-files: "10240" max-worker-connections: "32767" # 连接复用 enable-multi-accept: "true" # keep-alive 连接超时和最大请求数调整 keep-alive: "75" keep-alive-requests: "10000" # upstream-keepalive 与上游Pod连接超时与最大请求数调整 upstream-keepalive-time: "30m" upstream-keepalive-timeout: "60" upstream-keepalive-requests: "10000" upstream-keepalive-connections: "512" # proxy-connect 设置 ingress-nginx 与 pstream pod 之间连接请求超时实践。 # 设置与代理服务器建立连接的超时时间(不能超过75s) proxy-connect-timeout: "30" # 设置将请求传输到代理服务器的超时时间(以秒为单位)(超时仅在两个连续的写操作之间设置,而不是为整个请求的传输设置) proxy-send-timeout: "120" # 设置从代理服务器读取响应的超时时间(以秒为单位) proxy-read-timeout: "120"
开发测试:
# 启用 nginx Opentracing 扩展, 默认值:禁用 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)
apiVersion: networking.k8s.io/v1 kind: Ingress metadata: annotations: # 解决: 413 Request Entity Too Large 问题 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" # 解决: 504 网关超时即后端backend超时问题 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代理转发与后端服务文件上传缓存区设置(原生命令) nginx.ingress.kubernetes.io/server-snippet: | location ~ /fastfile { client_max_body_size 1024m; # 允许客户端请求的最大单文件字节数,若超过所设定的大小,返回413错误.人话:能上传多大文件 client_header_timeout 60; # 读取请求头的超时时间,若超过所设定的大小,返回408错误。 client_body_timeout 60; # 读取请求实体的超时时间,若超过所设定的大小,返回413错误。 client_body_buffer_size 10m; # 缓冲区代理缓冲用户端请求的最大字节数,人话:一次能接受多少文件,建议根据带宽上限设置,减少磁盘读写,加快速度 proxy_connect_timeout 60; # Nginx与后端代理连接超时时间,http请求无法立即被容器(tomcat, netty等)处理,被放在nginx的待处理池中等待被处理。 proxy_read_timeout 180; # 后端服务器响应时间(代理接收超时)时间,http请求被容器(tomcat, netty等)处理后,nginx会等待处理结果,也就是容器返回的response。 proxy_send_timeout 30; # http请求被服务器处理完后,把数据传返回给Nginx的用时,默认60秒。 proxy_buffer_size 1024k; # 设置代理服务器(nginx)保存用户头信息的缓冲区大小 proxy_buffers 6 500k; # proxy_buffers缓冲区,网页平均在32k以下的话>,这样设置 proxy_busy_buffers_size 1024k; # 高负荷下缓冲大小(proxy_buffers*2) proxy_temp_file_write_size 1024k; # 设定缓存文件夹大小,大于这个值将从upstream服务器传输 }
0x08 Kubernetes中ingress-nginx安全配置
描述: 在 K8s 集群中部署安装 ingress-nginx 后默认并未根据应用安全需要进行相应的安全配置,本小结将针对于生产环境的中的 ingress-nginx 控制器以及应用常见进行安全安全配置。
1.配置指定的 Ingress Class 描述: 如果一个K8S集群中部署了多个ingress controller时,我们可以在创建ingress的时候,通过ingressClassName指定ingress class,如下所示:
$ kubectl get ingress -n web www-weiyigeek -o yaml apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: blog-weiyigeek-top annotations: # ....Kubernetes 注释添加到特定的 Ingress 对象自定义其行为.... 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.
示例:
metadata: annotations: # 通过重定向实施服务器端 HTTPS # 启用了TLS,则控制器会将 (308) 重定向到 HTTPS nginx.ingress.kubernetes.io/ssl-redirect: "true" # 强制重定向到 HTTPS nginx.ingress.kubernetes.io/force-ssl-redirect: "true" # 保留 URI 中的尾部斜杠 redirectnginx.ingress.kubernetes.io/preserve-trailing-slash: "true"
3.安全配置之跨域访问cors 描述: 当将Ingress-Nginx作为API网关,必须进行跨域配置否则会对业务造成影响,例如我们公司的CDN业务必须设置跨域方面配置。
示例:
metadata: annotations: # 启用cors跨域 (字面意义) nginx.ingress.kubernetes.io/enable-cors: "true" # 设置控制 CORS 的可接受来源,遵循以下格式 http(s)://origin-site.com[:port] nginx.ingress.kubernetes.io/cors-allow-origin:"https://*.weiyigeek.top" # 设置支持跨域请求的方法 (GET, PUT, POST, DELETE, PATCH, OPTIONS) nginx.ingress.kubernetes.io/cors-allow-methods: "GET, PUT, POST, OPTIONS" # 设置支持跨域请求的标头 (DNT,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range,Authorization) nginx.ingress.kubernetes.io/cors-allow-headers: "X-Forwarded-For, X-App-Weiyigeek" # 设置支持响应公开那些指定表头 nginx.ingress.kubernetes.io/cors-expose-headers: "*, X-CustomResponseHeader" # 设置在 CORS 操作期间传递凭据 (为了安全设置false除非业务确实需要) nginx.ingress.kubernetes.io/cors-allow-credentials: "false" # 设置控制可以缓存预检请求的时间长度 1728000 nginx.ingress.kubernetes.io/cors-max-age: 600
4.安全配置之防止DDOS请求限流 描述: 通常针对于文件下载服务器我们需要进行一系列的配置, 我们可以通过 rps 限制每秒请求数,rpm 限制每分钟请求数,connections限制连接数, 如若超过将返回 503 。
示例:
metadata: annotations: # 每分钟从给定 IP 接受的请求数。突发流量限制设置为此限制乘以突发乘数,默认乘数为 5。(顺序3) nginx.ingress.kubernetes.io/limit-rps: "5" # 每秒从给定 IP 接受的请求数,突发流量限制设置为此限制乘以突发乘数,默认乘数为 5。(顺序2) nginx.ingress.kubernetes.io/limit-rpm: "300" # 允许来自单个 IP 地址的并发连接数,超过此限制时返回 503 错误。(顺序1) nginx.ingress.kubernetes.io/limit-connections: "10" # 突发大小限制速率的乘数。默认突发乘数为 5 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加入到白名单中,才不受安全策略限制影响。
示例:
metadata: annotations: # 通过注释指定允许的客户端 IP 源范围,该值是以逗号分隔的CIDR列表。(全局) ingress.kubernetes.io/whitelist-source-range: "10.0.0.0/24,172.10.0.1"
6.安全配置之请求访问日志记录 描述: 为了等保合规通常需要将各类日志存储 180 天及以上,所以ingress也是非常重要,当业务被攻击时我们可以快速溯源追踪,以及其行为分析。
示例:
metadata: annotations: # 启用访问日志,默认情况处于启用状态.但在某些情况下可能需要为给定入口禁用访问日志设置为False即可。 nginx.ingress.kubernetes.io/enable-access-log: "true" # 启用重写日志,默认情况处于未启用状态,如启用重写日志在通知级别发送到error_log文件。 nginx.ingress.kubernetes.io/enable-rewrite-log: "true"
7.安全配置之Nginx指定代理响应标头 描述: 在使用 ingress-nginx 场景中免不了配置使用代理或响应表头,例如常见的X-Frame-Options规定了允许那些站点嵌入配置目标iframe站点。
示例:
metadata: annotations: # 用于插入 server 块中的代码段 nginx.ingress.kubernetes.io/server-snippet: | # 隐藏nginx版本 server_tokens off; # Frame 安全控制 添加 X-Frame-Options 头 add_header X-Frame-Options SAMEORIGIN; # MIME 模拟探测 、XXS-Protection、Spider Robots 爬取策略限制 more_set_headers 'x-content-type-options: nosniff ' 'x-xss-protection: 1; mode=block' 'X-Robots-Tag: none;'; # 用于插入 location 块代码段 nginx.ingress.kubernetes.io/configuration-snippet: | # 自定义访问后端服务时所带头 proxy_set_header My-Custom-Header $http_my_custom_header; # 将 X-FORWARDED-FOR 字段获取到外部IP带入后端服务 proxy_set_header X-FORWARDED-FOR $http_X_FORWARDED_FOR;
8.安全配置之Nginx禁止访问某一目录 描述: 在使用 ingress-nginx 场景中,如何禁止客户端访问 ingress 站点某一目录下的所有资源,我们可以配置 server-snippet 注释,例如。
示例:
metadata: annotations: # 用于插入 server 块中的代码段,禁止访问 站点的 /itwork/ 目录下资源 nginx.ingress.kubernetes.io/server-snippet: | location /itwork/ { deny all;return 403;}
9.安全配置之请求访问认证
描述: 针对于某些未有认证的API接口应用,可以通过在 Ingress 规则中添加额外的注释来添加身份验证。
metadata: annotations: # 认证类型 [basic|digest] nginx.ingress.kubernetes.io/auth-type: # 包含用户/密码定义的密码的名称,使用configMap存储htpasswd生成热认证 # $ htpasswd -c auth weiyigee # $ kubectl create secret generic basic-auth --from-file=auth nginx.ingress.kubernetes.io/auth-secret: basic-auth # 认证密钥有两种形式: # auth-secret 默认,密钥中的 htpasswd 文件位于密钥中auth # auth-map 密钥是用户名,值是散列密码 nginx.ingress.kubernetes.io/auth-secret-type:auth-file # 设置认证提示 nginx.ingress.kubernetes.io/auth-realm: "身份认证"
10.安全配置之启用SSL握手加密套件 描述: 通常为了应用安全我们会为其添加证书,但是一些已知脆弱性的SSL加密方式会影响到应用信息安全,例如 RC4 与 MD5 等
示例:
# 方式1.ConfigMap $ 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 协议 ssl-protocols: "TLSv1.1 TLSv1.2 TLSv1.3" # ssl 会话复用 ssl_session_cache: "shared:SSL:10m;" ssl-session-timeout: "10m" # 方式2.Annotations metadata: annotations: # 配置指定在使用 SSLv3 和 TLS 协议时,服务器密码应优先于客户端密码。 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.ConfigMap $ kubectl edit cm -n ingress-nginx ingress-nginx-controller apiVersion: v1 data: # 启用 modsecurity waf模块拦截常规Web攻击 enable-modsecurity: "true" # 方式2.Annotations metadata: annotations: # 启用 modsecurity nginx.ingress.kubernetes.io/enable-modsecurity: "true" # 设置以下注释来启用 OWASP 核心规则集 nginx.ingress.kubernetes.io/enable-owasp-core-rules: "true" # 设置以下内容从nginx传递事务ID nginx.ingress.kubernetes.io/modsecurity-transaction-id: "$request_id" # 通过代码段添加自己的一组 modsecurity 规则: 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
加载全部内容