Spring Cloud Kubernetes环境下使用Jasypt
迪丽瓦拉
2024-05-26 14:01:59
0

前言

最近半年着手开始做了基于微服务的中台项目,整个项目的技术栈采用的是Java + Spring Cloud + Kubernetes + Istio。

业务开放上还是相当顺利的。但是在安全审核上,运维组提出了一个简易。现在项目一些敏感配置,例如MySQL用户的密码,Redis的密码等现在都是明文保存在Kubernetes的ConfigMap中的(是的,我们并没有Nacos作为微服务的配置中心)。这样可能存在安全隐患。

首次尝试

既然有问题,那就解决问题。要给配置文件中的属性项目加密很简单,稍微Google一下,就有现成的方案了。

现在比较常用的解决方案就是集成Jasypt,然后通过jasypt-spring-boot-starter来融合进Spring。

POM包加入jasypt-spring-boot-starter

com.github.ulisesbocchiojasypt-spring-boot-starter3.0.4

Dockerfile中增加java参数

...
ENTRYPOINT ["sh","-c","java $JAVA_OPTS -jar app.jar --jasypt.encryptor.password=helloworld $PARAMS"]

在ConfigMap中添加加密属性

apiVersion: v1
kind: ConfigMap
metadata:name: demo
data:application.yaml: |-test2: ENC(94Y7Ds3+RKraxQQlura9sDx+9yF0zDLMGMwi2TjyCFZOkkHfreRFSb6fxbyvCKs7)

利用actuator接口测试

在management.endpoints.web.exposure.include属性中增加env,这样我们就可以通过调用/actuator/env来查看一下env接口返回的整个Spring 容器中所有的PropertySource。

{..."propertySources": [{"name": "bootstrapProperties-configmap.demo.default","properties": {"test2": {"value": "Hello,world"}}}...]
}

OK, 这下配置项已经加密了。问题解决了。

但是...

新的问题

自从项目集成了Jayspt以后,出现了一个奇怪的问题。每次项目试图通过修改ConfigMap的配置文件,然后试图通过spring-cloud-starter-kubernetes-fabric8-config来做自动Reload,都失败了。然而查阅应用日志,并没有出现任何异常。无奈只能打开spring-cloud和jasypt-spring-boot的DEBUG日志。

进过几天对日志和两边源代码的分析。终于找到了原因

原因

在Spring Boot启动时jasypt-spring-boot会将下面6种配置(并不仅限与这6种配置文件)

  • Classpath下的application.yaml

  • Classpath下的bootstrap.yaml

  • 集群里名称为${spring.cloud.kubernetes.config.name} 的ConfigMap

  • 集群里名称为${spring.cloud.kubernetes.config.name}-kubernetes的ConfigMap

  • Java启动参数

  • 环境变量

转换成jasypt-spring-boot自己的PropertySource实现类EncryptableMapPropertySourceWrapper。

但是如果使用Kubernetes的ConfigMap来作微服务配置中心的时候,Spring Cloud会在ConfigurationChangeDetector中查找配置类org.springframework.cloud.bootstrap.config.BootstrapPropertySource, 并依据BootstrapPropertySource的类型来判断容器内的配置与集群中ConfigMap里的配置是否有差异,来触发配置reload。

由于jasypt-spring-boot已经将所有的配置文件转型成了EncryptableMapPropertySourceWrapper, 所以ConfigurationChangeDetector无法找到BootstrapPropertySource所以会一直任务ConfigMap的里的配置没有变化,导致整个Reload失效(无论是使用polling还是event方式)

解决问题

为了保证ConfigMap变化后自动Reload的功能,所以jasypt-spring-boot不能把BootstrapPropertySource转换成EncryptableMapPropertySourceWrapper

所以我们需要设置jasypt.encryptor.skip-property-sources配置项, Classpath中的application.yaml需要增加配置

jasypt:encryptor:skip-property-sources: org.springframework.cloud.bootstrap.config.BootstrapPropertySource

skip-property-sources配置项配置后,加密项目就不能配置在ConfigMap里了,毕竟已经被我们忽略了。那么我们只能另外找一个PropertySource来存放加密项目了。

Classpath中的两个Yaml由于编译时会被Maven打包进Jar文件,会牵涉多个CI/CD多个流程显然不合适,启动参数配置项的也要影响到Docker镜像制作这个流程。所以判断下来最适合的PropertySource就是环境变量了。

环境变量增加加密项

在Kubernetes的部署Yaml中,添加加密数据项application.test.str

apiVersion: apps/v1
kind: Deployment
metadata:labels:app: demoname: demo
spec:replicas: 1selector:matchLabels:app: demotemplate:metadata:labels:app: demospec:containers:- env:- name: TZvalue: Asia/Shanghai- name: application.test.strvalue: >-ENC(94Y7Ds3+RKraxQQlura9sDx+9yF0zDLMGMwi2TjyCFZOkkHfreRFSb6fxbyvCKs7)....

如果需要更加严密的加密方针的话,我们可以把环境变量的内容放进Kubernetes的Secrets中。

在ConfigMap中引用application.test.str

apiVersion: v1
kind: ConfigMap
metadata:name: demo
data:application.yaml: |-test2: ENC(94Y7Ds3+RKraxQQlura9sDx+9yF0zDLMGMwi2TjyCFZOkkHfreRFSb6fxbyvCKs7)test3: ${application.test.str}

通过actuator接口来测试

通过actuator\env接口来测试一下

{..."propertySources": [{"name": "bootstrapProperties-configmap.demo.default","properties": {"test2": {"value": "ENC(94Y7Ds3+RKraxQQlura9sDx+9yF0zDLMGMwi2TjyCFZOkkHfreRFSb6fxbyvCKs7)"},"test3": {"value": "Hello,world"}}}...]
}

这样ConfigMap中的配置项test3就可以通过环境变量引用并使用加密配置项了。同时修改ConfigMap依然可以触发auto reload了。这下终于算是解决了。

相关内容