vault: support configuration of KV mountpoint
Support end user configuration of KV store in Vault to use for
key storage allowing more flexibility in Vault configuration.
Change-Id: I625a819c2b9b542677258de709a9c520fb86858b
Closes-Bug: 1797148
James Page
4 years ago
43 | 43 | from castellan.key_manager import key_manager |
44 | 44 | |
45 | 45 | DEFAULT_VAULT_URL = "http://127.0.0.1:8200" |
46 | DEFAULT_MOUNTPOINT = "secret" | |
46 | 47 | |
47 | 48 | vault_opts = [ |
48 | 49 | cfg.StrOpt('root_token_id', |
51 | 52 | help='AppRole role_id for authentication with vault'), |
52 | 53 | cfg.StrOpt('approle_secret_id', |
53 | 54 | help='AppRole secret_id for authentication with vault'), |
55 | cfg.StrOpt('kv_mountpoint', | |
56 | default=DEFAULT_MOUNTPOINT, | |
57 | help='Mountpoint of KV store in Vault to use, for example: ' | |
58 | '{}'.format(DEFAULT_MOUNTPOINT)), | |
54 | 59 | cfg.StrOpt('vault_url', |
55 | 60 | default=DEFAULT_VAULT_URL, |
56 | 61 | help='Use this endpoint to connect to Vault, for example: ' |
97 | 102 | self._cached_approle_token_id = None |
98 | 103 | self._approle_token_ttl = None |
99 | 104 | self._approle_token_issue = None |
105 | self._kv_mountpoint = self._conf.vault.kv_mountpoint | |
100 | 106 | self._vault_url = self._conf.vault.vault_url |
101 | 107 | if self._vault_url.startswith("https://"): |
102 | 108 | self._verify_server = self._conf.vault.ssl_ca_crt_file or True |
113 | 119 | if self._vault_kv_version: |
114 | 120 | return self._vault_kv_version |
115 | 121 | |
116 | resource_url = self._get_url() + 'v1/sys/internal/ui/mounts/secret' | |
122 | resource_url = '{}v1/sys/internal/ui/mounts/{}'.format( | |
123 | self._get_url(), | |
124 | self._kv_mountpoint | |
125 | ) | |
117 | 126 | resp = self._do_http_request(self._session.get, resource_url) |
118 | 127 | |
119 | 128 | if resp.status_code == requests.codes['not_found']: |
124 | 133 | return self._vault_kv_version |
125 | 134 | |
126 | 135 | def _get_resource_url(self, key_id=None): |
127 | return '{}v1/secret/{}{}'.format( | |
136 | return '{}v1/{}/{}{}'.format( | |
128 | 137 | self._get_url(), |
138 | self._kv_mountpoint, | |
129 | 139 | |
130 | 140 | '' if self._get_api_version() == '1' else |
131 | 141 | 'data/' if key_id else |
40 | 40 | retry_delay=None, number_of_retries=None, verify_ssl=None, |
41 | 41 | api_class=None, vault_root_token_id=None, |
42 | 42 | vault_approle_role_id=None, vault_approle_secret_id=None, |
43 | vault_url=None, | |
43 | vault_kv_mountpoint=None, vault_url=None, | |
44 | 44 | vault_ssl_ca_crt_file=None, vault_use_ssl=None, |
45 | 45 | barbican_endpoint_type=None): |
46 | 46 | """Set defaults for configuration values. |
58 | 58 | :param vault_approle_role_id: Use this for the approle role_id for vault. |
59 | 59 | :param vault_approle_secret_id: Use this for the approle secret_id |
60 | 60 | for vault. |
61 | :param vault_kv_mountpoint: Mountpoint of KV store in vault to use. | |
61 | 62 | :param vault_url: Use this for the url for vault. |
62 | 63 | :param vault_use_ssl: Use this to force vault driver to use ssl. |
63 | 64 | :param vault_ssl_ca_crt_file: Use this for the CA file for vault. |
108 | 109 | if vault_approle_secret_id is not None: |
109 | 110 | conf.set_default('approle_secret_id', vault_approle_secret_id, |
110 | 111 | group=vkm.VAULT_OPT_GROUP) |
112 | if vault_kv_mountpoint is not None: | |
113 | conf.set_default('kv_mountpoint', vault_kv_mountpoint, | |
114 | group=vkm.VAULT_OPT_GROUP) | |
111 | 115 | if vault_url is not None: |
112 | 116 | conf.set_default('vault_url', vault_url, |
113 | 117 | group=vkm.VAULT_OPT_GROUP) |
129 | 129 | |
130 | 130 | class VaultKeyManagerAppRoleTestCase(VaultKeyManagerOSLOContextTestCase): |
131 | 131 | |
132 | mountpoint = 'secret' | |
133 | ||
132 | 134 | def _create_key_manager(self): |
133 | 135 | key_mgr = vault_key_manager.VaultKeyManager(cfg.CONF) |
134 | 136 | |
146 | 148 | self.session = requests.Session() |
147 | 149 | self.session.headers.update({'X-Vault-Token': self.root_token_id}) |
148 | 150 | |
151 | self._mount_kv(self.mountpoint) | |
149 | 152 | self._enable_approle() |
150 | 153 | self._create_policy(vault_policy) |
151 | 154 | self._create_approle(vault_approle, vault_policy) |
153 | 156 | key_mgr._approle_role_id, key_mgr._approle_secret_id = ( |
154 | 157 | self._retrieve_approle(vault_approle) |
155 | 158 | ) |
159 | key_mgr._kv_mountpoint = self.mountpoint | |
156 | 160 | key_mgr._vault_url = self.vault_url |
157 | 161 | return key_mgr |
162 | ||
163 | def _mount_kv(self, vault_mountpoint): | |
164 | backends = self.session.get( | |
165 | '{}/v1/sys/mounts'.format(self.vault_url)).json() | |
166 | if vault_mountpoint not in backends: | |
167 | params = { | |
168 | 'type': 'kv', | |
169 | 'options': { | |
170 | 'version': 2, | |
171 | } | |
172 | } | |
173 | self.session.post( | |
174 | '{}/v1/sys/mounts/{}'.format(self.vault_url, | |
175 | vault_mountpoint), | |
176 | json=params) | |
158 | 177 | |
159 | 178 | def _enable_approle(self): |
160 | 179 | params = { |
170 | 189 | |
171 | 190 | def _create_policy(self, vault_policy): |
172 | 191 | params = { |
173 | 'rules': TEST_POLICY.format(backend='secret'), | |
192 | 'rules': TEST_POLICY.format(backend=self.mountpoint), | |
174 | 193 | } |
175 | 194 | self.session.put( |
176 | 195 | '{}/{}'.format( |
212 | 231 | )).json()['data']['secret_id'] |
213 | 232 | ) |
214 | 233 | return (approle_role_id, approle_secret_id) |
234 | ||
235 | ||
236 | class VaultKeyManagerAltMountpointTestCase(VaultKeyManagerAppRoleTestCase): | |
237 | ||
238 | mountpoint = 'different-secrets' |