跳转到主要内容

一旦了解了Vault的基本原理,下一步就是开始将系统与Vault集成,以保护组织的机密。

本教程是一个网络研讨会的配套,其中包括如何使用Vault在公共云中管理机密、访问和加密的现场演示。

https://youtu.be/NxL2-XuZ3kc

本演示中的Java应用程序利用了Spring Cloud Vault库,该库为在分布式环境中连接到Vault提供了轻量级客户端支持。

挑战

暴露敏感信息的数据泄露事件成为头条新闻的频率比我们喜欢听到的频率更高。无论数据是在传输中还是在静止中,通过加密来保护数据变得越来越重要。然而,自己创建一个高度安全和复杂的解决方案需要时间和资源,当组织面临持续的威胁时,这些都是需要的。

解决方案

Vault集中管理用于保护数据的加密服务。您的系统可以通过Vault API轻松地与Vault通信,以加密和解密您的数据,而且加密密钥永远不必离开Vault。

Encryption as a Service

先决条件

该实验室在macOS上使用基于x86_64的处理器进行了测试。如果您在基于Apple硅的处理器上运行macOS,请在首选云提供商中使用基于x86_64的Linux虚拟机。

要执行本教程中描述的任务,请执行以下操作:

  • 安装HashiCorp Vagrant

步骤1:查看演示应用程序的实现

通过克隆或从GitHub下载[hashicorp/vault guides]存储库来检索配置。

克隆存储库。

git clone https://github.com/hashicorp/vault-guides.git

或者下载存储库。

Download

此存储库包含所有Vault学习教程的支持内容。本教程的特定内容可以在子目录中找到。

源代码可以在src/main目录下找到。

cd vault-guides/secrets/spring-cloud-vault/src/main
tree

.
├── java
│   └── com
│       └── hashicorp
│           └── vault
│               └── spring
│                   └── demo
│                       ├── BeanUtil.java
│                       ├── Order.java
│                       ├── OrderAPIController.java
│                       ├── OrderRepository.java
│                       ├── Secret.java
│                       ├── SecretController.java
│                       ├── TransitConverter.java
│                       └── VaultDemoOrderServiceApplication.java
└── resources
    └── application.yaml

7 directories, 9 files

演示Java应用程序利用Spring Cloud Vault库与Vault通信。

在TransitConverter类中,convertToDatabaseColumn方法调用Vault操作来加密订单。类似地,convertToEntityAttribute方法解密订单数据。

@Override
public String convertToDatabaseColumn(String customer) {
  VaultOperations vaultOps = BeanUtil.getBean(VaultOperations.class);
  Plaintext plaintext = Plaintext.of(customer);
  String cipherText = vaultOps.opsForTransit().encrypt("order", plaintext).getCiphertext();
  return cipherText;
}

@Override
public String convertToEntityAttribute(String customer) {
  VaultOperations vaultOps = BeanUtil.getBean(VaultOperations.class);
  Ciphertext ciphertext = Ciphertext.of(customer);
  String plaintext = vaultOps.opsForTransit().decrypt("order", ciphertext).asString();
  return plaintext;
}

VaultDemoOrderServiceApplication类定义了主方法。

public class VaultDemoOrderServiceApplication  {

  private static final Logger logger = LoggerFactory.getLogger(VaultDemoOrderServiceApplication.class);

  @Autowired
  private SessionManager sessionManager;

  @Value("${spring.datasource.username}")
  private String dbUser;

  @Value("${spring.datasource.password}")
  private String dbPass;

  public static void main(String[] args) {
    SpringApplication.run(VaultDemoOrderServiceApplication.class, args);
  }

  @PostConstruct
  public void initIt() throws Exception {
    logger.info("Got Vault Token: " + sessionManager.getSessionToken().getToken());
    logger.info("Got DB User: " + dbUser);
  }
}

OrderAPIController类定义API端点(API/orders)。

步骤2:部署并查看演示环境

在本教程中,您将使用Vagrant在本地配置一台Linux机器。然而,GitHub存储库提供了支持文件来提供网络研讨会中演示的环境。

