Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/ttl support #170

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 36 additions & 21 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,11 @@ Credentials stored in the credential-store are versioned and immutable. That is,

If you use incrementing integer version numbers (for example, `[1, 2, 3, ...]`), then you can use the `-a` flag with the `put` command to automatically increment the version number. However, because of the lexicographical sorting in DynamoDB, `credstash` will left-pad the version representation with zeros (for example, `[001, 025, 103, ...]`, except to 19 characters, enough to handle `sys.maxint` on 64-bit systems).

### Expiring Secrets
There may be situations where secrets can expire after a fixed time period. For example, SSL certificates may expire after 1 year. Use the `-e` flag when you `put` a secret to set the number of seconds the record will be valid. Credstash uses the Dynamo DB TTL feature to automatically delete this record when the expiry period is up. Note that Dynamo DB may take up to 48 hours to delete records after the expiry period is up.

When the credential store table is created credstash attempts to enable DynamoDB TTL. This attempt may fail in which case the table will be created without TTL and secrets will not be automatically removed. Credstash will report any exceptions that occur while enabling TTL but the table will be created regardless. Very likely, if TTL cannot be enabled on the table the feature is not supported in the selected region. This being the case you can still use credstash without the auto expiry feature.

#### Special Note for Those Using Credstash Auto-Versioning Before December 2015
Prior to December 2015, `credstash` auto-versioned with unpadded integers. This resulted in a sorting error once a key hit ten versions. To ensure support for versions that were not numbers (such as dates, build versions, names, etc.), the lexicographical sorting behavior was retained, but the auto-versioning behavior was changed to left-pad integer representations.

Expand Down Expand Up @@ -171,29 +176,39 @@ list
usage: credstash list [-h] [-r REGION] [-t TABLE]

put
usage: credstash put [-h] [-k KEY] [-v VERSION] [-a]
credential value [context [context ...]]
usage: credstash put [-h] [-k KEY] [-v VERSION] [-a]
[-d {SHA,MD5,RIPEMD,SHA384,SHA224,SHA256,SHA512,WHIRLPOOL}]
[-e EXPIRY]
credential value [context [context ...]]

positional arguments:
credential the name of the credential to store
value the value of the credential to store or, if beginning
with the "@" character, the filename of the file
containing the value
context encryption context key/value pairs associated with the
credential in the form of "key=value"
positional arguments:
credential the name of the credential to store
value the value of the credential to store or, if beginning
with the "@" character, the filename of the file
containing the value, or pass "-" to read the value
from stdin
context encryption context key/value pairs associated with the
credential in the form of "key=value"

optional arguments:
-h, --help show this help message and exit
-k KEY, --key KEY the KMS key-id of the master key to use. See the
README for more information. Defaults to
alias/credstash
-v VERSION, --version VERSION
Put a specific version of the credential (update the
credential; defaults to version `1`).
-a, --autoversion Automatically increment the version of the credential
to be stored. This option causes the `-v` flag to be
ignored. (This option will fail if the currently
stored version is not numeric.)
optional arguments:
-h, --help show this help message and exit
-k KEY, --key KEY the KMS key-id of the master key to use. See the
README for more information. Defaults to
alias/credstash
-v VERSION, --version VERSION
Put a specific version of the credential (update the
credential; defaults to version `1`).
-a, --autoversion Automatically increment the version of the credential
to be stored. This option causes the `-v` flag to be
ignored. (This option will fail if the currently
stored version is not numeric.)
-d {SHA,MD5,RIPEMD,SHA384,SHA224,SHA256,SHA512,WHIRLPOOL}, --digest {SHA,MD5,RIPEMD,SHA384,SHA224,SHA256,SHA512,WHIRLPOOL}
the hashing algorithm used to to encrypt the data.
Defaults to SHA256
-e EXPIRY, --expiry EXPIRY
Set an expiry time for this record. The number of
seconds the record will be valid for. Defaults to -1,
i.e. 'never expires'.

setup
usage: credstash setup [-h] [-r REGION] [-t TABLE]
Expand Down
34 changes: 32 additions & 2 deletions credstash.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@
import re
import boto3
import botocore.exceptions
import calendar
from time import gmtime

try:
from StringIO import StringIO
Expand Down Expand Up @@ -264,7 +266,7 @@ def listSecrets(region=None, table="credential-store", **kwargs):
return response["Items"]


def putSecret(name, secret, version="", kms_key="alias/credstash",
def putSecret(name, secret, version="", expiry=-1, kms_key="alias/credstash",
region=None, table="credential-store", context=None,
digest=DEFAULT_DIGEST, **kwargs):
'''
Expand All @@ -290,6 +292,10 @@ def putSecret(name, secret, version="", kms_key="alias/credstash",
'version': paddedInt(version),
}
data.update(sealed)
if expiry != -1:
data.update({
'ttl': calendar.timegm(gmtime()) + expiry
});

return secrets.put_item(Item=data, ConditionExpression=Attr('name').not_exists())

Expand Down Expand Up @@ -369,8 +375,16 @@ def putSecretAction(args, region, **session_params):
"version: %s is not an int" % latestVersion)
else:
version = args.version
if args.expiry != "":
try:
expiry = int(args.expiry)
except ValueError:
fatal("Expiry must be an integer value. The number "
"of seconds the record will be valid for.")
else:
expiry = -1
try:
if putSecret(args.credential, args.value, version,
if putSecret(args.credential, args.value, version, expiry,
kms_key=args.key, region=region, table=args.table,
context=args.context, digest=args.digest,
**session_params):
Expand Down Expand Up @@ -523,6 +537,18 @@ def createDdbTable(region=None, table="credential-store", **kwargs):
client = session.client("dynamodb", region_name=region)
client.get_waiter("table_exists").wait(TableName=table)

try:
client.update_time_to_live(
TableName=table,
TimeToLiveSpecification={
'Enabled': True,
'AttributeName': 'ttl'
}
)
except botocore.exceptions.ClientError as e:
print("Exception while enabling TTL:")
print(str(e), file=sys.stderr)

print("Table has been created. "
"Go read the README about how to create your KMS key")

Expand Down Expand Up @@ -776,6 +802,10 @@ def get_parser():
choices=HASHING_ALGORITHMS,
help="the hashing algorithm used to "
"to encrypt the data. Defaults to SHA256")
parsers[action].add_argument("-e", "--expiry", default="",
help="Set an expiry time for this record. "
"The number of seconds the record will be "
"valid for. Defaults to -1, i.e. 'never expires'.")
parsers[action].set_defaults(action=action)

action = 'setup'
Expand Down
2 changes: 1 addition & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
cryptography>=1.5
boto3>=1.1.1
boto3>=1.4.7