jumpserver/apps/assets/automations/change_secret/manager.py

192 lines
7.0 KiB
Python
Raw Normal View History

2022-10-20 12:34:15 +00:00
import os
2022-10-13 09:47:29 +00:00
import random
import string
2022-10-20 12:34:15 +00:00
from hashlib import md5
2022-10-14 08:33:24 +00:00
from copy import deepcopy
2022-10-20 12:34:15 +00:00
from socket import gethostname
2022-10-14 08:33:24 +00:00
from collections import defaultdict
from django.utils import timezone
2022-10-13 09:47:29 +00:00
2022-10-20 12:34:15 +00:00
from common.utils import lazyproperty, gen_key_pair, ssh_pubkey_gen, ssh_key_string_to_obj
2022-10-19 09:05:21 +00:00
from assets.models import ChangeSecretRecord
from assets.const import (
2022-10-20 12:34:15 +00:00
AutomationTypes, SecretType, SecretStrategy, SSHKeyStrategy, DEFAULT_PASSWORD_RULES
2022-10-19 09:05:21 +00:00
)
2022-10-14 08:33:24 +00:00
from ..base.manager import BasePlaybookManager
2022-10-13 09:47:29 +00:00
class ChangeSecretManager(BasePlaybookManager):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.method_hosts_mapper = defaultdict(list)
2022-10-21 10:19:09 +00:00
self.secret_type = self.execution.snapshot['secret_type']
self.secret_strategy = self.execution.snapshot['secret_strategy']
2022-10-13 09:47:29 +00:00
self._password_generated = None
self._ssh_key_generated = None
self.name_recorder_mapper = {} # 做个映射,方便后面处理
@classmethod
def method_type(cls):
2022-10-21 10:19:09 +00:00
return AutomationTypes.change_secret
2022-10-13 09:47:29 +00:00
@lazyproperty
def related_accounts(self):
pass
@staticmethod
def generate_ssh_key():
private_key, public_key = gen_key_pair()
return private_key
2022-10-20 12:34:15 +00:00
@staticmethod
def generate_public_key(private_key):
return ssh_pubkey_gen(private_key=private_key, hostname=gethostname())
@staticmethod
def generate_private_key_path(secret, path_dir):
key_name = '.' + md5(secret.encode('utf-8')).hexdigest()
key_path = os.path.join(path_dir, key_name)
if not os.path.exists(key_path):
ssh_key_string_to_obj(secret, password=None).write_private_key_file(key_path)
os.chmod(key_path, 0o400)
return key_path
2022-10-13 09:47:29 +00:00
def generate_password(self):
2022-10-21 10:19:09 +00:00
kwargs = self.execution.snapshot['password_rules'] or {}
2022-10-13 09:47:29 +00:00
length = int(kwargs.get('length', DEFAULT_PASSWORD_RULES['length']))
symbol_set = kwargs.get('symbol_set')
if symbol_set is None:
symbol_set = DEFAULT_PASSWORD_RULES['symbol_set']
2022-10-19 09:05:21 +00:00
no_special_chars = string.ascii_letters + string.digits
chars = no_special_chars + symbol_set
first_char = random.choice(no_special_chars)
password = ''.join([random.choice(chars) for _ in range(length - 1)])
password = first_char + password
2022-10-13 09:47:29 +00:00
return password
def get_ssh_key(self):
2022-10-19 09:05:21 +00:00
if self.secret_strategy == SecretStrategy.custom:
2022-10-21 10:19:09 +00:00
ssh_key = self.execution.snapshot['ssh_key']
2022-10-19 09:05:21 +00:00
if not ssh_key:
raise ValueError("Automation SSH key must be set")
return ssh_key
elif self.secret_strategy == SecretStrategy.random_one:
2022-10-13 09:47:29 +00:00
if not self._ssh_key_generated:
self._ssh_key_generated = self.generate_ssh_key()
return self._ssh_key_generated
else:
2022-10-19 09:05:21 +00:00
return self.generate_ssh_key()
2022-10-13 09:47:29 +00:00
def get_password(self):
2022-10-19 09:05:21 +00:00
if self.secret_strategy == SecretStrategy.custom:
2022-10-21 10:19:09 +00:00
password = self.execution.snapshot['secret']
2022-10-19 09:05:21 +00:00
if not password:
2022-10-13 09:47:29 +00:00
raise ValueError("Automation Password must be set")
2022-10-19 09:05:21 +00:00
return password
elif self.secret_strategy == SecretStrategy.random_one:
2022-10-13 09:47:29 +00:00
if not self._password_generated:
self._password_generated = self.generate_password()
return self._password_generated
else:
2022-10-19 09:05:21 +00:00
return self.generate_password()
2022-10-13 09:47:29 +00:00
2022-10-20 12:34:15 +00:00
def get_secret(self):
if self.secret_type == SecretType.ssh_key:
2022-10-13 09:47:29 +00:00
secret = self.get_ssh_key()
2022-10-20 12:34:15 +00:00
elif self.secret_type == SecretType.password:
2022-10-13 09:47:29 +00:00
secret = self.get_password()
2022-10-19 09:05:21 +00:00
else:
2022-10-13 09:47:29 +00:00
raise ValueError("Secret must be set")
return secret
2022-10-20 12:34:15 +00:00
def get_kwargs(self, account, secret):
kwargs = {}
if self.secret_type != SecretType.ssh_key:
return kwargs
2022-10-21 10:19:09 +00:00
kwargs['strategy'] = self.execution.snapshot['ssh_key_change_strategy']
2022-10-20 12:34:15 +00:00
kwargs['exclusive'] = 'yes' if kwargs['strategy'] == SSHKeyStrategy.set else 'no'
if kwargs['strategy'] == SSHKeyStrategy.set_jms:
kwargs['dest'] = '/home/{}/.ssh/authorized_keys'.format(account.username)
kwargs['regexp'] = '.*{}$'.format(secret.split()[2].strip())
return kwargs
def host_callback(self, host, asset=None, account=None, automation=None, path_dir=None, **kwargs):
2022-10-13 09:47:29 +00:00
host = super().host_callback(host, asset=asset, account=account, automation=automation, **kwargs)
if host.get('error'):
return host
accounts = asset.accounts.all()
if account:
accounts = accounts.exclude(id=account.id)
2022-10-21 10:19:09 +00:00
if '*' not in self.execution.snapshot['accounts']:
accounts = accounts.filter(username__in=self.execution.snapshot['accounts'])
accounts = accounts.filter(secret_type=self.secret_type)
2022-10-13 09:47:29 +00:00
method_attr = getattr(automation, self.method_type() + '_method')
method_hosts = self.method_hosts_mapper[method_attr]
method_hosts = [h for h in method_hosts if h != host['name']]
inventory_hosts = []
records = []
2022-10-20 12:34:15 +00:00
host['secret_type'] = self.secret_type
2022-10-13 09:47:29 +00:00
for account in accounts:
h = deepcopy(host)
h['name'] += '_' + account.username
2022-10-20 12:34:15 +00:00
new_secret = self.get_secret()
2022-10-13 09:47:29 +00:00
recorder = ChangeSecretRecord(
account=account, execution=self.execution,
old_secret=account.secret, new_secret=new_secret,
)
records.append(recorder)
self.name_recorder_mapper[h['name']] = recorder
2022-10-20 12:34:15 +00:00
private_key_path = None
if self.secret_type == SecretType.ssh_key:
private_key_path = self.generate_private_key_path(new_secret, path_dir)
new_secret = self.generate_public_key(new_secret)
h['kwargs'] = self.get_kwargs(account, new_secret)
2022-10-13 09:47:29 +00:00
h['account'] = {
'name': account.name,
'username': account.username,
'secret_type': account.secret_type,
'secret': new_secret,
2022-10-20 12:34:15 +00:00
'private_key_path': private_key_path
2022-10-13 09:47:29 +00:00
}
inventory_hosts.append(h)
method_hosts.append(h['name'])
self.method_hosts_mapper[method_attr] = method_hosts
ChangeSecretRecord.objects.bulk_create(records)
return inventory_hosts
2022-10-14 08:33:24 +00:00
def on_host_success(self, host, result):
recorder = self.name_recorder_mapper.get(host)
if not recorder:
return
recorder.status = 'succeed'
recorder.date_finished = timezone.now()
recorder.save()
account = recorder.account
account.secret = recorder.new_secret
account.save(update_fields=['secret'])
def on_host_error(self, host, error, result):
recorder = self.name_recorder_mapper.get(host)
if not recorder:
return
recorder.status = 'failed'
recorder.date_finished = timezone.now()
recorder.error = error
recorder.save()
2022-10-13 09:47:29 +00:00
def on_runner_failed(self, runner, e):
pass