• United States+1
  • United Kingdom+44
  • Afghanistan (‫افغانستان‬‎)+93
  • Albania (Shqipëri)+355
  • Algeria (‫الجزائر‬‎)+213
  • American Samoa+1684
  • Andorra+376
  • Angola+244
  • Anguilla+1264
  • Antigua and Barbuda+1268
  • Argentina+54
  • Armenia (Հայաստան)+374
  • Aruba+297
  • Australia+61
  • Austria (Österreich)+43
  • Azerbaijan (Azərbaycan)+994
  • Bahamas+1242
  • Bahrain (‫البحرين‬‎)+973
  • Bangladesh (বাংলাদেশ)+880
  • Barbados+1246
  • Belarus (Беларусь)+375
  • Belgium (België)+32
  • Belize+501
  • Benin (Bénin)+229
  • Bermuda+1441
  • Bhutan (འབྲུག)+975
  • Bolivia+591
  • Bosnia and Herzegovina (Босна и Херцеговина)+387
  • Botswana+267
  • Brazil (Brasil)+55
  • British Indian Ocean Territory+246
  • British Virgin Islands+1284
  • Brunei+673
  • Bulgaria (България)+359
  • Burkina Faso+226
  • Burundi (Uburundi)+257
  • Cambodia (កម្ពុជា)+855
  • Cameroon (Cameroun)+237
  • Canada+1
  • Cape Verde (Kabu Verdi)+238
  • Caribbean Netherlands+599
  • Cayman Islands+1345
  • Central African Republic (République centrafricaine)+236
  • Chad (Tchad)+235
  • Chile+56
  • China (中国)+86
  • Christmas Island+61
  • Cocos (Keeling) Islands+61
  • Colombia+57
  • Comoros (‫جزر القمر‬‎)+269
  • Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)+243
  • Congo (Republic) (Congo-Brazzaville)+242
  • Cook Islands+682
  • Costa Rica+506
  • Côte d’Ivoire+225
  • Croatia (Hrvatska)+385
  • Cuba+53
  • Curaçao+599
  • Cyprus (Κύπρος)+357
  • Czech Republic (Česká republika)+420
  • Denmark (Danmark)+45
  • Djibouti+253
  • Dominica+1767
  • Dominican Republic (República Dominicana)+1
  • Ecuador+593
  • Egypt (‫مصر‬‎)+20
  • El Salvador+503
  • Equatorial Guinea (Guinea Ecuatorial)+240
  • Eritrea+291
  • Estonia (Eesti)+372
  • Ethiopia+251
  • Falkland Islands (Islas Malvinas)+500
  • Faroe Islands (Føroyar)+298
  • Fiji+679
  • Finland (Suomi)+358
  • France+33
  • French Guiana (Guyane française)+594
  • French Polynesia (Polynésie française)+689
  • Gabon+241
  • Gambia+220
  • Georgia (საქართველო)+995
  • Germany (Deutschland)+49
  • Ghana (Gaana)+233
  • Gibraltar+350
  • Greece (Ελλάδα)+30
  • Greenland (Kalaallit Nunaat)+299
  • Grenada+1473
  • Guadeloupe+590
  • Guam+1671
  • Guatemala+502
  • Guernsey+44
  • Guinea (Guinée)+224
  • Guinea-Bissau (Guiné Bissau)+245
  • Guyana+592
  • Haiti+509
  • Honduras+504
  • Hong Kong (香港)+852
  • Hungary (Magyarország)+36
  • Iceland (Ísland)+354
  • India (भारत)+91
  • Indonesia+62
  • Iran (‫ایران‬‎)+98
  • Iraq (‫العراق‬‎)+964
  • Ireland+353
  • Isle of Man+44
  • Israel (‫ישראל‬‎)+972
  • Italy (Italia)+39
  • Jamaica+1876
  • Japan (日本)+81
  • Jersey+44
  • Jordan (‫الأردن‬‎)+962
  • Kazakhstan (Казахстан)+7
  • Kenya+254
  • Kiribati+686
  • Kosovo+383
  • Kuwait (‫الكويت‬‎)+965
  • Kyrgyzstan (Кыргызстан)+996
  • Laos (ລາວ)+856
  • Latvia (Latvija)+371
  • Lebanon (‫لبنان‬‎)+961
  • Lesotho+266
  • Liberia+231
  • Libya (‫ليبيا‬‎)+218
  • Liechtenstein+423
  • Lithuania (Lietuva)+370
  • Luxembourg+352
  • Macau (澳門)+853
  • Macedonia (FYROM) (Македонија)+389
  • Madagascar (Madagasikara)+261
  • Malawi+265
  • Malaysia+60
  • Maldives+960
  • Mali+223
  • Malta+356
  • Marshall Islands+692
  • Martinique+596
  • Mauritania (‫موريتانيا‬‎)+222
  • Mauritius (Moris)+230
  • Mayotte+262
  • Mexico (México)+52
  • Micronesia+691
  • Moldova (Republica Moldova)+373
  • Monaco+377
  • Mongolia (Монгол)+976
  • Montenegro (Crna Gora)+382
  • Montserrat+1664
  • Morocco (‫المغرب‬‎)+212
  • Mozambique (Moçambique)+258
  • Myanmar (Burma) (မြန်မာ)+95
  • Namibia (Namibië)+264
  • Nauru+674
  • Nepal (नेपाल)+977
  • Netherlands (Nederland)+31
  • New Caledonia (Nouvelle-Calédonie)+687
  • New Zealand+64
  • Nicaragua+505
  • Niger (Nijar)+227
  • Nigeria+234
  • Niue+683
  • Norfolk Island+672
  • North Korea (조선 민주주의 인민 공화국)+850
  • Northern Mariana Islands+1670
  • Norway (Norge)+47
  • Oman (‫عُمان‬‎)+968
  • Pakistan (‫پاکستان‬‎)+92
  • Palau+680
  • Palestine (‫فلسطين‬‎)+970
  • Panama (Panamá)+507
  • Papua New Guinea+675
  • Paraguay+595
  • Peru (Perú)+51
  • Philippines+63
  • Poland (Polska)+48
  • Portugal+351
  • Puerto Rico+1
  • Qatar (‫قطر‬‎)+974
  • Réunion (La Réunion)+262
  • Romania (România)+40
  • Russia (Россия)+7
  • Rwanda+250
  • Saint Barthélemy (Saint-Barthélemy)+590
  • Saint Helena+290
  • Saint Kitts and Nevis+1869
  • Saint Lucia+1758
  • Saint Martin (Saint-Martin (partie française))+590
  • Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)+508
  • Saint Vincent and the Grenadines+1784
  • Samoa+685
  • San Marino+378
  • São Tomé and Príncipe (São Tomé e Príncipe)+239
  • Saudi Arabia (‫المملكة العربية السعودية‬‎)+966
  • Senegal (Sénégal)+221
  • Serbia (Србија)+381
  • Seychelles+248
  • Sierra Leone+232
  • Singapore+65
  • Sint Maarten+1721
  • Slovakia (Slovensko)+421
  • Slovenia (Slovenija)+386
  • Solomon Islands+677
  • Somalia (Soomaaliya)+252
  • South Africa+27
  • South Korea (대한민국)+82
  • South Sudan (‫جنوب السودان‬‎)+211
  • Spain (España)+34
  • Sri Lanka (ශ්‍රී ලංකාව)+94
  • Sudan (‫السودان‬‎)+249
  • Suriname+597
  • Svalbard and Jan Mayen+47
  • Swaziland+268
  • Sweden (Sverige)+46
  • Switzerland (Schweiz)+41
  • Syria (‫سوريا‬‎)+963
  • Taiwan (台灣)+886
  • Tajikistan+992
  • Tanzania+255
  • Thailand (ไทย)+66
  • Timor-Leste+670
  • Togo+228
  • Tokelau+690
  • Tonga+676
  • Trinidad and Tobago+1868
  • Tunisia (‫تونس‬‎)+216
  • Turkey (Türkiye)+90
  • Turkmenistan+993
  • Turks and Caicos Islands+1649
  • Tuvalu+688
  • U.S. Virgin Islands+1340
  • Uganda+256
  • Ukraine (Україна)+380
  • United Arab Emirates (‫الإمارات العربية المتحدة‬‎)+971
  • United Kingdom+44
  • United States+1
  • Uruguay+598
  • Uzbekistan (Oʻzbekiston)+998
  • Vanuatu+678
  • Vatican City (Città del Vaticano)+39
  • Venezuela+58
  • Vietnam (Việt Nam)+84
  • Wallis and Futuna+681
  • Western Sahara (‫الصحراء الغربية‬‎)+212
  • Yemen (‫اليمن‬‎)+967
  • Zambia+260
  • Zimbabwe+263
  • Åland Islands+358
