平时在开发Serverless服务的时候,很多人用Knative来快速部署,但是最近遇到的一个小故障,让我意识到安全策略配置不当是多么容易踩坑。那是公司测试环境里的一个用户信息查询服务,内部同事访问一切正常,但是外部测试组的同事反馈从公网调用返回403 Forbidden,一开始以为是网络不通,折腾了半小时才找出问题,现在把整个排查和分析过程写出来,给大家避坑。
一、从一个访问故障说起
这个服务是前两周刚部署的,是给公司内部用户提供基础信息查询的,当时运维同学部署的时候,怕测试环境被外部乱访问,特意给Knative的服务加了权限限制。本来计划是等测试完内部功能,再调整成允许外部测试,结果忘做了,就导致外部调用一直失败。
1.1 第一次排查:流量是不是没到网关
首先怀疑的是外部网络的问题,比如防火墙、DNS解析不对。于是先让测试同事查了下服务的公网地址,用nslookup确认域名解析是对的,然后用curl从自己的测试机访问,确实返回403,说明请求确实发出去了,也到了Knative的网关,只是被拒绝了。
1.2 第二次排查:是不是服务本身的问题
接着看Knative服务的状态,用kubectl命令查了一下服务的详细配置,发现服务的annotation里有一个serving.knative.dev/visibility,值是ClusterLocal。这个参数我之前知道,是控制服务可见性的,ClusterLocal的意思就是这个服务只能在Kubernetes集群内部访问,外部网络根本看不到这个服务,自然就返回403了。
二、Knative里的安全策略到底管什么
Knative作为Serverless平台,它的安全策略主要是从两个层面来控制服务的访问权限:一个是服务本身的可见性,就是能不能被外部网络或者其他命名空间的服务访问;另一个是网络层面的策略,就是Kubernetes的NetworkPolicy,用来控制流量的来源。
2.1 示例:Knative服务可见性的配置
下面是这个故障里涉及的Knative服务配置,技术栈是Kubernetes + Knative Serving v1,配置如下:
# Knative Serving Service 配置,技术栈:Kubernetes + Knative Serving v1
apiVersion: serving.knative.dev/v1
kind: Service
metadata:
name: user-info-service
namespace: test
# 这个关键的annotation用来控制服务的访问范围
annotations:
serving.knative.dev/visibility: ClusterLocal
spec:
template:
spec:
containers:
- image: registry.cn-hangzhou.aliyuncs.com/knative-sample/user-info:v1
ports:
- containerPort: 8080
这个配置里的annotation是核心,把visibility设成ClusterLocal,Knative就不会把这个服务的路由发布到外部Ingress里,外部的请求自然就进不来。如果要允许外部访问,只需要把这个值改成External就可以,非常简单。
2.2 排查配置的命令示例
遇到类似问题的时候,直接用下面的Shell命令查看Knative服务的详细配置,就能快速找到问题:
# 查看指定命名空间下Knative服务的完整配置,包括所有annotation
kubectl get ksvc user-info-service -n test -o yaml
这个命令会输出ksvc的所有YAML内容,只要找到metadata.annotations里的serving.knative.dev/visibility字段,就能知道服务的可见性设置是不是有问题。
三、为什么这个配置会出问题?
3.1 应用场景
Knative的这个visibility配置非常适合那种只需要内部访问的服务,比如公司内部的管理后台、内部工具服务之类的,不用折腾Ingress的配置,直接在ksvc里改几个字符就搞定,运维的工作量大大降低。但是如果开发人员对这个配置不熟悉,或者部署的时候没考虑到后续需要外部访问,就很容易出问题,比如这次的情况,就是部署的时候设置了ClusterLocal,但是忘了改过来给外部测试用。
3.2 技术优缺点
优点:1. 配置简单,一行annotation就能控制,不用修改Ingress的规则,适合快速部署;2. 执行效率高,Knative的网关在转发请求的时候会直接根据这个配置判断要不要把请求路由到服务,不用额外的网络策略处理;3. 兼容Kubernetes的安全模型,和其他K8s资源的配置方式统一,容易理解。 缺点:1. 只能控制可见性,不能细化到具体的IP或者命名空间,比如不能只允许某个特定的外部IP访问,只能全开放或者全关闭;2. 容易和其他安全策略联动出问题,比如同时设置了NetworkPolicy,visibility改了之后还要检查网络策略是否允许外部流量;3. 如果文档不明确,开发人员很容易误解这个配置的作用,以为改了就没事,其实还要配合其他配置。
3.3 注意事项
这里列几个关键点,帮助大家避免踩坑:
- 环境区分:不同环境的服务可见性要对应,比如生产环境如果是外部用户用的,必须设成External,内部测试环境如果不需要外部访问就设ClusterLocal,不要搞混;
- 联动检查:改了visibility之后,一定要检查Kubernetes的NetworkPolicy,是不是允许外部流量访问这个服务,比如如果NetworkPolicy只允许内部命名空间的流量,那即使visibility设成External,外部还是访问不了;
- 排查顺序:遇到访问故障的时候,先查Knative服务的visibility配置,再查NetworkPolicy,最后查Ingress日志,按顺序来效率更高,不用瞎猜;
- 统一文档:团队里用Knative的话,要给所有开发和运维人员写清楚这个配置的说明,比如什么时候用ClusterLocal,什么时候用External,避免乱改。
四、故障解决的完整步骤
这次的故障解决其实分两步,因为一开始改了visibility之后,外部还是访问不了,后来才发现还有NetworkPolicy的问题: 第一步:修改Knative服务的可见性,把ClusterLocal改成External,对应的配置如下:
# 修改后的Knative服务配置,允许外部访问
apiVersion: serving.knative.dev/v1
kind: Service
metadata:
name: user-info-service
namespace: test
annotations:
serving.knative.dev/visibility: External # 改成External
spec:
template:
spec:
containers:
- image: registry.cn-hangzhou.aliyuncs.com/knative-sample/user-info:v1
ports:
- containerPort: 8080
第二步:检查NetworkPolicy的配置,原来的NetworkPolicy是只允许internal命名空间的流量访问test命名空间的pod,所以修改NetworkPolicy,允许外部流量,或者加一个规则:
# 修改后的NetworkPolicy,允许所有外部流量访问test命名空间的pod
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-external-access
namespace: test
spec:
podSelector: {}
policyTypes:
- Ingress
ingress:
- from:
- ipBlock:
cidr: 0.0.0.0/0 # 允许所有IP访问,测试环境可以这样,生产环境要改特定IP
第三步:测试,用curl从外部访问服务的公网地址,返回200,说明故障解决了。
五、技术总结
总结一下,Knative Serverless的安全策略配置虽然简单,但是容易踩坑,主要的问题点就是服务可见性的配置和NetworkPolicy的联动。遇到权限访问故障的时候,不要急着查底层的网络,先看Knative服务的annotation,确认可见性设置是否正确,再看网络策略的规则,是不是有多余的限制。另外,团队协作的时候一定要把配置的说明写清楚,让大家都明白每个配置项的作用,避免因为配置不当导致的故障。还有,测试环境和生产环境的配置要严格分开,不要在生产环境乱用测试用的配置,比如ClusterLocal这样的限制,会影响生产环境的外部访问。
评论
围绕“Knative Serverless工作负载的安全策略配置不当引发的权限访问故障排查”参与讨论