健康监测

当使用占位符来配置URI的时候,Spring Cloud Config会为配置中心服务端创建一个健康监测器,该检测器中默认构建了一个application为app的仓库。而根据之前的配置规则:{application}-config.git,该检测器会不断地检查https://github.com/licheetools/app-config仓库是否可以连通(此处的app就是上面的{application})。此时我们可以访问配置中心的/health端点来查看它的健康状态,如果无法连通https://github.com/licheetools/app-config仓库,那么配置中心的可用状态将是DOWN。虽然我们依然可以通过URI的方式来访问该配置中心,但是当我们将这个配置中心当做一个服务来使用的时候,那么它的状态将会影响到它的服务可用性判断,因此了解它的健康监测配置对于微服务架构来说显得尤为重要。

其实我们可以直接在Git仓库中创建一个名为app-config的配置库,让健康检测器能够访问到它。当然了,我们也可以配置一个实际存在的仓库来进行连通检测。举个例子,笔者已经在GitHub上配置了一个envy-config仓库,接下来就通过它来进行监控检测。首先在application.properties配置文件中添加如下配置:

1
2
3
4
5
6
spring.cloud.config.server.git.uri=https://github.com/licheetools/{application}-config
spring.cloud.config.server.git.username=username
spring.cloud.config.server.git.password=password
spring.cloud.config.server.health.repositories.check.name=envy
spring.cloud.config.server.health.repositories.check.label=label
spring.cloud.config.server.health.repositories.check.profiles=default

请注意,由于健康监测的repositories是一个Map对象,所以在实际使用的时候我们可以配置多个值,且每个配置中包含了与定位仓库地址时需要的三个元素:name表示应用名称;label表示分支名;profiles表示环境名。

当然了,如果不想使用该健康监测器,可以使用spring.cloud.config.server.health.enabled=false参数来关闭它,不过笔者建议在实际开发过程中还是尽量开启健康监测功能。

属性覆盖

Config Server也提供了属性覆盖的特性,它可以让开发人员为所有的应用提供配置属性,开发者只需在配置文件中通过spring.cloud.config.server.overrides属性来设置键值对的参数,这些参数都会以Map的方式加载到客户端的配置中。举个例子:

1
2
spring.cloud.config.server.overrides.name=envythink
spring.cloud.config.server.overrides.from=beijing

通过该属性配置的参数,是不允许被Spring Cloud客户端所修改的,且Spring Cloud客户端从Config Server中获取配置信息时,都会取得这些配置信息。也就是我们在Spring Cloud服务端设置的信息,在Spring Cloud客户端只有读取的权限,这个特性可以很方便的给Spring Cloud应用配置一些共同属性或者是默认的属性。当然客户端的优先级是高于服务端的,因此我们可以通过改变客户端中更高优先级的配置方式(如配置环境变量或是系统属性),来选择是否使用Config Server提供的默认值。

安全保护

一般来说配置中心存储的信息都是较为敏感的,因此都需要做一些安全处理,给配置中心实现安全保护的方式有很多,如物理网络限制、OAuth2授权等,这些都是不错的选择,但是考虑到微服务应用和配置中心都是建立在SpringBoot上,因此使用Spring家族自身的安全框架SpringSecurity则是更为明智的选择。那么如何使用SpringSecurity来给配置中心设置安全访问呢?

首先在pom.xml文件中,引入SpringSecurity的依赖,如下所示:

1
2
3
4
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>

其实这样我们就给这个config-server项目添加了访问保护,此时可以启动该项目,然后直接访问配置中心,就会出现登录框,默认的用户名是user,密码则是在每次启动的时候在控制台都会随机的输出,显然这种方式是不合理的。正确的做法是在application.yml配置文件中指定用户的账号和密码等信息,如下所示:

1
2
spring.security.user.name=envy
spring.security.user.password=password

这样我们就为config-server项目设置了安全保护,光这样是不够的,我们还需要在客户端中设置对应的安全信息,否则会在获取配置信息的时候返回401错误。

因此需要在config-client项目的bootstrap.properties启动文件中添加安全信息来通过上述验证,如用户名和密码等信息,如下所示:

1
2
spring.cloud.config.username=envy
spring.cloud.config.password=password

加密解密

