Skip to content
This repository has been archived by the owner on Jul 16, 2024. It is now read-only.

RSocket Security

linux_china edited this page May 4, 2021 · 14 revisions

Alibaba RSocket Broker的安全模型介绍。RSocket Broker采用零信任架构方案,如要求所有的接入应用都要经过验证,JWT token设计上增加授权信息等。

  • 安全访问控制: 对所有连接到RSocket Broker的应用必须经过认证、授权和加密,每一次服务请求,都需要经过再次验证,没有信任连接。
  • 无边界设计: RSocket Broker并不会对从特定网络来的连接进行特殊处理,与应用能获得的服务没有关系,完全是基于身份控制。但是你可以添加plugin进行来源认证。
  • 上下文感知: 根据应用接入时的网络环境或设备的了解,来授予所获得的服务,这个目前并没有实现,但是可以通过RSocket Broker后台进行干预
  • 资源管控:在RSocket Broker的设计中, 资源主要是指:1. 接入到Broker的应用 2. 注册到Broker的服务,所以访问这两者都需要经过认证,实际的业务场景中,多是基于发布的服务。
  • 安全审计:所有的连接创建、服务请求等,都可以以日志或者事件方式记录,方便其他安全系统对接进行安全审计。

TLS + JWT Token

目前RSocket Broker的安全策略主要源自TLS和JWT Token。其中TLS主要负责通讯的加密,而连接认证则是基于JWT Token,力求保证每一个新建的连接都是被验证的。

TLS

目前RSocket Broker支持的TLS规范为TLSv1.3和TLSv.1.2,从而确保通讯通道的安全。考虑到TLS代理的性能损失、内部部署的场景,默认TLS是不启用的。

JWT Token

关于JWT的详细介绍,大家可以参考官方站点 https://jwt.io/ ,DZone上的这篇文章也不错 JWT Token: Lightweight, Token-Based Authentication 目前JWT Token的验证策略是RSA256,默认秘钥长度为2048。使用RSA私钥负责生成JWT Token,使用RSA公钥负责验证JWT Token,这样可以避免中心化验证服务的性能瓶颈和复杂性,同时基于公钥验证不会有安全泄露风险。

Alibaba RSocket 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的默认安全机制

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 Requester的IP地址

在某些场景下,我们想在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,这样只有授权的应用才可以调用这些服务。

参考

RSocket

Network Protocol

  • Binary: byte stream
  • Async message
  • Multi transports
  • Reactive Semantics

Symmetric interactions

  • request/response
  • request/stream
  • fire-and-forget
  • channel

Transports

  • TCP+TLS
  • WebSocket+TLS
  • UDP(Aeron)
  • RDMA

Polyglot

Clone this wiki locally