šŸ¤« Easily manage configs and secrets in your Python projects (with CLI support)

Overview

confidential

badge

Installation

pip install confidential

How does it work?

Confidential manages secrets for your project, using AWS Secrets Manager.

First, store a secret in AWS Secrets Manager. Then, create a secrets file, say my_secrets.json. A value will be decrypted if the word secret precedes it, like the database value below:

{
  "database": "secret:database_details",
  "environment": "production",
  "debug_mode": false
}

You can decrypt this file either in Python, or directly using the CLI. Ensure AWS CLI is set up, then run:

confidential my_secrets.json

which outputs the file with decrypted values

{
  "database": {
    "url": "https://example.com",
    "username": "admin",
    "password": "[email protected]",
    "port": 5678
  },
  "environment": "production",
  "debug_mode": false
}

image

Can I use it in my Python projects?

Yes, simply import and instantiate SecretsManager, like so:

settings.py

from confidential import SecretsManager


secrets = SecretManager(
    secrets_file=".secrets/production.json",
    secrets_file_default=".secrets/defaults.json",  # Overridable defaults you can use in common environments
    region_name="us-east-1",
)

DATABASES = {
    'default': secrets["database"]
}

Testing

First, install all dependencies:

poetry install

Then run the tests

poetry run pytest
Comments
  • Handle user permissions error

    Handle user permissions error

    Old behavior: Throw TypeError, unhelpful error message, hard to debug underlying issue.

    New behavior: Throw IOError with helpful error message indicating cause of failure.

    Also decided to remove the .idea folder. Let me know if that should be kept for some reason.

    Checklist:

    • [x] Bump minor version to 2.3.0
    • [x] Add appropriate pytests
    opened by candid-elliott 2
  • Make confidential say when it doesnā€™t have access to decrypt

    Make confidential say when it doesnā€™t have access to decrypt

    If the SECRETS_FILE is not available it'll drop an error and trace:

    [email protected]:/opt/app# export SECRETS_FILE=".secrets/stage.json"
    [email protected]:/opt/app# python manage.py shell
    Traceback (most recent call last):
      File "manage.py", line 22, in <module>
        execute_from_command_line(sys.argv)
      File "/usr/local/lib/python3.6/site-packages/django/core/management/__init__.py", line 381, in execute_from_command_line
        utility.execute()
      File "/usr/local/lib/python3.6/site-packages/django/core/management/__init__.py", line 325, in execute
        settings.INSTALLED_APPS
      File "/usr/local/lib/python3.6/site-packages/django/conf/__init__.py", line 57, in __getattr__
        self._setup(name)
      File "/usr/local/lib/python3.6/site-packages/django/conf/__init__.py", line 44, in _setup
        self._wrapped = Settings(settings_module)
      File "/usr/local/lib/python3.6/site-packages/django/conf/__init__.py", line 107, in __init__
        mod = importlib.import_module(self.SETTINGS_MODULE)
      File "/usr/local/lib/python3.6/importlib/__init__.py", line 126, in import_module
        return _bootstrap._gcd_import(name[level:], package, level)
      File "<frozen importlib._bootstrap>", line 994, in _gcd_import
      File "<frozen importlib._bootstrap>", line 971, in _find_and_load
      File "<frozen importlib._bootstrap>", line 941, in _find_and_load_unlocked
      File "<frozen importlib._bootstrap>", line 219, in _call_with_frames_removed
      File "<frozen importlib._bootstrap>", line 994, in _gcd_import
      File "<frozen importlib._bootstrap>", line 971, in _find_and_load
      File "<frozen importlib._bootstrap>", line 955, in _find_and_load_unlocked
      File "<frozen importlib._bootstrap>", line 665, in _load_unlocked
      File "<frozen importlib._bootstrap_external>", line 678, in exec_module
      File "<frozen importlib._bootstrap>", line 219, in _call_with_frames_removed
      File "/opt/app/project/__init__.py", line 8, in <module>
        if settings.DOGSTATSD_ENABLED:
      File "/usr/local/lib/python3.6/site-packages/django/conf/__init__.py", line 57, in __getattr__
        self._setup(name)
      File "/usr/local/lib/python3.6/site-packages/django/conf/__init__.py", line 44, in _setup
        self._wrapped = Settings(settings_module)
      File "/usr/local/lib/python3.6/site-packages/django/conf/__init__.py", line 107, in __init__
        mod = importlib.import_module(self.SETTINGS_MODULE)
      File "/usr/local/lib/python3.6/importlib/__init__.py", line 126, in import_module
        return _bootstrap._gcd_import(name[level:], package, level)
      File "<frozen importlib._bootstrap>", line 994, in _gcd_import
      File "<frozen importlib._bootstrap>", line 971, in _find_and_load
      File "<frozen importlib._bootstrap>", line 955, in _find_and_load_unlocked
      File "<frozen importlib._bootstrap>", line 665, in _load_unlocked
      File "<frozen importlib._bootstrap_external>", line 678, in exec_module
      File "<frozen importlib._bootstrap>", line 219, in _call_with_frames_removed
      File "/opt/app/project/settings.py", line 27, in <module>
        region_name=AWS_REGION,
      File "/usr/local/lib/python3.6/site-packages/confidential/secrets_manager.py", line 23, in __init__
        secrets = self.parse_secrets_file(secrets_file) if secrets_file else {}
      File "/usr/local/lib/python3.6/site-packages/confidential/secrets_manager.py", line 85, in parse_secrets_file
        config[key] = json.loads(decrypted_string)
      File "/usr/local/lib/python3.6/json/__init__.py", line 348, in loads
        'not {!r}'.format(s.__class__.__name__))
    TypeError: the JSON object must be str, bytes or bytearray, not 'NoneType'
    
    opened by lassiter 2
  • Handle nested keys

    Handle nested keys

    what

    Addresses #12, an issue with accessing secrets in nested keys.

    Example:

    {
    "secrets": 
    	{
    	"key": "secret:value"
    	}
    }
    
    opened by vagelim 1
  • Support objects in json structure.

    Support objects in json structure.

    Currently, if you have a key inside an object in <env>.json that is

    {
        'service': {   
                  'api_key': 'secret:env/app/my_api_key',
                  'base_url': 'url'
        }
    }
    

    It will not see the secret inside the service object.

    However, if it's top level, it'll properly decrypt:

    {
       'another_key':  'secret:env/app/my_api_key'
    }
    

    Steps to Reproduce

    Take a working key from a top level of the json and place it inside the object like the first example. You should only see the the string stored in .json from where you store your secret paths.

    opened by lassiter 1
  • add private pypi push

    add private pypi push

    what

    Adds push to private pypi.

    why

    Confidential was apparently pushed manually to pypi using dvf's account. Until we get that access we should push to our internal pypi so that we can still iterate.

    opened by vagelim 0
  • Raise permission error on none-type SecretString response

    Raise permission error on none-type SecretString response

    Helpful error messaging for AWS permissions error. Currently unhelpful response is:

    manager.py", line 100, in decrypt_string
        result = json.loads(decrypted_string)
      File "/Users/dvf/.pyenv/versions/3.7.4/lib/python3.7/json/__init__.py", line 341, in loads
        raise TypeError(f'the JSON object must be str, bytes or bytearray, '
    TypeError: the JSON object must be str, bytes or bytearray, not NoneType
    

    ā­ļø Added test for {"SecretString": None} secrets manager response.

    opened by candid-elliott 0
  • add optional profile parameter

    add optional profile parameter

    What does this do?

    Adds an optional parameter (-p,--profile) to allow the user to pass in an alternative profile.

    Why did you do this?

    The current configuration limits confidential to the default profile. For users with multiple profiles, they may wish to use confidential with something beyond the default profile.

    How did you test this change?

    Ran with a known working profile, with a known non-working profile, with a profile that doesn't exist, and lastly, with no profile specified at all.

    etc

    I had thought of catching the exception raised when a profile is specified but could not be found (botocore.exceptions.ProfileNotFound) but found the error message to be descriptive enough on its own.

    ex:

    botocore.exceptions.ProfileNotFound: The config profile (candidco) could not be found
    
    opened by vagelim 0
  • Fix regression

    Fix regression

    We introduced a regression in v.2.1.0 where we don't check for an integer. This should fix that. Also added some more tests.

    Thanks to @jsundy for finding it.

    opened by dvf 0
  • fix builds

    fix builds

    what

    fixes builds

    why

    they dont work, specifically because of deprecated options passed to black. also because of an unavailable flag being passed to poetry ([ValueError] Setting settings.virtualenvs.create does not exist ##[error]Process completed with exit code 1.)

    https://github.com/candidco/confidential/runs/428287858

    opened by vagelim 0
  • Added deep overrides of secrets

    Added deep overrides of secrets

    Secrets files can now be deeply-merged:

    # defaults.json
    {
      "django": {
        "debug": true,
        "database": {
          "hostname": "123",
          "port": 8000,
        }
      }
    }
    
    # overrides.json
    {
      "django": {
        "database": {
          "hostname": "456",
        }
      }
    }
    

    Result:

    {
      "django": {
        "debug": true,
        "database": {
          "hostname": "456",
          "port": 8000,
        }
      }
    }
    

    Also fixed a minor typo.

    opened by dvf 0
  • Bump certifi from 2021.10.8 to 2022.12.7

    Bump certifi from 2021.10.8 to 2022.12.7

    Bumps certifi from 2021.10.8 to 2022.12.7.

    Commits

    Dependabot compatibility score

    Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting @dependabot rebase.


    Dependabot commands and options

    You can trigger Dependabot actions by commenting on this PR:

    • @dependabot rebase will rebase this PR
    • @dependabot recreate will recreate this PR, overwriting any edits that have been made to it
    • @dependabot merge will merge this PR after your CI passes on it
    • @dependabot squash and merge will squash and merge this PR after your CI passes on it
    • @dependabot cancel merge will cancel a previously requested merge and block automerging
    • @dependabot reopen will reopen this PR if it is closed
    • @dependabot close will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
    • @dependabot ignore this major version will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this minor version will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this dependency will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
    • @dependabot use these labels will set the current labels as the default for future PRs for this repo and language
    • @dependabot use these reviewers will set the current reviewers as the default for future PRs for this repo and language
    • @dependabot use these assignees will set the current assignees as the default for future PRs for this repo and language
    • @dependabot use this milestone will set the current milestone as the default for future PRs for this repo and language

    You can disable automated security fix PRs for this repo from the Security Alerts page.

    dependencies 
    opened by dependabot[bot] 0
Releases(v2.3.2)
Owner
Candidā„¢ļø
Candidā„¢ļø
A compact library for Python 3.10x that allows users to configure their SimPads real-time

SimpadLib v1.0.6 What is this? This is a python library programmed by Ashe Muller that allows users to interface directly with their SimPad devices, a

Ashe Muller 2 Jan 08, 2022
Simple dataclasses configuration management for Python with hocon/json/yaml/properties/env-vars/dict support.

Simple dataclasses configuration management for Python with hocon/json/yaml/properties/env-vars/dict support, based on awesome and lightweight pyhocon parsing library.

Teo Stocco 62 Dec 23, 2022
Secsie is a configuration language made for speed, beauty, and ease of use.

secsie-conf pip3 install secsie-conf Secsie is a configuration language parser for Python, made for speed and beauty. Instead of writing config files

Noah Broyles 3 Feb 19, 2022
Django-environ allows you to utilize 12factor inspired environment variables to configure your Django application.

Django-environ django-environ allows you to use Twelve-factor methodology to configure your Django application with environment variables. import envi

Daniele Faraglia 2.7k Jan 03, 2023
Kubernates Config Manager

Kubernates Config Manager Sometimes we need manage more than one kubernates cluster at the same time. Switch cluster configs is a dangerous and troubl

å‘Øę–‡é˜³ 3 Jan 10, 2022
Pyleri is an easy-to-use parser created for SiriDB

Python Left-Right Parser Pyleri is an easy-to-use parser created for SiriDB. We first used lrparsing and wrote jsleri for auto-completion and suggesti

Cesbit 106 Dec 06, 2022
Python-dotenv reads key-value pairs from a .env file and can set them as environment variables.

python-dotenv Python-dotenv reads key-value pairs from a .env file and can set them as environment variables. It helps in the development of applicati

Saurabh Kumar 5.5k Jan 04, 2023
Dag-bakery - Dag Bakery enables the capability to define Airflow DAGs via YAML.

DAG Bakery - WIP šŸ”§ dag-bakery aims to simplify our DAG development by removing all the boilerplate and duplicated code when defining multiple DAG cro

Typeform 2 Jan 08, 2022
Configuration Management for Python āš™

dynaconf - Configuration Management for Python. Features Inspired by the 12-factor application guide Settings management (default values, validation,

Bruno Rocha 2.8k Jan 06, 2023
A small example project for efficiently configuring a Python application with YAMLs and the CLI

Hydra Example Project for Python A small example project for efficiently configuring a Python application with YAMLs and the CLI. Why should I care? A

Florian Wilhelm 4 Dec 31, 2022
Configuration Extractor for EXE4J PE files

EXE4J Configuration Extractor This script helps reverse engineering Portable Executable files created with EXE4J by extracting their configuration dat

Karsten Hahn 6 Jun 29, 2022
A tool to manage configuration files, build scripts etc. across multiple projects.

A tool to manage configuration files, build scripts etc. across multiple projects.

8 Dec 14, 2022
Sync any your configuration file to remote. Currently only support gist.

Sync your configuration to remote, such as vimrc. You can use EscSync to manage your configure of editor, shell, etc.

Me1onRind 0 Nov 21, 2022
KConfig Browser is a graphical application which allows you to modify KDE configuration files found in ~/.config

kconfig_browser KConfig Browser is a graphical application which allows you to modify KDE configuration files found in ~/.config Screenshot Why I crea

11 Sep 15, 2022
An application pulls configuration information from JSON files generated

AP Provisioning Automation An application pulls configuration information from JSON files generated by Ekahau and then uses Netmiko to configure the l

Cisco GVE DevNet Team 1 Dec 17, 2021
A helper for organizing Django project settings by relying on well established programming patterns.

django-configurations django-configurations eases Django project configuration by relying on the composability of Python classes. It extends the notio

Jazzband 955 Jan 05, 2023
Inject your config variables into methods, so they are as close to usage as possible

Inject your config variables into methods, so they are as close to usage as possible

GDWR 7 Dec 14, 2022
Load Django Settings from Environmental Variables with One Magical Line of Code

DjEnv: Django + Environment Load Django Settings Directly from Environmental Variables features modify django configuration without modifying source c

Daniel J. Dufour 28 Oct 01, 2022
Read configuration settings from python configuration files.

Maison Read configuration settings from python configuration files. Motivation When developing a python application, e.g a command-line tool, it can b

9 Jan 04, 2023
Python Marlin Configurator to make valid configuration files to be used to compile Marlin with.

marlin-configurator Concept originally imagined by The-EG using PowerShell Build Script for Marlin Configurations The purpose of this project is to pa

DevPeeps 2 Oct 09, 2021