在微服务架构中,我们通常会采用DevOps的组织方式来降低因团队间沟通所造成的巨大成本,以加速微服务应用的交付能力。这就使得原来由运维团队控制的线上信息直接交到由微服务所属组织的成员来自行维护,按照前面的配置,这些大量的敏感信息,如数据库的账户与密码等将会直接以明文的形式存储于微服务应用的配置文件中,这是非常危险的操作。

针对上述危险情况,Spring Cloud Config提供了对属性加密的功能,以保护配置文件中的信息安全。那么如何在配置中心使用该属性加密功能呢?

前期准备

默认情况下,JRE中自带了JCE(Unlimited Strength Java Cryptography Extension),但是它是一个有限长度的版本,而这里需要一个不限长度的JCE,因此需要在Oracle官网 下载 后进行解压,可以得到3个文件,如下所示:

然后我们需要将这里面的2个jar包复制到%JAVA_HOME%\jre\lib\security\policy\unlimited目录下,以覆盖之前的默认Jar包,这样前期工作就完成了。

相关端点

前期准备工作完成后,接下来就可以尝试启动配置中心,然后访问http://localhost:4001/encrypt/status链接,可以看到页面出现下面的信息:

1
2
3
4
{
description: "The encryption algorithm is not strong enough",
status: "INVALID"
}

那是因为此时还没有为加密服务配置对应的密钥,所以当前配置中心的加密功能还是不能使用的。

当然除了/encrypt/status接口外,还有其他的接口,这些接口的含义如下所示:

  • /encrypt/status:查看加密功能状态的端点;
  • /key:查看密钥的端点;
  • /encrypt:对请求的body内容进行加密的端点;
  • /decrypt:对请求的body内容进行解密的端点。

    配置密钥

    加密方式有两种:对称性加密和非对称性加密。
    对称性加密
    对称性加密比较简单,直接配置密钥就可以,首先理清思路,我们是在服务端加密后再由客户端解密,因此需要在config-server项目的bootstrap.properties启动文件中(必须是该文件)指定密钥信息,如下所示:
    1
    encrypt.key=envythink
    之后重启配置中心(config-server项目),然后在浏览器中访问http://localhost:4001/encrypt/status链接,可以看到输出结果为:

出现上面的结果就表示配置中心的加密解密功能就可以使用了。接着就可以使用上面介绍的/encrypt/decrypt接口来对请求的内容进行加密和解密处理,需要注意的是这两个端点都是POST请求,加密信息需要通过请求体来发送。如果开发者本地有POSTMAN、RestClient等测试工具,则可按照如下进行演示(为了更好的演示,可以将之前添加的spring-boot-starter-security依赖给注释掉,避免因为权限验证而导致的各种问题):

这个例子是使用/encrypt对envythink.com字符串进行先加密的结果:

这是对上面加密后的结果进行解密后得到的结果,可以看到和前面完全一样:

当然了,如果开发者本地没有按照POSTMAN等测试工具,则可以使用curl命令来验证接口,如下图所示:

这里我们通过采用对称加密方式在服务器端配置encrypt.key参数来指定密钥,可以看到它操作起来教务方便,只需一个参数即可。此外开发者也可以使用环境变量ENCRYPT_KEY来进行配置,让密钥信息外部化存储。

还有一个问题就是上面都是在服务器端对属性进行了加密和解码,而实际情况则是在服务端对属性进行加密,而在客户端对属性进行解密,且这种解密最好是可以自动进行,Spring Cloud Config就是这样实现的。

Spring Cloud Config通过在属性值前使用{cipher}前缀来标准给内容是一个加密值,当微服务客户端加载配置的时候,配置中心就自动会为带有{cipher}前缀的值进行解密。通过这种机制,运维团队就可以放心的将线上信息的加密资源给微服务团队,不用担心敏感信息的泄露问题了。

将下来尝试将之前envy-prod.yml配置文件中envythink: prod-1.0Map对象对应的value值prod-1.0进行加密和解密处理:

然后将这个加密后的一串字符串复制到之前的envy-prod.yml配置文件中,如下所示:

请注意上面的字符串必须以{cipher}开头,表示该值是一个加密字符,这样配置中心config-server在获取到这个值之后会先对该值进行解密,解密之后才会返回给客户端进行使用。

