1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
|
Credential Plugins
==================
By default, sensitive credential values (such as SSH passwords, SSH private
keys, API tokens for cloud services, etc.) in AWX are stored in the AWX database
after being encrypted with a symmetric encryption cipher utilizing AES-256 in
CBC mode alongside a SHA-256 HMAC.
Alternatively, AWX supports retrieving secret values from third-party secret
management systems, such as HashiCorp Vault and Microsoft Azure Key Vault.
These external secret values will be fetched on demand every time they are
needed (generally speaking, immediately before running a playbook that needs
them).
Configuring Secret Lookups
--------------------------
When configuring AWX to pull a secret from a third party system, there are
generally three steps.
Here is an example of creating an (1) AWX Machine Credential with
a static username, `example-user` and (2) an externally-sourced secret from
HashiCorp Vault Key/Value system which will populate the (3) password field on
the Machine Credential:
1. Create the Machine Credential with a static username, `example-user`.
2. Create a second credential used to _authenticate_ with the external
secret management system (in this example, specifying a URL and an
OAuth2.0 token _to access_ HashiCorp Vault)
3. _Link_ the `password` field for the Machine Credential to the external
system by specifying the source (in this example, the HashiCorp Credential)
and metadata about the path (e.g., `/some/path/to/my/password/`).
Note that you can perform these lookups on *any* field for any non-external
credential, including those with custom credential types. You could just as
easily create an AWS Credential and use lookups to retrieve the Access Key and
Secret Key from an external secret management system. External credentials
cannot have lookups applied to their fields.
Writing Custom Credential Plugins
---------------------------------
Credential Plugins in AWX are just importable Python functions that are
registered using setuptools entrypoints
(https://setuptools.readthedocs.io/en/latest/setuptools.html#dynamic-discovery-of-services-and-plugins)
Example plugins officially supported in AWX can be found in the source code at
`awx.main.credential_plugins`.
For instructions on writing and installing your own custom credential plugin, see: https://github.com/ansible/awx-custom-credential-plugin-example
Programmatic Secret Fetching
----------------------------
If you want to programmatically fetch secrets from a supported external secret
management system (for example, if you wanted to compose an AWX database connection
string in `/etc/tower/conf.d/postgres.py` using an external system rather than
storing the password in plaintext on your disk), doing so is fairly easy:
```python
from awx.main.credential_plugins import hashivault
hashivault.hashivault_kv_plugin.backend(
url='https://hcv.example.org',
token='some-valid-token',
api_version='v2',
secret_path='/path/to/secret',
secret_key='dbpass'
)
```
Supported Plugins
=================
HashiCorp Vault KV
------------------
AWX supports retrieving secret values from HashiCorp Vault KV
(https://www.vaultproject.io/api/secret/kv/)
The following example illustrates how to configure a Machine Credential to pull
its password from a HashiCorp Vault:
1. Look up the ID of the Machine and HashiCorp Vault Secret Lookup Credential
types (in this example, `1` and `15`):
```shell
~ curl -sik "https://awx.example.org/api/v2/credential_types/?name=Machine" \
-H "Authorization: Bearer <token>"
HTTP/1.1 200 OK
{
"results": [
{
"id": 1,
"url": "/api/v2/credential_types/1/",
"name": "Machine",
...
```
```shell
~ curl -sik "https://awx.example.org/api/v2/credential_types/?name__startswith=HashiCorp" \
-H "Authorization: Bearer <token>"
HTTP/1.1 200 OK
{
"results": [
{
"id": 15,
"url": "/api/v2/credential_types/15/",
"name": "HashiCorp Vault Secret Lookup",
...
```
2. Create a Machine and a HashiCorp Vault Credential:
```shell
~ curl -sik "https://awx.example.org/api/v2/credentials/" \
-H "Authorization: Bearer <token>" \
-H "Content-Type: application/json" \
-X POST \
-d '{"user": N, "credential_type": 1, "name": "My SSH", "inputs": {"username": "example"}}'
HTTP/1.1 201 Created
{
"credential_type": 1,
"description": "",
"id": 1,
...
```
```shell
~ curl -sik "https://awx.example.org/api/v2/credentials/" \
-H "Authorization: Bearer <token>" \
-H "Content-Type: application/json" \
-X POST \
-d '{"user": N, "credential_type": 15, "name": "My Hashi Credential", "inputs": {"url": "https://vault.example.org", "token": "vault-token", "api_version": "v2"}}'
HTTP/1.1 201 Created
{
"credential_type": 15,
"description": "",
"id": 2,
...
```
3. Link the Machine Credential to the HashiCorp Vault Credential:
```shell
~ curl -sik "https://awx.example.org/api/v2/credentials/1/input_sources/" \
-H "Authorization: Bearer <token>" \
-H "Content-Type: application/json" \
-X POST \
-d '{"source_credential": 2, "input_field_name": "password", "metadata": {"secret_path": "/kv/my-secret", "secret_key": "password"}}'
HTTP/1.1 201 Created
```
HashiCorp Vault SSH Secrets Engine
----------------------------------
AWX supports signing public keys via HashiCorp Vault's SSH Secrets Engine
(https://www.vaultproject.io/api/secret/ssh/)
The following example illustrates how to configure a Machine Credential to sign
a public key using HashiCorp Vault:
1. Look up the ID of the Machine and HashiCorp Vault Signed SSH Credential
types (in this example, `1` and `16`):
```shell
~ curl -sik "https://awx.example.org/api/v2/credential_types/?name=Machine" \
-H "Authorization: Bearer <token>"
HTTP/1.1 200 OK
{
"results": [
{
"id": 1,
"url": "/api/v2/credential_types/1/",
"name": "Machine",
...
```
```shell
~ curl -sik "https://awx.example.org/api/v2/credential_types/?name__startswith=HashiCorp" \
-H "Authorization: Bearer <token>"
HTTP/1.1 200 OK
{
"results": [
{
"id": 16,
"url": "/api/v2/credential_types/16/",
"name": "HashiCorp Vault Signed SSH",
```
2. Create a Machine and a HashiCorp Vault Credential:
```shell
~ curl -sik "https://awx.example.org/api/v2/credentials/" \
-H "Authorization: Bearer <token>" \
-H "Content-Type: application/json" \
-X POST \
-d '{"user": N, "credential_type": 1, "name": "My SSH", "inputs": {"username": "example", "ssh_key_data": "RSA KEY DATA"}}'
HTTP/1.1 201 Created
{
"credential_type": 1,
"description": "",
"id": 1,
...
```
```shell
~ curl -sik "https://awx.example.org/api/v2/credentials/" \
-H "Authorization: Bearer <token>" \
-H "Content-Type: application/json" \
-X POST \
-d '{"user": N, "credential_type": 16, "name": "My Hashi Credential", "inputs": {"url": "https://vault.example.org", "token": "vault-token"}}'
HTTP/1.1 201 Created
{
"credential_type": 16,
"description": "",
"id": 2,
...
```
3. Link the Machine Credential to the HashiCorp Vault Credential:
```shell
~ curl -sik "https://awx.example.org/api/v2/credentials/1/input_sources/" \
-H "Authorization: Bearer <token>" \
-H "Content-Type: application/json" \
-X POST \
-d '{"source_credential": 2, "input_field_name": "password", "metadata": {"public_key": "UNSIGNED PUBLIC KEY", "secret_path": "/ssh/", "role": "example-role"}}'
HTTP/1.1 201 Created
```
4. Associate the Machine Credential with a Job Template. When the Job Template
is run, AWX will use the provided HashiCorp URL and token to sign the
unsigned public key data using the HashiCorp Vault SSH Secrets API.
AWX will generate an `id_rsa` and `id_rsa-cert.pub` on the fly and
apply them using `ssh-add`.
|