mirror of https://github.com/jumpserver/jumpserver
perf: update pam
parent
82222f9c23
commit
39f87c1c64
|
@ -96,7 +96,6 @@ class GatherAccountsFilter:
|
||||||
user['groups'] = username_groups.get(username) or ''
|
user['groups'] = username_groups.get(username) or ''
|
||||||
user['sudoers'] = username_sudo.get(username) or ''
|
user['sudoers'] = username_sudo.get(username) or ''
|
||||||
user['authorized_keys'] = username_authorized.get(username) or ''
|
user['authorized_keys'] = username_authorized.get(username) or ''
|
||||||
|
|
||||||
result[username] = user
|
result[username] = user
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
|
|
@ -20,9 +20,13 @@ logger = get_logger(__name__)
|
||||||
class GatherAccountsManager(AccountBasePlaybookManager):
|
class GatherAccountsManager(AccountBasePlaybookManager):
|
||||||
diff_items = [
|
diff_items = [
|
||||||
'authorized_keys', 'sudoers', 'groups',
|
'authorized_keys', 'sudoers', 'groups',
|
||||||
'date_password_change', 'date_password_expired',
|
|
||||||
]
|
]
|
||||||
long_time = timezone.timedelta(days=90)
|
long_time = timezone.timedelta(days=90)
|
||||||
|
datetime_check_items = [
|
||||||
|
{'field': 'date_last_login', 'risk': 'zombie', 'delta': long_time},
|
||||||
|
{'field': 'date_password_change', 'risk': 'long_time_password', 'delta': long_time},
|
||||||
|
{'field': 'date_password_expired', 'risk': 'password_expired', 'delta': timezone.timedelta(seconds=1)}
|
||||||
|
]
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
|
@ -70,6 +74,7 @@ class GatherAccountsManager(AccountBasePlaybookManager):
|
||||||
self.asset_account_info[asset] = accounts
|
self.asset_account_info[asset] = accounts
|
||||||
|
|
||||||
def on_runner_failed(self, runner, e):
|
def on_runner_failed(self, runner, e):
|
||||||
|
print("Runner failed: ", e)
|
||||||
raise e
|
raise e
|
||||||
|
|
||||||
def on_host_success(self, host, result):
|
def on_host_success(self, host, result):
|
||||||
|
@ -167,6 +172,7 @@ class GatherAccountsManager(AccountBasePlaybookManager):
|
||||||
now = timezone.now().isoformat()
|
now = timezone.now().isoformat()
|
||||||
diff = self.get_items_diff(ori_account, d)
|
diff = self.get_items_diff(ori_account, d)
|
||||||
|
|
||||||
|
print("Diff items: ", diff)
|
||||||
if not diff:
|
if not diff:
|
||||||
return
|
return
|
||||||
|
|
||||||
|
@ -175,20 +181,43 @@ class GatherAccountsManager(AccountBasePlaybookManager):
|
||||||
asset=ori_account.asset, username=ori_account.username,
|
asset=ori_account.asset, username=ori_account.username,
|
||||||
risk=k+'_changed', details=[{'datetime': now, 'diff': v}]
|
risk=k+'_changed', details=[{'datetime': now, 'diff': v}]
|
||||||
))
|
))
|
||||||
|
print("Pending add risks: ", self.pending_add_risks)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def perform_save_risks(risks):
|
def perform_save_risks(risks):
|
||||||
|
# 提前取出来,避免每次都查数据库
|
||||||
assets = {r.asset for r in risks}
|
assets = {r.asset for r in risks}
|
||||||
assets_risks = AccountRisk.objects.filter(asset__in=assets)
|
assets_risks = AccountRisk.objects.filter(asset__in=assets)
|
||||||
assets_risks = {f"{r.asset_id}_{r.username}": r for r in assets_risks}
|
assets_risks = {f"{r.asset_id}_{r.username}_{r.risk}": r for r in assets_risks}
|
||||||
|
|
||||||
for r in risks:
|
for r in risks:
|
||||||
found = assets_risks.get(f"{r.asset_id}_{r.username}")
|
found = assets_risks.get(f"{r.asset_id}_{r.username}_{r.risk}")
|
||||||
|
|
||||||
if not found:
|
if not found:
|
||||||
r.save()
|
r.save()
|
||||||
else:
|
continue
|
||||||
|
|
||||||
found.details.extend(r.details)
|
found.details.extend(r.details)
|
||||||
|
found.save(update_fields=['details'])
|
||||||
|
|
||||||
|
def _analyse_datetime_changed(self, ori_account, d, asset, username):
|
||||||
|
for item in self.datetime_check_items:
|
||||||
|
field = item['field']
|
||||||
|
risk = item['risk']
|
||||||
|
delta = item['delta']
|
||||||
|
|
||||||
|
date = d.get(field)
|
||||||
|
if not date:
|
||||||
|
continue
|
||||||
|
|
||||||
|
pre_date = ori_account and getattr(ori_account, field)
|
||||||
|
if pre_date == date:
|
||||||
|
continue
|
||||||
|
|
||||||
|
if date and date < timezone.now() - delta:
|
||||||
|
self.pending_add_risks.append(
|
||||||
|
AccountRisk(asset=asset, username=username, risk=risk)
|
||||||
|
)
|
||||||
|
|
||||||
def batch_analyse_risk(self, asset, ori_account, d, batch_size=20):
|
def batch_analyse_risk(self, asset, ori_account, d, batch_size=20):
|
||||||
if asset is None:
|
if asset is None:
|
||||||
|
@ -204,26 +233,10 @@ class GatherAccountsManager(AccountBasePlaybookManager):
|
||||||
self._analyse_item_changed(ori_account, d)
|
self._analyse_item_changed(ori_account, d)
|
||||||
else:
|
else:
|
||||||
self.pending_add_risks.append(
|
self.pending_add_risks.append(
|
||||||
AccountRisk(**basic, risk='ghost', )
|
AccountRisk(**basic, risk='ghost')
|
||||||
)
|
)
|
||||||
|
|
||||||
last_login = d.get('date_last_login')
|
self._analyse_datetime_changed(ori_account, d, asset, d['username'])
|
||||||
if last_login and last_login < timezone.now() - self.long_time:
|
|
||||||
self.pending_add_risks.append(
|
|
||||||
AccountRisk(**basic, risk='zombie')
|
|
||||||
)
|
|
||||||
|
|
||||||
date_password_change = d.get('date_password_change')
|
|
||||||
if date_password_change and date_password_change < timezone.now() - self.long_time:
|
|
||||||
self.pending_add_risks.append(
|
|
||||||
AccountRisk(**basic, risk='long_time_password')
|
|
||||||
)
|
|
||||||
|
|
||||||
date_password_expired = d.get('date_password_expired')
|
|
||||||
if date_password_expired and date_password_expired < timezone.now():
|
|
||||||
self.pending_add_risks.append(
|
|
||||||
AccountRisk(**basic, risk='password_expired')
|
|
||||||
)
|
|
||||||
|
|
||||||
if len(self.pending_add_risks) > batch_size:
|
if len(self.pending_add_risks) > batch_size:
|
||||||
self.batch_analyse_risk(None, None, {})
|
self.batch_analyse_risk(None, None, {})
|
||||||
|
@ -279,6 +292,7 @@ class GatherAccountsManager(AccountBasePlaybookManager):
|
||||||
else:
|
else:
|
||||||
self.batch_update_gathered_account(ori_account, d)
|
self.batch_update_gathered_account(ori_account, d)
|
||||||
|
|
||||||
|
print("Batch analyse risk")
|
||||||
self.batch_analyse_risk(asset, ori_account, d)
|
self.batch_analyse_risk(asset, ori_account, d)
|
||||||
|
|
||||||
self.update_gather_accounts_status(asset)
|
self.update_gather_accounts_status(asset)
|
||||||
|
|
|
@ -0,0 +1,18 @@
|
||||||
|
# Generated by Django 4.1.13 on 2024-11-12 06:35
|
||||||
|
|
||||||
|
from django.db import migrations
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
("accounts", "0010_accountrisk_details_alter_accountrisk_comment"),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.RenameField(
|
||||||
|
model_name="gatheredaccount",
|
||||||
|
old_name="date_change_password",
|
||||||
|
new_name="date_password_change",
|
||||||
|
),
|
||||||
|
]
|
|
@ -22,7 +22,7 @@ class GatheredAccount(JMSOrgBaseModel):
|
||||||
groups = models.TextField(default='', blank=True, verbose_name=_("Groups"))
|
groups = models.TextField(default='', blank=True, verbose_name=_("Groups"))
|
||||||
remote_present = models.BooleanField(default=True, verbose_name=_("Remote present")) # 远端资产上是否还存在
|
remote_present = models.BooleanField(default=True, verbose_name=_("Remote present")) # 远端资产上是否还存在
|
||||||
present = models.BooleanField(default=False, verbose_name=_("Present")) # 系统资产上是否还存在
|
present = models.BooleanField(default=False, verbose_name=_("Present")) # 系统资产上是否还存在
|
||||||
date_change_password = models.DateTimeField(null=True, verbose_name=_("Date change password"))
|
date_password_change = models.DateTimeField(null=True, verbose_name=_("Date change password"))
|
||||||
date_password_expired = models.DateTimeField(null=True, verbose_name=_("Date password expired"))
|
date_password_expired = models.DateTimeField(null=True, verbose_name=_("Date password expired"))
|
||||||
status = models.CharField(max_length=32, default='', blank=True, choices=ConfirmOrIgnore.choices, verbose_name=_("Status"))
|
status = models.CharField(max_length=32, default='', blank=True, choices=ConfirmOrIgnore.choices, verbose_name=_("Status"))
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
from .account import *
|
from .account import *
|
||||||
from .backup import *
|
from .backup import *
|
||||||
from .base import *
|
from .base import *
|
||||||
from .gathered_account import *
|
|
||||||
from .template import *
|
from .template import *
|
||||||
from .virtual import *
|
from .virtual import *
|
||||||
|
|
|
@ -1,42 +0,0 @@
|
||||||
from django.utils.translation import gettext_lazy as _
|
|
||||||
|
|
||||||
from accounts.models import GatheredAccount
|
|
||||||
from orgs.mixins.serializers import BulkOrgResourceModelSerializer
|
|
||||||
from .account import AccountAssetSerializer as _AccountAssetSerializer
|
|
||||||
from .base import BaseAccountSerializer
|
|
||||||
|
|
||||||
__all__ = [
|
|
||||||
'GatheredAccountSerializer',
|
|
||||||
'GatheredAccountActionSerializer',
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class AccountAssetSerializer(_AccountAssetSerializer):
|
|
||||||
class Meta(_AccountAssetSerializer.Meta):
|
|
||||||
fields = [f for f in _AccountAssetSerializer.Meta.fields if f != 'auto_config']
|
|
||||||
|
|
||||||
|
|
||||||
class GatheredAccountSerializer(BulkOrgResourceModelSerializer):
|
|
||||||
asset = AccountAssetSerializer(label=_('Asset'))
|
|
||||||
|
|
||||||
class Meta(BaseAccountSerializer.Meta):
|
|
||||||
model = GatheredAccount
|
|
||||||
fields = [
|
|
||||||
'id', 'asset', 'username',
|
|
||||||
'date_updated', 'address_last_login',
|
|
||||||
'groups', 'sudoers', 'authorized_keys',
|
|
||||||
'remote_present', 'present',
|
|
||||||
'date_last_login', 'status'
|
|
||||||
]
|
|
||||||
read_only_fields = fields
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def setup_eager_loading(cls, queryset):
|
|
||||||
""" Perform necessary eager loading of data. """
|
|
||||||
queryset = queryset.prefetch_related('asset', 'asset__platform')
|
|
||||||
return queryset
|
|
||||||
|
|
||||||
|
|
||||||
class GatheredAccountActionSerializer(GatheredAccountSerializer):
|
|
||||||
class Meta(GatheredAccountSerializer.Meta):
|
|
||||||
read_only_fields = list(set(GatheredAccountSerializer.Meta.read_only_fields) - {'status'})
|
|
|
@ -28,7 +28,7 @@ class AccountRiskSerializer(serializers.ModelSerializer):
|
||||||
model = AccountRisk
|
model = AccountRisk
|
||||||
fields = [
|
fields = [
|
||||||
'id', 'asset', 'username', 'risk', 'status',
|
'id', 'asset', 'username', 'risk', 'status',
|
||||||
'date_created', 'comment'
|
'date_created', 'details',
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,14 +1,16 @@
|
||||||
# -*- coding: utf-8 -*-
|
from django.utils.translation import gettext_lazy as _
|
||||||
#
|
|
||||||
from accounts.const import AutomationTypes
|
from accounts.const import AutomationTypes
|
||||||
from accounts.models import GatherAccountsAutomation
|
from accounts.models import GatherAccountsAutomation
|
||||||
from common.utils import get_logger
|
from accounts.models import GatheredAccount
|
||||||
|
from accounts.serializers.account.account import AccountAssetSerializer as _AccountAssetSerializer
|
||||||
|
from accounts.serializers.account.base import BaseAccountSerializer
|
||||||
|
from orgs.mixins.serializers import BulkOrgResourceModelSerializer
|
||||||
from .base import BaseAutomationSerializer
|
from .base import BaseAutomationSerializer
|
||||||
|
|
||||||
logger = get_logger(__file__)
|
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
|
'GatheredAccountSerializer',
|
||||||
|
'GatheredAccountActionSerializer',
|
||||||
'GatherAccountAutomationSerializer',
|
'GatherAccountAutomationSerializer',
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -25,3 +27,33 @@ class GatherAccountAutomationSerializer(BaseAutomationSerializer):
|
||||||
@property
|
@property
|
||||||
def model_type(self):
|
def model_type(self):
|
||||||
return AutomationTypes.gather_accounts
|
return AutomationTypes.gather_accounts
|
||||||
|
|
||||||
|
|
||||||
|
class AccountAssetSerializer(_AccountAssetSerializer):
|
||||||
|
class Meta(_AccountAssetSerializer.Meta):
|
||||||
|
fields = [f for f in _AccountAssetSerializer.Meta.fields if f != 'auto_config']
|
||||||
|
|
||||||
|
|
||||||
|
class GatheredAccountSerializer(BulkOrgResourceModelSerializer):
|
||||||
|
asset = AccountAssetSerializer(label=_('Asset'))
|
||||||
|
|
||||||
|
class Meta(BaseAccountSerializer.Meta):
|
||||||
|
model = GatheredAccount
|
||||||
|
fields = [
|
||||||
|
'id', 'asset', 'username',
|
||||||
|
'date_last_login', 'address_last_login',
|
||||||
|
'remote_present', 'present',
|
||||||
|
'date_updated', 'status',
|
||||||
|
]
|
||||||
|
read_only_fields = fields
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def setup_eager_loading(cls, queryset):
|
||||||
|
""" Perform necessary eager loading of data. """
|
||||||
|
queryset = queryset.prefetch_related('asset', 'asset__platform')
|
||||||
|
return queryset
|
||||||
|
|
||||||
|
|
||||||
|
class GatheredAccountActionSerializer(GatheredAccountSerializer):
|
||||||
|
class Meta(GatheredAccountSerializer.Meta):
|
||||||
|
read_only_fields = list(set(GatheredAccountSerializer.Meta.read_only_fields) - {'status'})
|
||||||
|
|
Loading…
Reference in New Issue