1
0

Compare commits

...

2 Commits

Author SHA1 Message Date
2b1d4366d4
Configure secrets folder
All checks were successful
Test Python / Test (push) Successful in 23s
2024-09-04 19:37:06 +02:00
788c8dcfd4
Inspiration 2024-09-04 17:58:34 +02:00
3 changed files with 26 additions and 8 deletions

View File

@ -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.

View File

@ -0,0 +1 @@
HELLO SECRET

View File

@ -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