共计 6787 个字符,预计需要花费 17 分钟才能阅读完成。
Spring Cloud Config Server
Spring Cloud Config Server 为外部配置提供基于 HTTP 资源的 API(名称—值对或等效的 YAML 内容),通过使用 @EnableConfigServer 注解,服务器可嵌入 Spring Boot 应用程序中,因此,以下应用程序是配置服务器:
ConfigServer.java
@SpringBootApplication
@EnableConfigServer
public class ConfigServer {
public static void main(String[] args) {
SpringApplication.run(ConfigServer.class, args);
}
}
与所有 Spring Boot 应用程序一样,它默认在端口 8080 上运行,但你可以通过各种方式将其切换到更传统的端口 8888。最简单的,也是设置默认配置存储库,是通过 spring.config.name=configserver 启动它(Config Server jar 中有一个 configserver.yml),另一种方法是使用你自己的 application.properties,如以下示例所示:
application.properties
server.port: 8888
spring.cloud.config.server.git.uri: file://${user.home}/config-repo
其中 ${user.home}/config-repo 是一个包含 YAML 和属性文件的 git 存储库。
在 Windows 上,如果文件 URL 是绝对的驱动器前缀,则需要额外的“/”(例如,file:///${user.home}/config-repo)。
以下清单显示了在前面的示例中创建 git 存储库的步骤:
$ cd $HOME
$ mkdir config-repo
$ cd config-repo
$ git init .
$ echo info.foo: bar > application.properties
$ git add -A .
$ git commit -m “Add application.properties”
使用 git 存储库的本地文件系统仅用于测试,生产中你应该使用服务器托管配置存储库。
如果只保留文本文件,则配置存储库的初始克隆可以快速有效,如果存储二进制文件(尤其是大型文件),则第一次请求配置可能会出现延迟或服务器中遇到内存不足错误。
环境存储库
应该在哪里存储配置服务器的配置数据?管理此行为的策略是 EnvironmentRepository,为 Environment 对象提供服务,此 Environment 是 Spring Environment 中域的浅拷贝(包括 propertySources 作为主要功能),Environment 资源由三个变量参数化:
{application},它映射到客户端的 spring.application.name。
{profile},它映射到客户端(逗号分隔列表)的 spring.profiles.active。
{label},这是标记配置文件集“版本化”的服务器端特性。
存储库实现通常表现得像 Spring Boot 应用程序,从 spring.config.name 等于 {application} 参数,spring.profiles.active 等于 {profiles} 参数加载配置文件。配置文件的优先规则也与常规 Spring Boot 应用程序中的规则相同:活动配置文件优先于默认配置文件,如果有多个配置文件,则最后一个配置文件获胜(类似于向 Map 添加条目)。
以下示例客户端应用程序具有此 bootstrap 配置:
bootstrap.yml
spring:
application:
name: foo
profiles:
active: dev,mysql
像通常一样,Spring Boot 应用程序也可以通过环境变量或命令行参数来设置这些属性。
如果存储库是基于文件的,则服务器从 application.yml(在所有客户端之间共享)和 foo.yml(以 foo.yml 优先)创建 Environment。如果 YAML 文件中包含指向 Spring 配置文件的文档,那么这些文档将以更高的优先级应用(按列出的配置文件的顺序)。如果存在特定配置文件的 YAML(或属性)文件,则这些文件的优先级也高于默认值,较高的优先级转换为 Environment 中先前列出的 PropertySource(这些相同的规则适用于独立的 Spring Boot 应用程序)。
你可以将 spring.cloud.config.server.accept-empty 设置为 false,以便如果应用程序找不到,则 Server 返回 HTTP 404 状态,默认情况下,此标志设置为 true。
健康指示器
Config Server 附带一个健康指示器,用于检查配置的 EnvironmentRepository 是否正常工作,默认情况下,它会向 EnvironmentRepository 请求名为 app 的应用程序、default 配置文件以及 EnvironmentRepository 实现提供的默认标签。
你可以配置健康指示器以检查更多应用程序以及自定义配置文件和自定义标签,如以下示例所示:
spring:
cloud:
config:
server:
health:
repositories:
myservice:
label: mylabel
myservice-dev:
name: myservice
profiles: development
你可以通过设置 spring.cloud.config.server.health.enabled=false 来禁用监控指示器。
安全性
你可以以对你有意义的任何方式保护你的 Config Server(从物理网络安全到 OAuth2 承载令牌),因为 Spring Security 和 Spring Boot 为许多安全安排提供支持。
要使用默认的 Spring Boot 配置的 HTTP Basic 安全性,请在类路径中包含 Spring Security(例如,通过 spring-boot-starter-security),默认值为 user 的用户名和随机生成的密码,随机密码在实践中没有用,因此建议你配置密码(通过设置 spring.security.user.password)并对其进行加密(有关如何执行此操作的说明,请参阅下文)。
加密和解密
要使用加密和解密特性,你需要在 JVM 中安装完整的 JCE(默认情况下不包括),你可以从 Oracle 下载“Java Cryptography Extension(JCE)Unlimited Strength Jurisdiction Policy Files”并按照安装说明进行操作(实际上,你需要将 JRE lib/security 目录中的两个策略文件替换为你下载的策略文件)。
如果远程属性源包含加密内容(以 {cipher} 开头的值),则在通过 HTTP 发送到客户端之前对它们进行解密,此设置的主要优点是,属性值在“静止”时不必是纯文本格式(例如,在 git 存储库中)。如果某个值无法解密,则会从属性源中删除该值,并添加一个附加属性,该属性具有相同的键但前缀为 invalid,且值为“不适用”(通常为 <n/a>),这主要是为了防止密文被用作密码并意外泄露。
如果为配置客户端应用程序设置远程配置存储库,则它可能包含类似于以下内容的 application.yml:
spring:
datasource:
username: dbuser
password: ‘{cipher}FKSAJDFGYOS8F7GLHAKERGFHLSAJ’
.properties 文件中的加密值不能用引号括起来,否则,该值不会被解密,以下示例显示了有效的值:
application.properties
spring.datasource.username: dbuser
spring.datasource.password: {cipher}FKSAJDFGYOS8F7GLHAKERGFHLSAJ
你可以安全地将此纯文本推送到共享的 git 存储库,并且密码仍然受到保护。
服务器还公开 /encrypt 和 /decrypt 端点(假设这些端点是安全的并且只能由授权代理访问),如果编辑远程配置文件,则可以使用 Config Server 通过 POST 到 /encrypt 端点来加密值,如以下示例所示:
$ curl localhost:8888/encrypt -d mysecret
682bc583f4641835fa2db009355293665d2647dade3375c0ee201de2a49f7bda
如果你加密的值中包含需要进行 URL 编码的字符,则应使用 –data-urlencode 选项进行 curl 以确保它们已正确编码。
请确保不要在加密值中包含任何 curl 命令统计信息,将值输出到文件可以帮助避免此问题。
通过 /decrypt 也可以使用反向操作(前提是服务器配置了对称密钥或完整密钥对),如以下示例所示:
$ curl localhost:8888/decrypt -d 682bc583f4641835fa2db009355293665d2647dade3375c0ee201de2a49f7bda
mysecret
如果你用 curl 测试,那么使用 –data-urlencode(替代 -d)或设置一个显式的 Content-Type: text/plain 来确保 curl 在有特殊字符时正确编码数据(’+’ 特别棘手)。
获取加密值并添加 {cipher} 前缀,然后再将其放入 YAML 或属性文件中,然后再提交并将其推送到远程(可能不安全)存储。
/encrypt 和 /decrypt 端点也接受 /*/{name}/{profiles}形式的路径,当客户端调用主环境资源时,可用于在每个应用程序(名称)和每个配置文件的基础上控制加密。
要以这种精细的方式控制加密,你还必须提供类型为 TextEncryptorLocator 的 @Bean,它为每个名称和配置文件创建不同的加密器,默认情况下提供的那个不会这样做(所有加密都使用相同的密钥)。
spring 命令行客户端(安装了 Spring Cloud CLI 扩展)也可用于加密和解密,如以下示例所示:
$ spring encrypt mysecret –key foo
682bc583f4641835fa2db009355293665d2647dade3375c0ee201de2a49f7bda
$ spring decrypt –key foo 682bc583f4641835fa2db009355293665d2647dade3375c0ee201de2a49f7bda
mysecret
要使用文件中的密钥(例如用于加密的 RSA 公钥),请在密钥值前加上“@”并提供文件路径,如以下示例所示:
$ spring encrypt mysecret –key @${HOME}/.ssh/id_rsa.pub
AQAjPgt3eFZQXwt8tsHAVv/QHiY5sI2dRcR+…
–key 参数是必需的(尽管有 – 前缀)。
密钥管理
Config Server 可以使用对称(共享)密钥或非对称密钥(RSA 密钥对),非对称选择在安全性方面更优越,但使用对称密钥通常更方便,因为它是在 bootstrap.properties 中配置的单个属性值。
要配置对称密钥,需要将 encrypt.key 设置为秘密字符串(或使用 ENCRYPT_KEY 环境变量将其排除在纯文本配置文件之外)。
无法使用 encrypt.key 配置非对称密钥。
要配置非对称密钥,请使用密钥库(例如,由 JDK 附带的 keytool 实用工具创建),密钥库属性是 encrypt.keyStore.*,* 等于:
属性
描述
encrypt.keyStore.location
包含 Resource 的位置
encrypt.keyStore.password
保存解锁密钥库的密码
encrypt.keyStore.alias
标识要使用存储中的哪个密钥
加密是使用公钥完成的,并且需要私钥进行解密,因此,原则上,如果只想加密(并准备使用私钥本地解密值),则只配置服务器中的公钥。实际上,你可能不希望在本地进行解密,因为它会围绕所有客户端传播密钥管理过程,而不是将其集中在服务器中,另一方面,如果你的配置服务器相对不安全且只有少数客户端需要加密属性,那么它可能是一个有用的选项。
创建用于测试的密钥库
要创建用于测试的密钥库,可以使用类似于以下内容的命令:
$ keytool -genkeypair -alias mytestkey -keyalg RSA \
-dname “CN=Web Server,OU=Unit,O=Organization,L=City,S=State,C=US” \
-keypass changeme -keystore server.jks -storepass letmein
将 server.jks 文件放在类路径中(例如),然后在 bootstrap.yml 中为 Config Server 创建以下设置:
encrypt:
keyStore:
location: classpath:/server.jks
password: letmein
alias: mytestkey
secret: changeme
使用多个密钥和密钥轮换
除了加密属性值中的 {cipher} 前缀之外,Config Server 还会在(Base64 编码)密文开头之前查找零个或多个 {name:value} 前缀,密钥传递给 TextEncryptorLocator,它可以执行为密文定位 TextEncryptor 所需的任何逻辑。如果已配置密钥库(encrypt.keystore.location),则默认定位器将查找具有 key 前缀提供的别名的密钥,密文类似于以下内容:
foo:
bar: `{cipher}{key:testkey}…`
定位器查找名为“testkey”的密钥,也可以通过在前缀中使用 {secret:…} 值来提供秘密,但是,如果未提供,则默认使用密钥库密码(这是你在构建密钥库时未指定秘密),如果你提供秘密,你还应该使用自定义 SecretLocator 加密秘密。
当密钥仅用于加密几个字节的配置数据时(也就是说,它们没有在其他地方使用),在加密方面几乎不需要密钥轮换。但是,你可能偶尔需要更改密钥(例如,在发生安全漏洞时),在这种情况下,所有客户端都需要更改其源配置文件(例如,在 git 中)并在所有密文中使用新的 {key:…} 前缀,请注意,客户端需要首先检查 Config Server 密钥库中的密钥别名是否可用。
如果你想让 Config Server 处理所有加密和解密,{name:value}前缀也可以作为纯文本添加发布到 /encrypt 端点。
提供加密属性
有时你希望客户端在本地解密配置,而不是在服务器中执行此操作。在这种情况下,如果你提供 encrypt.* 配置来定位密钥,你仍然可以拥有 /encrypt 和 /decrypt 端点,但是你需要通过在 bootstrap.[yml|properties]中放置 spring.cloud.config.server.encrypt.enabled=false 来明确地关闭输出属性的解密,如果你不关心端点,那么如果你不配置密钥或启用标志,它应该可以工作。
提供选择性的格式
环境端点的默认 JSON 格式非常适合 Spring 应用程序使用,因为它直接映射到 Environment 抽象,如果你愿意,可以通过向资源路径添加后缀(“.yml”,“.yaml”或“.properties”)来使用与 YAML 或 Java 属性相同的数据,对于不关心 JSON 端点结构或它们提供的额外元数据的应用程序来说,这可能很有用(例如,不使用 Spring 的应用程序可能会受益于此方法的简单性)。
YAML 和属性表示有一个额外的标志(作为名为 resolvePlaceholders 的布尔查询参数提供),表示源文档中的占位符(在标准的 Spring ${…}形式)应该在渲染之前在输出中解析(在可能的情况),对于不了解 Spring 占位符约定的消费者而言,这是一个有用的特性。
使用 YAML 或属性格式存在限制,主要与元数据丢失有关。例如,JSON 为属性源的有序列表结构,其名称与源相关,YAML 和属性形式合并为单个映射,即使值的来源有多个源,并且原始源文件的名称丢失。此外,YAML 表示不一定是支持存储库中 YAML 源的可靠表示,它由一个平面属性源列表构成,必须对键的形式进行假设。
上一篇:Spring Cloud Config 快速入门
下一篇:提供纯文本