Using vaultr in packages

2018-12-10

vaultr includes some machinery for using vault and vaultr within packages, and within tests in particular. They are designed to work well with testthat but should be easily adapted to work with any other testing framework.

In order to use this, you must set some environment variables:

• VAULTR_TEST_SERVER_BIN_PATH must be set to a directory where the vault binary can be found.
• VAULTR_TEST_SERVER_PORT can be set to the port where we start creating vault servers (by default this is 18200 but any high port number can be selected - we’ll create servers starting at this port number and incrementing - see below for details)
• NOT_CRAN must be set to "true" as this cannot be run on CRAN as it requires installation of a binary from a website.

For first time setup you need to install a test server. To do this set an additional environmental variable:

• VAULTR_TEST_SERVER_INSTALL to "true".

To install the test server, run:

vaultr::vault_test_server_install()

To create a vault server, run:

srv <- vaultr::vault_test_server()

As soon as srv goes out of scope and is garbage collected, the vault server will be stopped. So keep srv within the scope of your tests.

This object contains

• addr: which is vault’s address
• token: a root token for this vault
• keys: a vector of unseal keys

By default the vault server is stared in “Dev” server mode in which we run with http (not https), a single unseal key and in-memory storage. It is not suited for any production use.

You can create clients using vaultr::vault_client() and passing in appropriate parameters, but it may be more convenient to use srv$client(): vault <- srv$client()
vault
## <vault: base>
##   Command groups:
##     audit: Interact with vault's audit devices
##     auth: administer vault's authentication methods
##     kv1: Interact with vault's key/value store (version 1)
##     kv2: Interact with vault's key/value store (version 2)
##     lease: Interact with leases
##     operator: Administration commands for vault operators
##     policy: Interact with policies
##     secrets: Interact with secret engines
##     token: Interact and configure vault's token support
##     tools: General tools provided by vault
##   Commands:
##     api()
##     delete(path)
##     list(path, full_names = FALSE)
##     login(..., method = "token", mount = NULL, renew = FALSE,
##         quiet = FALSE, token_only = FALSE, use_cache = TRUE)
##     status()
##     write(path, data)
vault$list("secret") ## character(0) By default the client is logged in, but you can pass login = FALSE to create a client that needs to log in: vault <- srv$client(login = FALSE)
vault$list("secret") ## Error: Have not authenticated against vault vault$login(token = srv$token) ## Verifying token vault$list("secret")
## character(0)

You can use $export to export appropriate environment variables to connect to your vault: srv$export()
Sys.getenv("VAULT_ADDR")
## [1] "http://127.0.0.1:18202"
Sys.getenv("VAULT_TOKEN")
## [1] "f53074fe-4cee-63a6-7862-e60b7f1740c5"

Handling lack of vault gracefully

The vaultr::vault_test_server function takes an argument if_disabled which is a callback function that will be called on failure to start a vault server. This could be for reasons such as:

• the user has not opted in by setting VAULTR_TEST_SERVER_BIN_PATH
• the binary is not in place
• a port could not be opened

By default this calls testthat::skip, which interactively will appear to cause an error but if called within a test_that block in a test will gracefully skip a test

Sys.setenv("VAULTR_TEST_SERVER_BIN_PATH" = NA_character_)

r srv <- vaultr::vault_test_server() ## Error: vault is not enabled

srv <- vaultr::vault_test_server(if_disabled = message)
## vault is not enabled
srv
## NULL

With that approach, you might wrap vault-requiring tests with

if (!is.null(srv)) {
# ... vault requiring code here ...
}

All together (and assuming testthat), use of vault within tests might look like this example from the vaultr tests:

test_that("list", {
srv <- vault_test_server()
cl <- srv$client() cl$write("secret/a", list(key = 1))
cl$write("secret/b/c", list(key = 2)) cl$write("secret/b/d/e", list(key = 2))

expect_setequal(cl$list("secret"), c("a", "b/")) expect_setequal(cl$list("secret", TRUE), c("secret/a", "secret/b/"))
expect_setequal(cl$list("secret/b"), c("c", "d/")) expect_setequal(cl$list("secret/b", TRUE), c("secret/b/c", "secret/b/d/"))
})`

If you use one vault per test, as here, there’s no need to clean up - we can assume that the vault is empty at the start of the test block and not worry about cleanup at the end. If vault is not enabled this test will be skipped over gracefully.