Skip to content

Commit

Permalink
some more crumbs
Browse files Browse the repository at this point in the history
  • Loading branch information
krysopath committed Apr 11, 2019
1 parent 40d1642 commit 0ca2709
Show file tree
Hide file tree
Showing 15 changed files with 187 additions and 52 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
# test files
tests/new*

# security
secring.*

Expand Down
33 changes: 26 additions & 7 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,20 +1,39 @@
FROM python:3.6.4-alpine3.7 as production
ARG BASE_IMAGE

FROM python:$BASE_IMAGE as developement
WORKDIR /code
COPY requirements.txt .

RUN mkdir secrets \
RUN mkdir secrets assets\
&& apk add --no-cache \
gnupg\
openssl\
make\
&& pip3 install -r requirements.txt\
&& adduser -D vaultify\
&& chown vaultify .

COPY ./Makefile Makefile
COPY ./vaultify vaultify
COPY ./entry.py entry.py

COPY ./setup.py setup.py
COPY ./tests tests
COPY ./runtests.py runtests.py
RUN make run/tests
RUN make artifact/pkg
USER vaultify

ENTRYPOINT ["python3"]
CMD ["/code/entry.py"]
CMD ["/code/entry.py", "{*}"]

FROM python:$BASE_IMAGE as production
WORKDIR /code
COPY --from=0 /code/dist/vaultify*.whl /code/
COPY --from=0 /code/requirements.txt .
RUN mkdir secrets assets\
&& apk add --no-cache \
gnupg\
openssl\
&& pip3 install -r requirements.txt\
&& pip3 install vaultify*.whl\
&& adduser -D vaultify\
&& chown vaultify .
USER vaultify
ENTRYPOINT ["vaultify"]
38 changes: 37 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
@@ -1,20 +1,56 @@
TARGET := production
BASE_IMAGE := 3.7-alpine3.7

run/test:
ci: run/tests\
artifact/pkg\
artifact/docker\
artifact/tag

