Completely unauthenticated access isn't supported in vault. Tokens are fundamentally tied to how vault is exposed to end users, including operators.
There is no way around it, a token will be involved in the final result, and therefore, token expiration, revocation, and renewal. Using a periodic token is the simplest approach possible.
»Periodic Tokens
In some cases, having a token be revoked would be problematic [...] over a long period of time. In this scenario, a periodic token can be used. Periodic tokens can be created in a few ways:
- By [...] a root token with the
auth/token/create
endpoint
[...] as long as the token is successfully renewed within each of these periods of time, it will never expire. Outside of root tokens, it is currently the only way for a token in Vault to have an unlimited lifetime.
Given that a root token is going to be used for this, creating a token includes one last quality of life feature for "public" access, which is the ability to set the token id of a created token.
»Create Token
Creates a new token. Certain options are only available when called by a root token.
»Parameters
id
(string: ""
) – The ID of the client token. Can only be specified by a root token. The ID provided may not contain a .
character. Otherwise, the token ID is a randomly generated value.
When a token gets created, it needs a policy attached to it, otherwise it inherits the scope of the token which created it. For a root token, this is not desirable. Creating a policy and binding it to a token includes a special built-in policy to simplify token lifecycle tasks, the default token policy.
»Default Policy
The default policy is a built-in Vault policy that cannot be removed. By default, it is attached to all tokens, but may be explicitly excluded at token creation time by supporting authentication methods.
[...]
To view all permissions granted by the default policy on your Vault installation, run:
$ vault read sys/policy/default
To disable attachment of the default policy:
$ vault token create -no-default-policy
This default policy is going to be needed disabled, since it allows for owners of the token to access auth/token/revoke-self
, and destroy the token's ability to be used by everyone.
$ vault read sys/policy/default -format=table
Key Value
--- -----
name default
rules # Allow tokens to look up their own properties
path "auth/token/lookup-self" {
capabilities = ["read"]
}
# Allow tokens to renew themselves
path "auth/token/renew-self" {
capabilities = ["update"]
}
# Allow tokens to revoke themselves
path "auth/token/revoke-self" {
capabilities = ["update"]
}
Using these facts, setting up access to a vault endpoint for reading the public part of a jwt signing key boils down to using the root token to:
- create a vault token
- with a publicly shared
id
value, "jwk"
- as a periodic token, with no parent
- with the highest possible value for the period's TTL
- without the default policy attached to it
- bound to a custom policy
- which can read the public jwt signing key
- can lookup-self on its token
- can renew-self on its token
Here's a script to demonstrate how all this might fit together:
#!/bin/bash
mkdir -p /vault/config/
cat <<EOT > /vault/config/init.hcl
storage "inmem" {}
disable_mlock = "true"
listener "tcp" {
tls_disable = "true"
address = "0.0.0.0:8200"
}
api_addr = "http://127.0.0.1:8200"
EOT
nohup vault server --config /vault/config/init.hcl > /vault/nohup.log 2>&1 &
echo $! > /vault/nohup_pid.txt
sleep 1
VAULT_INIT=$(vault operator init -key-shares=1 -key-threshold=1 2>&1)
VAULT_ROOT=$(echo "$VAULT_INIT" 2>&1 | jq -r ".root_token")
VAULT_UNSEAL=$(echo "$VAULT_INIT" 2>&1 | jq -r ".unseal_keys_hex[0]")
UNSEAL_OPERATION=$(vault operator unseal "$VAULT_UNSEAL" 2>&1)
LOGIN_OPERATION=$(printf "$VAULT_ROOT" | vault login - 2>&1)
vault secrets enable transit 2>&1 >/dev/null
vault write "transit/keys/jwt-signing" type=rsa-4096 exportable=false 2>&1 >/dev/null
cat <<EOT > /vault/config/jwk.policy.hcl
path "transit/keys/jwt-signing" {
capabilities = ["read"]
}
path "/auth/token/lookup-self" {
capabilities = ["read"]
}
path "/auth/token/renew-self" {
capabilities = ["update"]
}
EOT
vault policy write "jwk" /vault/config/jwk.policy.hcl 2>&1 >/dev/null
vault write auth/token/create-orphan id="jwk" no_default_policy="true" policies="jwk" ttl="0" explicit_max_ttl="0" num_uses="0" period="2764800" 2>&1 >/dev/null
curl -s -H "X-Vault-Token: jwk" "http://localhost:8200/v1/transit/keys/jwt-signing" | jq -r '.data.keys["1"].public_key'
Which prints:
-----BEGIN PUBLIC KEY-----
MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAxoZ3HfPOTtcKGXpPSg15
l/ElL4NzNr4YjQrMlRCxw3LPfum1VIyAXupvFb6+dIsmyF9kEE8kXCePpioaEZwo
hlqrqTOrrJRzbsA5znnJPW/S8BuMm1o44x4EXQuc8cWTFc3aP2IgNnMSP31urIpy
/gFoHlYkNCydHSMzCB/oQ33IwpB6KCy7c8ChB2ZqTSlfiXHYkCA4QVLxY/Aoitqz
pAN988QwFDtXwzFxaayT/awS6pSgH75Twrs5K7JnC/97uEl2IQDYrB+DXugGvoHa
ZFi1KqdA2sCRW70ephox0+byLjAYsYI55eWdMtOdCcLfDoIHt/V5YaolzqJgNMSv
t6VfIL139xnnX2L66P/BJ2MOr/BtDlb7bUhVoGFojBoZd2UvNBSK+b1Jgydy+ZNy
5XIDtEnW+TzEHhgMyjLnBl4r54ncDsF++2qqRoyxjFQNBLxTNgxLj6joxfu3OvOP
MQOpBlYwHIYpsvImY5tV2XVioT8VTVeHmZfL52H7K+CkIoP5rebX6R+JdrIuvFht
Gn9GpMsJBJe+g0zCwM1tEfWct/vkIMNcOp4gwhUI129tLOeEep0sbAQCj5Ee+U9U
1M4yh4U1yvRIK2Y5jjZnr7JeW+jf2jHbpKmunFq3s5HMTjxOTmfM84cvdt5bqbzB
ly1rdyPowulT/l7qQtUJCi8CAwEAAQ==
-----END PUBLIC KEY-----
This is as close as I can get to "unauthenticated" access.
Don't forget to periodically renew this token at least once a month!
curl -s -X POST -H "X-Vault-Token: jwk" http://localhost:8200/v1/auth/token/renew-self