mirror of
https://github.com/getsops/sops.git
synced 2026-02-05 12:45:21 +01:00
Added all-in-one example
This commit is contained in:
1
examples/all_in_one/.gitignore
vendored
Normal file
1
examples/all_in_one/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
config/secret.json
|
||||
81
examples/all_in_one/README.rst
Normal file
81
examples/all_in_one/README.rst
Normal file
@@ -0,0 +1,81 @@
|
||||
All-in-one example
|
||||
==================
|
||||
This directory is an example configuration for SOPS inside of a project. We will cover the files used and relevant scripts for developers.
|
||||
|
||||
This example is optimized for saving developer time by storing all secrets in a single file (e.g. ``secret.enc.json``).
|
||||
|
||||
One downside is any configurations which should be stored side by side might not be.
|
||||
|
||||
Getting started
|
||||
---------------
|
||||
To use this example, run the following:
|
||||
|
||||
.. code:: bash
|
||||
|
||||
# From the `sops` root directory
|
||||
# Import the test key
|
||||
gpg --import tests/sops_functional_tests_key.asc
|
||||
|
||||
# Navigate to our example directory
|
||||
cd examples/all_in_one
|
||||
|
||||
# Decrypt our secrets
|
||||
bin/decrypt-config.sh
|
||||
|
||||
# Optionally edit a secret
|
||||
# bin/edit-secret.sh config/secret.enc.json
|
||||
|
||||
# Run a script that uses our decrypted secrets
|
||||
python main.py
|
||||
|
||||
Storage
|
||||
-------
|
||||
In both development and production, we will be storing the secrets file unencrypted on disk. This is for a few reasons:
|
||||
|
||||
- Can't store file in an encrypted manner because we would need to know the secret to decode it
|
||||
- Loading it into memory at boot is impractical
|
||||
|
||||
- Requires reimplementing SOPS' decryption logic to multiple languages which increases chance of human error which is bad for security
|
||||
- If someone uses an automatic process reloader during development, then it could get expensive with AWS
|
||||
|
||||
- We could cache the results from AWS but those secrets would wind up being stored on disk
|
||||
|
||||
As peace of mind, think about this:
|
||||
|
||||
- Unencrypted on disk is fine because if the attacker ever gains access to the server, then they can run ``sops --decrypt`` as well.
|
||||
|
||||
Files
|
||||
-----
|
||||
- ``bin/decrypt-config.sh`` - Script to decrypt secret file
|
||||
- ``bin/edit-config-file.sh`` - Script to edit a secret file and then decrypt it
|
||||
- ``config/secret.enc.json`` - Catch-all file containing our secrets
|
||||
- ``config/secret.json`` - Decrypted catch-all secrets file
|
||||
- ``config/static.py`` - Configuration file which imports secrets
|
||||
- ``.gitignore`` - Ignore file for decrypted secret file
|
||||
- ``main.py`` - Example script
|
||||
|
||||
Usage
|
||||
-----
|
||||
Development
|
||||
~~~~~~~~~~~
|
||||
For development, each developer must have access to the PGP/KMS keys. This means:
|
||||
|
||||
- If we are using PGP, then each developer must have the private key installed on their local machine
|
||||
- If we are using KMS, then each developer must have AWS access to the appropriate key
|
||||
|
||||
Testing
|
||||
~~~~~~~
|
||||
For testing in a public CI, we can copy ``secret.enc.json`` to ``secret.json``. This will represent the same structure as ``secret.enc.json`` with an additional ``sops`` key but not reveal any secret information.
|
||||
|
||||
..
|
||||
|
||||
For convenience, we can run ``CONFIG_COPY_ONLY=TRUE bin/decrypt-config.sh`` which will use ``cp`` rather than ``sops --decrypt``.
|
||||
|
||||
For testing in a private CI where we need private information, see the `Production instructions <#production>`_.
|
||||
|
||||
Production
|
||||
~~~~~~~~~~
|
||||
For production, we have a few options:
|
||||
|
||||
- Build an archive (e.g. ``.tar.gz``) in a private CI which contains the secrets and deploy our service via the archive
|
||||
- Install PGP private key/KMS credentials on production machine, decrypt secrets during deployment process on production machine
|
||||
22
examples/all_in_one/bin/decrypt-config.sh
Executable file
22
examples/all_in_one/bin/decrypt-config.sh
Executable file
@@ -0,0 +1,22 @@
|
||||
#!/usr/bin/env bash
|
||||
# Exit on first error
|
||||
set -e
|
||||
|
||||
# Define our secret files
|
||||
secret_files="secret.enc.json"
|
||||
|
||||
# For each of our files in our encrypted config
|
||||
for file in $secret_files; do
|
||||
# Determine src and target for our file
|
||||
src_file="config/$file"
|
||||
target_file="$(echo "config/$file" | sed -E "s/.enc.json/.json/")"
|
||||
|
||||
# If we only want to copy, then perform a copy
|
||||
# DEV: We allow `CONFIG_COPY_ONLY` to handle tests in Travis CI
|
||||
if test "$CONFIG_COPY_ONLY" = "TRUE"; then
|
||||
cp "$src_file" "$target_file"
|
||||
# Otherwise, decrypt it
|
||||
else
|
||||
sops --decrypt "$src_file" > "$target_file"
|
||||
fi
|
||||
done
|
||||
36
examples/all_in_one/bin/edit-config-file.sh
Executable file
36
examples/all_in_one/bin/edit-config-file.sh
Executable file
@@ -0,0 +1,36 @@
|
||||
#!/usr/bin/env bash
|
||||
# Exit on first error
|
||||
set -e
|
||||
|
||||
# Define our secret files
|
||||
secret_files="secret.enc.json"
|
||||
|
||||
# Look up our file
|
||||
filepath="$1"
|
||||
if test "$filepath" = ""; then
|
||||
echo "Expected \`filepath\` but received nothing" 1>&2
|
||||
echo "Usage: $0 <filepath>" 1>&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# If our file is a secret
|
||||
filename="$(basename "$filepath")"
|
||||
if echo "$secret_files" | grep "$filename"; then
|
||||
# Load it into SOPS and run our sync script
|
||||
sops "$filepath"
|
||||
bin/decrypt-config.sh
|
||||
# Otherwise (it's a normal file)
|
||||
else
|
||||
# Resolve our editor via `sops` logic
|
||||
editor="$EDITOR"
|
||||
if test "$editor" = ""; then
|
||||
editor="$(which vim nano | head -n 1)"
|
||||
fi
|
||||
if test "$editor" = ""; then
|
||||
echo "Expected \`EDITOR\` environment variable to be defined but it was not" 1>&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Edit our file
|
||||
"$editor" "$filepath"
|
||||
fi
|
||||
0
examples/all_in_one/config/__init__.py
Normal file
0
examples/all_in_one/config/__init__.py
Normal file
22
examples/all_in_one/config/secret.enc.json
Normal file
22
examples/all_in_one/config/secret.enc.json
Normal file
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"github_oauth_token": "ENC[AES256_GCM,data:B2/q7WKJRqAUf4vvh8o=,iv:WpyxvYMHVMz4UvX2xZf79jaQYTSdepF96k87nMcuEro=,tag:FIqBSEuhh14JyjkGlW4hvw==,type:str]",
|
||||
"sops": {
|
||||
"lastmodified": "2016-02-12T05:14:20Z",
|
||||
"attention": "This section contains key material that should only be modified with extra care. See `sops -h`.",
|
||||
"unencrypted_suffix": "_unencrypted",
|
||||
"mac": "ENC[AES256_GCM,data:In6U2fZvaX2JI9jSnNYwWEYAuyh5YPUwCaAV5qeWON47zxxy4f2ad9gNwqxS4WsA7jf/DsYXv73OyXTqWV0sb8CBtzyVjzm9tgIcwNkKncROXY2njT+Y1LZAdIDPnz1Rw9PHOfZwHM8xxlly78uK/TuzX0nXADBtkIAAksVa3xw=,iv:/GjlxMSNouhw8yqsAG+bfGIz+YWz+/LNfrOHh/LC4OU=,tag:/5I+0uEWQ3hIL9ztuPQZwA==,type:str]",
|
||||
"version": 1.6,
|
||||
"kms": [
|
||||
{
|
||||
"arn": ""
|
||||
}
|
||||
],
|
||||
"pgp": [
|
||||
{
|
||||
"fp": "1022470DE3F0BC54BC6AB62DE05550BC07FB1A0A",
|
||||
"created_at": "2016-02-12T05:14:20Z",
|
||||
"enc": "-----BEGIN PGP MESSAGE-----\nVersion: GnuPG v1\n\nhIwDEEVDpnzXnMABA/9hQVFyHXnNCnE0YIcXvar4YtFyRNnuV5zTaQskRqPYS14z\nPkqUpx03PVsT4c84YLx3bAu9OM0so8fsXAW7+YpX5A1ZChWpy0Qt7lg6k/4qyUSl\nMO5x+4ZdU5C866no0Q3UHrBy0oxORYUwbKsWU8IMSNWuSXGcqRsU0nyschvhb9Je\nARNsbbGsL2qeaYqjTwD3p3awkef2voVwYGTuSbvKcEfb5X1JrWrX1Igk8wwCe/uw\nlB2SXpk1bQ16ZfsT39+bOaeu6v8mREHG+KKk3k7ddFmML5TbPYqSme7BcW1IQA==\n=c6L/\n-----END PGP MESSAGE-----\n"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
12
examples/all_in_one/config/static.py
Normal file
12
examples/all_in_one/config/static.py
Normal file
@@ -0,0 +1,12 @@
|
||||
# Load in our dependencies
|
||||
import json
|
||||
|
||||
# Load in our secrets
|
||||
with open('config/secret.json', 'r') as file:
|
||||
secret = json.loads(file.read())
|
||||
|
||||
# Define our configuration
|
||||
common = {
|
||||
'github_oauth_token': secret['github_oauth_token'],
|
||||
'port': 8080,
|
||||
}
|
||||
18
examples/all_in_one/main.py
Normal file
18
examples/all_in_one/main.py
Normal file
@@ -0,0 +1,18 @@
|
||||
# Load in our dependencies
|
||||
from __future__ import absolute_import
|
||||
from config.static import common
|
||||
|
||||
|
||||
# Define our main function
|
||||
def main():
|
||||
# Output our configuration
|
||||
print('Configuration')
|
||||
print('-------------')
|
||||
for key in common:
|
||||
# Example: `port: "8080"`
|
||||
print('{key}: "{val}"'.format(key=key, val=common[key]))
|
||||
|
||||
|
||||
# If this script is being invoked directly, then run our main function
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
Reference in New Issue
Block a user