Skip to content

Commit

Permalink
Merge pull request #13 from deeprave/develop
Browse files Browse the repository at this point in the history
Enhance SecretsManager to support additional env variables.
  • Loading branch information
deeprave authored Sep 11, 2023
2 parents 23b2d4e + ddd2c8d commit 12c0fe4
Show file tree
Hide file tree
Showing 5 changed files with 71 additions and 11 deletions.
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
# ChangeLog

### v2.2.0

- Allow base_path to be set via env variable $VAULT_PATH.
- Add support for $VAULT_CLIENT_CERT and $VAULT_CLIENT_KEY

These changes allow full configuration of the secrets manager backend using the environment.

### v2.1.0

- Simplified and extended SecretsManager back-end
Expand Down
15 changes: 15 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -142,3 +142,18 @@ format <appname>/<envname>/<key> - for example, myapp/prod/DB_PASSWORD. The util
with create permission for the base secrets path on the vault server.
The utility currently assumes that the kv secrets engine is mounted at secret/. The
final path where the secrets are stored will be secret/data/<appname>/<envname>/<key>.

### Environment Variables

The SecretsManager and Vault client leverage environment variables for their configuration.
This ensures a degree of transparency as it allows the client to use them but mitigates the need for the client code to be aware of their presence.
A summary of these variables is in the following table:

| Variable | Description |
|-------------------|--------------------------------------------------------------------------|
| VAULT_ADDR | The URL of the vault server |
| VAULT_TOKEN | The vault token to use for authentication |
| VAULT_CACERT | The path to the CA certificate to use for TLS verification |
| VAULT_CAPATH | The path to a directory of CA certificates to use for TLS verification |
| VAULT_CLIENT_CERT | The path to the client certificate to use for TLS connection |
| VAULT_CLIENT_KEY | The path to the client key to use for TLS connection |
28 changes: 18 additions & 10 deletions envex/env_wrapper.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ def __init__(
verify: bool = True,
cache_enabled: bool = True,
base_path: str = None,
engine: str | None = None,
mount_point: str | None = None,
**kwargs,
):
"""
Expand All @@ -40,20 +42,24 @@ def __init__(
@param environ: dict | None default base environment (os.environ is default)
@param exception: (optional) Exception class to raise on error (default=KeyError)
@param readenv: read values from .env files (default=False)
if true, the following additional args are accepted:
- if readenv=True, the following additional args may be used
@param env_file: str name of the environment file (.env or $ENV default)
search_path: None | Union[List[str], List[Path]], str] path(s) to search for env_file
overwrite: bool whether to overwrite existing environment variables (default=False)
parents: bool whether to search parent directories for env_file (default=False)
update: bool whether to update os.environ with values from env_file (default=False)
errors: bool whether to raise error on missing env_file (default=False)
@param search_path: None | Union[List[str], List[Path]], str] path(s) to search for env_file
@param overwrite: bool whether to overwrite existing environment variables (default=False)
@param parents: bool whether to search parent directories for env_file (default=False)
@param update: bool whether to update os.environ with values from env_file (default=False)
@param errors: bool whether to raise error on missing env_file (default=False)
- kwargs for vault/secrets manager:
@param url: (optional) vault url, default is $VAULT_ADDR
@param token: (optional) vault token, default is $VAULT_TOKEN or ~/.vault-token
@param cert: (optional) tuple (cert, key) or str path to client cert/key files
@param verify: (optional) bool whether to verify server cert (default=True)
@param verify: (optional) bool | str whether to verify server cert or set ca cert path (default=True)
@param cache_enabled: (optional) bool whether to cache secrets (default=True)
@param base_path: (optional) str base path, or "environment" for secrets (default=None)
@param base_path: (optional) str base path for secrets (default=None)
@param engine: (optional) str vault secrets engine (default=None)
@param mount_point: (optional) str vault secrets mount point (default=None, determined by engine)
@param working_dirs: (optional) bool whether to include PWD/CWD (default=True)
-
@param kwargs: (optional) environment variables to add/override
"""
self._env = self.os_env() if environ is None else environ
Expand All @@ -70,6 +76,8 @@ def __init__(
verify=verify,
cache_enabled=cache_enabled,
base_path=base_path,
engine=engine,
mount_point=mount_point,
)
self.exception = exception or self._EXCEPTION_CLS

Expand Down Expand Up @@ -169,7 +177,7 @@ def list(self, var, default=None) -> list:
val = self.get(var, default)
return val if isinstance(val, (list, tuple)) else self._list(val)

__typemap = {
env_typemap = {
"str": get,
"int": int,
"bool": bool,
Expand All @@ -184,7 +192,7 @@ def __call__(self, var, default=None, **kwargs):
_type = kwargs.get("type", str)
_type = _type if isinstance(_type, str) else _type.__name__
try:
func = self.__typemap[_type]
func = self.env_typemap[_type]
return func(self, var, default=default)
except KeyError:
pass
Expand Down
30 changes: 30 additions & 0 deletions envex/lib/hvac_env.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,27 @@ def expand(path: str):
return os.path.expandvars(os.path.expanduser(path))


def read_pem(variable: str, is_key: bool = False):
"""
Get the value of the given environment variable and return it as a PEM string.
:param variable: The name of the environment variable to retrieve.
:return: The PEM string value of the environment variable if it looks valid.
"""
value = os.getenv(variable)
if value is not None:
value = expand(value)
if os.path.isfile(value):
with open(value, "r") as f:
value = f.read()
if is_key:
intro = "PRIVATE KEY"
else:
intro = "BEGIN CERTIFICATE"
value = value if intro in value else None
return value


class SecretsManager:
def __init__(
self,
Expand Down Expand Up @@ -54,6 +75,15 @@ def __init__(
verify = os.getenv("VAULT_åCACERT") or True
if isinstance(verify, str):
verify = expand(verify)
if cert is None:
cert = (
read_pem("VAULT_CLIENT_CERT", False),
read_pem("VAULT_CLIENT_KEY", True),
)
if cert[0] is None:
cert = None
if base_path is None:
base_path = os.getenv("VAULT_PATH", None)
# noinspection PyBroadException
try:
import hvac
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[tool.poetry]
name = "envex"
version = "2.1.0"
version = "2.2.0"
description = "Environment interface with .env and hashicorp vault support"
authors = ["David Nugent <[email protected]>"]
readme = "README.md"
Expand Down

0 comments on commit 12c0fe4

Please sign in to comment.