Thanks! We'll be in touch in the next 12 hours
Oops! Something went wrong while submitting the form.

How to Setup HashiCorp Vault HA Cluster with Integrated Storage (Raft)

As businesses move their data to the public cloud, one of the most pressing issues is how to keep it safe from illegal access.

Using a tool like HashiCorp Vault gives you greater control over your sensitive credentials and fulfills cloud security regulations.

In this blog, we'll walk you through HashiCorp Vault High Availability Setup.

Hashicorp Vault

Hashicorp Vault is an open-source tool that provides a secure, reliable way to store and distribute sensitive information like API keys, access tokens, passwords, etc. Vault provides high-level policy management, secret leasing, audit logging, and automatic revocation to protect this information using UI, CLI, or HTTP API.

High Availability

Vault can run in a High Availability mode to protect against outages by running multiple Vault servers. When running in HA mode, Vault servers have two additional states, i.e., active and standby. Within a Vault cluster, only a single instance will be active, handling all requests, and all standby instances redirect requests to the active instance.

Integrated Storage Raft

The Integrated Storage backend is used to maintain Vault's data. Unlike other storage backends, Integrated Storage does not operate from a single source of data. Instead, all the nodes in a Vault cluster will have a replicated copy of Vault's data. Data gets replicated across all the nodes via the Raft Consensus Algorithm.

Raft is officially supported by Hashicorp.

