mirror of https://github.com/jumpserver/jumpserver
perf: Change secret
parent
d9af381570
commit
eefda353d2
|
@ -46,7 +46,7 @@ class ChangeSecretManager(AccountBasePlaybookManager):
|
||||||
def method_type(cls):
|
def method_type(cls):
|
||||||
return AutomationTypes.change_secret
|
return AutomationTypes.change_secret
|
||||||
|
|
||||||
def get_ssh_params(self, account, secret, secret_type):
|
def get_ssh_params(self, secret, secret_type):
|
||||||
kwargs = {}
|
kwargs = {}
|
||||||
if secret_type != SecretType.SSH_KEY:
|
if secret_type != SecretType.SSH_KEY:
|
||||||
return kwargs
|
return kwargs
|
||||||
|
@ -65,6 +65,7 @@ class ChangeSecretManager(AccountBasePlaybookManager):
|
||||||
asset = privilege_account.asset
|
asset = privilege_account.asset
|
||||||
accounts = asset.accounts.all()
|
accounts = asset.accounts.all()
|
||||||
accounts = accounts.filter(id__in=self.account_ids)
|
accounts = accounts.filter(id__in=self.account_ids)
|
||||||
|
|
||||||
if self.secret_type:
|
if self.secret_type:
|
||||||
accounts = accounts.filter(secret_type=self.secret_type)
|
accounts = accounts.filter(secret_type=self.secret_type)
|
||||||
|
|
||||||
|
@ -74,37 +75,36 @@ class ChangeSecretManager(AccountBasePlaybookManager):
|
||||||
)
|
)
|
||||||
return accounts
|
return accounts
|
||||||
|
|
||||||
def gen_new_secret(self, account, path_dir):
|
def get_secret(self, account):
|
||||||
if self.secret_type is None:
|
if self.secret_strategy == SecretStrategy.custom:
|
||||||
new_secret = account.secret
|
new_secret = self.execution.snapshot['secret']
|
||||||
else:
|
else:
|
||||||
if self.secret_strategy == SecretStrategy.custom:
|
generator = SecretGenerator(
|
||||||
new_secret = self.execution.snapshot['secret']
|
self.secret_strategy, self.secret_type,
|
||||||
else:
|
self.execution.snapshot.get('password_rules')
|
||||||
generator = SecretGenerator(
|
)
|
||||||
self.secret_strategy, self.secret_type,
|
new_secret = generator.get_secret()
|
||||||
self.execution.snapshot.get('password_rules')
|
return new_secret
|
||||||
)
|
|
||||||
new_secret = generator.get_secret()
|
|
||||||
|
|
||||||
recorder_secret = new_secret
|
def gen_new_secret(self, account, new_secret, path_dir):
|
||||||
private_key_path = None
|
private_key_path = None
|
||||||
if account.secret_type == SecretType.SSH_KEY:
|
if account.secret_type == SecretType.SSH_KEY:
|
||||||
private_key_path = self.generate_private_key_path(new_secret, path_dir)
|
private_key_path = self.generate_private_key_path(new_secret, path_dir)
|
||||||
new_secret = self.generate_public_key(new_secret)
|
new_secret = self.generate_public_key(new_secret)
|
||||||
return new_secret, private_key_path, recorder_secret
|
return new_secret, private_key_path
|
||||||
|
|
||||||
def get_or_create_record(self, asset, account, new_secret, name):
|
def get_or_create_record(self, asset, account, name):
|
||||||
asset_account_id = f'{asset.id}-{account.id}'
|
asset_account_id = f'{asset.id}-{account.id}'
|
||||||
|
|
||||||
if asset_account_id in self.record_map:
|
if asset_account_id in self.record_map:
|
||||||
record_id = self.record_map[asset_account_id]
|
record_id = self.record_map[asset_account_id]
|
||||||
recorder = ChangeSecretRecord.objects.filter(id=record_id).first()
|
recorder = ChangeSecretRecord.objects.filter(id=record_id).first()
|
||||||
else:
|
else:
|
||||||
|
new_secret = self.get_secret(account)
|
||||||
recorder = self.create_record(asset, account, new_secret)
|
recorder = self.create_record(asset, account, new_secret)
|
||||||
|
|
||||||
if recorder:
|
self.name_recorder_mapper[name] = recorder
|
||||||
self.name_recorder_mapper[name] = recorder
|
return recorder
|
||||||
|
|
||||||
@bulk_create_decorator(ChangeSecretRecord)
|
@bulk_create_decorator(ChangeSecretRecord)
|
||||||
def create_record(self, asset, account, new_secret):
|
def create_record(self, asset, account, new_secret):
|
||||||
|
@ -115,11 +115,9 @@ class ChangeSecretManager(AccountBasePlaybookManager):
|
||||||
)
|
)
|
||||||
return recorder
|
return recorder
|
||||||
|
|
||||||
def gen_change_secret_inventory(self, host, account, new_secret, private_key_path, asset):
|
def gen_change_secret_inventory(self, h, account, new_secret, private_key_path, asset):
|
||||||
h = deepcopy(host)
|
|
||||||
secret_type = account.secret_type
|
secret_type = account.secret_type
|
||||||
h['name'] += '(' + account.username + ')'
|
h['ssh_params'].update(self.get_ssh_params(new_secret, secret_type))
|
||||||
h['ssh_params'].update(self.get_ssh_params(account, new_secret, secret_type))
|
|
||||||
h['account'] = {
|
h['account'] = {
|
||||||
'name': account.name,
|
'name': account.name,
|
||||||
'username': account.username,
|
'username': account.username,
|
||||||
|
@ -144,7 +142,7 @@ class ChangeSecretManager(AccountBasePlaybookManager):
|
||||||
host['ssh_params'] = {}
|
host['ssh_params'] = {}
|
||||||
|
|
||||||
accounts = self.get_accounts(account)
|
accounts = self.get_accounts(account)
|
||||||
error_msg = _("! No pending accounts found")
|
error_msg = _("No pending accounts found")
|
||||||
if not accounts:
|
if not accounts:
|
||||||
print(f'{asset}: {error_msg}')
|
print(f'{asset}: {error_msg}')
|
||||||
return []
|
return []
|
||||||
|
@ -154,13 +152,15 @@ class ChangeSecretManager(AccountBasePlaybookManager):
|
||||||
|
|
||||||
inventory_hosts = []
|
inventory_hosts = []
|
||||||
if asset.type == HostTypes.WINDOWS and self.secret_type == SecretType.SSH_KEY:
|
if asset.type == HostTypes.WINDOWS and self.secret_type == SecretType.SSH_KEY:
|
||||||
print(f'! Windows {asset} does not support ssh key push')
|
print(f'Windows {asset} does not support ssh key push')
|
||||||
return inventory_hosts
|
return inventory_hosts
|
||||||
|
|
||||||
for account in accounts:
|
for account in accounts:
|
||||||
new_secret, private_key_path, recorder_secret = self.gen_new_secret(account, path_dir)
|
h = deepcopy(host)
|
||||||
h = self.gen_change_secret_inventory(host, account, new_secret, private_key_path, asset)
|
h['name'] += '(' + account.username + ')' # To distinguish different accounts
|
||||||
self.get_or_create_record(asset, account, recorder_secret, h['name'])
|
record = self.get_or_create_record(asset, account, h['name'])
|
||||||
|
new_secret, private_key_path = self.gen_new_secret(account, record.new_secret, path_dir)
|
||||||
|
h = self.gen_change_secret_inventory(h, account, new_secret, private_key_path, asset)
|
||||||
inventory_hosts.append(h)
|
inventory_hosts.append(h)
|
||||||
|
|
||||||
return inventory_hosts
|
return inventory_hosts
|
||||||
|
|
|
@ -10,3 +10,6 @@ class PushAccountManager(ChangeSecretManager, AccountBasePlaybookManager):
|
||||||
@classmethod
|
@classmethod
|
||||||
def method_type(cls):
|
def method_type(cls):
|
||||||
return AutomationTypes.push_account
|
return AutomationTypes.push_account
|
||||||
|
|
||||||
|
def get_secret(self, account):
|
||||||
|
return account.secret
|
||||||
|
|
|
@ -64,19 +64,25 @@ class ChangeSecretMixin(SecretWithRandomMixin):
|
||||||
verbose_name=_('Check connection after change')
|
verbose_name=_('Check connection after change')
|
||||||
)
|
)
|
||||||
get_all_assets: callable # get all assets
|
get_all_assets: callable # get all assets
|
||||||
|
accounts: list # account usernames
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
abstract = True
|
abstract = True
|
||||||
|
|
||||||
def create_nonlocal_accounts(self, usernames, asset):
|
def gen_nonlocal_accounts(self, usernames, asset):
|
||||||
pass
|
return []
|
||||||
|
|
||||||
def get_account_ids(self):
|
def get_account_ids(self):
|
||||||
|
account_objs = []
|
||||||
usernames = self.accounts
|
usernames = self.accounts
|
||||||
accounts = Account.objects.none()
|
assets = self.get_all_assets()
|
||||||
for asset in self.get_all_assets():
|
for asset in assets:
|
||||||
self.create_nonlocal_accounts(usernames, asset)
|
objs = self.gen_nonlocal_accounts(usernames, asset)
|
||||||
accounts = accounts | asset.accounts.all()
|
account_objs.extend(objs)
|
||||||
|
|
||||||
|
Account.objects.bulk_create(account_objs)
|
||||||
|
|
||||||
|
accounts = Account.objects.filter(asset__in=assets)
|
||||||
account_ids = accounts.filter(
|
account_ids = accounts.filter(
|
||||||
username__in=usernames, secret_type=self.secret_type
|
username__in=usernames, secret_type=self.secret_type
|
||||||
).values_list('id', flat=True)
|
).values_list('id', flat=True)
|
||||||
|
|
|
@ -11,20 +11,22 @@ __all__ = ['PushAccountAutomation']
|
||||||
|
|
||||||
class PushAccountAutomation(ChangeSecretMixin, AccountBaseAutomation):
|
class PushAccountAutomation(ChangeSecretMixin, AccountBaseAutomation):
|
||||||
|
|
||||||
def create_nonlocal_accounts(self, usernames, asset):
|
def gen_nonlocal_accounts(self, usernames, asset):
|
||||||
secret_type = self.secret_type
|
secret_type = self.secret_type
|
||||||
account_usernames = asset.accounts \
|
account_usernames = asset.accounts \
|
||||||
.filter(secret_type=self.secret_type) \
|
.filter(secret_type=self.secret_type) \
|
||||||
.values_list('username', flat=True)
|
.values_list('username', flat=True)
|
||||||
create_usernames = set(usernames) - set(account_usernames)
|
create_usernames = set(usernames) - set(account_usernames)
|
||||||
create_account_objs = [
|
|
||||||
|
create_accounts = [
|
||||||
Account(
|
Account(
|
||||||
name=f'{username}-{secret_type}', username=username,
|
name=f'{username}-{secret_type}',
|
||||||
|
username=username, secret=self.get_secret(),
|
||||||
secret_type=secret_type, asset=asset,
|
secret_type=secret_type, asset=asset,
|
||||||
)
|
)
|
||||||
for username in create_usernames
|
for username in create_usernames
|
||||||
]
|
]
|
||||||
Account.objects.bulk_create(create_account_objs)
|
return create_accounts
|
||||||
|
|
||||||
def save(self, *args, **kwargs):
|
def save(self, *args, **kwargs):
|
||||||
self.type = AutomationTypes.push_account
|
self.type = AutomationTypes.push_account
|
||||||
|
|
|
@ -54,10 +54,9 @@ class SecretWithRandomMixin(models.Model):
|
||||||
)
|
)
|
||||||
|
|
||||||
def get_secret(self):
|
def get_secret(self):
|
||||||
if self.secret_strategy == 'random':
|
if self.secret_strategy == SecretStrategy.custom:
|
||||||
return self.secret_generator.get_secret()
|
|
||||||
else:
|
|
||||||
return self.secret
|
return self.secret
|
||||||
|
return self.secret_generator.get_secret()
|
||||||
|
|
||||||
|
|
||||||
class BaseAccount(VaultModelMixin, JMSOrgBaseModel):
|
class BaseAccount(VaultModelMixin, JMSOrgBaseModel):
|
||||||
|
|
|
@ -86,3 +86,7 @@ class AccountTemplate(LabeledMixin, BaseAccount, SecretWithRandomMixin):
|
||||||
""" 批量同步账号密码 """
|
""" 批量同步账号密码 """
|
||||||
self.bulk_update_accounts(accounts)
|
self.bulk_update_accounts(accounts)
|
||||||
self.bulk_create_history_accounts(accounts, user_id)
|
self.bulk_create_history_accounts(accounts, user_id)
|
||||||
|
|
||||||
|
def save(self, *args, **kwargs):
|
||||||
|
self.secret = self.get_secret()
|
||||||
|
super().save(*args, **kwargs)
|
||||||
|
|
|
@ -1,9 +1,7 @@
|
||||||
from django.utils.translation import gettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
from rest_framework import serializers
|
from rest_framework import serializers
|
||||||
|
|
||||||
from accounts.const import SecretStrategy, SecretType
|
|
||||||
from accounts.models import AccountTemplate
|
from accounts.models import AccountTemplate
|
||||||
from accounts.utils import SecretGenerator
|
|
||||||
from common.serializers import SecretReadableMixin
|
from common.serializers import SecretReadableMixin
|
||||||
from common.serializers.fields import ObjectRelatedField
|
from common.serializers.fields import ObjectRelatedField
|
||||||
from .base import BaseAccountSerializer
|
from .base import BaseAccountSerializer
|
||||||
|
@ -47,21 +45,6 @@ class AccountTemplateSerializer(BaseAccountSerializer):
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def generate_secret(attrs):
|
|
||||||
secret_type = attrs.get('secret_type', SecretType.PASSWORD)
|
|
||||||
secret_strategy = attrs.get('secret_strategy', SecretStrategy.custom)
|
|
||||||
password_rules = attrs.get('password_rules')
|
|
||||||
if secret_strategy != SecretStrategy.random:
|
|
||||||
return
|
|
||||||
generator = SecretGenerator(secret_strategy, secret_type, password_rules)
|
|
||||||
attrs['secret'] = generator.get_secret()
|
|
||||||
|
|
||||||
def validate(self, attrs):
|
|
||||||
attrs = super().validate(attrs)
|
|
||||||
self.generate_secret(attrs)
|
|
||||||
return attrs
|
|
||||||
|
|
||||||
|
|
||||||
class AccountTemplateSecretSerializer(SecretReadableMixin, AccountTemplateSerializer):
|
class AccountTemplateSecretSerializer(SecretReadableMixin, AccountTemplateSerializer):
|
||||||
class Meta(AccountTemplateSerializer.Meta):
|
class Meta(AccountTemplateSerializer.Meta):
|
||||||
|
|
Loading…
Reference in New Issue