Encryption as a Service

现在让我们运行演示应用程序并检查它的行为。

为了保持简单和轻量级,您将使用Vagrant在本地运行Linux虚拟机。

在vault guides/secrets/spring cloud vault/vagrant本地文件夹中,提供了一个vagrant文件,用于启动安装和配置演示组件的Linux机器。

首先,将工作目录更改为流浪本地。

cd vault-guides/secrets/spring-cloud-vault/vagrant-local

创建并配置Linux机器。这大约需要3分钟。

 vagrant up
...
demo: Success! Data written to: database/roles/order
demo: Success! Enabled the transit secrets engine at: transit/
demo: Success! Data written to: transit/keys/order
demo: Success! Data written to: secret/spring-vault-demo

 Verify that the virtual machine was successfully created and running
 vagrant status
Current machine states:
demo                      running (virtualbox)
...

最后,连接到演示机器。

vagrant ssh demo

机器上有3个Docker容器:spring、vault和postgres。

 docker ps
CONTAINER ID        IMAGE               COMMAND                  ...    NAMES
cd629a609847        spring              "java -Djava.secur..."   ...    spring
623217149b68        vault:latest        "docker-entrypoint..."   ...    vault
32ff06ac02da        postgres            "docker-entrypoint..."   ...    postgres

任务2:检查Vault环境

在演示机器配置期间,/scripts/vault。执行sh脚本以执行以下操作:

  • 已创建名为order的策略
  • 启用了传输机密引擎并创建了名为order的加密密钥
  • 启用数据库机密引擎并创建名为order的角色

查看vault日志:

 docker logs vault

==> Vault server configuration:
             Api Address: http://0.0.0.0:8200
                     Cgo: disabled
         Cluster Address: https://0.0.0.0:8201
              Listener 1: tcp (addr: "0.0.0.0:8200", cluster address: "0.0.0.0:8201", max_request_duration: "1m30s", max_request_size: "33554432", tls: "disabled")
               Log Level: info
                   Mlock: supported: true, enabled: false
           Recovery Mode: false
                 Storage: inmem
                 Version: Vault v1.3.1
WARNING! dev mode is enabled! In this mode, Vault runs entirely in-memory
and starts unsealed with a single unseal key. The root token is already
authenticated to the CLI, so you can immediately begin using Vault.
You may need to set the following environment variable:
     export VAULT_ADDR='http://0.0.0.0:8200'
The unseal key and root token are displayed below in case you want to
seal/unseal the Vault or re-authenticate.
Unseal Key: Wgx++e73Y/htvXYzSZHAQ/3iE+Q0cuaBvTsM4ujn2xA=
Root Token: root

请注意,日志指示Vault服务器正在开发模式下运行,并且根令牌是root。

您可以访问Vault UI,网址为http://localhost:8200/ui.输入root并单击“登录”。

选择transit/secrets引擎,您应该会找到一个名为order的加密密钥。

Vault UI

在“策略”下,验证订单策略是否存在。

Vault UI

此订单政策适用于应用程序。它允许读取数据库/creds/order路径,以便演示应用程序可以从Vault获取动态生成的数据库凭据。因此,PostgreSQL凭据在任何地方都不是硬编码的。

path "database/creds/order"
{
  capabilities = ["read"]
}

更新权限允许应用程序使用Vault中的订单加密密钥请求数据加密和解密。

## ...snip...
path "transit/decrypt/order" {
  capabilities = ["update"]
}

path "transit/encrypt/order" {
  capabilities = ["update"]
}
## ...snip...

任务3:检查Spring容器

请记住,VaultDemoOrderServiceApplication类在成功执行initIt()期间记录消息:

@PostConstruct
    public void initIt() throws Exception {
        logger.info("Got Vault Token: " + sessionManager.getSessionToken().getToken());
        logger.info("Got DB User: " + dbUser);
    // ...
}

验证日志是否表明演示应用程序已成功从Vault获取数据库凭据:

 docker logs spring | grep Got

...VaultDemoOrderServiceApplication : Got Vault Token: root
...VaultDemoOrderServiceApplication : Got DB User: v-token-order-rywqz61432yyx2x27w8r-1524067226

