Consul Intention详解:服务网格中的服务间访问控制
工试云启 考证服务中心整理

1. Intention是什么服务网格里的专用访问控制规则1.1 为什么传统的防火墙规则不够用做微服务架构时间长了你一定会碰到这种场景订单服务需要调用用户服务但用户服务不想让订单服务之外的任何服务访问。传统做法是写防火墙规则比如在用户服务的服务器上限制来源IP或者在安全组里加一条“只允许订单服务的IP访问”。刚开始服务少这套方案还能凑合等服务数量到了几十上百问题就全来了。容器化之后IP是漂移的服务一扩缩容IP就变了防火墙规则跟着改来改去改到后面自己都不知道哪些规则还有用。更麻烦的是微服务之间的调用链复杂光靠IP根本看不出是谁在调用谁。IP只是网络层的标识跟业务服务名完全是两回事。到了这一步你真正需要的其实是一套“以服务为维度”的访问控制规则让业务方直接说“我允许订单服务访问我”而不是“我允许10.0.3.18访问我”。Consul里的intention就是干这个的——它是服务网格里专门用来做服务间访问控制的规则引擎同Consul Connect的mTLS能力绑定在一起从连接层面直接控制一个服务能不能调用另一个服务。我第一次接触intention是在某个跨平台系统的业务改造里。当时我们的现状是服务一多网络策略乱成一团安全审计说不清楚到底谁可以访问谁。引入intention之后每一对服务间的权限都变成了一条明确的规则——来源是谁、目标是谁、允许还是拒绝清清楚楚。这个东西改变的不只是运维方式更多是安全模型上的思路转变从“依赖网络边界防护”变成“每一个服务直接声明自己的访问意愿”。1.2 Intention的四个核心字段与工作流程Intention虽然名字听着高级但规则本身特别简单。一条intention里最重要的四个字段是SourceName来源服务名也就是调用方的身份。DestinationName目标服务名也就是被调服务。Action动作只有两个取值allow或者deny。SourceType来源类型默认是consul表示以Consul里的服务身份作为判断依据。你只需要用这四要素描述“我希望哪个服务能访问哪个服务”剩下的就交给Consul处理。举个最直观的例子假设web服务要访问api服务你可以创建一条规则SourceNamewebDestinationNameapiActionallow。这条规则表示只有以web身份建立的Connect流量才被允许进入api。其他服务哪怕是web的IP、web的子网也进不来——因为判断依据是mTLS认证出来的服务身份不是IP。整个链路是怎么跑起来的启用Connect之后每个服务实例旁边都会部署一个sidecar代理。服务发起的请求会先走sidecarsidecar之间通过mTLS互相认证。在认证的过程中Consul会把匹配到的intention规则返回给代理代理根据这些规则决定是放行还是拒绝这个连接。换句话说intention不只是一个静态的策略文件它是实实在在参与到了每一次RPC调用的鉴权过程里。这里有一个非常关键的点值得所有做访问控制的人注意intention只管Connect流量。如果你的服务还在用普通的HTTP端口不走Connect也没有sidecar代理那这些流量是完全不受intention管控的。很多人配置了半天发现规则没生效查来查去最后发现服务调用根本没走Connect和sidecar这个问题在排查时优先级得排到第一位。还有一个常见的理解误区是“默认放行”。很多人刚接触Consul service mesh时会默认系统是白名单机制但实际上一旦你的服务开启了Connect也没有配置任何intention那么服务之间的访问是默认拒绝的。是的默认全部拒掉没有规则就拒绝。刚开始我不太适应这一点总担心“规则突然不见了服务就挂了”。后来想明白了这才是服务网格该有的安全姿势平时收紧显式放行所有访问行为都记录在案。真实业务中这个默认策略帮我们挡住了好几次“新服务上线后无权限访问”的问题。2. 从创建到验证Intention的完整实操2.1 环境准备与前置条件动手配置intention之前先确认你的Consul环境满足条件。至少需要一个可用的Consul数据中心建议1.9版本以上。1.9之前intention的功能已经基本稳定但1.9开始提供了intention check命令排查问题方便非常多。服务已经注册进Consul目录并且服务名跟你在intention里写的名字完全一致。服务已启用Connect。你能看到每个服务实例旁边有sidecar代理并且服务间的流量确实经过代理转发。如果你的服务还完全没有接入Connect那么intention对你来说只是一个“看得到、用不上”的功能。这不是说intention没用而是首先要完成服务网格的接入改造。具体接入流程不同的技术栈有差异这里我默认你已经具备基础环境直接在已有集群上操作。实际操作里我还遇到过另一种情况环境里既有普通的Consul服务发现也有Connect服务混着跑。这个时候单独配置intention是没问题的但你要清楚自己关心的那对服务是不是真的走Connect。如果其中一端没有开启Connect频繁跳过的流量会让intention的数据看起来对不上对排查会有干扰。2.2 命令行创建与查看Consul CLI是配置intention最直接的方式。我平时用得最多的三个命令是create、list和get。先看创建。允许web服务访问api服务consul intention create -source web -destination api -action allow执行完之后系统会返回Created: web - api (allow)说明规则已经写入。看一眼就明白这个命令做的事情就是把前面提到的核心字段填好。再看拒绝场景。假设后台任务服务report不允许访问用户中心的用户数据服务那就加一条denyconsul intention create -source report -destination user-center -action deny创建完规则之后第一时间要验证。list可以看全量规则consul intention list输出会以表格形式列出所有规则。实际生产环境里规则几十上百条都很常见所以list更适合用来做全量浏览。单条规则用get看更清楚consul intention get web api这条命令返回web到api的精确匹配规则包括SourceType、Action等详细信息。验证完规则之后真正想要确认“当前服务间能不能通信”更直接的检查方法是走一次真实调用。你可以在目标服务的机器上curl自己的服务端口如果返回403或者连接被拒说明sidecar已经根据intention拒绝了这个来源。这里要补充一个细节Consul CLI创建规则默认会生成一条SourceTypeconsul的规则。这个字段在大部分场景不需要手动指定它是含义是“以Consul服务身份认证结果作为来源凭据”。如果某些特殊场景需要支持非Consul身份比如集群外部的调用才需要修改这个字段但这样的用法比较少见我建议你在还没完全理解身份模型之前先保持默认。2.3 API与配置文件方式CLI适合日常操作但真正到了生产环境我更推荐通过API或者配置文件来管理intention。为什么因为CLI操作是“点状”的难以追踪和审计配置文件则是“面状”的可以进入版本库、可以做代码评审、可以和其他配置一起统一发布。API方式创建intention直接调用Consul的HTTP接口curl --request PUT \ --data { SourceName: web, DestinationName: api, Action: allow } \ http://127.0.0.1:8500/v1/connect/intentions返回结果会包含新规则的ID。API方式适合自动化运维场景比如在发布流程里发布脚本自动为目标服务添加临时访问权限流程跑完再删除。这块我实际用过有个报表系统每月底需要临时读取数据库数据如果一直开着权限会扩大暴露面。我们就用脚本在任务开始时创建intention结束后自动删除整个操作都走了API接口比人肉在控制台点来点去可靠得多。配置文件方式则是把intention直接写进Consul的配置目录。例如在配置文件里加一段service_intentions { source_name web destination_name api action allow }配上之后需要reload或者重启Consul agent。这种方式的好处是规则和Consul集群的基础配置放在一起代码评审能看回滚能操作环境也能保持一致。如果你用了基础设施即代码的工具管理Consul集群配置文件方式几乎是最佳选择。三种方式放在一起对比管理方式适用场景优点注意点CLI临时调整、快速验证操作简单直接操作不可追溯容易遗漏API自动化流程、动态授权灵活可控便于二次开发需要编写额外脚本配置文件生产环境标准化管理可版本化、可评审配置更新需要reload2.4 更新、删除与批量管理intention的管理还有个比较麻烦的地方规则没有“修改”操作。你要改一条规则通常是先删除再重新创建。所以别再找什么consul intention update命令了至少到目前还没出现直接改的命令。正确姿势是consul intention delete web api consul intention create -source web -destination api -action allow这里积累了一个小经验删除和重建之间有短暂的时间窗口即便只是毫秒级也有可能导致正在进行的调用被拒绝。所以改造规则时尽量选择低峰期或者在代码层面做一下瞬时容错。批量管理方面当规则量很大时建议提前想好命名规则和分组策略。比如所有涉及支付链路的访问控制和普通业务访问控制在配置目录里分开存放用不同的文件管理通过目录结构做分类。这比把所有规则揉在同一个文件里要清晰得多。还有一个建议是定期做规则清理。我见过不少环境里积累了大量没有业务依赖的intention。这些死规则既影响list出来的可读性也会略微增加Consul server的处理负担。可以借助API导出所有规则对照服务的真实调用链筛出最近几个月都没有命中的规则评估后删除。这个清理动作建议至少一个季度做一次。3. 复杂场景下的策略设计通配、网关与跨数据中心3.1 通配符与服务范围真实业务里服务数量多了不可能每对服务间都写一条明明白白的规则。通配符*在这里就很有用。例如我想允许所有服务访问基础公共组件commonconsul intention create -source * -destination common -action allow这条规则表示任意来源的服务都能访问common。反过来如果某个网段的服务全都不能访问财务系统financeconsul intention create -source * -destination finance -action deny通配符在intention中的匹配逻辑是有优先级的。具体规则精确匹配优先于通配符匹配。也就是说如果同时存在web - api (allow)和* - api (deny)那么web到api的真实结果是allow因为web比*更具体。这个机制非常重要否则你没法在“默认拒绝”的底座上做定向放行。结合默认拒绝策略一个典型的安全设计是先为整个集群建立“所有流向内部服务默认deny”的兜底规则然后只放行那些明确需要的调用关系。虽然在Consul Connect继承环境下没有显式intention其实已经默认拒绝但把这条规则显式写出来会让策略文档更直观也让新接手的人一眼看懂访问边界在哪里。在实际项目里我还发现一种容易犯的错误把通配符写反了。比如想允许api服务访问任何服务正确写法是-source api -destination * -action allow但有人会写成-source * -destination api -action allow含义完全变了。这条规则会允许所有服务访问api而不是api访问所有服务。配置intention时建议养成先写源再写目标的习惯用完订单之后花一分钟想一下这个流量方向到底是什么。3.2 与Ingress/terminating网关的配合服务网格里的流量入口往往不是直接打到服务上而是先经过网关。网关类型的接入会让intention配置跟着复杂一档。先说Ingress网关。Ingress网关是集群内部流量的入口外部HTTP请求进入集群由Ingress网关转发到后端服务。在这个链路里网关也是以sidecar代理的身份与服务建立mTLS连接的。所以你会遇到一个问题Ingress网关转发到服务时来源身份是什么答案是网关配置的service name。因此当你希望外部流量能够通过Ingress网关访问到后端api服务除了一定要在Ingress网关的配置里把路由指到api服务之外还需要有一条intention允许Ingress网关身份访问api服务。这条规则具体长这样consul intention create -source ingress-gateway -destination api -action allow其中ingress-gateway是你在Consul里注册Ingress网关时给定的服务名。有的团队会忘记这一步结果外部流量打到Ingress网关能通但网关转发到服务时就断了。排查起来很容易走弯路因为日志通常只会体现“网关到后端连接被拒绝”。再说Terminating网关。Terminating网关的作用是让集群内部服务可以安全访问外部服务比如调用第三方平台接口或者访问传统数据库。这种场景下目标服务不在Consul目录里你需要在Consul中注册一个虚拟的service再通过Terminating网关转发流量。对应的intention规则是允许内部服务访问这个虚拟服务名。举个例子订单服务需要访问某外部支付平台注册的外部服务名叫external-pay那么规则是consul intention create -source order -destination external-pay -action allow这个场景我踩过坑。一开始我以为外部服务的访问控制是网络层面的事没在intention里配结果服务调用超时。后来调试到Terminating网关的日志才发现原来是缺了intention。说白了无论流量走向哪里只凡是经过Connect链路来源身份和目标身份之间的许可就是intention说了算。3.3 跨数据中心的Intention设计单数据中心场景下intention已经够用但很多公司会做多活或者容灾Consul集群会部署在多个数据中心。跨数据中心的访问intention的生效范围和设计方式就需要重新审视。先明确一点intention规则是数据中心本地的。你在DC1创建的web - api规则不会自动同步到DC2。如果DC2中的api服务也需要允许DC2的web访问那必须在DC2里也创建一条相同配置的intention。跨DC场景里最常见的陷阱就是只配了主数据中心的规则灾备切换后发现备数据中心的服务间通信全部异常。跨DC的服务间调用通常要经过Mesh Gateway。Mesh Gateway负责跨数据中心的流量转发避免直接暴露服务节点的地址。这种架构下除了目标服务对应数据中心的intention还需要保证Mesh Gateway自身的服务身份在链路中不被误拦截。实际配置时规则会涉及到源服务、源DC的Mesh Gateway、目标DC的Mesh Gateway、目标服务这四类角色的协调。规则配置数量明显变多逻辑上却并不复杂每一段转发链路都对应一条明确的源到目标identity的连接授权。我在设计跨DC规则时通常会先在纸上把调用链路画一遍。不用画得很正式就画服务A - Mesh GW DC1 - Mesh GW DC2 - 服务B然后检查每一段的intention是否都配了。如果链路里只是两个服务在一份配置里各自写了一条规则看起来只差一条实际上可能差了三四条漏了就白搭。另外跨DC场景里涉及规则同步的问题也需要重视。如果你的团队用配置文件方式管理intention那么不同数据中心之间的一致性可以通过同一套配置模板来保证。只要你按DC维度做了参数化每个DC生成的intention内容自然就一致。不要靠人肉在多个DC里执行命令那样迟早会漏配。3.4 与ACL规则的边界与配合讨论intention的时候很容易和Consul另一个安全组件ACL搞混。ACL管的是“谁有权限操作Consul自身的API”比如谁能读服务目录、谁能写KV、谁能执行配置变更。intention管的是“服务之间能否通信”。一个管控制平面一个管数据平面两者职责边界完全不同。举一个实际场景。某公司的基础设施团队管理Consul集群他们拥有ACL的全部权限可以增删服务注册、可以修改KV。业务团队管理自己的服务通过intention来限制服务间的通信。这两个层面如果混在一起理解很容易出现“我明明有权限为什么服务还是访问不了”的困惑。答案很简单你有操作Consul的权限不代表业务服务之间的调用已经被放行。前者管的是你操作Consul的身份后者管的是服务Runtime之间的身份。同时两者也会互相影响。最典型的例子是如果ACL token缺失或失效服务注册和sidecar连接Consul agent都会出问题这时即使intention规则存在服务之间也可能因为拿不到正确证书而握手失败。排查顺序上建议先确认底层基础通信正常服务能正常获取配置、能正常注册再检查intention规则层面。我见过有人在intention规则里反复检查了很多轮最后发现是ACL token轮换导致代理无法启动这个教训还是很值得记住的。4. 常见问题与排查思路4.1 Intention不生效的排查清单intention配置好之后在实际业务调用中发现不生效的情况并不少见。根据我的经验按这个顺序排查效率最高。第一确认调用流量走了Connect。没有sidecar代理或者流量不经过代理intention是不参与决策的。这个方法最简单查看服务监听端口旁边是否启了额外的代听端口或者看请求的实际连接地址是不是sidecar的地址。某次一个开发对我说“intention不生效”我让他看了服务的监听端口服务的原生端口直接暴露了压根没走Connect。这种问题不是intention配置错了而是接入改造不彻底。第二确认规则方向没有写反。-source web -destination api和-source api -destination web含义完全不同。如果你创建的规则方向反了流量自然没被命中。出现这个问题的概率相当高特别是服务间存在双向调用时。检查时就拿consul intention get看精确匹配规则确认来源和目标与你判断的调用方向一致。第三确认规则优先级没被通配符规则覆盖。通配符和精确匹配同时存在时精确匹配优先。但你需要注意是否存在两条同样具体级别的规则一个allow一个deny。如果两条规则同样具体deny优先。这意味着如果有一方写了deny哪怕你后加一条allow最终结果仍然是拒绝。这一点经常让人意外我在生产环境里遇到过运营平台电话打过来问“为什么加出来的allow规则没生效”查了一下发现之前为了临时阻断某次异常调用而加过一条deny规则后来忘了清理。deny规则的存在本身就是安全设计的一部分防止误allow导致权限逆扩展所以设计上让deny胜出。第四确认规则已经在集群中全部生效。Consul集群存在收敛时间。你刚创建完规则立刻发起调用有极小的概率规则还没全部下发。如果你用的是配置文件方式还需要检查agent是否完成了reload。等几秒再重试多半就通了。第五检查是否命中了无关的wildcard deny。有时候不是规则不生效而是有一条更大范围的deny规则把原本放行的流量挡住了。排查时用match命令看实际命中的规则consul intention match -method exact api这条命令可以列出所有目标为api且当前环境中匹配的规则。输出结果里你会看到实际参与决策的所有规则这样就能直观看到是哪条规则在起作用。我几乎每次排查都会用它确认“当前规则集里到底谁在决策”比人眼从全量list里找高效得多。4.2 日志与调试技巧如果规则和数据流看起来都没问题但服务间访问仍然失败就得看日志了。日志排查的关键在于分清楚日志到底在哪一段。第一段是sidecar代理的日志。Consul Connect的代理负责执行allow/deny决策它会打印出连接被拒绝的日志。代理日志里如果出现类似于拒绝访问、permission denied之类的字段那至少说明你的代理已经进入了鉴权流程并正确地拒绝了连接。这时就更需要注意规则匹配层面的表达。第二段是Consul server的日志。当代理需要获取证书或者匹配intention时会向Consul server发起请求。如果server返回了异常代理拿不到正确数据自然无法建立连接。出现过一种情况agent的ACL token权限不足代理在请求证书时被拒导致转发链路没有机会把intention规则加载进去。日志里会出现模块auth和证书相关的错误码看到这类信息基本可以往认证方向去查。第三段是业务的原始日志。我建议你在排查时保留业务的调用日志方便判断请求到底走到了哪一层。比如业务日志显示连接超时很可能流量被代理层直接断开业务日志显示400/401那可能是代理层证书验证或者下发规则出问题。多段日志交叉对比通常几分钟就能定位到问题在哪一段。调试时还有一个实用技巧直接手工验证规则是否命中不依赖真实流量。使用consul intention check命令consul intention check web api返回结果如果是Allow说明从web到api的调用被允许返回Deny则说明规则层面已经拒绝。这个命令绕开了业务链路直接从规则引擎层面给出结论是排查的时候最快的一步。即使没有真实流量触发也能确认规则集是否正确特别适合在发布前做预检。4.3 实际项目中的策略设计经验最后分享一点策略设计层面的经验。intention虽然是规则引擎但在业务规模大了之后规则本身也需要治理。没有治理的规则和没有治理的代码一样最后会变得难以维护。我的第一条建议是从业务出发做规则分层。不要只盯着服务名要理解这些服务背后的业务角色。比如订单服务和支付服务之间的规则和两个内部util服务之间的规则虽然都是intention但重要程度、变更频率、影响面完全不一样。可以按核心链路和普通链路把规则分开管理核心链路的规则变更走更严格的审批流程。第二条建议是规则只加不减删除前要做评估。拒绝规则要谨慎添加因为在白名单模式下重建allow规则本身就要写操作allow规则更不能随意删删掉一条看似没用的规则可能直接断掉一条少有人知的关键调用链。我有一个习惯每条规则创建时都备注用途和责任人删除前先看这个备注再对照服务拓扑做二次确认。第三条建议是把intention规则和服务的部署变更联系起来看。每次新服务上线或者服务下线都会被撂下没清理的intention留在集群里。新服务上线时你希望它能迅速获得必要的访问权限服务下线时你同样希望把对应规则一收。理顺这两件事的办法是把intention变更纳入发布流程上线checklist里加上“创建访问规则”下线checklist里加上“清理该服务相关规则”。这样就不会每次都靠事后亡羊补牢。我在实际运维中感受最深的一点是intention本身不复杂复杂的是服务间的真实调用关系。很多团队把intention当成了防火墙规则在用想起来就加一条出问题就临时删结果规则越积越多最后没人说得清哪条规则是必需的。所以如果你问我intention的长期使用心得我会说规则本身只是工具规则治理才是访问控制真正要解决的问题。每一条intention都该有它的业务理由经得起业务和技术双重审视。保持规则集精简、方向和含义清晰然后用好检查命令定期做核对这套流程跑顺了intention就能真正发挥它作为服务网格访问控制核心组件的作用而不是变成另一个需要花大力气运维的资产业物。