Skip to content

Commit

Permalink
Merge pull request #42 from allenxiang/master
Browse files Browse the repository at this point in the history
Support both Vault auth token and GitHub auth token
  • Loading branch information
jhaals committed May 10, 2017
2 parents 6ebca57 + fb307b5 commit 826f18a
Show file tree
Hide file tree
Showing 2 changed files with 58 additions and 25 deletions.
14 changes: 10 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,18 @@ The source for the plugin can be pointed to via a _requirements.yml_ file, and
accessed via [`ansible-galaxy`](http://docs.ansible.com/ansible/galaxy.html).

### Configuration
The address to the Vault server and the auth token are fetched from
environment variables.
The address to the Vault server:

export VAULT_ADDR=http://192.168.33.10:8200/

The plugin supports both Vault auth token and GitHub auth token. To use Vault auth token:

export VAULT_TOKEN=56f48aef-8ad3-a0c4-447b-8e96990776ff

If your Vault server is configured to use GitHub auth token:

export VAULT_GITHUB_API_TOKEN=56f48aef-8ad3-a0c4-447b-8e96990776ff

The plugin also supports Vault's CA-related environment variables, to
enable use of a server certificate issued by a not-widely-trusted
Certificate Authority. Use of this feature in the plugin requires
Expand All @@ -25,11 +31,11 @@ Python 2.7.9.
export VAULT_CAPATH=/etc/ssl/localCA

The Vault address, CA certificate, and path can also be set via the Ansible
variables `vault_addr`, `vault_cacert`, and `vault_capath`, respectively.
variables `vault_addr`, `vault_cacert`, and `vault_capath`, respectively.

export VAULT_CAHOSTVERIFY="no"

This avoid the hostname check for Vault certificate (useful with self-signed certicates).
This avoid the hostname check for Vault certificate (useful with self-signed certicates).
This option can also be set via the Ansible variable `vault_cahostverify`.

For more information on setting variables in Ansible, see the
Expand Down
69 changes: 48 additions & 21 deletions vault.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import os

# compatibility with Python >= 2.7.13
try:
import urllib2
Expand Down Expand Up @@ -50,6 +49,16 @@ def run(self, terms, inject=None, variables=None, **kwargs):
term_split = terms[0].split(' ', 1)
key = term_split[0]

python_version_cur = ".".join([str(version_info.major),
str(version_info.minor),
str(version_info.micro)])
python_version_min = "2.7.9"
if StrictVersion(python_version_cur) < StrictVersion(python_version_min):
raise AnsibleError('Unable to read %s from vault:'
' Using Python %s, and vault lookup plugin requires at least %s'
' to use an SSL context (VAULT_CACERT or VAULT_CAPATH)'
% (key, python_version_cur, python_version_min))

try:
parameters = term_split[1]
parameters = parameters.split(' ')
Expand Down Expand Up @@ -81,18 +90,19 @@ def run(self, terms, inject=None, variables=None, **kwargs):
# the environment variable takes precedence over the file-based token.
# intentionally do *not* support setting this via an Ansible variable,
# so as not to encourage bad security practices.
token = os.getenv('VAULT_TOKEN')
if not token:
github_token = os.getenv('VAULT_GITHUB_API_TOKEN')
vault_token = os.getenv('VAULT_TOKEN')
if not vault_token and not github_token:
token_path = os.path.join(os.getenv('HOME'), '.vault-token')
try:
with open(token_path) as token_file:
token = token_file.read().strip()
vault_token = token_file.read().strip()
except IOError as err:
if err.errno != errno.ENOENT:
raise AnsibleError('Error occurred when opening ' + token_path + ': ' + err.strerror)
if not token:
raise AnsibleError('Vault authentication token missing. Specify with'
' VAULT_TOKEN environment variable or in $HOME/.vault-token '
if not github_token and not vault_token:
raise AnsibleError('Vault or GitHub authentication token missing. Specify with'
' VAULT_TOKEN/VAULT_GITHUB_API_TOKEN environment variable or in $HOME/.vault-token '
'(Current $HOME value is ' + os.getenv('HOME') + ')')

cafile = os.getenv('VAULT_CACERT') or (variables or inject).get('vault_cacert')
Expand All @@ -102,13 +112,40 @@ def run(self, terms, inject=None, variables=None, **kwargs):
if _use_vault_cache and key in _vault_cache:
result = _vault_cache[key]
else:
result = self._fetch_remotely(cafile, capath, data, key, token, url, cahostverify)
if not vault_token:
token_result = self._fetch_token(cafile, capath, github_token, url, cahostverify)
vault_token = token_result['auth']['client_token']
result = self._fetch_remotely(cafile, capath, data, key, vault_token, url, cahostverify)
if _use_vault_cache:
_vault_cache[key] = result

return [result['data'][field]] if field is not None else [result['data']]

def _fetch_remotely(self, cafile, capath, data, key, token, url, cahostverify):
def _fetch_token(self, cafile, capath, github_token, url, cahostverify):
try:
context = None
if cafile or capath:
context = ssl.create_default_context(cafile=cafile, capath=capath)
if cahostverify == 'no':
context.check_hostname = False
else:
context.check_hostname = True
request_url = urljoin(url, "v1/auth/github/login")
req_params = {}
req_params['token'] = github_token
req = urllib2.Request(request_url, json.dumps(req_params))
req.add_header('Content-Type', 'application/json')
response = urllib2.urlopen(req, context=context) if context else urllib2.urlopen(req)
except AttributeError as e:
raise AnsibleError('Unable to retrieve personal token from vault: %s' % (e))
except urllib2.HTTPError as e:
raise AnsibleError('Unable to retrieve personal token from vault: %s' % (e))
except Exception as e:
raise AnsibleError('Unable to retrieve personal token from vault: %s' % (e))
result = json.loads(response.read())
return result

def _fetch_remotely(self, cafile, capath, data, key, vault_token, url, cahostverify):
try:
context = None
if cafile or capath:
Expand All @@ -119,21 +156,11 @@ def _fetch_remotely(self, cafile, capath, data, key, token, url, cahostverify):
context.check_hostname = True
request_url = urljoin(url, "v1/%s" % (key))
req = urllib2.Request(request_url, data)
req.add_header('X-Vault-Token', token)
req.add_header('X-Vault-Token', vault_token)
req.add_header('Content-Type', 'application/json')
response = urllib2.urlopen(req, context=context) if context else urllib2.urlopen(req)
except AttributeError as e:
python_version_cur = ".".join([str(version_info.major),
str(version_info.minor),
str(version_info.micro)])
python_version_min = "2.7.9"
if StrictVersion(python_version_cur) < StrictVersion(python_version_min):
raise AnsibleError('Unable to read %s from vault:'
' Using Python %s, and vault lookup plugin requires at least %s'
' to use an SSL context (VAULT_CACERT or VAULT_CAPATH)'
% (key, python_version_cur, python_version_min))
else:
raise AnsibleError('Unable to read %s from vault: %s' % (key, e))
raise AnsibleError('Unable to read %s from vault: %s' % (key, e))
except urllib2.HTTPError as e:
raise AnsibleError('Unable to read %s from vault: %s' % (key, e))
except Exception as e:
Expand Down

0 comments on commit 826f18a

Please sign in to comment.