在spring容器中创建一个新的shell会话。

 docker exec -it spring sh
/ #

查看引导。yaml文件:

/ #  cat bootstrap.yaml
spring.application.name: spring-vault-demo
spring.cloud.vault:
  authentication: TOKEN
  token: ${VAULT_TOKEN}
  host: localhost
  port: 8200
  scheme: http
  fail-fast: true
  config.lifecycle.enabled: true
  generic:
    enabled: true
    backend: secret
  database:
    enabled: true
    role: order
    backend: database
spring.datasource:
  url: jdbc:postgresql://localhost:5432/postgres

Vagrant将客户端令牌作为环境变量(VAULT_token)注入到spring容器中。

输入exit以关闭spring容器中的shell会话。

/ # exit

任务4:检查PostgreSQL数据库

连接到postgres容器中运行的PostgreSQL数据库:

 docker exec -it postgres psql -U postgres -d postgres

psql (10.3 (Debian 10.3-1.pgdg90+1))
Type "help" for help.

postgres= \d orders
                                          Table "public.orders"
    Column     |            Type             | Collation | Nullable |              Default
---------------+-----------------------------+-----------+----------+------------------------------------
 id            | bigint                      |           | not null | nextval('orders_id_seq'::regclass)
 customer_name | character varying(60)       |           | not null |
 product_name  | character varying(20)       |           | not null |
 order_date    | timestamp without time zone |           | not null |
Indexes:
    "orders_pkey" PRIMARY KEY, btree (id)

让我们列出现有的数据库角色。

postgres-# \du
                                                     List of roles
                   Role name                   |                         Attributes                         | Member of
-----------------------------------------------+------------------------------------------------------------+-----------
 postgres                                      | Superuser, Create role, Create DB, Replication, Bypass RLS | {}
 v-token-order-rywqz61432yyx2x27w8r-1524067226 | Password valid until 2018-04-18 20:56:31+00   

注意,有一个以v-token-order开头的角色名,它是由数据库机密引擎动态创建的。

注意:要了解有关数据库机密引擎的更多信息,请阅读机密作为服务:动态机密教程。

输入\q退出psql会话,或者您可以在演示虚拟机中打开另一个终端和SSH。

步骤3:运行演示应用程序

如果在第2步中一切都很好,那么就可以编写一些数据了。

Vault UI

您已在春季日志中验证了演示应用程序在初始化期间成功从Vault服务器检索到数据库凭据。