Architecture

Prerequisites

This setup requires Vault, Sudo access on the machines, and the below configuration to create the cluster.

  • Install Vault v1.6.3+ent or later on all nodes in the Vault cluster 

In this example, we have 3 CentOs VMs provisioned using VMware. 

Setup

1. Verify the Vault version on all the nodes using the below command (in this case, we have 3 nodes node1, node2, node3):

vault --version
view raw .sh hosted with ❤ by GitHub

2. Configure SSL certificates

Note: Vault should always be used with TLS in production to provide secure communication between clients and the Vault server. It requires a certificate file and key file on each Vault host.

We can generate SSL certs for the Vault Cluster on the Master and copy them on the other nodes in the cluster.

Refer to: https://developer.hashicorp.com/vault/tutorials/secrets-management/pki-engine#scenario-introduction for generating SSL certs.

  • Copy tls.crt tls.key tls_ca.pem to /etc/vault.d/ssl/ 
  • Change ownership to `vault`

[user@node1 ~]$ cd /etc/vault.d/ssl/           
[user@node1 ssl]$ sudo chown vault. tls*
view raw .sh hosted with ❤ by GitHub

  • Copy tls* from /etc/vault.d/ssl to of the nodes

3. Configure the enterprise license. Copy license on all nodes:

cp /root/vault.hclic /etc/vault.d/vault.hclic
chown root:vault /etc/vault.d/vault.hclic
chmod 0640 /etc/vault.d/vault.hclic
view raw .sh hosted with ❤ by GitHub

4. Create the storage directory for raft storage on all nodes:

sudo mkdir --parents /opt/raft
sudo chown --recursive vault:vault /opt/raft
view raw .sh hosted with ❤ by GitHub

5. Set firewall rules on all nodes:

sudo firewall-cmd --permanent --add-port=8200/tcp
sudo firewall-cmd --permanent --add-port=8201/tcp
sudo firewall-cmd --reload
view raw .sh hosted with ❤ by GitHub

6. Create vault configuration file on all nodes:

### Node 1 ###
[user@node1 vault.d]$ cat vault.hcl
storage "raft" {
path = "/opt/raft"
node_id = "node1"
retry_join
{
leader_api_addr = "https://node2.int.us-west-1-dev.central.example.com:8200"
leader_ca_cert_file = "/etc/vault.d/ssl/tls_ca.pem"
leader_client_cert_file = "/etc/vault.d/ssl/tls.crt"
leader_client_key_file = "/etc/vault.d/ssl/tls.key"
}
retry_join
{
leader_api_addr = "https://node3.int.us-west-1-dev.central.example.com:8200"
leader_ca_cert_file = "/etc/vault.d/ssl/tls_ca.pem"
leader_client_cert_file = "/etc/vault.d/ssl/tls.crt"
leader_client_key_file = "/etc/vault.d/ssl/tls.key"
}
}
listener "tcp" {
address = "0.0.0.0:8200"
tls_disable = false
tls_cert_file = "/etc/vault.d/ssl/tls.crt"
tls_key_file = "/etc/vault.d/ssl/tls.key"
tls_client_ca_file = "/etc/vault.d/ssl/tls_ca.pem"
tls_cipher_suites = "TLS_TEST_128_GCM_SHA256,
TLS_TEST_128_GCM_SHA256,
TLS_TEST20_POLY1305,
TLS_TEST_256_GCM_SHA384,
TLS_TEST20_POLY1305,
TLS_TEST_256_GCM_SHA384"
}
api_addr = "https://node1.int.us-west-1-dev.central.example.com:8200"
cluster_addr = "https://node1.int.us-west-1-dev.central.example.com:8201"
disable_mlock = true
ui = true
log_level = "trace"
disable_cache = true
cluster_name = "POC"
# Enterprise license_path
# This will be required for enterprise as of v1.8
license_path = "/etc/vault.d/vault.hclic"

### Node 2 ###
[user@node2 vault.d]$ cat vault.hcl
storage "raft" {
path = "/opt/raft"
node_id = "node2"
retry_join
{
leader_api_addr = "https://node1.int.us-west-1-dev.central.example.com:8200"
leader_ca_cert_file = "/etc/vault.d/ssl/tls_ca.pem"
leader_client_cert_file = "/etc/vault.d/ssl/tls.crt"
leader_client_key_file = "/etc/vault.d/ssl/tls.key"
}
retry_join
{
leader_api_addr = "https://node3.int.us-west-1-dev.central.example.com:8200"
leader_ca_cert_file = "/etc/vault.d/ssl/tls_ca.pem"
leader_client_cert_file = "/etc/vault.d/ssl/tls.crt"
leader_client_key_file = "/etc/vault.d/ssl/tls.key"
}
}
listener "tcp" {
address = "0.0.0.0:8200"
tls_disable = false
tls_cert_file = "/etc/vault.d/ssl/tls.crt"
tls_key_file = "/etc/vault.d/ssl/tls.key"
tls_client_ca_file = "/etc/vault.d/ssl/tls_ca.pem"
tls_cipher_suites = "TLS_TEST_128_GCM_SHA256,
TLS_TEST_128_GCM_SHA256,
TLS_TEST20_POLY1305,
TLS_TEST_256_GCM_SHA384,
TLS_TEST20_POLY1305,
TLS_TEST_256_GCM_SHA384"
}
api_addr = "https://node2.int.us-west-1-dev.central.example.com:8200"
cluster_addr = "https://node2.int.us-west-1-dev.central.example.com:8201"
disable_mlock = true
ui = true
log_level = "trace"
disable_cache = true
cluster_name = "POC"
# Enterprise license_path
# This will be required for enterprise as of v1.8
license_path = "/etc/vault.d/vault.hclic"

