机构的方言止步于对外网关
一句话总结
外联前置机(FEP)是与银行之外的机构对接的中继层。各机构的报文规格、证书和允许地址都不同,把这些差异关在一个地方(外联前置机),不让内层系统知道,就是这一层存在的理由。内层只认识 LH-STD 标准,外联前置机翻译成各机构的方言,并用各机构专属的身份(证书)去对接。
为什么需要它
内部系统之间可以制定标准。在 LabHub 银行内部,所有人都使用 LH-STD 报文头。然而他行、信用信息公司、发卡公司、保险公司各有各的规格,不会迁就我们。有的机构报文长度包含它自身,有的机构以 HTTPS 接收 JSON,有的机构响应码是两位。如果让账户系统直接处理这些差异,每增加一个机构,就得修改账本系统。
安全的分量也不同。内部网络是我们可控的,而外部环节则与别人的网络相连。所以对外对接几乎总是双向认证——正如我们确认机构的服务器一样,机构也会用证书确认我们是谁。入站的一方也一样,只接受来自约定机构地址的连接。证书要向每个机构分别申请,到期日各不相同,更换流程也各不相同。如果不在一个地方统一管理,某一天某个机构的证书就会悄悄到期,与该机构的交易就会全部停摆。
工作原理
双向 TLS(mTLS)。在 TLS 1.3(RFC 8446)中,服务器可以在握手期间发送 CertificateRequest,要求客户端证书。客户端发送自己的证书,以及用该证书的私钥对握手内容签名得到的 CertificateVerify。服务器校验该证书是否链接到自己信任的 CA,如果不是,就发送告警(alert)并断开。所以,如果用 Garam 银行 CA 签发的证书去连接 Hanul 信用信息,在握手阶段就会被拒绝。反过来,我们也用该机构的 CA 来校验机构服务器的证书——如果随便什么证书都信任,就会把交易交给中间人。
CSR 与私有 CA。机构对接证书通常不是由公共 CA,而是由机构运营的私有 CA 签发。我们生成私钥,并发送装有其公钥的证书签名请求(CSR)。私钥不会离开我们这里。机构签名后返回的证书,在扩展用途(extendedKeyUsage)中会写明客户端认证。证书路径校验和有效期(notBefore、notAfter)由 X.509 规范文件 RFC 5280 规定——有效期之外的证书,校验会失败。
机构配置档案。各机构不同的值,不放在代码里,而放在数据里(与第 2 模块的路由表是同样的思路)。地址和端口、格式(定长/JSON)、编码、长度基准、CA·证书·私钥的路径、允许的来源地址。代码中不出现机构名称,而是读取配置档案来选择适配器。新机构只需要一行配置档案和一个适配器就能接入。
规格翻译。接收内层的标准请求(JSON),转换成机构规格,再把机构的响应转换回标准。本课程中 Garam 银行的报文长度包含自身的 4 字节。LH-STD 是不包含的。对于同一个 104 字节的报文,一方用 0104,另一方用 0100。如果弄错了这个差异,机构服务器要么按长度读取后,把剩下的 4 字节误认为下一个报文,要么等待不足的 4 字节而超时。响应码也要翻译。Garam 银行的 14(账户不存在)在内层是 B202。无论机构是谁,渠道看到的都是同一个代码。
入站一方的两层防护。机构向我们发起的连接,即使防火墙已经做了第一道过滤,应用程序也要再确认一次来源地址。防火墙规则由别的团队修改,而外联前置机的配置档案由我们管理——即使一方出了差错,另一方也能拦住,这就是两层。不在白名单上的地址,一个字节都不读取就断开,并留下记录。没有记录,就不知道是谁在敲门。
到期监控。定期读取证书的 notAfter,如果剩余天数少于基准,就告警。更换机构证书,从发送 CSR、签名到生效,有时要花好几周,所以告警必须比更换周期更早、更宽裕地响起。
在现场相遇的样子
对外对接故障的常客有三个。第一,证书到期。开通时装入了一年期的证书,没有人把它记在日历上。凌晨,与某个机构的交易全都因握手错误而停摆,日志里只有一行“certificate expired”。第二,证书弄混。机构有好几个,证书文件名又相近,在更换作业中装错了别的机构的证书——服务器以“unknown ca”拒绝。第三,对规格的误解。开发时用我们自己做的模拟服务器测试通过了,开通当天,真实机构因为长度基准不同,从第一个报文起就返回格式错误。这是模拟服务器把定义书中的一行模拟得不一样。所以对外对接要在机构提供的测试环境里,用机构的服务器来确认。
下一项实验要做什么
把两个机构(Garam 银行 201、Hanul 信用信息 301)的私有 CA 做成夹具,为每个机构生成密钥和 CSR 并获得签名。编写机构配置档案,与 Garam 银行通过双向 TLS 确认连接,并重现换装证书就会被拒绝的情形。然后制作按机构选择证书和格式发送的 fepgw.py、只从允许地址接收的 inbound.py,以及监控到期的 certcheck.py。