Cilium 基于磁盘文件的 Network Policy:static-cnp-path 实现策略即文件与实时热更新
【免费下载链接】ciliumeBPF-based Networking, Security, and Observability项目地址: https://gitcode.com/GitHub_Trending/ci/cilium
Cilium 的 Network Policy 通常通过 Kubernetes CRD(CiliumNetworkPolicy、CiliumClusterwideNetworkPolicy)下发到集群。而磁盘文件驱动的策略(Disk based Cilium Network Policies)提供了一条不依赖 K8s API Server 的路径:把策略 YAML 文件直接放到节点的文件系统目录中,Cilium agent 会自动读取、解析并加载进策略引擎,同时持续监听该目录,对新增、修改、删除的文件做出实时响应。本文以当前仓库 Documentation/security/policy/disk-based.rst 为核心骨架,结合pkg/policy/directory的源码实现,完整讲解该特性的原理、配置、验证与已知限制。
功能定位:策略 YAML 即文件,绕过 CRD 下发
该特性允许用户将网络策略 YAML 文件直接放置在节点文件系统中,无需通过 Kubernetes CRD 进行定义。通过设置配置字段static-cnp-path,用户可以指定策略加载目录;Cilium agent 会处理该目录下的所有策略 YAML 文件,将其转换为规则并纳入策略引擎。除此之外,Cilium agent 还会持续监控该目录:新出现的策略 YAML 文件会被加载,已存在文件的更新与删除也会同步反映到策略引擎的规则中。
需要明确该特性的边界——它只支持CiliumNetworkPolicy和CiliumClusterwideNetworkPolicy两种类型,普通 Kubernetes NetworkPolicy 不在其处理范围内。从源码实现看,目录监视器(Directory policy watcher)读取文件后统一反序列化为cilium_v2.CiliumNetworkPolicy对象(watcher.go),再调用cnp.Parse()转换为规则,因此文件内的kind决定了其作用域:命名空间级(CiliumNetworkPolicy)或集群级(CiliumClusterwideNetworkPolicy)。
配置方式:static-cnp-path 与 Helm 挂载
static-cnp-path是 Cilium agent 的启动参数,定义在 pkg/policy/directory/cell.go 中:
type Config struct { StaticCNPPath string } const ( // StaticCNPPath defines the directory path for static cilium network policy yaml files. staticCNPPath = "static-cnp-path" ) var defaultConfig = Config{ StaticCNPPath: "", // Disabled } func (cfg Config) Flags(flags *pflag.FlagSet) { flags.String(staticCNPPath, defaultConfig.StaticCNPPath, "Directory path to watch and load static cilium network policy yaml files.") }默认值为空字符串,即默认关闭该功能;一旦设置了非空路径,agent 启动后即开始监视该目录。该参数同样会出现在cilium-agent --help的完整输出中,对应--static-cnp-path选项(参见 Documentation/cmdref/cilium-agent.md):
--static-cnp-path string Directory path to watch and load static cilium network policy yaml files.Helm values 配置示例
Cilium agent 需要监视的目录必须通过卷挂载(volume mount)从宿主机挂载进来。对于使用 Helm 部署的用户,可以通过extraArgs与extraHostPathMounts开启,完整示例如下:
extraArgs: - --static-cnp-path=/policies extraHostPathMounts: - name: static-policies mountPath: /policies hostPath: /policies hostPathType: Directory参数说明:
| 字段 | 取值示例 | 含义 |
|---|---|---|
extraArgs | --static-cnp-path=/policies | 以 agent 命令行参数形式启用目录监视,/policies为容器内策略目录 |
extraHostPathMounts[].name | static-policies | 挂载项名称,便于在 Pod 中标识 |
extraHostPathMounts[].mountPath | /policies | 容器内挂载路径,须与--static-cnp-path一致 |
extraHostPathMounts[].hostPath | /policies | 宿主机上的策略目录路径 |
extraHostPathMounts[].hostPathType | Directory | 宿主机路径类型,明确声明为目录 |
extraHostPathMounts是 Cilium Helm Chart 为 agent DaemonSet 提供的标准扩展入口(在 Documentation/helm-values.rst 中亦有收录),因此无需修改 Chart 本身,即可将宿主机目录以 HostPath 形式挂载进 agent 容器。部署完成后,向宿主机/policies目录放置或修改策略 YAML,agent 即会自动感知。
策略文件的要求与目录监视机制
从源码 watcher.go 可以看出,目录监视器对文件名的校验非常明确:
func (p *policyWatcher) isValidCNPFileName(filePath string) bool { if filepath.Ext(filePath) != ".yaml" { return false } if reasons := validation.IsDNS1123Subdomain(filepath.Base(filePath)); len(reasons) > 0 { p.log.Error( "CNP name parse validation failed", logfields.Name, filepath.Base(filePath), logfields.Reasons, reasons, ) return false } return true }即文件必须满足:
- 扩展名为
.yaml(.yml、无扩展名等均会被忽略); - 文件名必须符合 DNS-1123 子域名规范(仅含小写字母、数字、
-、.,且以字母数字开头/结尾),命名不合法会直接打印错误日志并跳过。
文件的解析流程为:os.ReadFile读取 →yaml.YAMLToJSON转 JSON →json.Unmarshal为cilium_v2.CiliumNetworkPolicy对象(watcher.go)。这意味着文件必须是合法的 Cilium Network Policy YAML,任何语法错误或字段不合法都会导致翻译失败,agent 会记录Failed to translate policy yaml file to cnp object之类的错误日志。
目录监视本身基于 fsnotify),完整生命周期如下:
- agent 启动时通过
fsnotify.NewWatcher()创建监视器,并watcher.Add(dir)注册目录; - 先扫描目录中已存在的文件,逐个合法文件加载进策略引擎(保证 agent 重启后策略不丢失);
- 随后进入事件监听循环:
Create/Write事件 → 读取并解析文件,新增或更新策略;Remove/Rename事件 → 从策略引擎删除对应策略。
cell.go中通过 Hive 生命周期将监视器注册为 agent 启动钩子(cell.go):OnStart时启动watchDirectory,OnStop时通过context.WithCancel取消监听;而StaticCNPPath为空时,直接返回一个空的sync.WaitGroup,即完全禁用该能力。
文件 → 策略引擎的数据流
加载与删除的核心实现如下(watcher.go):
// addToPolicyEngine:读取 yaml 文件并转换为策略对象,然后加入策略引擎。 func (p *policyWatcher) addToPolicyEngine(cnp *cilium_v2.CiliumNetworkPolicy, cnpFilePath string) error { fileName := filepath.Base(cnpFilePath) resourceID := ipcacheTypes.NewResourceID( ipcacheTypes.ResourceKindFile, p.config.StaticCNPPath, fileName, ) // convert to rules rules, err := cnp.Parse(p.log, p.clusterName) if err != nil { return err } // update labels lbls := getLabels(fileName, cnp) for _, r := range rules { r.Labels = lbls } dc := make(chan uint64, 1) // add to policy engine p.policyImporter.UpdatePolicy(&policytypes.PolicyUpdate{ Rules: policyutils.RulesToPolicyEntries(rules), Source: source.Directory, Resource: resourceID, ProcessingStartTime: time.Now(), DoneChan: dc, }) <-dc // wait for policy to be applied p.fileNameToCnpCache[fileName] = cnp return err }关键点:
- 每个策略文件会携带由
filename、命名空间(若有)与policy-derived-from(值为CiliumNetworkPolicy或CiliumClusterwideNetworkPolicy)组成的标签集,来源统一标记为source.Directory(watcher.go); - 通过
PolicyImporter.UpdatePolicy()提交策略更新(policy_importer.go),提交至异步队列后由 importer 批量处理,DoneChan用于等待策略真正应用到策略引擎; fileNameToCnpCache缓存「文件名 → CNP 对象」映射,用于删除操作时反查对应的策略内容(watcher.go);- 删除时提交
Rules: nil的更新,即删除该文件对应的全部规则。
源码级测试对这条链路有完整覆盖(watcher_test.go):
TestTranslateToCNPObject:验证合法 YAML 能成功转为 CNP 对象、非法 YAML 返回错误;TestAddToPolicyEngine:验证策略加载后写入缓存,且同名文件重复加载即为更新(覆盖同一缓存条目);TestDeleteFromPolicyEngine:验证删除后缓存清空,且删除不存在的条目会返回fileNameToCnp map entry doesn't exist错误。
测试中使用的策略 YAML 可以作为放置到目录中的真实样例(以 CiliumClusterwideNetworkPolicy 为例):
apiVersion: cilium.io/v2 kind: CiliumClusterWideNetworkPolicy metadata: name: deny-egress-to-ip spec: endpointSelector: {} egressDeny: - toCIDR: - "11.1.0.4/32" enableDefaultDeny: egress: false策略来源验证:source 字段与 endpoint 关联
要确定某条策略是通过 Kubernetes CRD 建立,还是直接来自目录文件,可以执行cilium policy get并检查策略中的source属性。来自目录的策略其source字段为directory。此外,cilium endpoint get <endpoint_id>的输出中也包含与该 endpoint 关联策略的来源字段,可用于排查某端点究竟被哪一类来源的策略所约束。
从底层看,directory来源在 pkg/source/source.go 中定义:
// Directory is the source used for watching and reading // cilium network policy files from specific directory. Directory Source = "directory"同时,defaultSources按优先级从高到低排列了所有来源(source.go),AllowOverwrite()依据该顺序决定新状态能否覆盖旧状态(source.go)。从源码结构看,Directory的优先级介于ClusterMesh与LocalAPI之间,且 source_test.go 显示Directory来源的新状态可以覆盖Kubernetes、CustomResource、KVStore、Local、KubeAPIServer、ClusterMesh等既有来源,而LocalAPI、Generated、Restored则不可被覆盖。因此可以推断:当同一策略同时以多种来源定义时,最终生效的规则由来源优先级裁决,这也是排查「为什么目录中的策略与 CRD 中的策略行为不一致」时需要留意的点。
已知限制与版本注意点
对于Cilium 1.14 之前的版本,针对集群外部对端的 deny 策略(deny-policies for peers outside the cluster)有时无法生效,相关历史问题为 issue #15198。因此,如果依赖 deny 策略来管理发往集群的外部流量,请确保使用 1.14 或更高版本。
结合源码还可补充以下使用注意点:
- 目录中不合法(无法解析)的 YAML 文件会导致 agent 记录致命错误(
logging.Fatal),需保证放置的文件始终合法; - 文件名的合规性直接决定策略是否被加载,命名不符合 DNS-1123 的文件会被静默跳过;
- 该特性仅处理
CiliumNetworkPolicy/CiliumClusterwideNetworkPolicy,其他类型(如标准 Kubernetes NetworkPolicy)的文件即使放入目录也不会被转换为规则; - 目录必须通过卷挂载从宿主机映射进 agent 容器(Helm 下使用
extraHostPathMounts),且挂载路径需与--static-cnp-path保持一致,否则 agent 启动时会因无法访问目录而失败并记录Failed to watch policy directory。
小结
基于磁盘文件的 Network Policy 为 Cilium 提供了一条去 CRD 化的策略下发路径:写入即加载、修改即更新、删除即卸载,配合 agent 的目录监视能力实现策略的热变更。其核心实现位于 pkg/policy/directory/,配置入口为static-cnp-path(cell.go),完整监视与导入逻辑见 watcher.go,并通过 watcher_test.go 得到行为验证。在需要绕过 Kubernetes API 进行策略管理、或希望在节点本地直接维护策略文件的场景下,该特性提供了简单而可靠的选择;使用时注意版本(1.14+)与文件名、文件格式的合规性即可。
【免费下载链接】ciliumeBPF-based Networking, Security, and Observability项目地址: https://gitcode.com/GitHub_Trending/ci/cilium
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考