Skip to content

Instantly share code, notes, and snippets.

@cynici
Last active February 10, 2022 06:46
Show Gist options
  • Save cynici/6c425bcac436fd52f6b33a64a4ea80be to your computer and use it in GitHub Desktop.
Save cynici/6c425bcac436fd52f6b33a64a4ea80be to your computer and use it in GitHub Desktop.
DEVOPS-6911 Convert ipsec.secrets into Chef data bag per-user JSON files
#!/usr/bin/env python3
description = '''Convert vpn-{uat,prod}.mrdcourier.com:/etc/ipsec.secrets
into Chef data bag items, one user per item.
'''
import json
import os
import re
import sys
import logging
import argparse
from typing import List, NamedTuple, Dict
import pprint
def load_file(filepath: str, prefix: str, result: Dict) -> None:
'''Read input file and load into result'''
grep_jira = re.compile(r'^#\s*(DEVOPS-\d+).*')
grep_credential = re.compile(r'@(\S+)\s+:\s+EAP\s+"([^"]+)"')
grep_comment = re.compile(r"^\s*#")
with open(filepath) as fh:
lines = fh.readlines()
#print(len(lines))
for i, line in enumerate(lines):
if cred := grep_credential.search(line):
user, password = cred.groups()
disabled = grep_comment.search(line) is not None
if jira := grep_jira.search(lines[i-1]):
jira = jira.group(1)
else:
jira = None
if user not in result:
result[user] = {}
if prefix in result[user]:
print(f'Duplicate {prefix=} {user=} {i=} {line=}', file=sys.stderr)
result[user][prefix] = dict(disabled=disabled, password=password)
if jira:
result[user][prefix]['jira'] = jira
def merge_files_to_dict(args) -> None:
users = {}
load_file(args.uat_file, 'log_uat', users)
load_file(args.prod_file, 'log_prod', users)
#pprint.pprint(users, depth=3)
#print(json.dumps(users))
for user, d in users.items():
json_file = os.path.join(args.databag_dir, f'{user}.json')
with open(json_file, 'w') as fh:
databag = dict(id=user)
if 'log_uat' in d:
databag['log_uat'] = d['log_uat']
if 'log_prod' in d:
databag['log_prod'] = d['log_prod']
fh.write(json.dumps(databag, indent=2, sort_keys=True)+'\n')
def file_path(string: str) -> str:
if os.path.isfile(string):
return string
else:
raise argparse.ArgumentTypeError(f'{string} is not an existing file')
def dir_path(string: str) -> str:
if os.path.isdir(string):
return string
else:
raise argparse.ArgumentTypeError(f'{string} is not an existing directory')
if __name__ == "__main__":
parser = argparse.ArgumentParser(
description=description,
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
)
parser.add_argument('uat_file', type=file_path,
metavar='uat_file', help='UAT ipsec.secrets')
parser.add_argument('prod_file', type=file_path,
metavar='prod_file', help='production ipsec.secrets')
parser.add_argument('databag_dir', type=dir_path,
metavar='databag_dir', help='Directory to save output user data bags')
args = parser.parse_args()
merge_files_to_dict(args)
sys.exit(0)
@cynici
Copy link
Author

cynici commented Feb 10, 2022

Sample output JSON file

{
  "id": "muhammad.valodia",
  "log_prod": {
    "disabled": false,
    "jira": "DEVOPS-6468",
    "password": "XXXXX"
  },
  "log_uat": {
    "disabled": false,
    "jira": "DEVOPS-6468",
    "password": "XXXXX"
  }
}

I have decided to capture commented user accounts as well as JIRA ticket references useful for auditing. So, the cookbook logic has to take into account disabled.

users = data_bag(Your_databag_name)
erb_hash = {}
users.each do |user_id|
  u = data_bag_item(Your_databag_name, user_id)
  if u[environment] and not u[environment]['disabled']
    erb_hash[user_id] = u[environment]['password']
  end
end

if environment = 'log_prod'
  rsa_file = '/etc/ipsec.d/private/vpn-prod.mrdc.key'
else
  rsa_file = '/etc/ipsec.d/private/vpn-uat.mrdc.key'
end

template '/etc/ipsec.secrets' do
  source 'ipsec.secrets.erb'
  mode         '0444'
  owner        'root'
  group        'root'
  action       :create
  variables({
    :rsa_file   => rsa_file,
    :users         => erb_hash,
  notifies     :restart, "bash[reload_secrets]", :delayed
end

bash 'reload_secrets' do
  code <<-EOH
    ipsec secrets
  EOH
end

### ERB template
# This file holds shared secrets or RSA private keys for authentication.
# RSA private key for this host, authenticating it to any other host
# which knows the public part.
                    : RSA "<%= @rsa_file %>"
<%- @users.sort.each do |user_name, password| -%> 
<%= user_name %>: EAP "<%= password %>"
<%- end -%>

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment