Home | 简体中文 | 繁体中文 | 杂文 | 知乎专栏 | Github | OSChina 博客 | 云社区 | 云栖社区 | Facebook | Linkedin | 视频教程 | 打赏(Donations) | About
知乎专栏多维度架构 微信号 netkiller-ebook | QQ群:128659835 请注明“读者”

第 13 章 多维度架构之微服务

目录

13.1. 微服务安全吗?
13.1.1. 配置中心的隐患
13.1.2. 注册中心的隐患
13.1.3. Eureka 客户端
13.1.4. 最终总结
13.2. 熔断器解决了什么问题?
13.3. 微服务的性能
13.3.1. 微服务的开销
13.4. 多维度架构之微服务拆分
13.4.1. 分布式事务之路
13.4.2. 微服务拆分法则
13.4.2.1. 基于工作流拆分服务
13.4.2.2. 服务池的概念
13.4.3. 最后总结
13.5. 接口安全
13.5.1. Restful 安全问提
13.5.2. 第一个阶段采用 HTTP Basic Auth
13.5.3. 第二阶段 HTTP Basic Auth + SSL
13.5.4. 第三阶段 HTTP2 + HTTP Basic Auth + Oauth2
13.5.5. 第三阶段,终极版诞生,HTTP2 + HTTP Basic Auth + Oauth2 + Jwt

13.1. 微服务安全吗?

微服务安全吗?其实存在很多隐患,常规的做法是将微服务置于私有局域网中,通过网关报漏服务。如果破坏者一旦进入了你的私有局域网中,微服务是及其危险的。

13.1.1. 配置中心的隐患

配置中心的安全隐患

配置中心有以下几种安全隐患

  1. 配置中心报漏在公网IP之下
  2. 配置中心没有做用户验证
  3. 配置文件中存在敏感信息
  4. 明文传输内容

配置有泄漏敏感信息的隐患,你的配置中心是不是也这样?

			
iMac:workspace neo$ curl http://localhost:8888/netkiller-dev-master.json
{"sms":{"gateway":{"url":"https://sms.netkiller.cn/v1","username":"netkiller","password":"123456"}}}
			
			

给配置中心增加SSL和HTTP认证,可以让配置中心更安全。

			
iMac:resources neo$ curl -i -k https://config:s3cr3t@localhost:8888/netkiller-dev.json
HTTP/2 200 
set-cookie: JSESSIONID=9E77660C8DC7669121C8D122A48D8737; Path=/; Secure; HttpOnly
x-content-type-options: nosniff
x-xss-protection: 1; mode=block
cache-control: no-cache, no-store, max-age=0, must-revalidate
pragma: no-cache
expires: 0
strict-transport-security: max-age=31536000 ; includeSubDomains
x-frame-options: DENY
content-type: application/json
content-length: 100
date: Mon, 07 Sep 2020 08:24:39 GMT

{"sms":{"gateway":{"url":"https://sms.netkiller.cn/v1","username":"netkiller","password":"123456"}}}	
			
			

我们将 HTTP2 SSL 应用在配置中心后,就不担心配置文件被嗅探器抓到。

13.1.2. 注册中心的隐患

注册中心一不小心就被公网IP报漏出去,甚至有被恶意注册的风险。

注册中心有以下几种安全隐患

  1. 注册中心没有做用户验证,任何人都能访问
  2. 注册中心报漏在公网IP之下,被恶意注册的风险。
  3. 从openfeign 访问 euerka server 明文传输内容