### Node 3 ###
[user@node3 ~]$ cat /etc/vault.d/vault.hcl
storage "raft" {
path = "/opt/raft"
node_id = "node3"
retry_join
{
leader_api_addr = "https://node1.int.us-west-1-dev.central.example.com:8200"
leader_ca_cert_file = "/etc/vault.d/ssl/tls_ca.pem"
leader_client_cert_file = "/etc/vault.d/ssl/tls.crt"
leader_client_key_file = "/etc/vault.d/ssl/tls.key"
}
retry_join
{
leader_api_addr = "https://node2.int.us-west-1-dev.central.example.com:8200"
leader_ca_cert_file = "/etc/vault.d/ssl/tls_ca.pem"
leader_client_cert_file = "/etc/vault.d/ssl/tls.crt"
leader_client_key_file = "/etc/vault.d/ssl/tls.key"
}
}
listener "tcp" {
address = "0.0.0.0:8200"
tls_disable = false
tls_cert_file = "/etc/vault.d/ssl/tls.crt"
tls_key_file = "/etc/vault.d/ssl/tls.key"
tls_client_ca_file = "/etc/vault.d/ssl/tls_ca.pem"
tls_cipher_suites = "TLS_TEST_128_GCM_SHA256,
TLS_TEST_128_GCM_SHA256,
TLS_TEST20_POLY1305,
TLS_TEST_256_GCM_SHA384,
TLS_TEST20_POLY1305,
TLS_TEST_256_GCM_SHA384"
}
api_addr = "https://node3.int.us-west-1-dev.central.example.com:8200"
cluster_addr = "https://node3.int.us-west-1-dev.central.example.com:8201"
disable_mlock = true
ui = true
log_level = "trace"
disable_cache = true
cluster_name = "POC"
# Enterprise license_path
# This will be required for enterprise as of v1.8
license_path = "/etc/vault.d/vault.hclic"

7. Set environment variables on all nodes:

export VAULT_ADDR=https://$(hostname):8200
export VAULT_CACERT=/etc/vault.d/ssl/tls_ca.pem
export CA_CERT=`cat /etc/vault.d/ssl/tls_ca.pem`
view raw .sh hosted with ❤ by GitHub

8. Start Vault as a service on all nodes:

You can view the systemd unit file if interested by: 

cat /etc/systemd/system/vault.service
systemctl enable vault.service
systemctl start vault.service
systemctl status vault.service
view raw .sh hosted with ❤ by GitHub

9. Check Vault status on all nodes:

vault status
view raw .sh hosted with ❤ by GitHub

10. Initialize Vault with the following command on vault node 1 only. Store unseal keys securely.

[user@node1 vault.d]$ vault operator init -key-shares=1 -key-threshold=1
Unseal Key 1: HPY/g5OiT8ivD6L4Bqfjx9L1We2MVb4WZAqKZk6zFf8=
Initial Root Token: hvs.j4qTq1IZP9nscILMtN2p9GE0
Vault initialized with 1 key shares and a key threshold of 1.
Please securely distribute the key shares printed above.
When the Vault is re-sealed, restarted, or stopped, you must supply at least 1 of these keys to unseal it
before it can start servicing requests.
Vault does not store the generated root key.
Without at least 1 keys to reconstruct the root key, Vault will remain permanently sealed!
It is possible to generate new unseal keys, provided you have a
quorum of existing unseal keys shares. See "vault operator rekey" for more information.
view raw unseal node1 hosted with ❤ by GitHub

11. Set Vault token environment variable for the vault CLI command to authenticate to the server. Use the following command, replacing <initial-root- token> with the value generated in the previous step.

export VAULT_TOKEN=<initial-root-token>
echo "export VAULT_TOKEN=$VAULT_TOKEN" >> /root/.bash_profile
### Repeat this step for the other 2 servers.
view raw .sh hosted with ❤ by GitHub

12. Unseal Vault1 using the unseal key generated in step 10. Notice the Unseal Progress key-value change as you present each key. After meeting the key threshold, the status of the key value for Sealed should change from true to false.

[user@node1 vault.d]$ vault operator unseal HPY/g5OiT8ivD6L4Bqfjx9L1We2MVb4WZAqKZk6zFf8=
Key Value
--- -----
Seal Type shamir
Initialized true
Sealed false
Total Shares 1
Threshold 1
Version 1.11.0
Build Date 2022-06-17T15:48:44Z
Storage Type raft
Cluster Name POC
Cluster ID 109658fe-36bd-7d28-bf92-f095c77e860c
HA Enabled true
HA Cluster https://node1.int.us-west-1-dev.central.example.com:8201
HA Mode active
Active Since 2022-06-29T12:50:46.992698336Z
Raft Committed Index 36
Raft Applied Index 36

13. Unseal Vault2 (Use the same unseal key generated in step 10 for Vault1):

[user@node2 vault.d]$ vault operator unseal HPY/g5OiT8ivD6L4Bqfjx9L1We2MVb4WZAqKZk6zFf8=
Key Value
--- -----
Seal Type shamir
Initialized true
Sealed true
Total Shares 1
Threshold 1
Unseal Progress 0/1
Unseal Nonce n/a
Version 1.11.0
Build Date 2022-06-17T15:48:44Z
Storage Type raft
HA Enabled true
[user@node2 vault.d]$ vault status
Key Value
--- -----
Seal Type shamir
Initialized true
Sealed true
Total Shares 1
Threshold 1
Version 1.11.0
Build Date 2022-06-17T15:48:44Z
Storage Type raft
Cluster Name POC
Cluster ID 109658fe-36bd-7d28-bf92-f095c77e860c
HA Enabled true
HA Cluster https://node1.int.us-west-1-dev.central.example.com:8201
HA Mode standby
Active Node Address https://node1.int.us-west-1-dev.central.example.com:8200
Raft Committed Index 37
Raft Applied Index 37

14. Unseal Vault3 (Use the same unseal key generated in step 10 for Vault1):

[user@node3 ~]$ vault operator unseal HPY/g5OiT8ivD6L4Bqfjx9L1We2MVb4WZAqKZk6zFf8=
Key Value
--- -----
Seal Type shamir
Initialized true
Sealed true
Total Shares 1
Threshold 1
Unseal Progress 0/1
Unseal Nonce n/a
Version 1.11.0
Build Date 2022-06-17T15:48:44Z
Storage Type raft
HA Enabled true
[user@node3 ~]$ vault status
Key Value
--- -----
Seal Type shamir
Initialized true
Sealed false
Total Shares 1
Threshold 1
Version 1.11.0
Build Date 2022-06-17T15:48:44Z
Storage Type raft
Cluster Name POC
Cluster ID 109658fe-36bd-7d28-bf92-f095c77e860c
HA Enabled true
HA Cluster https://node1.int.us-west-1-dev.central.example.com:8201
HA Mode standby
Active Node Address https://node1.int.us-west-1-dev.central.example.com:8200
Raft Committed Index 39
Raft Applied Index 39

15. Check the cluster's raft status with the following command:

[user@node3 ~]$ vault operator raft list-peers
Node Address State Voter
---- ------- ----- -----
node1 node1.int.us-west-1-dev.central.example.com:8201 leader true
node2 node2.int.us-west-1-dev.central.example.com:8201 follower true
node3 node3.int.us-west-1-dev.central.example.com:8201 follower true
view raw raft list-peers hosted with ❤ by GitHub

16. Currently, node1 is the active node. We can experiment to see what happens if node1 steps down from its active node duty.

In the terminal where VAULT_ADDR is set to: https://node1.int.us-west-1-dev.central.example.com, execute the step-down command.

$ vault operator step-down # equivalent of stopping the node or stopping the systemctl service
Success! Stepped down: https://node2.int.us-west-1-dev.central.example.com:8200
view raw .sh hosted with ❤ by GitHub

In the terminal, where VAULT_ADDR is set to https://node2.int.us-west-1-dev.central.example.com:8200, examine the raft peer set.

[user@node1 ~]$ vault operator raft list-peers
Node Address State Voter
---- ------- ----- -----
node1 node1.int.us-west-1-dev.central.example.com:8201 follower true
node2 node2.int.us-west-1-dev.central.example.com:8201 leader true
node3 node3.int.us-west-1-dev.central.example.com:8201 follower true

Conclusion 

Vault servers are now operational in High Availability mode, and we can test this by writing a secret from either the active or standby Vault instance and see it succeed as a test of request forwarding. Also, we can shut down the active vault instance (sudo systemctl stop vault) to simulate a system failure and see the standby instance assumes the leadership.

Get the latest engineering blogs delivered straight to your inbox.
No spam. Only expert insights.
Thank you! Your submission has been received!
Oops! Something went wrong while submitting the form.
Thank you! Your submission has been received!
Oops! Something went wrong while submitting the form.

Did you like the blog? If yes, we're sure you'll also like to work with the people who write them - our best-in-class engineering team.

We're looking for talented developers who are passionate about new emerging technologies. If that's you, get in touch with us.

Explore current openings

How to Setup HashiCorp Vault HA Cluster with Integrated Storage (Raft)

As businesses move their data to the public cloud, one of the most pressing issues is how to keep it safe from illegal access.

Using a tool like HashiCorp Vault gives you greater control over your sensitive credentials and fulfills cloud security regulations.

In this blog, we'll walk you through HashiCorp Vault High Availability Setup.

Hashicorp Vault

