-
Notifications
You must be signed in to change notification settings - Fork 167
RSocket Security
Alibaba RSocket Broker的安全模型介绍。RSocket Broker采用零信任架构方案,如要求所有的接入应用都要经过验证,JWT token设计上增加授权信息等。
- 安全访问控制: 对所有连接到RSocket Broker的应用必须经过认证、授权和加密,每一次服务请求,都需要经过再次验证,没有信任连接。
- 无边界设计: RSocket Broker并不会对从特定网络来的连接进行特殊处理,与应用能获得的服务没有关系,完全是基于身份控制。但是你可以添加plugin进行来源认证。
- 上下文感知: 根据应用接入时的网络环境或设备的了解,来授予所获得的服务,这个目前并没有实现,但是可以通过RSocket Broker后台进行干预
- 资源管控:在RSocket Broker的设计中, 资源主要是指:1. 接入到Broker的应用 2. 注册到Broker的服务,所以访问这两者都需要经过认证,实际的业务场景中,多是基于发布的服务。
- 安全审计:所有的连接创建、服务请求等,都可以以日志或者事件方式记录,方便其他安全系统对接进行安全审计。
目前RSocket Broker的安全策略主要源自TLS和JWT Token。其中TLS主要负责通讯的加密,而连接认证则是基于JWT Token,力求保证每一个新建的连接都是被验证的。
目前RSocket Broker支持的TLS规范为TLSv1.3和TLSv.1.2,从而确保通讯通道的安全。考虑到TLS代理的性能损失、内部部署的场景,默认TLS是不启用的。
关于JWT的详细介绍,大家可以参考官方站点 https://jwt.io/ ,DZone上的这篇文章也不错 JWT Token: Lightweight, Token-Based Authentication 目前JWT Token的验证策略是RSA256,默认秘钥长度为2048。使用RSA私钥负责生成JWT Token,使用RSA公钥负责验证JWT Token,这样可以避免中心化验证服务的性能瓶颈和复杂性,同时基于公钥验证不会有安全泄露风险。
在介绍详细的验证和授权机制前,让我们看一下JWT Payload的结构,也就是Payload中包含哪些信息?
JWT Payload的元素信息:
- "iss" (Issuer): Token的签发者,目前为"RSocketBroker"
- "id"(Certification ID): 证书ID,这个可选的。例如在IoT场景,我们可能希望每一个设备都使用不同的证书,这样方便我们跟踪这些设备,如果给设备推送信息。
- "sub" (Subject) : JWT的面向的对象,这里为应用名称,如Spring Boot场景就是"spring.application.name"对应的值。我们建议不同的应用申请不同的Token,不要混用。
- "aud" (Audience) :JWT的接收人,这里为申请JWT Token的申请人或者负责人,如果token有问题,方便我们联系对应的人员。多名人员以逗号分隔,内容如姓名、email、手机号等。
- "iat" (Issued At): Token的颁发时间(issued timestamp)
- "exp" (Expiration Time):Token的过期时间
- "orgs" (organizations): JWT Token所属的机构ID,可以为多个,逗号分隔。 机构ID主要使用在多租户场景,不同的机构ID之间的服务不能相互调用。如果两个机构之间需要相互调用,请包含共同的机构ID,如"alibaba,taobao", "alibaba,alipay",这样可以保证两个机构之间可以通讯。
- "sas" (Service Accounts): 应用的服务账户列表,这个和K8S的Service Account机制一样,同一个Service Account下的应用可以相互通讯,缺失名称为default。注意: 即便机构ID相同,service account还必须相同,这样才可以通讯。多个服务账号名称之间都好分隔。
- "roles" (Roles): 角色列表,如"external", "partner", "internal", "vip", "admin"等,这个和安全认证中的role是一样的
- "authorities" (ACL): 权限列表,表示该token拥有访问特定权限,例如"account.read", "account.write"等权限点,权限点可以来源于服务元信息的tags,当然也可以为"com.example.UserService"等服务接口.
在JWT Token的信息中,认证是没有问题的,考虑得到实际的安全场景,原则上还要求添加授权信息,如角色列表和权限列表。
如果是在P2P的通讯场景中,如ServiceA要要访问ServiceB的com.example.UserService的服务,那么ServiceA创建和ServiceB的连接时,提供的JWT Payload中的authorities包含对应的服务接口名称, 这样的服务提供方可以使用RSA public key进行对应的JWT Token验证,然后允许相应的客户端创立对应的连接。
Alibaba RSocket Broker的默认的安全机制是基于JWT的organizations(orgs)和Service Accounts(sas)元信息。 每一个应用都会包含对应的orgs和sas字段, 如果两个应用要能够相互访问,则orgs和sas需要有共同的交集,也就是包含同样的机构和服务账号。 举一个例子:
- 如应用A的JWT包括
{orgs: ["taobao"], sas: ["user"]}
, 应用B的JWT也包扩{orgs: ["taobao"], sas: ["user"]}
,则A和B之间可以相互调用。如果你想让内部的应用都可以相互访问,应用的JWT只需要设置为{orgs: ["your_company"], sas: ["default"]}
就可以啦。 - 如应用A的JWT包括
{orgs: ["taobao"], sas: ["user"]}
, 应用B的JWT包扩{orgs: ["taobao"], sas: ["item"]}
, A和B之间没有交集(Service Account没有交集),则不能相互访问,如同一公司下不同产品线或者部门的应用不能相互访问。 - 如应用A的JWT包括
{orgs: ["taobao"], sas: ["user"]}
, 应用B的JWT包扩{orgs: ["taobao"], sas: ["user","item"]}
, 则A和B的交集为{orgs: ["taobao"], sas: ["user"]}
,则可以相互访问,如同一公司内部不同产品线的应用之间相互调用。 - 如应用A的JWT包括
{orgs: ["taobao"], sas: ["user"]}
, 应用B的JWT包扩{orgs: ["alipay"], sas: ["user"]}
,A和B之间没有共同的交集(组织不相同),则A和B之间不能相互访问,这表示不同组织的应用是不能相互访问的。 - 如应用A的JWT包括
{orgs: ["taobao", "alipay"], sas: ["user"]}
, 应用B的JWT包扩{orgs: ["alipay"], sas: ["user"]}
,则A和B的交集为{orgs: ["alipay"], sas: ["user"]}
,则可以相互访问,表示了某一组织其他组织的应用进行对应的授权。
这样模式主要是简化默认的权限认证,当然也符合大多数公司的策略,你可以将Service Account连接为一个产品线或者部门,同一部门下的产品通常是可以相互访问的,如果想缩减访问范围,可以考虑子Service Account这种方式。这样设计的目的是简单,同时能够方便多种语言接入的方便,在服务层提供者这一层完全不需要做任何安全设置。 JWT Token在生成时,要进行对应的安全设置,主要就是机构、服务账号和对应应用的角色列表,这样才能保证该默认规则的生效。 当然你可以通过Broker的plugin机制加强验证,如添加角色判断等,这个就需要你自己编写对应的安全验证啦。
在某些场景下,我们想在RSocket鉴权的时候,验证对方的IP地址,如是否在IP白名单内,从而保证一定的安全性。 在RSocket Java 1.1.0版本, DuplexConnection提供了remoteAddress()方法,你只需要通过鉴权时的requester对象的connection字段就可以就可以拿到连接方的IP地址。 下述代码中的FieldUtils类来自Apache commons-lang3。 样例如下:
public Mono<RSocket> createResponder(ConnectionSetupPayload setupPayload, RSocket requester) {
//CompositeMetadata compositeMetadata = new CompositeMetadata(setupPayload.metadata(), false);
//security authentication
try {
DuplexConnection connection = (DuplexConnection) FieldUtils.readField(requester, "connection", true);
InetSocketAddress remoteAddress = (InetSocketAddress) connection.remoteAddress();
} catch (Exception ignore) {
}
SimpleResponderImpl handler = new SimpleResponderImpl(setupPayload, requester);
return Mono.just(handler);
}
当然你也可以通过RSocket的plugin机制,来验证连接方的远程IP地址,如下:
RSocketServer.create()
.acceptor(responderFactory.responder())
.interceptors(registry -> {
registry.forConnection((type, duplexConnection) -> {
//check remote address
return duplexConnection;
});
})
处于安全考虑,应用接入的IP地址会被RSocket Broker所记录。
在内部的系统中,会员的一些信息,如手机号码、邮件等,这些属于敏感服务,不是所有的应用都可以访问这些服务。 你可以将该服务对应的token中的service account设置为UserPrivacy,其他应用如果想访问这些服务,在申请token的时候,JWT Token中的sas值可能就为"default,UserPrivacy",表示该应用可以访问会员的敏感服务。 而普通应用,由于服务账号中并没有包含"UserPrivacy",所以不能访问会员的敏感服务,做到非常好的安全保障。
JWT中包含orgs字段,标明应用所说的机构信息,根据该信息,可以做到机构内服务和外部隔离。 如果你需要将某一应用开放给其他机构访问,需要机构ID和Service Account两者配合。
- 包含共有的机构ID,如Token中的orgs都包含"alibaba"这个机构ID
- 包含共有的Service Account: 我们还需要设置一下共有的service account,不然对方的应用可能就访问你内部的所有服务,这个不是我们希望的。所有对应的Service Account可能就是"alipay_taobao",注意不要随意添加名称,不然可能会导致越权访问。
如果你的服务是开放给所有的应用调用的,但是你希望一些服务是提供给运维或者CRM的,这个时候,你可以给对应的Token添加roles或者authorities,这样只有授权的应用才可以调用这些服务。
- Zero Trust Architecture: NIST Special Publication 800-207 https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-207.pdf
- Binary: byte stream
- Async message
- Multi transports
- Reactive Semantics
- request/response
- request/stream
- fire-and-forget
- channel
- TCP+TLS
- WebSocket+TLS
- UDP(Aeron)
- RDMA