下一步是通过演示应用程序的订单API发送新的订单请求(http://localhost:8080/api/orders).

创建文件有效载荷。json,包含以下内容。

 tee payload.json <<EOF
{
  "customerName": "John",
  "productName": "Nomad"
}
EOF

使用cURL在请求中发送文件。

 curl --request POST --header "Content-Type: application/json" \
       --data @payload.json http://localhost:8080/api/orders | jq

 

您应该看到以下输出:

{
  "id": 1,
  "customerName": "John",
  "productName": "Nomad",
  "orderDate": "2018-04-18T22:07:42.916+0000"
}

注意:或者,您可以使用Postman等工具来调用API,而不是cURL。

Postman

您发送的订单数据将由Vault加密。数据库只能看到密文。让我们验证存储在数据库中的订单信息是否已加密。

 docker exec -it postgres psql -U postgres -d postgres

postgres= select * from orders;
 id |                     customer_name                     | product_name |       order_date
----+-------------------------------------------------------+--------------+-------------------------
  1 | vault:v1:UwL3HnyqTUac5ElS5WYAuNg3NdIMFtd6vvwukL+FaKun | Nomad        | 2018-04-18 22:07:42.916
(1 rows)

postgres= \q

在本演示中,Vault对客户名称进行加密;因此customer_name列中的值不会以人类可读的方式显示名称(“John”)。

现在,通过订单API检索订单数据:

 curl --header "Content-Type: application/json" \
       http://localhost:8080/api/orders | jq

输出应如下所示:

[
  {
    "id": 1,
    "customerName": "John",
    "productName": "Nomad",
    "orderDate": "2018-04-18T22:07:42.916+0000"
  }
]

客户名称应可读。请记住,订单策略允许演示应用程序使用Vault中的订单加密密钥加密和解密数据。

Web用户界面

Vault UI可以轻松解密数据。

  • 在“机密”选项卡中,选择“运输”>“订单”,然后选择“关键操作”。

Web UI

  • 从传输操作中选择解密。现在,从订单表中复制密文并粘贴进去。

Web UI

  • 单击解密。

Web UI

  • 单击从base64解码以显示客户名称。

Web UI

步骤4:重新加载静态秘密

现在,让我们测试另一个API端点,演示应用程序提供的API/secret。Secret是一个普通的旧Java对象,它为键和值定义了一个get方法。SecretController。java定义了API端点API/secret。

// SecretController.java
package com.hashicorp.vault.spring.demo;
// ...

@RefreshScope
@RestController
public class SecretController {

  @Value("${secret:n/a}")
  String secret;

  @RequestMapping("/api/secret")
  public Secret secret() {
    return new Secret("secret", secret);
  }
}

请记住,从步骤2开始,订单策略授予了secret/spring vault演示路径的权限。

path "secret/spring-vault-demo" {
  capabilities = ["create", "read", "update", "delete", "list"]
}

演示应用程序从secret/spring vault演示中检索到了秘密,并有一个本地副本。如果有人(或者可能是另一个应用程序)更新了这个秘密,那么演示应用程序所保存的秘密就会过时。

Static Secret

Spring提供了Spring Boot Actuator,可用于帮助重新加载静态秘密。

任务1:阅读秘密

初始密钥值由Vagrant在配置期间设置。(参见第48行的Vagrantfile。)

让我们调用演示应用程序的secret API(API/secret)。

curl -s http://localhost:8080/api/secret | jq

输出应如下所示:

{
  "key": "secret",
  "value": "hello-vault"
}

这是演示应用程序知道的秘密。

任务2:更新秘密

现在,使用API更新存储在Vault中的秘密。

 curl --header "X-Vault-Token: root" \
       --request POST \
       --data '{ "secret": "my-api-key" }' \
       http://127.0.0.1:8200/v1/secret/spring-vault-demo

验证机密值是否已更新。

curl --header "X-Vault-Token: root" \
       http://127.0.0.1:8200/v1/secret/spring-vault-demo | jq

输出将包括一个大数据结构,从以下内容开始:

{
  "request_id": "514601e4-a790-3dc6-14b0-537d6982a6c6",
  "lease_id": "",
  "renewable": false,
  "lease_duration": 2764800,
  "data": {
    "secret": "my-api-key"
  }
}

任务3:刷新演示应用程序上的秘密

再次运行演示应用程序的秘密API:

curl -s http://localhost:8080/api/secret | jq

您将看到以下输出:

{
  "key": "secret",
  "value": "hello-vault"
}

存储在Vault中的当前值现在是我的api密钥;然而,演示应用程序仍然支持hellovault。

Spring提供了一个执行器,可以利用它来刷新秘密值。在金库指南/秘密/春季云金库/pom的第54行。xml,您可以看到执行器已添加到项目中。

<!-- ... -->
<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<!-- ... -->

让我们使用执行器刷新秘密:

 curl -s --request POST http://localhost:8080/actuator/refresh | jq
[
  "secret"
]

再次从演示应用程序中读回秘密:

curl -s http://localhost:8080/api/secret | jq
{
  "key": "secret",
  "value": "my-api-key"
}

它应该显示正确的值。

完成演示实现的探索后,可以销毁虚拟机:

vagrant destroy

demo: Are you sure you want to destroy the 'demo' VM? [y/N] y
==> demo: Forcing shutdown of VM...
==> demo: Destroying VM and associated drives...

在网络研讨会中,演示环境在公共云中运行,Nomad和Consul也被安装和配置。如果您希望使用Kubernetes构建类似的环境,vault guides/secretes/spring cloud vault/kubenetes文件夹中的资产为您提供了一些指导。

帮助和参考

文章链接