Hashicorp Vault is an open-source tool that provides a secure, reliable way to store and distribute sensitive information like API keys, access tokens, passwords, etc. Vault provides high-level policy management, secret leasing, audit logging, and automatic revocation to protect this information using UI, CLI, or HTTP API.

High Availability

Vault can run in a High Availability mode to protect against outages by running multiple Vault servers. When running in HA mode, Vault servers have two additional states, i.e., active and standby. Within a Vault cluster, only a single instance will be active, handling all requests, and all standby instances redirect requests to the active instance.

Integrated Storage Raft

The Integrated Storage backend is used to maintain Vault's data. Unlike other storage backends, Integrated Storage does not operate from a single source of data. Instead, all the nodes in a Vault cluster will have a replicated copy of Vault's data. Data gets replicated across all the nodes via the Raft Consensus Algorithm.

Raft is officially supported by Hashicorp.

Architecture

Prerequisites

This setup requires Vault, Sudo access on the machines, and the below configuration to create the cluster.

  • Install Vault v1.6.3+ent or later on all nodes in the Vault cluster 

In this example, we have 3 CentOs VMs provisioned using VMware. 

Setup

1. Verify the Vault version on all the nodes using the below command (in this case, we have 3 nodes node1, node2, node3):

vault --version
view raw .sh hosted with ❤ by GitHub

2. Configure SSL certificates

Note: Vault should always be used with TLS in production to provide secure communication between clients and the Vault server. It requires a certificate file and key file on each Vault host.

We can generate SSL certs for the Vault Cluster on the Master and copy them on the other nodes in the cluster.

Refer to: https://developer.hashicorp.com/vault/tutorials/secrets-management/pki-engine#scenario-introduction for generating SSL certs.

  • Copy tls.crt tls.key tls_ca.pem to /etc/vault.d/ssl/ 
  • Change ownership to `vault`

[user@node1 ~]$ cd /etc/vault.d/ssl/           
[user@node1 ssl]$ sudo chown vault. tls*
view raw .sh hosted with ❤ by GitHub

  • Copy tls* from /etc/vault.d/ssl to of the nodes

3. Configure the enterprise license. Copy license on all nodes:

cp /root/vault.hclic /etc/vault.d/vault.hclic
chown root:vault /etc/vault.d/vault.hclic
chmod 0640 /etc/vault.d/vault.hclic
view raw .sh hosted with ❤ by GitHub

4. Create the storage directory for raft storage on all nodes:

sudo mkdir --parents /opt/raft
sudo chown --recursive vault:vault /opt/raft
view raw .sh hosted with ❤ by GitHub

5. Set firewall rules on all nodes:

sudo firewall-cmd --permanent --add-port=8200/tcp
sudo firewall-cmd --permanent --add-port=8201/tcp
sudo firewall-cmd --reload
view raw .sh hosted with ❤ by GitHub

6. Create vault configuration file on all nodes:

### Node 1 ###
[user@node1 vault.d]$ cat vault.hcl
storage "raft" {
path = "/opt/raft"
node_id = "node1"
retry_join
{
leader_api_addr = "https://node2.int.us-west-1-dev.central.example.com:8200"
leader_ca_cert_file = "/etc/vault.d/ssl/tls_ca.pem"
leader_client_cert_file = "/etc/vault.d/ssl/tls.crt"
leader_client_key_file = "/etc/vault.d/ssl/tls.key"
}
retry_join
{
leader_api_addr = "https://node3.int.us-west-1-dev.central.example.com:8200"
leader_ca_cert_file = "/etc/vault.d/ssl/tls_ca.pem"
leader_client_cert_file = "/etc/vault.d/ssl/tls.crt"
leader_client_key_file = "/etc/vault.d/ssl/tls.key"
}
}
listener "tcp" {
address = "0.0.0.0:8200"
tls_disable = false
tls_cert_file = "/etc/vault.d/ssl/tls.crt"
tls_key_file = "/etc/vault.d/ssl/tls.key"
tls_client_ca_file = "/etc/vault.d/ssl/tls_ca.pem"
tls_cipher_suites = "TLS_TEST_128_GCM_SHA256,
TLS_TEST_128_GCM_SHA256,
TLS_TEST20_POLY1305,
TLS_TEST_256_GCM_SHA384,
TLS_TEST20_POLY1305,
TLS_TEST_256_GCM_SHA384"
}
api_addr = "https://node1.int.us-west-1-dev.central.example.com:8200"
cluster_addr = "https://node1.int.us-west-1-dev.central.example.com:8201"
disable_mlock = true
ui = true
log_level = "trace"
disable_cache = true
cluster_name = "POC"
# Enterprise license_path
# This will be required for enterprise as of v1.8
license_path = "/etc/vault.d/vault.hclic"

