Compare commits
2 Commits
5fb4191818
...
2b1d4366d4
Author | SHA1 | Date | |
---|---|---|---|
2b1d4366d4 | |||
788c8dcfd4 |
|
@ -18,7 +18,9 @@ Secret loading order:
|
||||||
0. Hardcoded values. **This is purely for debugging, prototyping, and for
|
0. Hardcoded values. **This is purely for debugging, prototyping, and for
|
||||||
configuring below options.**
|
configuring below options.**
|
||||||
1. Files pointed to by environment variables. Docker friendly.
|
1. Files pointed to by environment variables. Docker friendly.
|
||||||
2. Secrets folder. Also Docker friendly.
|
2. Secrets folder. Also Docker friendly. Defaults to `secrets`, but can be
|
||||||
|
configured through the `SECRETS_DIRECTORY` key (NOTE: passed directly,
|
||||||
|
rather than through a file.)
|
||||||
3. [Pass: the standard unix password
|
3. [Pass: the standard unix password
|
||||||
manager](https://www.passwordstore.org/). Most suited for personal
|
manager](https://www.passwordstore.org/). Most suited for personal
|
||||||
usage; very unsuited for server environments. Requires `pass` installed
|
usage; very unsuited for server environments. Requires `pass` installed
|
||||||
|
@ -44,6 +46,7 @@ Secret loading order:
|
||||||
- [ ] Vault:
|
- [ ] Vault:
|
||||||
* [ ] Ensure vault code path works.
|
* [ ] Ensure vault code path works.
|
||||||
* [ ] Document usage and requirements.
|
* [ ] Document usage and requirements.
|
||||||
|
- [ ] Get inspiration from <https://cheatsheetseries.owasp.org/cheatsheets/Secrets_Management_Cheat_Sheet.html>
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
|
@ -60,14 +63,15 @@ logger = logging.getLogger(__name__)
|
||||||
logger.setLevel(logging.INFO)
|
logger.setLevel(logging.INFO)
|
||||||
|
|
||||||
ENV_KEY_PREFIX = 'ENV_KEY_PREFIX'
|
ENV_KEY_PREFIX = 'ENV_KEY_PREFIX'
|
||||||
|
ENV_KEY_SECRETS_DIRECTORY = 'SECRETS_DIRECTORY'
|
||||||
|
|
||||||
ENV_KEY_VAULT_URL = 'VAULT_URL'
|
ENV_KEY_VAULT_URL = 'VAULT_URL'
|
||||||
ENV_KEY_VAULT_TOKEN = 'VAULT_TOKEN'
|
ENV_KEY_VAULT_TOKEN = 'VAULT_TOKEN' #noqa: S105
|
||||||
ENV_KEY_VAULT_MOUNT_POINT = 'VAULT_MOUNT_POINT'
|
ENV_KEY_VAULT_MOUNT_POINT = 'VAULT_MOUNT_POINT'
|
||||||
|
|
||||||
ENV_KEY_PASS_FOLDER = 'PASS_STORE_SUBFOLDER'
|
ENV_KEY_PASS_FOLDER = 'PASS_STORE_SUBFOLDER' #noqa: S105
|
||||||
|
|
||||||
DEFAULT_SECRETS_DIRECTORY = Path('./secrets/')
|
DEFAULT_SECRETS_DIRECTORY = Path('secrets')
|
||||||
|
|
||||||
ERROR_MESSAGE_FORMAT = """Failed to load secret with key: \033[1m{secret_name}\033[0m
|
ERROR_MESSAGE_FORMAT = """Failed to load secret with key: \033[1m{secret_name}\033[0m
|
||||||
|
|
||||||
|
@ -108,6 +112,7 @@ class SecretLoader:
|
||||||
self.pass_folder = None
|
self.pass_folder = None
|
||||||
self.vault_client = None
|
self.vault_client = None
|
||||||
self.env_key_prefix = None
|
self.env_key_prefix = None
|
||||||
|
self.secret_folder = None
|
||||||
|
|
||||||
# Setup environment
|
# Setup environment
|
||||||
self.env_key_prefix = self._load_or_none(ENV_KEY_PREFIX)
|
self.env_key_prefix = self._load_or_none(ENV_KEY_PREFIX)
|
||||||
|
@ -119,6 +124,9 @@ class SecretLoader:
|
||||||
'_',
|
'_',
|
||||||
), 'Prefix must not end with _ (this will be added automatically)'
|
), 'Prefix must not end with _ (this will be added automatically)'
|
||||||
|
|
||||||
|
# Setup secrets path
|
||||||
|
self.secret_folder = Path(self.hardcoded.get(ENV_KEY_SECRETS_DIRECTORY) or self._load_or_none_env(ENV_KEY_SECRETS_DIRECTORY) or DEFAULT_SECRETS_DIRECTORY)
|
||||||
|
|
||||||
# Setup pass
|
# Setup pass
|
||||||
self.pass_folder = self._load_or_none(ENV_KEY_PASS_FOLDER)
|
self.pass_folder = self._load_or_none(ENV_KEY_PASS_FOLDER)
|
||||||
|
|
||||||
|
@ -171,12 +179,13 @@ class SecretLoader:
|
||||||
Returns `None` if the secret is not present in either the environment
|
Returns `None` if the secret is not present in either the environment
|
||||||
or the directory.
|
or the directory.
|
||||||
"""
|
"""
|
||||||
filepath: Path | str | None = os.environ.get(
|
filepath: Path | str | None = self._load_or_none_env(secret_name)
|
||||||
f'{self.env_key_prefix}_{secret_name.upper()}',
|
|
||||||
)
|
|
||||||
|
|
||||||
if filepath is None:
|
if filepath is None:
|
||||||
filepath = DEFAULT_SECRETS_DIRECTORY / secret_name.lower()
|
if self.secret_folder is not None:
|
||||||
|
filepath = self.secret_folder / secret_name.lower()
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
|
||||||
try:
|
try:
|
||||||
with open(filepath) as f:
|
with open(filepath) as f:
|
||||||
|
@ -184,6 +193,9 @@ class SecretLoader:
|
||||||
except FileNotFoundError:
|
except FileNotFoundError:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
def _load_or_none_env(self, secret_name) -> str | None:
|
||||||
|
return os.environ.get(f'{self.env_key_prefix}_{secret_name.upper()}')
|
||||||
|
|
||||||
def _load_or_none_local_password_store(self, secret_name: str) -> str | None:
|
def _load_or_none_local_password_store(self, secret_name: str) -> str | None:
|
||||||
"""Load secret from the `pass` password manager.
|
"""Load secret from the `pass` password manager.
|
||||||
|
|
||||||
|
|
1
test/example-secrets/my_secret
Normal file
1
test/example-secrets/my_secret
Normal file
|
@ -0,0 +1 @@
|
||||||
|
HELLO SECRET
|
|
@ -7,6 +7,11 @@ def test_hardcoded():
|
||||||
assert loader.load('KEY') == 'VALUE'
|
assert loader.load('KEY') == 'VALUE'
|
||||||
|
|
||||||
|
|
||||||
|
def test_lookup_secrets_dir():
|
||||||
|
loader = secret_loader.SecretLoader(SECRETS_DIRECTORY='test/example-secrets')
|
||||||
|
assert loader.load('MY_SECRET') == 'HELLO SECRET'
|
||||||
|
|
||||||
|
|
||||||
def test_lookup_unknown():
|
def test_lookup_unknown():
|
||||||
loader = secret_loader.SecretLoader()
|
loader = secret_loader.SecretLoader()
|
||||||
assert loader.load('UNKNOWN') is None
|
assert loader.load('UNKNOWN') is None
|
||||||
|
|
Loading…
Reference in New Issue
Block a user