你的注册中心是不是这样的?

			
iMac:workspace neo$ curl http://localhost:8761/eureka/apps
<applications>
  <versions__delta>1</versions__delta>
  <apps__hashcode>UP_1_</apps__hashcode>
  <application>
    <name>WEBFLUX</name>
    <instance>
      <instanceId>192.168.3.85:webflux</instanceId>
      <hostName>192.168.3.85</hostName>
      <app>WEBFLUX</app>
      <ipAddr>192.168.3.85</ipAddr>
      <status>UP</status>
      <overriddenstatus>UNKNOWN</overriddenstatus>
      <port enabled="true">8080</port>
      <securePort enabled="false">443</securePort>
      <countryId>1</countryId>
      <dataCenterInfo class="com.netflix.appinfo.InstanceInfo$DefaultDataCenterInfo">
        <name>MyOwn</name>
      </dataCenterInfo>
      <leaseInfo>
        <renewalIntervalInSecs>30</renewalIntervalInSecs>
        <durationInSecs>90</durationInSecs>
        <registrationTimestamp>1599106511367</registrationTimestamp>
        <lastRenewalTimestamp>1599106931380</lastRenewalTimestamp>
        <evictionTimestamp>0</evictionTimestamp>
        <serviceUpTimestamp>1599106511367</serviceUpTimestamp>
      </leaseInfo>
      <metadata>
        <management.port>8080</management.port>
      </metadata>
      <homePageUrl>http://192.168.3.85:8080/</homePageUrl>
      <statusPageUrl>http://192.168.3.85:8080/actuator/info</statusPageUrl>
      <healthCheckUrl>http://192.168.3.85:8080/actuator/health</healthCheckUrl>
      <vipAddress>webflux</vipAddress>
      <secureVipAddress>webflux</secureVipAddress>
      <isCoordinatingDiscoveryServer>false</isCoordinatingDiscoveryServer>
      <lastUpdatedTimestamp>1599106511368</lastUpdatedTimestamp>
      <lastDirtyTimestamp>1599106511299</lastDirtyTimestamp>
      <actionType>ADDED</actionType>
    </instance>
  </application>
</applications>			
			
			

经过安全加固后

Eureka Web 界面进入需要输入用户名和密码,HTTP2 SSL 加密传输页面内容。

https://localhost:8761

			
iMac:resources neo$ curl -k https://eureka:s3cr3t@localhost:8761/eureka/apps
<applications>
  <versions__delta>1</versions__delta>
  <apps__hashcode>UP_2_</apps__hashcode>
  <application>
    <name>MICROSERVICE-RESTFUL</name>
    <instance>
      <instanceId>192.168.3.85:microservice-restful:8081</instanceId>
      <hostName>192.168.3.85</hostName>
      <app>MICROSERVICE-RESTFUL</app>
      <ipAddr>192.168.3.85</ipAddr>
      <status>UP</status>
      <overriddenstatus>UNKNOWN</overriddenstatus>
      <port enabled="true">8081</port>
      <securePort enabled="false">443</securePort>
      <countryId>1</countryId>
      <dataCenterInfo class="com.netflix.appinfo.InstanceInfo$DefaultDataCenterInfo">
        <name>MyOwn</name>
      </dataCenterInfo>
      <leaseInfo>
        <renewalIntervalInSecs>30</renewalIntervalInSecs>
        <durationInSecs>90</durationInSecs>
        <registrationTimestamp>1599532959290</registrationTimestamp>
        <lastRenewalTimestamp>1599533499404</lastRenewalTimestamp>
        <evictionTimestamp>0</evictionTimestamp>
        <serviceUpTimestamp>1599532959290</serviceUpTimestamp>
      </leaseInfo>
      <metadata>
        <management.port>8081</management.port>
      </metadata>
      <homePageUrl>http://192.168.3.85:8081/</homePageUrl>
      <statusPageUrl>http://192.168.3.85:8081/actuator/info</statusPageUrl>
      <healthCheckUrl>http://192.168.3.85:8081/actuator/health</healthCheckUrl>
      <vipAddress>microservice-restful</vipAddress>
      <secureVipAddress>microservice-restful</secureVipAddress>
      <isCoordinatingDiscoveryServer>false</isCoordinatingDiscoveryServer>
      <lastUpdatedTimestamp>1599532959291</lastUpdatedTimestamp>
      <lastDirtyTimestamp>1599532959204</lastDirtyTimestamp>
      <actionType>ADDED</actionType>
    </instance>
  </application>
  <application>
    <name>OPENFEIGN</name>
    <instance>
      <instanceId>192.168.3.85:openfeign:8088</instanceId>
      <hostName>192.168.3.85</hostName>
      <app>OPENFEIGN</app>
      <ipAddr>192.168.3.85</ipAddr>
      <status>UP</status>
      <overriddenstatus>UNKNOWN</overriddenstatus>
      <port enabled="true">8088</port>
      <securePort enabled="false">443</securePort>
      <countryId>1</countryId>
      <dataCenterInfo class="com.netflix.appinfo.InstanceInfo$DefaultDataCenterInfo">
        <name>MyOwn</name>
      </dataCenterInfo>
      <leaseInfo>
        <renewalIntervalInSecs>30</renewalIntervalInSecs>
        <durationInSecs>90</durationInSecs>
        <registrationTimestamp>1599533216972</registrationTimestamp>
        <lastRenewalTimestamp>1599533517001</lastRenewalTimestamp>
        <evictionTimestamp>0</evictionTimestamp>
        <serviceUpTimestamp>1599533216972</serviceUpTimestamp>
      </leaseInfo>
      <metadata>
        <management.port>8088</management.port>
      </metadata>
      <homePageUrl>http://192.168.3.85:8088/</homePageUrl>
      <statusPageUrl>http://192.168.3.85:8088/actuator/info</statusPageUrl>
      <healthCheckUrl>http://192.168.3.85:8088/actuator/health</healthCheckUrl>
      <vipAddress>openfeign</vipAddress>
      <secureVipAddress>openfeign</secureVipAddress>
      <isCoordinatingDiscoveryServer>false</isCoordinatingDiscoveryServer>
      <lastUpdatedTimestamp>1599533216972</lastUpdatedTimestamp>
      <lastDirtyTimestamp>1599533216920</lastDirtyTimestamp>
      <actionType>ADDED</actionType>
    </instance>
  </application>