### Node 2 ###
[user@node2 vault.d]$ cat vault.hcl
storage "raft" {
path = "/opt/raft"
node_id = "node2"
retry_join
{
leader_api_addr = "https://node1.int.us-west-1-dev.central.example.com:8200"
leader_ca_cert_file = "/etc/vault.d/ssl/tls_ca.pem"
leader_client_cert_file = "/etc/vault.d/ssl/tls.crt"
leader_client_key_file = "/etc/vault.d/ssl/tls.key"
}
retry_join
{
leader_api_addr = "https://node3.int.us-west-1-dev.central.example.com:8200"
leader_ca_cert_file = "/etc/vault.d/ssl/tls_ca.pem"
leader_client_cert_file = "/etc/vault.d/ssl/tls.crt"
leader_client_key_file = "/etc/vault.d/ssl/tls.key"
}
}
listener "tcp" {
address = "0.0.0.0:8200"
tls_disable = false
tls_cert_file = "/etc/vault.d/ssl/tls.crt"
tls_key_file = "/etc/vault.d/ssl/tls.key"
tls_client_ca_file = "/etc/vault.d/ssl/tls_ca.pem"
tls_cipher_suites = "TLS_TEST_128_GCM_SHA256,
TLS_TEST_128_GCM_SHA256,
TLS_TEST20_POLY1305,
TLS_TEST_256_GCM_SHA384,
TLS_TEST20_POLY1305,
TLS_TEST_256_GCM_SHA384"
}
api_addr = "https://node2.int.us-west-1-dev.central.example.com:8200"
cluster_addr = "https://node2.int.us-west-1-dev.central.example.com:8201"
disable_mlock = true
ui = true
log_level = "trace"
disable_cache = true
cluster_name = "POC"
# Enterprise license_path
# This will be required for enterprise as of v1.8
license_path = "/etc/vault.d/vault.hclic"

### Node 3 ###
[user@node3 ~]$ cat /etc/vault.d/vault.hcl
storage "raft" {
path = "/opt/raft"
node_id = "node3"
retry_join
{
leader_api_addr = "https://node1.int.us-west-1-dev.central.example.com:8200"
leader_ca_cert_file = "/etc/vault.d/ssl/tls_ca.pem"
leader_client_cert_file = "/etc/vault.d/ssl/tls.crt"
leader_client_key_file = "/etc/vault.d/ssl/tls.key"
}
retry_join
{
leader_api_addr = "https://node2.int.us-west-1-dev.central.example.com:8200"
leader_ca_cert_file = "/etc/vault.d/ssl/tls_ca.pem"
leader_client_cert_file = "/etc/vault.d/ssl/tls.crt"
leader_client_key_file = "/etc/vault.d/ssl/tls.key"
}
}
listener "tcp" {
address = "0.0.0.0:8200"
tls_disable = false
tls_cert_file = "/etc/vault.d/ssl/tls.crt"
tls_key_file = "/etc/vault.d/ssl/tls.key"
tls_client_ca_file = "/etc/vault.d/ssl/tls_ca.pem"
tls_cipher_suites = "TLS_TEST_128_GCM_SHA256,
TLS_TEST_128_GCM_SHA256,
TLS_TEST20_POLY1305,
TLS_TEST_256_GCM_SHA384,
TLS_TEST20_POLY1305,
TLS_TEST_256_GCM_SHA384"
}
api_addr = "https://node3.int.us-west-1-dev.central.example.com:8200"
cluster_addr = "https://node3.int.us-west-1-dev.central.example.com:8201"
disable_mlock = true
ui = true
log_level = "trace"
disable_cache = true
cluster_name = "POC"
# Enterprise license_path
# This will be required for enterprise as of v1.8
license_path = "/etc/vault.d/vault.hclic"

7. Set environment variables on all nodes:

export VAULT_ADDR=https://$(hostname):8200
export VAULT_CACERT=/etc/vault.d/ssl/tls_ca.pem
export CA_CERT=`cat /etc/vault.d/ssl/tls_ca.pem`
view raw .sh hosted with ❤ by GitHub

8. Start Vault as a service on all nodes:

You can view the systemd unit file if interested by: 

cat /etc/systemd/system/vault.service
systemctl enable vault.service
systemctl start vault.service
systemctl status vault.service
view raw .sh hosted with ❤ by GitHub

9. Check Vault status on all nodes:

vault status
view raw .sh hosted with ❤ by GitHub

10. Initialize Vault with the following command on vault node 1 only. Store unseal keys securely.

[user@node1 vault.d]$ vault operator init -key-shares=1 -key-threshold=1
Unseal Key 1: HPY/g5OiT8ivD6L4Bqfjx9L1We2MVb4WZAqKZk6zFf8=
Initial Root Token: hvs.j4qTq1IZP9nscILMtN2p9GE0
Vault initialized with 1 key shares and a key threshold of 1.
Please securely distribute the key shares printed above.
When the Vault is re-sealed, restarted, or stopped, you must supply at least 1 of these keys to unseal it
before it can start servicing requests.
Vault does not store the generated root key.
Without at least 1 keys to reconstruct the root key, Vault will remain permanently sealed!
It is possible to generate new unseal keys, provided you have a
quorum of existing unseal keys shares. See "vault operator rekey" for more information.
view raw unseal node1 hosted with ❤ by GitHub

11. Set Vault token environment variable for the vault CLI command to authenticate to the server. Use the following command, replacing <initial-root- token> with the value generated in the previous step.

export VAULT_TOKEN=<initial-root-token>
echo "export VAULT_TOKEN=$VAULT_TOKEN" >> /root/.bash_profile
### Repeat this step for the other 2 servers.
view raw .sh hosted with ❤ by GitHub

