HashiCorp `Vault`_ can be used as a secure key management service for
`Server-Side Encryption`_ (SSE-KMS).
-#. `Configure Vault`_
-#. `Configure the Ceph Object Gateway`_
-#. `Create a key in Vault`_
+#. `Vault Access`_
+#. `Secrets Engines`_
+#. `Configure Ceph Object Gateway`_
#. `Upload object`_
-Configure Vault
+Vault Access
+============
+
+Access to Vault can be done through several authentication mechanisms.
+Object Gateway supports `token authentication method`_ and `vault agent`_.
+
+Token Authentication
+--------------------
+The Token authentication method expects a Vault token to be present in a plaintext file. You can configure
+the token option in Ceph's configuration file as follows::
+
+ rgw crypt vault auth = token
+ rgw crypt vault token file = /etc/ceph/vault.token
+
+For security reasons, ensure the file is readable by the Object Gateway only.
+
+.. note:: Token authentication is not recommended configuration for production environments.
+
+Vault Agent
+-----------
+Vault Agent is a client daemon that provides authentication to Vault, manages the token renewal and caching.
+With a Vault agent, it is possible to use other Vault authentication mechanism such as AppRole, AWS, Certs, JWT, Azure...
+
+You can enable agent authentication in Ceph's configuration file with::
+
+ rgw crypt vault auth = agent
+ rgw crypt vault addr = http://localhost:8200
+
+.. note:: ``rgw_crypt_vault_token_file`` is ignored if ``rgw_crypt_vault_auth`` is set to ``agent``.
+
+Secrets Engines
===============
+Vault provides several Secret Engines, which can store, generate, and encrypt data. Currently, the Object Gateway supports:
+
+- `KV Secrets engine`_ version 2
+- `Transit engine`_
+
+.. important:: Examples in this section are for demonstration purposes only and assume token authentication.
-Vault provides several Secret Engines, which can store, generate, and encrypt
-data. Currently, the Object Gateway supports `KV Secrets engine`_ version 2
-an `KV Transit engine`_.
-Basic Vault Configuration
--------------------------
+KV Secrets engine
+-----------------
To enable the KV engine version 2 in Vault, use the Vault command line
tool::
vault secrets enable kv-v2
-Analogously for the Transit Engine::
- vault secrets enable transit
-
-Vault also provides several authentication mechanisms.
-To simplify user's interaction with Vault, the Object Gateway supports
-two modes: `token authentication method`_ and `agent authentication method`_.
-
When authenticating using the token method, a token must be obtained
for the Gateway and saved in a file as plain-text.
identity_policies []
policies ["default" "rgw-policy"]
-The actual token, displayed in the output of the above command, must be saved
-in a file as plain-text. The path to this file must then be provided in the
-Gateway configuration file (see section below). For security reasons, ensure
-the file is readable by the Object Gateway only.
-
-
-Configure the Ceph Object Gateway
-=================================
-
-Edit the Ceph configuration file to enable Vault as a KMS for server-side
-encryption. The following example uses the ``token`` authentication method (with
-a Vault token stored in a file), sets the Vault server address, and restricts
-the URLs where encryption keys can be retrieved from Vault using a path prefix::
- rgw crypt s3 kms backend = vault
- rgw crypt vault auth = token
- rgw crypt vault addr = http://vaultserver:8200
- rgw crypt vault prefix = /v1/secret/data
- rgw crypt vault token file = /etc/ceph/vault.token
-
-In this example, the Gateway will only fetch encryption keys under
-``http://vaultserver:8200/v1/secret/data``.
-
-Create a key in Vault
-=====================
+Create a key using KV Engine
+----------------------------
.. note:: Server-side encryption keys must be 256-bit long and base64 encoded.
export VAULT_ADDR='http://vaultserver:8200'
vault kv put secret/myproject/mybucketkey key=$(openssl rand -base64 32)
+.. important:: In the KV secrets engine, secrets are stored as key-value pairs, and the Gateway expects the key name to be key, i.e. the secret must be in the form key=<value>.
+
Output::
====== Metadata ======
=== Data ===
Key Value
--- -----
- key Ak5dRyLQjwX/wb7vo6Fq1qjsfk1dh2CiSicX+gLAhwk=
+ key Ak5dRyLQjwX/wb7vo6Fq1qjsfk1dh2CiSicX-gLAhwk=
+
+
+Transit Secrets Engine
+----------------------
+To enable the Transit engine in Vault, use the Vault command line tool::
+
+ vault secrets enable transit
+
+Create a key using Transit secrets engine
+-----------------------------------------
+Object Gateway supports Transit engine exportable keys only.
+To create an exportable key, use the command line tool::
+
+ export VAULT_ADDR='http://vaultserver:8200'
+ vault write -f transit/keys/mybucketkey exportable=true
+
+The command line above creates a keyring, which contains an aes256-gcm96 key type.
+To verify the key is created properly, use the command line tool::
+
+ vault read transit/export/encryption-key/mybucketkey/1
+
+Output::
+
+ Key Value
+ --- -----
+ keys map[1:-gbTI9lNpqv/V/2lDcmH2Nq1xKn6FPDWarCmFM2aNsQ=]
+ name mybucketkey
+ type aes256-gcm96
+
+.. note:: To use Transit Secrets engine in Object Gateway, you shall specify the full key, including its version.
+
+For security reasons, the Object Gateway should be given a Vault token with a
+restricted policy that allows it to fetch the dedicated keyrings only. Such a policy can be
+created in Vault using the Vault command line utility::
+
+ vault policy write rgw-transit-policy -<<EOF
+ path "transit/export/encryption-key/mybucketkey/*" {
+ capabilities = ["read"]
+ }
+ EOF
+
+Once the policy is created, a token can be generated by Vault administrator::
+
+ vault token create -policy=rgw-transit-policy
+
+Sample output::
+
+ Key Value
+ --- -----
+ token s.62KuPujbc1234dWB71poOmIZ
+ token_accessor jv95ZYBUFv6Ss84x7SCSy6lZ
+ token_duration 768h
+ token_renewable true
+ token_policies ["default" "rgw-transit-policy"]
+ identity_policies []
+ policies ["default" "rgw-transit-policy"]
+
+
+Configure Ceph Object Gateway
+=================================
+
+Edit the Ceph configuration file to enable Vault as a KMS for server-side
+encryption.::
+
+ rgw crypt s3 kms backend = vault
+ rgw crypt vault auth = { token | agent }
+ rgw crypt vault addr = { vault address e.g. http://localhost:8200 }
+ rgw crypt vault prefix = { prefix to secret engine e.g. /v1/transit/export/encryption-key }
+ rgw crypt vault token file = { absolute path to token file e.g. /etc/ceph/vault.token }
+
+The following example uses the ``token`` authentication method (with
+a Vault token stored in a file), sets the Vault server address, and restricts
+the URLs where encryption keys can be retrieved from Vault using a path prefix.
+In this example, the Gateway will only fetch transit encryption keys::
+
+ rgw crypt s3 kms backend = vault
+ rgw crypt vault auth = token
+ rgw crypt vault addr = https://vaultserver
+ rgw crypt vault secret engine = transit
+ rgw crypt vault prefix = /v1/transit/export/encryption-key
+ rgw crypt vault token file = /etc/ceph/vault.token
+
Upload object
=============
.. _Server-Side Encryption: ../encryption
.. _Vault: https://www.vaultproject.io/docs/
.. _token authentication method: https://www.vaultproject.io/docs/auth/token.html
+.. _vault agent: https://www.vaultproject.io/docs/agent/index.html
.. _KV Secrets engine: https://www.vaultproject.io/docs/secrets/kv/
+.. _Transit engine: https://www.vaultproject.io/docs/secrets/transit
private:
int get_key_version(boost::string_view key_id, string& version)
{
- vector<boost::string_view> tokens;
size_t pos = 0;
- pos = key_id.rfind("/", key_id.length());
+ pos = key_id.rfind("/");
if (pos != std::string::npos){
- boost::string_view token = key_id.substr(pos, key_id.length());
- if (tokens.size() >= 1 && token.find_first_not_of("0123456789") == std::string::npos){
+ boost::string_view token = key_id.substr(pos+1, key_id.length()-pos);
+ if (!token.empty() && token.find_first_not_of("0123456789") == std::string::npos){
version.assign(std::string(token));
return 0;
}
int get_key(boost::string_view key_id, std::string& actual_key)
{
- JSONParser* parser = new JSONParser();
+ JSONParser parser;
string version;
if (get_key_version(key_id, version) < 0){
return -EINVAL;
}
- int res = send_request(key_id, parser);
+ int res = send_request(key_id, &parser);
if (res < 0) {
return res;
}
- JSONObj* json_obj = &(*parser);
+ JSONObj* json_obj = &parser;
std::array<std::string, 3> elements = {"data", "keys", version};
for(const auto& elem : elements) {
json_obj = json_obj->find_obj(elem);
};
-class VaultKMS : KMS {
-
-protected:
- CephContext *cct;
- VaultSecretEngine *engine;
-
-public:
- int get_key(boost::string_view key_id, std::string& actual_key) {
- if (!engine){
- ldout(cct, 5) << "ERROR: No supported Vault Secret Engine found" << dendl;
- return -EINVAL;
- }
- return engine->get_key(key_id, actual_key);
- }
-
- VaultKMS(CephContext *cct) {
- std::string secret_engine = cct->_conf->rgw_crypt_vault_secret_engine;
-
- ldout(cct, 10) << "Selected " << secret_engine << " secret engine" << dendl;
-
- if (RGW_SSE_KMS_VAULT_SE_KV == secret_engine)
- engine = new KvSecretEngine(cct);
- else if (RGW_SSE_KMS_VAULT_SE_TRANSIT == secret_engine)
- engine = new TransitSecretEngine(cct);
- else
- ldout(cct, 0) << "Missing or invalid secret engine" << dendl;
-
- this->cct = cct;
- }
-
- ~VaultKMS(){
- delete engine;
- }
-
-};
-
-
static map<string,string> get_str_map(const string &str) {
map<string,string> m;
get_str_map(str, &m, ";, \t");
boost::string_view key_id,
std::string& actual_key)
{
+ std::string secret_engine = cct->_conf->rgw_crypt_vault_secret_engine;
ldout(cct, 20) << "Vault authentication method: " << cct->_conf->rgw_crypt_vault_auth << dendl;
- ldout(cct, 20) << "Vault Secrets Engine: " << cct->_conf->rgw_crypt_vault_secret_engine << dendl;
- VaultKMS *kms = new VaultKMS(cct);
- int res = kms->get_key(key_id, actual_key);
- delete kms;
- return res;
+ ldout(cct, 20) << "Vault Secrets Engine: " << secret_engine << dendl;
+
+ if (RGW_SSE_KMS_VAULT_SE_KV == secret_engine){
+ KvSecretEngine engine(cct);
+ return engine.get_key(key_id, actual_key);
+ }
+ else if (RGW_SSE_KMS_VAULT_SE_TRANSIT == secret_engine){
+ TransitSecretEngine engine(cct);
+ return engine.get_key(key_id, actual_key);
+ }
+ else{
+ ldout(cct, 0) << "Missing or invalid secret engine" << dendl;
+ return -EINVAL;
+ }
}
+
int get_actual_key_from_kms(CephContext *cct,
boost::string_view key_id,
boost::string_view key_selector,