</applications>			
			
			

13.1.3. Eureka 客户端

Eureka Client 的安全配置与Eureka Server/Config Server 类似

Eureka 客户端有以下几种安全隐患

  1. 服务报漏在公网IP之下,任何人都不经过 Eureka Server 和 Openfeign 绕开后直接访问服务
  2. 明文传输内容

我们给 Eureka Client 增加 HTTP/2 SSL 然后再注册到 Eureka Server,我通常会关闭 Eureka Client 端口,只保留 SSL 端口。

			
iMac:Architect neo$ curl -k https://eureka:s3cr3t@localhost:8761/eureka/apps 
<applications>
  <versions__delta>1</versions__delta>
  <apps__hashcode>UP_2_</apps__hashcode>
  <application>
    <name>MICROSERVICE-RESTFUL</name>
    <instance>
      <instanceId>192.168.3.85:microservice-restful:8081</instanceId>
      <hostName>192.168.3.85</hostName>
      <app>MICROSERVICE-RESTFUL</app>
      <ipAddr>192.168.3.85</ipAddr>
      <status>UP</status>
      <overriddenstatus>UNKNOWN</overriddenstatus>
      <port enabled="false">8081</port>
      <securePort enabled="true">8081</securePort>
      <countryId>1</countryId>
      <dataCenterInfo class="com.netflix.appinfo.InstanceInfo$DefaultDataCenterInfo">
        <name>MyOwn</name>
      </dataCenterInfo>
      <leaseInfo>
        <renewalIntervalInSecs>30</renewalIntervalInSecs>
        <durationInSecs>90</durationInSecs>
        <registrationTimestamp>1599547853553</registrationTimestamp>
        <lastRenewalTimestamp>1599548033559</lastRenewalTimestamp>
        <evictionTimestamp>0</evictionTimestamp>
        <serviceUpTimestamp>1599547853554</serviceUpTimestamp>
      </leaseInfo>
      <metadata>
        <management.port>8081</management.port>
      </metadata>
      <homePageUrl>http://192.168.3.85:8081/</homePageUrl>
      <statusPageUrl>http://192.168.3.85:8081/actuator/info</statusPageUrl>
      <healthCheckUrl>http://192.168.3.85:8081/actuator/health</healthCheckUrl>
      <secureHealthCheckUrl>https://192.168.3.85:8081/actuator/health</secureHealthCheckUrl>
      <vipAddress>microservice-restful</vipAddress>
      <secureVipAddress>microservice-restful</secureVipAddress>
      <isCoordinatingDiscoveryServer>false</isCoordinatingDiscoveryServer>
      <lastUpdatedTimestamp>1599547853554</lastUpdatedTimestamp>
      <lastDirtyTimestamp>1599547853483</lastDirtyTimestamp>
      <actionType>ADDED</actionType>
    </instance>
  </application>
  <application>
    <name>OPENFEIGN</name>
    <instance>
      <instanceId>192.168.3.85:openfeign:8088</instanceId>
      <hostName>192.168.3.85</hostName>
      <app>OPENFEIGN</app>
      <ipAddr>192.168.3.85</ipAddr>
      <status>UP</status>
      <overriddenstatus>UNKNOWN</overriddenstatus>
      <port enabled="true">8088</port>
      <securePort enabled="true">8088</securePort>
      <countryId>1</countryId>
      <dataCenterInfo class="com.netflix.appinfo.InstanceInfo$DefaultDataCenterInfo">
        <name>MyOwn</name>
      </dataCenterInfo>
      <leaseInfo>
        <renewalIntervalInSecs>30</renewalIntervalInSecs>
        <durationInSecs>90</durationInSecs>
        <registrationTimestamp>1599547953476</registrationTimestamp>
        <lastRenewalTimestamp>1599547953476</lastRenewalTimestamp>
        <evictionTimestamp>0</evictionTimestamp>
        <serviceUpTimestamp>1599547953476</serviceUpTimestamp>
      </leaseInfo>
      <metadata>
        <management.port>8088</management.port>
      </metadata>
      <homePageUrl>http://192.168.3.85:8088/</homePageUrl>
      <statusPageUrl>http://192.168.3.85:8088/actuator/info</statusPageUrl>
      <healthCheckUrl>http://192.168.3.85:8088/actuator/health</healthCheckUrl>
      <secureHealthCheckUrl>https://192.168.3.85:8088/actuator/health</secureHealthCheckUrl>
      <vipAddress>openfeign</vipAddress>
      <secureVipAddress>openfeign</secureVipAddress>
      <isCoordinatingDiscoveryServer>false</isCoordinatingDiscoveryServer>
      <lastUpdatedTimestamp>1599547953476</lastUpdatedTimestamp>
      <lastDirtyTimestamp>1599547953435</lastDirtyTimestamp>
      <actionType>ADDED</actionType>
    </instance>
  </application>
</applications>			
			
			

从上面配置中可以看到 port 已经禁用,也就意味着无法再通过 http:// 访问,securePort 是启用状态,只接受 https:// 访问。

			
      <port enabled="false">8081</port>
      <securePort enabled="true">8081</securePort>			
			
			

最好还要设置防火墙,只允许 Eureka Server 才能访问 Eureka Client。防止通过其他服务做为跳板,进入局域网,直接访问 Eureka Client。

13.1.4. 最终总结

为了防止不小心 公网IP保留微服务,我们需要将实例与局域网IP地址绑定,这样服务只能从局域网IP访问,即使服务器映射了公网IP地址也不用担心。

禁用 HTTP 访问,全部改为 HTTPS 访问

			
H5 / App 
    ^
    |
 HTTP2 ssl
    |
    V		
Openfeign <--- HTTP2 ssl ---> Eureka Server
                                  ^
                                  |
                                  |
                              HTTP2 ssl
                                  |
                                  |
                                  V
                             Eureka Client  <--- HTTP2 ssl ---> Config Server
			
			

如果可以,尽量为节点增加用户认证。