12. Unseal Vault1 using the unseal key generated in step 10. Notice the Unseal Progress key-value change as you present each key. After meeting the key threshold, the status of the key value for Sealed should change from true to false.

[user@node1 vault.d]$ vault operator unseal HPY/g5OiT8ivD6L4Bqfjx9L1We2MVb4WZAqKZk6zFf8=
Key Value
--- -----
Seal Type shamir
Initialized true
Sealed false
Total Shares 1
Threshold 1
Version 1.11.0
Build Date 2022-06-17T15:48:44Z
Storage Type raft
Cluster Name POC
Cluster ID 109658fe-36bd-7d28-bf92-f095c77e860c
HA Enabled true
HA Cluster https://node1.int.us-west-1-dev.central.example.com:8201
HA Mode active
Active Since 2022-06-29T12:50:46.992698336Z
Raft Committed Index 36
Raft Applied Index 36

13. Unseal Vault2 (Use the same unseal key generated in step 10 for Vault1):

[user@node2 vault.d]$ vault operator unseal HPY/g5OiT8ivD6L4Bqfjx9L1We2MVb4WZAqKZk6zFf8=
Key Value
--- -----
Seal Type shamir
Initialized true
Sealed true
Total Shares 1
Threshold 1
Unseal Progress 0/1
Unseal Nonce n/a
Version 1.11.0
Build Date 2022-06-17T15:48:44Z
Storage Type raft
HA Enabled true
[user@node2 vault.d]$ vault status
Key Value
--- -----
Seal Type shamir
Initialized true
Sealed true
Total Shares 1
Threshold 1
Version 1.11.0
Build Date 2022-06-17T15:48:44Z
Storage Type raft
Cluster Name POC
Cluster ID 109658fe-36bd-7d28-bf92-f095c77e860c
HA Enabled true
HA Cluster https://node1.int.us-west-1-dev.central.example.com:8201
HA Mode standby
Active Node Address https://node1.int.us-west-1-dev.central.example.com:8200
Raft Committed Index 37
Raft Applied Index 37

14. Unseal Vault3 (Use the same unseal key generated in step 10 for Vault1):

[user@node3 ~]$ vault operator unseal HPY/g5OiT8ivD6L4Bqfjx9L1We2MVb4WZAqKZk6zFf8=
Key Value
--- -----
Seal Type shamir
Initialized true
Sealed true
Total Shares 1
Threshold 1
Unseal Progress 0/1
Unseal Nonce n/a
Version 1.11.0
Build Date 2022-06-17T15:48:44Z
Storage Type raft
HA Enabled true
[user@node3 ~]$ vault status
Key Value
--- -----
Seal Type shamir
Initialized true
Sealed false
Total Shares 1
Threshold 1
Version 1.11.0
Build Date 2022-06-17T15:48:44Z
Storage Type raft
Cluster Name POC
Cluster ID 109658fe-36bd-7d28-bf92-f095c77e860c
HA Enabled true
HA Cluster https://node1.int.us-west-1-dev.central.example.com:8201
HA Mode standby
Active Node Address https://node1.int.us-west-1-dev.central.example.com:8200
Raft Committed Index 39
Raft Applied Index 39

15. Check the cluster's raft status with the following command:

[user@node3 ~]$ vault operator raft list-peers
Node Address State Voter
---- ------- ----- -----
node1 node1.int.us-west-1-dev.central.example.com:8201 leader true
node2 node2.int.us-west-1-dev.central.example.com:8201 follower true
node3 node3.int.us-west-1-dev.central.example.com:8201 follower true
view raw raft list-peers hosted with ❤ by GitHub

16. Currently, node1 is the active node. We can experiment to see what happens if node1 steps down from its active node duty.

In the terminal where VAULT_ADDR is set to: https://node1.int.us-west-1-dev.central.example.com, execute the step-down command.

$ vault operator step-down # equivalent of stopping the node or stopping the systemctl service
Success! Stepped down: https://node2.int.us-west-1-dev.central.example.com:8200
view raw .sh hosted with ❤ by GitHub

In the terminal, where VAULT_ADDR is set to https://node2.int.us-west-1-dev.central.example.com:8200, examine the raft peer set.

[user@node1 ~]$ vault operator raft list-peers
Node Address State Voter
---- ------- ----- -----
node1 node1.int.us-west-1-dev.central.example.com:8201 follower true
node2 node2.int.us-west-1-dev.central.example.com:8201 leader true
node3 node3.int.us-west-1-dev.central.example.com:8201 follower true

Conclusion 

Vault servers are now operational in High Availability mode, and we can test this by writing a secret from either the active or standby Vault instance and see it succeed as a test of request forwarding. Also, we can shut down the active vault instance (sudo systemctl stop vault) to simulate a system failure and see the standby instance assumes the leadership.

Did you like the blog? If yes, we're sure you'll also like to work with the people who write them - our best-in-class engineering team.

We're looking for talented developers who are passionate about new emerging technologies. If that's you, get in touch with us.

Explore current openings