接着将这个配置文件通过Git上传到GitHub仓库,然后启动配置中心,最后去浏览器地址栏中访问该envy-prod.yml文件,即可得到想要的数据。

非对称性加密

Spring Cloud Config的配置中心不仅可以使用对称性加密,而且还可以使用非对称性加密,如RSA密钥对,尽管非对称加密的密钥生成与配置相对来说较为复杂一些,但是它具有更高的安全性。一般来说,在企业中安全始终是摆在第一位的,因此接下来开始学习如何使用非对称性加密。

第一步,使用keytool工具来生成密钥对。JDK自带了一个密钥和证书管理工具keytool,它可以让用户管理自己的公钥/私钥对以及相关证书,用于(通过数字签名)认证(用户向其他用户/服务认证自己)或数据完整性以及认证服务。这个工具所在的地址是%JAVA_HOME%\bin\keytool.exe,如下所示:

可以使用如下命令来生成密钥:

1
keytool -genkeypair -alias config-server -keyalg RSA -keystore config-server.keystore

如果不想逐步输入那些提示信息,可以使用-dname参数来直接指定,而密钥库口令与密钥口令可使用-storepass-keypass来直接指定,这样可以通过下面的命令直接创建出和前面一样的密钥库:

1
2
3
4
5
keytool -genkeypair -alias config-server -keyalg RSA \
-dname "CN=envythink, OU=company, O=organization, L=city, ST=province, C=china" \
-keypass 666666 \
-keystore config-server.keystore \
-storepass 888888\

但是默认情况下使用上述命令创建的密钥只有90天的有效期,如果开发者想调整它的有效期,可以在上述命令中增加-validity参数来实现。我们可以通过使用下面的命令来让密钥的有效期延长到一年,如下所示:

1
2
3
4
5
6
keytool -genkeypair -alias config-server -keyalg RSA \
-dname "CN=envythink, OU=company, O=organization, L=city, ST=province, C=china" \
-keypass 666666 \
-keystore config-server.keystore \
-storepass 888888 \
-validity 365 \

最后会在当前执行命令的目录下生成一个名为config-server.keystore的文件。

第二步,移动密钥至资源目录。将之前生成的config-server.keystore文件移动到config-server项目的src\main\resources目录下,并在该项目的bootstrap.properties文件中添加如下配置信息:

1
2
3
4
encrypt.key-store.location=config-server.keystore
encrypt.key-store.alias=config-server
encrypt.key-store.password=envythink
encrypt.key-store.secret=envythink

当然非对称加密的配置信息也可以通过环境变量的方式来进行配置,它们对应的具体变量名如下所示:

1
2
3
4
ENCRYPT_KEY_STORE_LOCATION
ENCRYPT_KEY_STORE_ALIAS
ENCRYPT_KEY_STORE_PASSWORD
ENCRYPT_KEY_STORE_SECRET

使用环境变量这种方式来配置密钥库相关信息,可以获得更好的安全性,因此建议将敏感口令信息存储在配置中心的环境变量中。

高可用配置

一般来说我们都希望应用都是高可用的,因此当将配置中心部署到生产环境中时,也应该和服务注册中心一样采用多节点模式。Spring Cloud Config实现服务端的高可用非常简单,有以下两种方式:
(1)传统模式。这种方式非常简单,开发者不需要为这些服务端做任何额外的配置,所有的Config Server都遵循一个配置规则,都指向同一个Git仓库,这样所有的配置内容就通过统一的共享文件系统来维护。而客户端在指定Config Server位置时,只需配置Config Server上层的负载均衡设备地址即可,相应的结构图如下所示:

(2)服务模式。我们还可以将Config Server作为一个普通的微服务应用并纳入Eureka的服务治理体系中。这样我们的微服务应用就可以通过配置中心的服务名来获取配置信息,这种方式和传统模式相比更适合维护,因为对于服务端的负载均衡配置和客户端的配置中心指定都通过服务治理机制一起解决了,也就是说它既实现了高可用,又实现了自我维护,因此笔者建议优先考虑服务模式,因为它完全符合微服务的思想。

那么到这里关于Spring Cloud Config分布式配置中心服务端配置的介绍就学习到这,后续开始学习客户端的配置。