Loading...
墨滴

lyq

2021/11/17  阅读:27  主题:橙心

Spring Cloud Config配置详解(二)

Spring Cloud Config配置详解(二)

spring boot版本为:2.5.6

spring cloud版本为:2020.0.4

spring cloud config版本为:3.0.5

spring cloud config server

加密和解密

要使用加密和解密特性,您需要在JVM中安装全功能JCE(默认情况下不包括它)。您可以从Oracle下载“Java Cryptography Extension (JCE) Unlimited Strength管辖权策略文件”,并按照安装说明操作(实际上,您需要用下载的策略文件替换JRE lib/security目录中的两个策略文件)。 oracle下载地址

如果远程属性源包含加密的内容(以{cipher}开头的值),它们在通过HTTP发送给客户端之前会被解密。这种设置的主要优点是,当属性值处于“静止状态”(例如,在git存储库中)时,它们不需要以纯文本形式显示。如果一个值不能被解密,它将从属性源中删除,并使用相同的键添加一个附加属性,但前缀为invalid和表示“不适用”(通常为<n/a>)的值。这在很大程度上是为了防止密文被用作密码而意外泄露。

如果您为config client应用程序设置远程配置存储库,那么它可能包含一个application.yml,类似以下内容:

application.yml

spring:
  datasource:
    username: dbuser
    password: '{cipher}FKSAJDFGYOS8F7GLHAKERGFHLSAJ'

应用程序中的加密值。在application.properties文件中不能用引号括起来。否则,该值不被解密。下面的示例显示了可以工作的值:

application.properties

spring.datasource.username: dbuser
spring.datasource.password: {cipher}FKSAJDFGYOS8F7GLHAKERGFHLSAJ

您可以安全地将这个纯文本推送到共享的git存储库,并且保护密码。

服务器还公开/encrypt和/decrypt端点(假设这些端点将被保护并且只能由授权代理访问)。如果您正在编辑远程配置文件,可以使用Config Server通过POST到/encrypt端点来加密值,例如:

$ curl localhost:8888/encrypt -s -d mysecret
682bc583f4641835fa2db009355293665d2647dade3375c0ee201de2a49f7bda

如果您使用curl进行测试,那么使用——data-urlencode(而不是-d),并在值的前面加上= (curl需要这样做)或设置显式的Content-Type: text/plain,以确保curl在存在特殊字符('+'特别棘手)时正确编码数据。

请确保加密值中不包含任何curl命令统计信息,这就是为什么示例使用-s选项来关闭它们。将该值输出到文件可以帮助避免这个问题。

反向操作也可以通过/decrypt(如果服务器配置了对称密钥或完整密钥对),如下面的示例所示:

$ curl localhost:8888/decrypt -s -d 682bc583f4641835fa2db009355293665d2647dade3375c0ee201de2a49f7bda
mysecret

在将加密值放入YAML或属性文件以及提交并将其推入远程(可能不安全)存储之前,获取加密值并添加{cipher}前缀。

