Skip to content

Instantly share code, notes, and snippets.

@antillean
Last active March 22, 2016 14:47
Show Gist options
  • Save antillean/e40dc2b059b05380d495 to your computer and use it in GitHub Desktop.
Save antillean/e40dc2b059b05380d495 to your computer and use it in GitHub Desktop.
Abort Sensitive Files

These scripts are designed to be used as a pre-receive or update hook on a git server. The context is an Ansible project in which sensitive data is encrypted using Ansible vault, and with a convention that all encrypted files have the .vault extension. The python script (abort-sensitive-files.py) verifies that:

  1. Every file encrypted with Ansible vault has a name ending with .vault.
  2. Every file with a name ending with .vault is ecnrypted with Ansible vault.
  3. No file contains an RSA private key in plaintext.

It's called by the bash script abort-sensitive-files.sh, which will have to be renamed to 'pre-receive' or 'update' and placed in the appropriate place in the git server.

NOTE: These scripts haven't been extensively tested. In fact, they're here because I couldn't use them on a project using BitBucket Cloud, because as of the time of this writing BitBucket cloud doesn't support server-side pre-receive or update hooks.

#!/usr/bin/env python
import fileinput, sys, os
########## Constants ###########
VAULT_ENDING = '.vault'
ANSIBLE_VAULT_CONTENTS_START = '$ANSIBLE_VAULT'
RSA_PRIVATE_KEY_CONTENTS_START = '-----BEGIN RSA PRIVATE KEY-----'
THIS_FILE_NAME = 'abort-sensitive-files.py'
########## Functions ###########
def ends_with_dot_vault(file_name):
'''Returns True if the specified file name ends with '.vault', False otherwise.'''
return file_name.endswith(VAULT_ENDING)
def is_encrypted_vault(line1):
'''Returns True if the specified line starts with the Ansible Vault code, False otherwise.'''
return line1.startswith(ANSIBLE_VAULT_CONTENTS_START)
def is_plaintext_private_key(name, line):
'''Returns True if the specified line starts with the RSA private key starter, False otherwise.'''
if name == THIS_FILE_NAME:
return False
return RSA_PRIVATE_KEY_CONTENTS_START in line
########## Call the Functions ###########
if __name__ == '__main__':
fail = False
for file_name in fileinput.input():
file_name = file_name.strip()
## A diff might include deleted files. Don't try to check them.
if not os.path.isfile(file_name):
continue
dot_vault = ends_with_dot_vault(file_name)
with open(file_name, "r") as f:
encrypted_vault_file = False
plaintext_private_key = False
for line_no, line in enumerate(f, 1):
if line_no == 1:
encrypted_vault_file = is_encrypted_vault(line)
if not plaintext_private_key:
plaintext_private_key = is_plaintext_private_key(file_name, line)
## End the loop early if we've already verified that the file
## has a plaintext private key.
if plaintext_private_key:
break
if plaintext_private_key:
message = "'%s' contains an unecrypted RSA private key." % file_name
fail = True
if encrypted_vault_file and not dot_vault:
message = "'%s' is an encrypted vault file but its name doesn't end in '.vault'." % file_name
fail = True
if dot_vault and not encrypted_vault_file:
message = "'%s' has a name that ends in '.vault' but it's not an encrypted vault file." % file_name
fail = True
if fail:
sys.exit(message)
else:
sys.exit()
#!/usr/bin/env sh
## This will be run as a server-side update hook, which'll be run as though
## from the root of the repository, so the path for the Python file is relative
## to that.
REF_NAME=$1
PREV_SHA1=$2
NEXT_SHA1=$3
git diff ${PREV_SHA1} ${NEXT_SHA1} --name-only | ./abort-sensitive-files.py
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment