Skip to content
This repository has been archived by the owner on Apr 21, 2022. It is now read-only.

Commit

Permalink
#9: Support for inline private keys in deployment.yml, refactor to us…
Browse files Browse the repository at this point in the history
…e RKD's TempManager
  • Loading branch information
blackandred committed Jul 27, 2020
1 parent 49f5b5a commit 20d4ac3
Show file tree
Hide file tree
Showing 3 changed files with 51 additions and 33 deletions.
2 changes: 1 addition & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
rkd>=2.0.0.0rc5
rkd>=2.0.0.0rc6
rkd-snippet-cooperative>=1.0.0.0rc2
ansible>=2.8
63 changes: 45 additions & 18 deletions src/harbor/tasks/deployment/base.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import os
import subprocess
from uuid import uuid4
from abc import ABC
from jinja2 import Environment
from jinja2 import FileSystemLoader
Expand All @@ -11,6 +10,7 @@
from rkd.yaml_parser import YamlFileLoader
from rkd.exception import MissingInputException
from rkd.inputoutput import Wizard
from rkd.temp import TempManager
from ..base import HarborBaseTask
from ...exception import MissingDeploymentConfigurationError

Expand All @@ -23,7 +23,12 @@ class BaseDeploymentTask(HarborBaseTask, ABC):
vault_args: list = []

def get_config(self) -> dict:
"""Loads and parses deployment.yml file. Supports Ansible Vault encryption"""
"""Loads and parses deployment.yml file.
Supports:
- Ansible Vault encryption of deployment.yml
- SSH private key storage inside deployment.yml
"""

deployment_filenames = ['deployment.yml', 'deployment.yaml']

Expand All @@ -44,34 +49,55 @@ def get_config(self) -> dict:
# When file is encrypted, then decrypt it
#
if content.startswith('$ANSIBLE_VAULT;'):
tmp_vault_filename = '.tmp-' + str(uuid4())
tmp_vault_path = './.rkd/' + tmp_vault_filename
tmp_vault_path, tmp_vault_filename = TempManager.create_tmp_file_path()

self.io().info('Decrypting deployment file')
self.sh('cp %s %s' % (filename, tmp_vault_path))

try:
self.rkd([':harbor:vault:encrypt', '-d', tmp_vault_path] + self.vault_args)
self._config = YamlFileLoader(self._ctx.directories).load_from_file(
tmp_vault_filename,
'org.riotkit.harbor/deployment/v1'
)
finally:
self.sh('rm -f %s' % tmp_vault_path)
self.rkd([':harbor:vault:encrypt', '-d', tmp_vault_path] + self.vault_args)
self._config = YamlFileLoader(self._ctx.directories).load_from_file(
tmp_vault_filename,
'org.riotkit.harbor/deployment/v1'
)

self._process_config_private_keys()
return self._config

self._config = YamlFileLoader(self._ctx.directories).load_from_file(
filename,
'org.riotkit.harbor/deployment/v1'
)

self._process_config_private_keys()
return self._config

raise MissingDeploymentConfigurationError()

return self._config

def _process_config_private_keys(self):
"""Allow private keys to be pasted directly to the deployment.yml
On-the-fly those keys will be written into the temporary directory
"""

for group_name, nodes in self._config['nodes'].items():
for node_num in range(0, len(nodes)):
if 'private_key' not in self._config['nodes'][group_name][node_num]:
continue

if '-----BEGIN OPENSSH PRIVATE KEY' not in self._config['nodes'][group_name][node_num]['private_key']:
continue

tmp_path = TempManager.assign_temporary_file(mode=0o700)

self.io().info('Storing inline private key as "%s"' % tmp_path)
with open(tmp_path, 'w') as key_file:
key_file.write(self._config['nodes'][group_name][node_num]['private_key'].strip())
key_file.write("\n")

self._config['nodes'][group_name][node_num]['private_key'] = tmp_path