dev/ci:
ls vaultify/*py Dockerfile .vaultify.yml | entr -s 'make ci'

artifact/pkg:
python3 setup.py sdist bdist_wheel

artifact/docker: Dockerfile
docker build \
--build-arg BASE_IMAGE=$(BASE_IMAGE)\
-t vaultify:$(shell git rev-parse --short HEAD)\
--target $(TARGET)\
.
docker tag\
vaultify:$(shell git rev-parse --short HEAD)\
vaultify:$(shell git describe --abbrev=0 --tags)-py$(BASE_IMAGE)

artifact/tag:
docker tag\
vaultify:$(shell git rev-parse --short HEAD)\
vaultify:$(shell git describe --abbrev=0 --tags)

run/tests:
rm -rf tests/new* assets/*
gpg \
--symmetric\
--batch\
--passphrase=abc\
-o assets/test.gpg\
tests/secrets.env

openssl enc \
-k abc \
-aes-256-cbc \
-salt \
-a \
-in tests/secrets.env \
-out assets/test.enc

VAULTIFY_LOG_LEVEL=WARNING python3 runtests.py

manual:
@groff -man -Tascii man/vaultify.1

dev/install/packaging:
python3 -m pip install --user --upgrade pip setuptools wheel

dev/install/os:
sudo apt-get install gnupg openssl
27 changes: 17 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
# vaultify

Our greater purpose is to **never leave any secret file on a persisting
Our greater purpose is to **never leave any secret file on a persisting
filesystem.**

`vaultify` allows to integrate various means of secret provision into
any runtime environment, may it be some `containerd`, virtualized box or
bare-metal. Currently, we provide three means of fetching secrets and three
any runtime environment, may it be some `containerd`, virtualized box or
bare-metal. Currently, we provide three means of fetching secrets and three
means of consuming them.


Expand All @@ -32,6 +32,13 @@ current environment.
What will be done actually is configurable via the environment, that
executes vaultify.

## os dependencies

ubuntu:
```
apt-get install entr make
```

## usage

### first steps
Expand Down Expand Up @@ -68,12 +75,12 @@ pairs are supported:
| JsonWriter | y | y | y |
```

Assuming the pattern holds, we expect always full compatibility
Assuming the pattern holds, we expect always full compatibility
between any Provider/Consumer pair.

### providers

Providers are all classes, that create some `vaultify`-compliant dictionary,
Providers are all classes, that create some `vaultify`-compliant dictionary,
which then is used by `vaultify` consumers.

#### GPGProvider
Expand Down Expand Up @@ -118,7 +125,7 @@ openssl enc -aes-256-cbc -salt -a -in <file> -out <file>.enc
> Do not use aes-256-cbc, if there is aes-256-gcm available in your openssl.
This prevents Padding Oracle attacks against the cipher text. Currently
setting the aes cipher is not possible in `vaultify` but will be made, when
the default openssl library ships with AEAD compiled. If your OpenSSL CLI
the default openssl library ships with AEAD compiled. If your OpenSSL CLI
supports aes-256-gcm, please file a bug report against vaultify.

Below are environment variables, that are needed by this provider:
Expand All @@ -130,7 +137,7 @@ export VAULTIFY_SECRET=<passphrase>

#### VaultProvider

This provider fetches secrets from HashiCorp Vault API.
This provider fetches secrets from HashiCorp Vault API.


Below are environment variables, that are needed by this provider:
Expand All @@ -145,7 +152,7 @@ export VAULT_PATHS=<comma-separated-list-of-paths-for-vaults-kv-engine>
export VAULT_TOKEN=<a-valid-vault-token>
```

`VaultProvider` will use `VAULTIFY_SECRET` or `VAULT_TOKEN` for authentication,
`VaultProvider` will use `VAULTIFY_SECRET` or `VAULT_TOKEN` for authentication,
in that order.

### consumers
Expand Down Expand Up @@ -189,14 +196,14 @@ export VAULTIFY_DESTFILE=/a/path/to/where/secrets.json
#### EnvRunner

If you want to just execute a process with some secrets, then
`EnvRunner` consumer will run a subprocess with an enriched
`EnvRunner` consumer will run a subprocess with an enriched
environment for you.

>In that sense `EnvRunner` doubles as an entry point for docker runtimes.
Choose this, if you want to prevent any kind of secret persistence.

> one might not like having docker `tmpfs` volumes swapped or
> one might not like having docker `tmpfs` volumes swapped or
accidentally persist after a crash

Below are environment variables, that are needed by this consumer:
Expand Down
2 changes: 1 addition & 1 deletion entry.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-

import sys
from vaultify import main

if __name__ == "__main__":
Expand Down
6 changes: 5 additions & 1 deletion runtests.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,8 @@
suite = unittest.TestSuite()
for module in files:
suite.addTest(doctest.DocTestSuite(module))
unittest.TextTestRunner(verbosity=1).run(suite)

results = unittest.TextTestRunner(verbosity=1).run(suite)
if any([results.failures, results.errors]):
num = len(results.failures) + len(results.errors)
exit(num)
2 changes: 2 additions & 0 deletions setup.cfg
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
[metadata]
description-file = README.md
23 changes: 23 additions & 0 deletions setup.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
#!/usr/bin/env python3
"""
run setup for vaultify
"""
from setuptools import setup

setup(
name='vaultify',
packages=['vaultify'],
version='2.0.0',
description='A hexagon of secret provisoning',
author='Georg vom Endt',
author_email='[email protected]',
url='https://github.com/krysopath/vaultify',
download_url='https://github.com/author/repo/tarball/2.0.0',
keywords=['vault', 'gpg', 'secrets', 'docker', 'cli'],
entry_points={
'console_scripts': [
'vaultify=vaultify.vaultify:main',
],
},
classifiers=[],
)
5 changes: 2 additions & 3 deletions vaultify/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@

class Provider:
def __str__(self):
return f'{self.__class__}'
return '{}'.format(self.__class__)

@abc.abstractmethod
def get_secrets(self) -> dict:
Expand All @@ -23,8 +23,7 @@ def get_secrets(self) -> dict:

class Consumer(metaclass=abc.ABCMeta):
def __str__(self):
return f'{self.__class__}'

return '{}'.format(self.__class__)

@abc.abstractmethod
def consume_secrets(self,
Expand Down
7 changes: 4 additions & 3 deletions vaultify/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@
MODULE_BASE_DIR = os.path.dirname(
os.path.realpath(__file__))
ETC_DEFAULT_CONFIG = '/etc/default/vaultify.yml'
USER_CONFIG = f'{os.environ.get("HOME")}/.vaultify.yml'
LOCAL_CONFIG = f'{os.environ.get("PWD")}/.vaultify.yml'
USER_CONFIG = '{}/.vaultify.yml'.format(os.environ.get("HOME"))
LOCAL_CONFIG = '{}/.vaultify.yml'.format(os.environ.get("PWD"))

CFG_DEFAULT_FILES = [
ETC_DEFAULT_CONFIG,
Expand Down Expand Up @@ -140,5 +140,6 @@ def configure(
"LOCAL_CONFIG",
"CFG_DEFAULT_FILES",
"BASE_CFG",
"LOG_CFG"
"LOG_CFG",
"configure"
)
15 changes: 7 additions & 8 deletions vaultify/consumers.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,11 +60,11 @@ def consume_secrets(self, data: dict) -> bool:
That file can be sourced or evaluated with the unix shell
"""
if os.path.exists(self.path) and not self.overwrite:
raise RuntimeError(f'{self.path} already exists')
raise RuntimeError('{} already exists'.format(self.path))

with open(self.path, 'w') as secrets_file:
logger.info(
f"consuming {self}",
"consuming {}".format(self)
)

secrets_file.write(
Expand Down Expand Up @@ -103,16 +103,14 @@ def __init__(self, path: str, overwrite: bool = False):
self.path = path
self.overwrite = overwrite

def __str__(self):
return f'{self.__class__}->{self.path}'

def consume_secrets(self, data: dict):
"""
Write data as json into fname
That file can be evaluated by any json-aware application.
"""
if os.path.exists(self.path) and not self.overwrite:
raise RuntimeError(f'{self.path} already exists')
raise RuntimeError('{} already exists'.format(self.path))

with open(self.path, 'w') as json_file:
logger.info(
Expand Down Expand Up @@ -159,7 +157,7 @@ def consume_secrets(self, data: dict):
{key: value}
)
logger.info(
f'{self} enriched the environment')
'{} enriched the environment'.format(self))

try:
# TODO Overhaul this
Expand All @@ -170,11 +168,12 @@ def consume_secrets(self, data: dict):
env=prepared_env
)
logger.info(
f'running the process "{self.path}"')
'running the process "{}"'.format(self.path))

except FileNotFoundError as error:
logger.critical(
f'error in {self} executing "{self.path}"')
'error in {} executing "{}"'.format(self, self.path)
)
raise error

print(
Expand Down
2 changes: 2 additions & 0 deletions vaultify/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@


class ValidationError(Exception):
def __str___(self):
return "{} did not validate successfully".format(self.args)
"""
Serves as the common base class for vaultify validation problems.
"""
Expand Down
14 changes: 7 additions & 7 deletions vaultify/providers.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ def get_secrets(self):
for path in self.paths:
secrets[path] = self.client.read(path)["data"]
logger.info(
f'provided secrets from {path}')
'provided secrets from {}'.format(path))
return secrets


Expand Down Expand Up @@ -101,14 +101,14 @@ def get_secrets(self):
out = run_process(
['openssl', 'aes-256-cbc',
'-d', '-a',
'-in', f'{filename}',
'-k', f'{self.secret}'],
'-in', filename,
'-k', self.secret],
self.popen_kwargs
)

secrets[filename] = env2dict(out)
logger.info(
f'provided secrets from {filename}')
'provided secrets from {}'.format(filename))
return secrets


Expand Down Expand Up @@ -139,12 +139,12 @@ def get_secrets(self):
out = run_process(
['gpg', '-qd',
'--yes', '--batch',
f'--passphrase={self.secret}',
f'{filename}'],
'--passphrase={}'.format(self.secret),
filename],
self.popen_kwargs
)
secrets[filename] = env2dict(out)
logger.info(
f'provided secrets from {filename}')
'provided secrets from {}'.format(filename))

return secrets
Loading

0 comments on commit 0ca2709

Please sign in to comment.