perf: update pam

pull/14517/head
ibuler 2024-11-12 16:00:41 +08:00
parent 82222f9c23
commit 39f87c1c64
9 changed files with 103 additions and 83 deletions

View File

@ -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

View File

@ -49,13 +49,13 @@
register: user_authorized register: user_authorized
- set_fact: - set_fact:
info: info:
users: "{{ users.stdout_lines }}" users: "{{ users.stdout_lines }}"
last_login: "{{ last_login.stdout_lines }}" last_login: "{{ last_login.stdout_lines }}"
user_groups: "{{ user_groups.stdout_lines }}" user_groups: "{{ user_groups.stdout_lines }}"
user_sudo: "{{ user_sudo.stdout_lines }}" user_sudo: "{{ user_sudo.stdout_lines }}"
user_authorized: "{{ user_authorized.stdout_lines }}" user_authorized: "{{ user_authorized.stdout_lines }}"
passwd_date: "{{ passwd_date.stdout_lines }}" passwd_date: "{{ passwd_date.stdout_lines }}"
- debug: - debug:
var: info var: info

View File

@ -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)

View File

@ -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",
),
]

View File

@ -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"))

View File

@ -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 *

View File

@ -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'})

View File

@ -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',
] ]

View File

@ -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'})