def _verify_synced_version(self, abs_ansible_dir: str):
"""Verifies last synchronization - displays warning if Harbor version was changed after last
files synchronization"""
Expand Down Expand Up @@ -108,6 +134,8 @@ def _ask_and_set_var(self, ctx: ExecutionContext, arg_name: str, title: str, att
if not ctx.get_arg(arg_name):
return

wizard = Wizard(self).ask(title, attribute=attribute, secret=secret)

for group_name, nodes in self._config['nodes'].items():
node_num = 0

Expand All @@ -116,7 +144,6 @@ def _ask_and_set_var(self, ctx: ExecutionContext, arg_name: str, title: str, att
if attribute in self._config['nodes'][group_name][node_num - 1]:
continue

wizard = Wizard(self).ask(title, attribute=attribute, secret=secret)
self._config['nodes'][group_name][node_num - 1][attribute] = wizard.answers[attribute]

def install_and_configure_role(self, ctx: ExecutionContext, force_update: bool = False) -> bool:
Expand Down Expand Up @@ -245,7 +272,10 @@ def _preserve_vault_parameters_for_usage_in_inner_tasks(self, ctx: ExecutionCont
self.vault_args.append('--ask-vault-pass')

def _get_vault_opts(self, ctx: ExecutionContext, chdir: str = '') -> str:
"""Creates options to pass in Ansible Vault commandline"""
"""Creates options to pass in Ansible Vault commandline
The output will be a temporary vault file with password entered inline or a --ask-vault-pass switch
"""

try:
vault_passwords = ctx.get_arg_or_env('--vault-passwords').split('||')
Expand All @@ -269,7 +299,7 @@ def _get_vault_opts(self, ctx: ExecutionContext, chdir: str = '') -> str:
self.io().error('Vault password file "%s" does not exist, calling --ask-vault-pass' % passwd)
enforce_ask_pass = True
else:
tmp_vault_file = './.rkd/.tmp-vault-' + str(uuid4())
tmp_vault_file = TempManager.assign_temporary_file()

with open(tmp_vault_file, 'w') as f:
f.write(passwd)
Expand All @@ -281,9 +311,6 @@ def _get_vault_opts(self, ctx: ExecutionContext, chdir: str = '') -> str:

return opts

def _clear_old_vault_temporary_files(self):
self.sh('rm -f ./.rkd/.tmp-vault*', capture=True)

@classmethod
def _add_vault_arguments_to_argparse(cls, parser: ArgumentParser):
parser.add_argument('--ask-vault-pass', '-v', help='Ask for vault password interactively', action='store_true')
Expand Down
19 changes: 5 additions & 14 deletions src/harbor/tasks/deployment/vault.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,10 +44,7 @@ def run(self, context: ExecutionContext) -> bool:
vault_opts = self._get_vault_opts(context)
filename = context.get_arg('filename')

try:
subprocess.check_call('ansible-vault edit %s %s' % (vault_opts, filename), shell=True)
finally:
self._clear_old_vault_temporary_files()
subprocess.check_call('ansible-vault edit %s %s' % (vault_opts, filename), shell=True)

return True

Expand Down Expand Up @@ -81,10 +78,7 @@ def run(self, context: ExecutionContext) -> bool:
filename = context.get_arg('filename')
mode = 'decrypt' if context.get_arg('--decrypt') else 'encrypt'

try:
self.sh('ansible-vault %s %s %s' % (mode, vault_opts, filename), capture=False)
finally:
self._clear_old_vault_temporary_files()
self.sh('ansible-vault %s %s %s' % (mode, vault_opts, filename), capture=False)

return True

Expand Down Expand Up @@ -126,12 +120,9 @@ def run(self, context: ExecutionContext) -> bool:
src = '.env-prod'
dst = '.env'

try:
self.sh('cp %s %s-tmp' % (src, dst))
self.sh('ansible-vault %s %s %s-tmp' % (mode, vault_opts, dst), capture=False)
self.sh('mv %s-tmp %s' % (dst, dst))
finally:
self._clear_old_vault_temporary_files()
self.sh('cp %s %s-tmp' % (src, dst))
self.sh('ansible-vault %s %s %s-tmp' % (mode, vault_opts, dst), capture=False)
self.sh('mv %s-tmp %s' % (dst, dst))

if mode == 'encrypt':
try:
Expand Down

0 comments on commit 20d4ac3

Please sign in to comment.