1--- 2layout: "guides" 3page_title: "Java Application Demo - Guides" 4sidebar_title: "Java Application Demo" 5sidebar_current: "guides-encryption-spring-demo" 6description: |- 7 This guide discusses the concepts necessary to help users 8 understand Vault's AppRole authentication pattern and how to use it to 9 securely introduce a Vault authentication token to a target server, 10 application, container, etc. in a Java environment. 11--- 12 13# Java Sample App using Spring Cloud Vault 14 15Once you have learned the fundamentals of Vault, the next step is to start 16integrating your system with Vault to secure your organization's secrets. 17 18The purpose of this guide is to go through the working implementation demo 19introduced in the [Manage secrets, access, and encryption in the public cloud 20with 21Vault](https://www.hashicorp.com/resources/solutions-engineering-webinar-series-episode-2-vault) 22webinar. 23 24[![YouTube](/img/vault-java-demo-1.png)](https://youtu.be/NxL2-XuZ3kc) 25 26The Java application in this demo leverages the [_Spring Cloud 27Vault_](https://cloud.spring.io/spring-cloud-vault/) library which provides 28lightweight client-side support for connecting to Vault in a distributed 29environment. 30 31 32## Reference Material 33 34- [Encryption as a Service](/guides/encryption/transit.html) 35- [Manage secrets, access, and encryption in 36the public cloud with 37Vault](https://www.hashicorp.com/resources/solutions-engineering-webinar-series-episode-2-vault) 38- [Spring Cloud Vault](https://cloud.spring.io/spring-cloud-vault/) 39- [Transit Secrets Engine](/docs/secrets/transit/index.html) 40- [Secrets as a Service: Dynamic Secrets](/guides/secret-mgmt/dynamic-secrets.html) 41 42 43## Estimated Time to Complete 44 4515 minutes 46 47 48## Challenge 49 50Incidents of data breaches which expose sensitive information make headlines more 51often than we like to hear. It becomes more and more important to protect data 52by encrypting it whether the data is in-transit or at-rest. However, creating 53a highly secure and sophisticated solution by yourself requires time and resources 54which are in demand when an organization is facing a constant threat. 55 56 57## Solution 58 59Vault centralizes management of cryptographic services used to protect your 60data. Your system can communicate with Vault easily through the Vault API to 61encrypt and decrypt your data, and the encryption keys never have to leave the 62Vault. 63 64![Encryption as a Service](/img/vault-eaas.png) 65 66 67## Prerequisites 68 69To perform the tasks described in this guide: 70 71- Install [HashiCorp Vagrant](https://www.vagrantup.com/intro/getting-started/install.html) 72- Clone or download the demo assets from the [hashicorp/vault-guides](https://github.com/hashicorp/vault-guides/tree/master/secrets/spring-cloud-vault) 73GitHub repository 74 75 76## Steps 77 78-> For the purposes of this guide, you are going to provision a Linux machine 79locally using Vagrant. However, the GitHub repository provides supporting files 80to provision the environment demonstrated in the webinar. 81 82After downloading the [demo assets](#prerequisites) from the GitHub repository, 83you should find the following folders: 84 85| Folder | Description | 86|------------------|-----------------------------------------------------------| 87| `aws` | Supporting files to deploy the demo app to AWS | 88| `kubernetes` | Supporting files to deploy the demo app to Kubernetes | 89| `nomad` | Supporting files to deploy the demo app to Nomad | 90| `scripts` | Scripts to setup PostgreSQL and Vault | 91| `src/main` | Sample app source code | 92| `vagrant-local` | Vagrant file to deploy the demo locally | 93 94 95<br> 96 97In this guide, you will perform the following: 98 991. [Review the demo application implementation](#step1) 1001. [Deploy and review the demo environment](#step2) 1011. [Run the demo application](#step3) 1021. [Reload the Static Secrets](#step4) 103 104![Encryption as a Service](/img/vault-java-demo-10.png) 105 106### <a name="step1"></a>Step 1: Review the demo application implementation 107 108The source code can be found under the `src/main` directory. 109 110```plaintext 111├── java 112│ └── com 113│ └── hashicorp 114│ └── vault 115│ └── spring 116│ └── demo 117│ ├── BeanUtil.java 118│ ├── Order.java 119│ ├── OrderAPIController.java 120│ ├── OrderRepository.java 121│ ├── Secret.java 122│ ├── SecretController.java 123│ ├── TransitConverter.java 124│ └── VaultDemoOrderServiceApplication.java 125└── resources 126 └── application.yaml 127``` 128 129The demo Java application leverages the Spring Cloud Vault library to 130communicate with Vault. 131 132In the `TransitConverter` class, the `convertToDatabaseColumn` method invokes a 133Vault operation to encrypt the `order`. Similarly, the 134`convertToEntityAttribute` method decrypts the `order` data. 135 136```plaintext 137... 138 @Override 139 public String convertToDatabaseColumn(String customer) { 140 VaultOperations vaultOps = BeanUtil.getBean(VaultOperations.class); 141 Plaintext plaintext = Plaintext.of(customer); 142 String cipherText = vaultOps.opsForTransit().encrypt("order", plaintext).getCiphertext(); 143 return cipherText; 144 } 145 146 @Override 147 public String convertToEntityAttribute(String customer) { 148 VaultOperations vaultOps = BeanUtil.getBean(VaultOperations.class); 149 Ciphertext ciphertext = Ciphertext.of(customer); 150 String plaintext = vaultOps.opsForTransit().decrypt("order", ciphertext).asString(); 151 return plaintext; 152... 153``` 154 155The `VaultDemoOrderServiceApplication` class defines the `main` method. 156 157```plaintext 158public class VaultDemoOrderServiceApplication { 159 160 private static final Logger logger = LoggerFactory.getLogger(VaultDemoOrderServiceApplication.class); 161 162 @Autowired 163 private SessionManager sessionManager; 164 165 @Value("${spring.datasource.username}") 166 private String dbUser; 167 168 @Value("${spring.datasource.password}") 169 private String dbPass; 170 171 public static void main(String[] args) { 172 SpringApplication.run(VaultDemoOrderServiceApplication.class, args); 173 } 174 175 @PostConstruct 176 public void initIt() throws Exception { 177 logger.info("Got Vault Token: " + sessionManager.getSessionToken().getToken()); 178 logger.info("Got DB User: " + dbUser); 179 } 180} 181``` 182 183The `OrderAPIController` class defines the API endpoint (`api/orders`). 184 185 186### <a name="step2"></a>Step 2: Deploy and review the demo environment 187 188Now let's run the demo app and examine how it behaves. 189 190~> To keep it simple and lightweight, you are going to run a Linux virtual 191machine locally using Vagrant. 192 193#### Task 1: Run Vagrant 194 195In the **`vault-guides/secrets/spring-cloud-vault/vagrant-local`** folder, 196a `Vagrantfile` is provided which spins up a Linux machine where the demo 197components are installed and configured. 198 199```shell 200# Change the working directory to vagrant-local 201$ cd /vault-guides/secrets/spring-cloud-vault/vagrant-local 202 203# Create and configure a Linux machine. This takes about 3 minutes 204$ vagrant up 205... 206demo: Success! Data written to: database/roles/order 207demo: Success! Enabled the transit secrets engine at: transit/ 208demo: Success! Data written to: transit/keys/order 209demo: Success! Data written to: secret/spring-vault-demo 210 211# Verify that the virtual machine was successfully created and running 212$ vagrant status 213Current machine states: 214demo running (virtualbox) 215... 216 217# Connect to the demo machine 218$ vagrant ssh demo 219``` 220 221 222There are 3 Docker containers running on the machine: `spring`, `vault`, and `postgres`. 223 224```plaintext 225[vagrant@demo ~]$ docker ps 226CONTAINER ID IMAGE COMMAND CREATED STATUS NAMES 227684d8fb23ae5 spring "java -Djava.secur..." 7 minutes ago Up 7 minutes spring 228dc6a3454b323 vault:0.10.0 "docker-entrypoint..." 7 minutes ago Up 7 minutes vault 2294093a45c209f postgres "docker-entrypoint..." 7 minutes ago Up 7 minutes postgres 230``` 231 232#### Task 2: Examine the Vault environment 233 234During the demo machine provisioning, the `/scripts/vault.sh` script was 235executed to perform the following: 236 237- Created a policy named, **`order`** 238- Enabled the `transit` secret engine and created an encryption key named, **`order`** 239- Enabled the `database` secret engine and created a role named, **`order`** 240 241View the `vault` log: 242 243```plaintext 244[vagrant@demo ~]$ docker logs vault 245... 246==> Vault shutdown triggered 247==> Vault server configuration: 248 Api Address: http://0.0.0.0:8200 249 Cgo: disabled 250 Cluster Address: https://0.0.0.0:8201 251 Listener 1: tcp (addr: "0.0.0.0:8200", cluster address: "0.0.0.0:8201", tls: "disabled") 252 Log Level: info 253 Mlock: supported: true, enabled: false 254 Storage: inmem 255 Version: Vault v0.10.0 256 Version Sha: 5dd7f25f5c4b541f2da62d70075b6f82771a650d 257WARNING! dev mode is enabled! In this mode, Vault runs entirely in-memory 258and starts unsealed with a single unseal key. The root token is already 259authenticated to the CLI, so you can immediately begin using Vault. 260You may need to set the following environment variable: 261 $ export VAULT_ADDR='http://0.0.0.0:8200' 262The unseal key and root token are displayed below in case you want to 263seal/unseal the Vault or re-authenticate. 264Unseal Key: 2QIPWPDykRG/xWWl0quSHiXq8u+pFg3yEq0sgJPhMbA= 265Root Token: root 266... 267``` 268 269Notice that the log indicates that the Vault server is running in the `dev` 270mode, and the root token is `root`. 271 272You can visit the Vault UI at http://localhost:8200/ui. Enter **`root`** and 273click **Sign In**. 274 275 276Select the **`transit/`** secrets engine, and you should find an encryption key 277named, "`order`". 278 279![Vault UI](/img/vault-java-demo-2.png) 280 281Under the **Policies**, verify that the `order` policy exists. 282 283![Vault UI](/img/vault-java-demo-3.png) 284 285This `order` policy is for the application. It permits `read` on the 286`database/creds/order` path so that the demo app can get a dynamically generated 287database credential from Vault. Therefore, the PostgreSQL credentials are not 288hard-coded anywhere. 289 290```plaintext 291path "database/creds/order" 292{ 293 capabilities = ["read"] 294} 295``` 296 297An `update` permission allows the app to request data encryption and decryption 298using the `order` encryption key in Vault. 299 300```plaintext 301... 302path "transit/decrypt/order" { 303 capabilities = ["update"] 304} 305 306path "transit/encrypt/order" { 307 capabilities = ["update"] 308} 309... 310``` 311 312 313 314#### Task 3: Examine the Spring container 315 316Remember that the `VaultDemoOrderServiceApplication` class logs messages during 317the successful execution of `initIt()`: 318 319```plaintext 320... 321 @PostConstruct 322 public void initIt() throws Exception { 323 logger.info("Got Vault Token: " + sessionManager.getSessionToken().getToken()); 324 logger.info("Got DB User: " + dbUser); 325... 326``` 327 328Verify that the log indicates that the demo app obtained a database 329credentials from Vault successfully: 330 331```plaintext 332[vagrant@demo ~]$ docker logs spring | grep Got 333...VaultDemoOrderServiceApplication : Got Vault Token: root 334...VaultDemoOrderServiceApplication : Got DB User: v-token-order-rywqz61432yyx2x27w8r-1524067226 335``` 336 337Create a new shell session in the `spring` container. 338 339```plaintext 340[vagrant@demo ~]$ docker exec -it spring sh 341/ # 342``` 343 344Find the `bootstrap.yaml` file: 345 346```plaintext 347/ # ls -al 348total 36720 349drwxr-xr-x 1 root root 51 Apr 18 16:00 . 350drwxr-xr-x 1 root root 51 Apr 18 16:00 .. 351-rwxr-xr-x 1 root root 0 Apr 18 16:00 .dockerenv 352-rwxr--r-- 1 root root 37587245 Apr 18 15:59 app.jar 353drwxr-xr-x 2 root root 4096 Jan 9 19:37 bin 354-rw-r--r-- 1 1000 1000 426 Apr 17 17:58 bootstrap.yaml 355... 356 357/ # cat bootstrap.yaml 358spring.application.name: spring-vault-demo 359spring.cloud.vault: 360 authentication: TOKEN 361 token: ${VAULT_TOKEN} 362 host: localhost 363 port: 8200 364 scheme: http 365 fail-fast: true 366 config.lifecycle.enabled: true 367 generic: 368 enabled: true 369 backend: secret 370 database: 371 enabled: true 372 role: order 373 backend: database 374spring.datasource: 375 url: jdbc:postgresql://localhost:5432/postgres 376``` 377 378The client token was injected into the `spring` container as an environment 379variable (`VAULT_TOKEN`) by Vagrant. 380 381Enter `exit` to close the shell session in the `spring` container. 382 383 384#### Task 4: Examine the PostgreSQL database 385 386Connect to the PostgreSQL database running in the `postgres` container: 387 388```plaintext 389[vagrant@demo ~]$ docker exec -it postgres psql -U postgres -d postgres 390psql (10.3 (Debian 10.3-1.pgdg90+1)) 391Type "help" for help. 392 393 394postgres=# \d orders 395 Table "public.orders" 396 Column | Type | Collation | Nullable | Default 397---------------+-----------------------------+-----------+----------+------------------------------------ 398 id | bigint | | not null | nextval('orders_id_seq'::regclass) 399 customer_name | character varying(60) | | not null | 400 product_name | character varying(20) | | not null | 401 order_date | timestamp without time zone | | not null | 402Indexes: 403 "orders_pkey" PRIMARY KEY, btree (id) 404``` 405 406Let's list the existing database roles. 407 408```plaintext 409postgres-# \du 410 List of roles 411 Role name | Attributes | Member of 412-----------------------------------------------+------------------------------------------------------------+----------- 413 postgres | Superuser, Create role, Create DB, Replication, Bypass RLS | {} 414 v-token-order-rywqz61432yyx2x27w8r-1524067226 | Password valid until 2018-04-18 20:56:31+00 | {} 415``` 416 417Notice that there is a role name starting with `v-token-order-` which was 418dynamically created by the database secret engine. 419 420~> **NOTE:** To learn more 421about the database secret engine, read the [Secrets as a Service: Dynamic 422Secrets](/guides/secret-mgmt/dynamic-secrets.html) guide. 423 424Enter `\q` to exit out of the `psql` session, or you can open another terminal 425and SSH into the demo virtual machine. 426 427 428 429### <a name="step3"></a>Step 3: Run the demo application 430 431If everything looked fine in [Step 2](#step2), you are ready to write some data. 432 433![Vault UI](/img/vault-java-demo-9.png) 434 435You have [verified in the `spring` log](#task-3-examine-the-sprig-container) 436that the demo app successfully retrieved a database credential from the Vault 437server during its initialization. 438 439The next step is to send a new order request via the demo app's _orders_ API 440(http://localhost:8080/api/orders). 441 442```shell 443# Create a new order data 444[vagrant@demo ~]$ tee payload.json<<EOF 445{ 446 "customerName": "John", 447 "productName": "Nomad" 448} 449EOF 450 451# Send a request using cURL 452[vagrant@demo ~]$ curl --request POST --header "Content-Type: application/json" \ 453 --data @payload.json http://localhost:8080/api/orders | jq 454{ 455 "id": 2, 456 "customerName": "John", 457 "productName": "Nomad", 458 "orderDate": "2018-04-18T22:07:42.916+0000" 459} 460``` 461 462**NOTE:** Alternatively, you can use tool such as 463[Postman](https://www.getpostman.com/apps) instead of cURL to invoke the API if 464you prefer. 465 466![Postman](/img/vault-java-demo-4.png) 467 468 469The order data you sent gets encrypted by Vault. The database only sees the 470ciphertext. Let's verify that the order information stored in the database 471is encrypted. 472 473```plaintext 474[vagrant@demo ~]$ docker exec -it postgres psql -U postgres -d postgres 475 476postgres=# select * from orders; 477 id | customer_name | product_name | order_date 478----+-------------------------------------------------------+--------------+------------------------- 479 1 | vault:v1:Qj0lx5DSZvwcHeMOX/5UX/ErHTaDPA3mVlSSpaXd1tbM | VE | 2018-04-18 21:56:37.924 480 2 | vault:v1:UwL3HnyqTUac5ElS5WYAuNg3NdIMFtd6vvwukL+FaKun | Nomad | 2018-04-18 22:07:42.916 481(2 rows) 482 483postgres=# \q 484``` 485 486>In this demo, Vault encrypts the customer names; therefore, the values in the 487**`customer_name`** column do not display the names in a human readable 488manner (e.g. "James" and "John"). 489 490Now, retrieve the order data via the orders API: 491 492```plaintext 493[vagrant@demo ~]$ curl --header "Content-Type: application/json" \ 494 http://localhost:8080/api/orders | jq 495[ 496 { 497 "id": 1, 498 "customerName": "James", 499 "productName": "VE", 500 "orderDate": "2018-04-18T21:56:37.924+0000" 501 }, 502 { 503 "id": 2, 504 "customerName": "John", 505 "productName": "Nomad", 506 "orderDate": "2018-04-18T22:07:42.916+0000" 507 } 508] 509``` 510 511The customer names should be readable. Remember that the **`order`** policy 512permits the demo app to encrypt and decrypt data using the `order` encryption 513key in Vault. 514 515#### Web UI 516 517Vault UI makes it easy to decrypt the data. 518 519In the **Secrets** tab, select **`transit/` > `orders`**, and select **Key 520actions**. 521 522![Web UI](/img/vault-java-demo-5.png) 523 524Select **Decrypt** from the transit actions. Now, copy the ciphertext from the 525**`orders`** table and paste it in. 526 527![Web UI](/img/vault-java-demo-6.png) 528 529Click **Decrypt**. 530 531![Web UI](/img/vault-java-demo-7.png) 532 533Finally, click **Decode from base64** to reveal the customer name. 534 535![Web UI](/img/vault-java-demo-8.png) 536 537 538### <a name="step4"></a>Step 4: Reloading the Static Secrets 539 540Now, let's test another API endpoint, **`api/secret`** provided by the demo app. 541A plain old Java object, `Secret` defines a get method for `key` and `value`. 542The `SecretController.java` defines an API endpoint, **`api/secret`**. 543 544```plaintext 545package com.hashicorp.vault.spring.demo; 546... 547 548@RefreshScope 549@RestController 550public class SecretController { 551 552 @Value("${secret:n/a}") 553 String secret; 554 555 @RequestMapping("/api/secret") 556 public Secret secret() { 557 return new Secret("secret", secret); 558 } 559} 560``` 561 562Remember from [Step 2](#task-2-examine-the-vault-environment) that the 563**`order`** policy granted permissions on the `secret/spring-vault-demo` path. 564 565```plaintext 566path "secret/spring-vault-demo" { 567 capabilities = ["create", "read", "update", "delete", "list"] 568} 569... 570``` 571<br> 572 573The demo app retrieved the secret from `secret/spring-vault-demo` and has a 574local copy. If someone (or perhaps another app) updates the secret, it makes the 575secret held by the demo app to be obsolete. 576 577![Static Secret](/img/vault-java-demo-11.png) 578 579Spring offers [Spring Boot 580Actuator](https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#production-ready) 581which can be used to facilitate the reloading of the static secret. 582 583#### Task 1: Read the secret 584 585The initial key-value was set by Vagrant during the provisioning. (See the 586`Vagrantfile` at line 48.) 587 588Let's invoke the demo app's secret API (**`api/secret`**): 589 590```plaintext 591$ curl -s http://localhost:8080/api/secret | jq 592{ 593 "key": "secret", 594 "value": "hello-vault" 595} 596``` 597 598This is the secret that the demo app knows about. 599 600 601#### Task 2: Update the Secrets 602 603Now, update the secret stored in Vault using API: 604 605```shell 606# Update the value via API 607$ curl --header "X-Vault-Token: root" \ 608 --request POST \ 609 --data '{ "secret": "my-api-key" }' \ 610 http://127.0.0.1:8200/v1/secret/spring-vault-demo 611 612# Verify that the secret value was updated 613$ curl --header "X-Vault-Token: root" \ 614 http://127.0.0.1:8200/v1/secret/spring-vault-demo | jq 615{ 616 "request_id": "514601e4-a790-3dc6-14b0-537d6982a6c6", 617 "lease_id": "", 618 "renewable": false, 619 "lease_duration": 2764800, 620 "data": { 621 "secret": "my-api-key" 622 }, 623... 624} 625``` 626 627 628#### Task 3: Refresh the secret on demo app 629 630Run the demo app's secret API again: 631 632```plaintext 633$ curl -s http://localhost:8080/api/secret | jq 634{ 635 "key": "secret", 636 "value": "hello-vault" 637} 638``` 639 640The current value stored in Vault is now `my-api-key`; however, the demo app 641still holds `hello-vault`. 642 643 644Spring provides an 645[actuator](https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#production-ready) 646which can be leveraged to refresh the secret value. At line 54 of the 647`vault-guides/secrets/spring-cloud-vault/pom.xml`, you see that the actuator was 648added to the project. 649 650```plaintext 651... 652<dependency> 653 <groupId>org.springframework.boot</groupId> 654 <artifactId>spring-boot-starter-actuator</artifactId> 655</dependency> 656... 657``` 658 659Let's refresh the secret using the actuator: 660 661```plaintext 662$ curl -s --request POST http://localhost:8080/actuator/refresh | jq 663[ 664 "secret" 665] 666``` 667 668Read back the secret from the demo app again: 669 670```plaintext 671$ curl -s http://localhost:8080/api/secret | jq 672{ 673 "key": "secret", 674 "value": "my-api-key" 675} 676``` 677 678It should display the correct value. 679 680--- 681 682When you are done exploring the demo implementation, you can destroy the virtual 683machine: 684 685```plaintext 686$ vagrant destroy 687demo: Are you sure you want to destroy the 'demo' VM? [y/N] y 688==> demo: Forcing shutdown of VM... 689==> demo: Destroying VM and associated drives... 690``` 691 692~> In the webinar, the demo environment was running in a public cloud, and Nomad 693and Consul were also installed and configured. If you wish to build a similar 694environment using Kubernetes, the assets in the `vault-guides/secrets/spring-cloud-vault/kubernetes` 695folder provides you with some guidance. 696 697## Next steps 698 699[AppRole](/docs/auth/approle.html) is an authentication mechanism within Vault 700to allow machines or apps to acquire a token to interact with Vault. Read the 701[AppRole Pull Authentication](/guides/identity/authentication.html) guide 702which introduces the steps to generate tokens for machines or apps by enabling 703AppRole auth method. 704