/encrypt/decrypt端点也都接受/*/{application}/{profiles}形式的路径,当客户端调用主环境资源时,可以使用该路径在每个应用程序(名称)和每个配置文件的基础上控制加密。

要以这种细粒度方式控制加密,还必须提供TextEncryptorLocator类型的@Bean,该@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参数是强制性的(尽管有——前缀)。

密钥管理

配置服务器可以使用对称(共享)密钥或非对称(RSA密钥对)密钥。非对称选择在安全性方面更优越,但使用对称密钥通常更方便,因为它在bootstrap.properties文件中采用单独的属性值来配置

要配置对称密钥,需要设置encrypt.key到一个秘密字符串(或者使用ENCRYPT_KEY环境变量使其不在纯文本配置文件中)。

不能使用encrypt.key配置非对称密钥

要配置非对称密钥,请使用keystore(例如,由JDK自带的keytool工具创建)。密钥存储库属性是encrypt.keyStore.**等于:

属性 描述
encrypt.keyStore.location 包含资源位置
encrypt.keyStore.password 保存解锁密钥存储库的密码
encrypt.keyStore.alias 标识要使用存储中的哪个键
encrypt.keyStore.type 要创建的KeyStore的类型。默认为jks

加密使用公钥,解密需要私钥。因此,原则上,如果您只想加密(并且准备在本地使用私钥对值进行解密),则只能在服务器中配置公钥。在实践中,您可能不希望在本地进行解密,因为它将密钥管理过程分散到所有客户机,而不是将其集中在服务器上。另一方面,如果配置服务器相对不安全,并且只有少数客户端需要加密属性,那么它可能是一个有用的选项。

创建用于测试的密钥库

要创建用于测试的密钥存储库,可以使用如下命令:

$ 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

当使用JDK 11或以上版本时,您可能会在使用上面的命令时收到以下警告。在这种情况下,您可能希望确保keypass和storepass值匹配。

Warning:  Different store and key passwords not supported for PKCS12 KeyStores. Ignoring user-specified -keypass value.

server.jks文件放在类路径中,然后在配置服务器的bootstrap.yml中,创建以下设置:

encrypt:
  keyStore:
    location: classpath:/server.jks
    password: letmein
    alias: mytestkey
    secret: changeme

在当前版本中,经测试,需要放到application.yml文件中,不知道是不是官方文档没有及时维护,所以看文档时,还请加以辩证

使用多个键和键旋转

除了加密属性值中的{cipher}前缀外,配置服务器还会在(Base64编码的)密文开始之前查找0个或多个{name:value}前缀。密钥被传递给一个TextEncryptorLocator,它可以执行任何需要的逻辑来为密码定位一个TextEncryptor。如果您已经配置了密钥存储库(encrypt.keystore.location),默认定位器将查找由密钥前缀提供的别名的密钥,并使用类似如下的密文:

foo:
  bar: `{cipher}{key:testkey}...`

定位器寻找一个名为“testkey”的键。也可以在前缀中使用{secret:…}值来提供secret。但是,如果没有提供密钥库,则默认使用密钥库密码(这是在构建密钥库且不指定密钥时获得的密码)。如果您确实提供了一个secret,您还应该使用自定义的SecretLocator加密这个secret

当密钥只用于加密配置数据的几个字节时(也就是说,它们不在其他地方使用),从加密的角度来看,密钥旋转几乎是没有必要的。但是,您可能偶尔需要更改密钥(例如,在出现安全漏洞的情况下)。在这种情况下,所有客户端都需要更改他们的源配置文件(例如,在git中),并在所有密码中使用一个新的{key:…}前缀。请注意,客户端需要首先检查Config Server密钥存储库中是否有密钥别名。

如果你想让配置服务器处理所有加密和解密,{name:value}前缀也可以作为纯文本添加到/encrypt端点。(将{name:value}放入请求体中)

提供加密属性

有时您希望客户机在本地解密配置,而不是在服务器中进行。在这种情况下,如果您提供encrypt.*配置来定位密钥,你仍然可以使用/encrypt/decrypt端点,但是你需要通过在bootstrap.[yml|properties]放置spring.cloud.config.server.encrypt=false显式地关闭对传出属性的解密。如果您不关心端点,那么如果您不配置密钥或启用标志,它应该可以工作。

在当前版本中,经测试,放在bootstrap.yml无效,需要放在application.yml

提供纯文本

您的应用程序可能需要通用的纯文本配置文件,而不是使用Environment抽象(或使用YAML或属性格式的另一种表示形式)。配置服务器通过/{application}/{profile}/{label}/{path}的附加端点提供这些,其中application、profile和label与常规环境端点具有相同的含义,但是path是一个文件名(例如log.xml)的路径。这个端点的源文件的定位方式与环境端点的定位方式相同。属性和YAML文件使用相同的搜索路径。但是,不是聚合所有匹配的资源,而是只返回第一个匹配的资源。

定位资源后,使用提供的应用程序名称、概要文件和标签的有效Environment解析正常格式的占位符(${…})。通过这种方式,资源端点与环境端点紧密集成。

与用于环境配置的源文件一样,profile用于解析文件名。因此,如果您想要一个特定于概要文件的文件,/*/development/*/logback.xml可以通过一个名为logback-development.xml的文件来解析(而不是logback.xml)

如果不想提供标签并让服务器使用默认标签,可以提供useDefaultLabel请求参数。因此,前面的默认配置文件示例可以是/sample/default/nginx.conf?useDefaultLabel

目前,Spring Cloud Config可以为git、SVN、本地后端和AWS S3提供明文服务。对git、SVN和本地后端的支持是相同的。AWS S3的工作方式略有不同。下面的小节展示了它们的工作原理

Git、SVN和本地后端

考虑以下GIT或SVN存储库或本地后端示例:

application.yml
nginx.conf

其中nginx.conf看起来像这样:

server {
    listen              80;
    server_name         ${nginx.server.name};
}

application.yml看起来像这样:

nginx:
  server:
    name: example.com
---
spring:
  profiles: development
nginx:
  server:
    name: develop.com

/sample/default/master/nginx.conf资源如下所示:

server {
    listen              80;
    server_name         example.com;
}

/sample/development/master/nginx.conf如下所示:

server {
    listen              80;
    server_name         develop.com;
}

AWS S3

要为AWS s3提供纯文本服务,配置服务器应用程序需要包含对Spring Cloud AWS的依赖。关于如何设置该依赖项的详细信息,请参见Spring Cloud AWS参考指南。然后,您需要配置Spring Cloud AWS,请参见Spring Cloud AWS参考指南

解密纯文本

缺省情况下,明文文件中的加密值不被解密。为了启用对纯文本文件的解密,在bootstrap.[yml|properties]中设置spring.cloud.config.server.encrypt.enabled=truespring.cloud.config.server.encrypt.plainTextEncrypt=true

解密纯文本文件只支持YAML、JSON和属性文件扩展名。

如果启用此特性,并且请求不受支持的文件扩展名,则将不会解密文件中的任何加密值。

嵌入配置服务器

配置服务器最好作为独立应用程序运行,但如果需要,可以将其嵌入到另一个应用程序中。只需使用@EnableConfigServer注释。在这种情况下可以使用的可选属性是spring.cloud.config.server.bootstrap,它是一个标志,表示服务器应该从自己的远程存储库配置自身。该标志默认关闭,因为它可能会延迟启动,但是当嵌入在另一个应用程序中时,以与其他应用程序相同的方式初始化是有意义的。当将spring.cloud.config.server.bootstrap设置为true时,还必须使用复合环境存储库配置。例如:

spring:
  application:
    name: configserver
  profiles:
    active: composite
  cloud:
    config:
      server:
        composite:
          - type: native
            search-locations: ${HOME}/Desktop/config
        bootstrap: true

如果使用bootstrap标志,配置服务器需要在bootstrap.yml中配置其名称和存储库URI。

要更改服务器端点的位置,您可以(可选)设置spring.cloud.config.server.prefix,例如“/ config”,以提供前缀下的资源。前缀应该开始但不以“/”结尾。它应用于Config Server中的@RequestMappings(即Spring Boot前缀server.servletPath和server.contextPath)之下。

如果您想直接从后端存储库(而不是从配置服务器)读取应用程序的配置,这基本上是一个没有端点的嵌入式配置服务器。如果不使用@EnableConfigServer注释(只设置spring.cloud.config.server.bootstrap=true),则可以完全关闭端点。

推送通知和Spring Cloud Bus

许多源代码存储库提供商(如Github, Gitlab, Gitea, Gitee, Gogs,或Bitbucket)通过webhook通知你存储库中的变化。您可以通过提供商的用户界面将webhook配置为一个URL和一组您感兴趣的事件。例如,Github对webhook使用POST, JSON体包含一个提交列表和头(X-Github-Event)设置为push。如果添加一个spring-cloud-config-monitor依赖项,并在配置服务器中激活Spring Cloud Bus,那么将启用/monitor端点。

webhook被激活时,配置服务器发送一个RefreshRemoteApplicationEvent,目标是它认为可能已经改变的应用程序。可以对变更检测进行策略化。然而,默认情况下,它查找与应用程序名称匹配的文件中的更改(例如,foo.properties针对foo应用程序,而application.properties针对所有应用程序)。当您希望覆盖该行为时,要使用的策略是PropertyPathNotificationExtractor,它接受请求头和请求体作为参数,并返回更改后的文件路径列表。

Github, Gitlab, Gitea, Gitee, Gogs或Bitbucket都是默认配置。除了来自Github、Gitlab、Gitee或Bitbucket的JSON通知,你还可以通过使用表单编码的体参数path={application}的模式post到/monitor来触发更改通知。这样做会广播到匹配{application}模式(可以包含通配符)的应用程序。

只有在配置服务器和客户端应用程序中激活spring cloud bus时,才会传输RefreshRemoteApplicationEvent

默认配置还检测本地git存储库中的文件系统更改。在这种情况下,webhook不会被使用。但是,一旦您编辑配置文件,就会广播刷新。

欢迎关注我的公众号:程序员L札记

更多原创文章,请扫码关注我的微信公众号
更多原创文章,请扫码关注我的微信公众号

lyq

2021/11/17  阅读:27  主题:橙心

作者介绍

lyq