mirror of https://github.com/jumpserver/jumpserver
perf: 更新风险发现
parent
d3804156c8
commit
f5d611acce
|
@ -39,11 +39,16 @@ class CheckAccountExecutionViewSet(AutomationExecutionViewSet):
|
||||||
|
|
||||||
class AccountRiskViewSet(OrgBulkModelViewSet):
|
class AccountRiskViewSet(OrgBulkModelViewSet):
|
||||||
model = AccountRisk
|
model = AccountRisk
|
||||||
search_fields = ('username',)
|
search_fields = ('username', 'asset')
|
||||||
|
filterset_fields = ('risk', 'status', 'asset')
|
||||||
serializer_classes = {
|
serializer_classes = {
|
||||||
'default': serializers.AccountRiskSerializer,
|
'default': serializers.AccountRiskSerializer,
|
||||||
'assets': serializers.AssetRiskSerializer,
|
'assets': serializers.AssetRiskSerializer,
|
||||||
}
|
}
|
||||||
|
ordering_fields = (
|
||||||
|
'asset', 'risk', 'status', 'username', 'date_created'
|
||||||
|
)
|
||||||
|
ordering = ('-asset', 'date_created')
|
||||||
rbac_perms = {
|
rbac_perms = {
|
||||||
'sync_accounts': 'assets.add_accountrisk',
|
'sync_accounts': 'assets.add_accountrisk',
|
||||||
'assets': 'accounts.view_accountrisk'
|
'assets': 'accounts.view_accountrisk'
|
||||||
|
|
|
@ -44,6 +44,14 @@ class GatherAccountsFilter:
|
||||||
continue
|
continue
|
||||||
username_sudo[username.strip()] = sudo.strip()
|
username_sudo[username.strip()] = sudo.strip()
|
||||||
|
|
||||||
|
last_login = info.pop('last_login', '')
|
||||||
|
user_last_login = {}
|
||||||
|
for line in last_login:
|
||||||
|
if not line.strip() or ' ' not in line:
|
||||||
|
continue
|
||||||
|
username, login = line.split(' ', 1)
|
||||||
|
user_last_login[username] = login
|
||||||
|
|
||||||
user_authorized = info.pop('user_authorized', [])
|
user_authorized = info.pop('user_authorized', [])
|
||||||
username_authorized = {}
|
username_authorized = {}
|
||||||
for line in user_authorized:
|
for line in user_authorized:
|
||||||
|
@ -52,27 +60,39 @@ class GatherAccountsFilter:
|
||||||
username, authorized = line.split(':', 1)
|
username, authorized = line.split(':', 1)
|
||||||
username_authorized[username.strip()] = authorized.strip()
|
username_authorized[username.strip()] = authorized.strip()
|
||||||
|
|
||||||
|
passwd_date = info.pop('passwd_date', [])
|
||||||
|
username_password_date = {}
|
||||||
|
for line in passwd_date:
|
||||||
|
if ':' not in line:
|
||||||
|
continue
|
||||||
|
username, password_date = line.split(':', 1)
|
||||||
|
username_password_date[username.strip()] = password_date.strip().split()
|
||||||
|
|
||||||
result = {}
|
result = {}
|
||||||
users = info.pop('users', '')
|
users = info.pop('users', '')
|
||||||
for line in users:
|
|
||||||
parts = line.split()
|
|
||||||
if len(parts) < 4:
|
|
||||||
continue
|
|
||||||
|
|
||||||
username = parts[0]
|
for username in users:
|
||||||
if not username:
|
if not username:
|
||||||
continue
|
continue
|
||||||
user = dict()
|
user = dict()
|
||||||
address = parts[2]
|
|
||||||
user['address_last_login'] = address
|
|
||||||
login_time = parts[3]
|
|
||||||
|
|
||||||
|
login = user_last_login.get(username) or ''
|
||||||
|
if login and len(login) == 3:
|
||||||
|
user['address_last_login'] = login[1][:32]
|
||||||
try:
|
try:
|
||||||
login_date = timezone.datetime.fromisoformat(login_time)
|
login_date = timezone.datetime.fromisoformat(login[2])
|
||||||
user['date_last_login'] = login_date
|
user['date_last_login'] = login_date
|
||||||
except ValueError:
|
except ValueError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
start_date = timezone.make_aware(timezone.datetime(1970, 1, 1))
|
||||||
|
_password_date = username_password_date.get(username) or ''
|
||||||
|
if _password_date and len(_password_date) == 2:
|
||||||
|
if _password_date[0] and _password_date[0] != '0':
|
||||||
|
user['date_password_change'] = start_date + timezone.timedelta(days=int(_password_date[0]))
|
||||||
|
if _password_date[1] and _password_date[1] != '0':
|
||||||
|
user['date_password_expired'] = start_date + timezone.timedelta(days=int(_password_date[1]))
|
||||||
|
|
||||||
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 ''
|
||||||
|
|
|
@ -4,23 +4,28 @@
|
||||||
- name: Get users
|
- name: Get users
|
||||||
ansible.builtin.shell:
|
ansible.builtin.shell:
|
||||||
cmd: >
|
cmd: >
|
||||||
getent passwd | awk -F: '$7 !~ /(false|nologin)$/' | grep -v '^$' | awk -F":" '{ print $1 }'
|
getent passwd | awk -F: '$7 !~ /(false|nologin|true|sync)$/' | grep -v '^$' | awk -F":" '{ print $1 }'
|
||||||
register: users
|
register: users
|
||||||
|
|
||||||
- name: Gather posix account
|
- name: Gather posix account last login
|
||||||
ansible.builtin.shell: |
|
ansible.builtin.shell: |
|
||||||
for user in {{ users.stdout_lines | join(" ") }}; do
|
for user in {{ users.stdout_lines | join(" ") }}; do
|
||||||
k=$(last -i --time-format iso -1 ${user} | head -1 | grep -v ^$ )
|
last -i --time-format iso -n 1 ${user} | awk '{ print $1,$3,$4, $NF }' | head -1 | grep -v ^$
|
||||||
if [ -n "$k" ]; then
|
|
||||||
echo $k
|
|
||||||
fi
|
|
||||||
done
|
done
|
||||||
register: last_login
|
register: last_login
|
||||||
|
|
||||||
|
- name: Get user password change date and expiry
|
||||||
|
ansible.builtin.shell: |
|
||||||
|
for user in {{ users.stdout_lines | join(" ") }}; do
|
||||||
|
k=$(getent shadow $user | awk -F: '{ print $3, $5 }')
|
||||||
|
echo "$user:$k"
|
||||||
|
done
|
||||||
|
register: passwd_date
|
||||||
|
|
||||||
- name: Get user groups
|
- name: Get user groups
|
||||||
ansible.builtin.shell: |
|
ansible.builtin.shell: |
|
||||||
for user in {{ users.stdout_lines | join(" ") }}; do
|
for user in {{ users.stdout_lines | join(" ") }}; do
|
||||||
echo "$(groups $user)"
|
echo "$(groups $user)" | sed 's@ : @:@g'
|
||||||
done
|
done
|
||||||
register: user_groups
|
register: user_groups
|
||||||
|
|
||||||
|
@ -38,17 +43,19 @@
|
||||||
echo -n "$user:"
|
echo -n "$user:"
|
||||||
if [[ -f ${home}/.ssh/authorized_keys ]]; then
|
if [[ -f ${home}/.ssh/authorized_keys ]]; then
|
||||||
cat ${home}/.ssh/authorized_keys | tr '\n' ';'
|
cat ${home}/.ssh/authorized_keys | tr '\n' ';'
|
||||||
echo
|
|
||||||
fi
|
fi
|
||||||
|
echo
|
||||||
done
|
done
|
||||||
register: user_authorized
|
register: user_authorized
|
||||||
|
|
||||||
- set_fact:
|
- set_fact:
|
||||||
info:
|
info:
|
||||||
users: "{{ last_login.stdout_lines }}"
|
users: "{{ users.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 }}"
|
||||||
|
|
||||||
- debug:
|
- debug:
|
||||||
var: info
|
var: info
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
|
|
||||||
|
from django.utils import timezone
|
||||||
|
|
||||||
from accounts.const import AutomationTypes
|
from accounts.const import AutomationTypes
|
||||||
from accounts.models import GatheredAccount, Account, GatheredAccountDiff
|
from accounts.models import GatheredAccount, Account, AccountRisk
|
||||||
from assets.models import Asset
|
from assets.models import Asset
|
||||||
from common.const import ConfirmOrIgnore
|
from common.const import ConfirmOrIgnore
|
||||||
from common.utils import get_logger
|
from common.utils import get_logger
|
||||||
|
@ -16,7 +18,11 @@ logger = get_logger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class GatherAccountsManager(AccountBasePlaybookManager):
|
class GatherAccountsManager(AccountBasePlaybookManager):
|
||||||
diff_items = ['authorized_keys', 'sudoers', 'groups']
|
diff_items = [
|
||||||
|
'authorized_keys', 'sudoers', 'groups',
|
||||||
|
'date_password_change', 'date_password_expired',
|
||||||
|
]
|
||||||
|
long_time = timezone.timedelta(days=90)
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
|
@ -30,7 +36,7 @@ class GatherAccountsManager(AccountBasePlaybookManager):
|
||||||
self.is_sync_account = self.execution.snapshot.get('is_sync_account')
|
self.is_sync_account = self.execution.snapshot.get('is_sync_account')
|
||||||
self.pending_add_accounts = []
|
self.pending_add_accounts = []
|
||||||
self.pending_update_accounts = []
|
self.pending_update_accounts = []
|
||||||
self.pending_add_diffs = []
|
self.pending_add_risks = []
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def method_type(cls):
|
def method_type(cls):
|
||||||
|
@ -60,8 +66,6 @@ class GatherAccountsManager(AccountBasePlaybookManager):
|
||||||
self.asset_usernames_mapper[asset].add(username)
|
self.asset_usernames_mapper[asset].add(username)
|
||||||
|
|
||||||
d = {'asset': asset, 'username': username, 'remote_present': True, **info}
|
d = {'asset': asset, 'username': username, 'remote_present': True, **info}
|
||||||
if len(d['address_last_login']) > 32:
|
|
||||||
d['address_last_login'] = d['address_last_login'][:32]
|
|
||||||
accounts.append(d)
|
accounts.append(d)
|
||||||
self.asset_account_info[asset] = accounts
|
self.asset_account_info[asset] = accounts
|
||||||
|
|
||||||
|
@ -147,7 +151,7 @@ class GatherAccountsManager(AccountBasePlaybookManager):
|
||||||
def batch_create_gathered_account(self, d, batch_size=20):
|
def batch_create_gathered_account(self, d, batch_size=20):
|
||||||
if d is None:
|
if d is None:
|
||||||
if self.pending_add_accounts:
|
if self.pending_add_accounts:
|
||||||
GatheredAccount.objects.bulk_create(self.pending_add_accounts)
|
GatheredAccount.objects.bulk_create(self.pending_add_accounts, ignore_conflicts=True)
|
||||||
self.pending_add_accounts = []
|
self.pending_add_accounts = []
|
||||||
return
|
return
|
||||||
|
|
||||||
|
@ -159,32 +163,103 @@ class GatherAccountsManager(AccountBasePlaybookManager):
|
||||||
if len(self.pending_add_accounts) > batch_size:
|
if len(self.pending_add_accounts) > batch_size:
|
||||||
self.batch_create_gathered_account(None)
|
self.batch_create_gathered_account(None)
|
||||||
|
|
||||||
def batch_update_gathered_account(self, ori_account, d, batch_size=20):
|
def _analyse_item_changed(self, ori_account, d):
|
||||||
if not ori_account or d is None:
|
diff = self.get_items_diff(ori_account, d)
|
||||||
if self.pending_update_accounts:
|
|
||||||
GatheredAccount.objects.bulk_update(self.pending_update_accounts, [*self.diff_items])
|
|
||||||
self.pending_update_accounts = []
|
|
||||||
|
|
||||||
if self.pending_add_diffs:
|
if not diff:
|
||||||
GatheredAccountDiff.objects.bulk_create(self.pending_add_diffs)
|
|
||||||
self.pending_add_diffs = []
|
|
||||||
return
|
return
|
||||||
|
|
||||||
|
for k, v in diff.items():
|
||||||
|
self.pending_add_risks.append(AccountRisk(
|
||||||
|
asset=ori_account.asset, username=ori_account.username,
|
||||||
|
risk=k+'_changed', comment=v
|
||||||
|
))
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def perform_save_risks(risks):
|
||||||
|
assets = {r.asset for r in risks}
|
||||||
|
assets_risks = AccountRisk.objects.filter(asset__in=assets)
|
||||||
|
assets_risks = {f"{r.asset_id}_{r.username}": r for r in assets_risks}
|
||||||
|
|
||||||
|
for r in risks:
|
||||||
|
found = assets_risks.get(f"{r.asset_id}_{r.username}")
|
||||||
|
|
||||||
|
if not found:
|
||||||
|
r.save()
|
||||||
|
else:
|
||||||
|
found.comment = r.comment + '\n------\n' + found.comment
|
||||||
|
|
||||||
|
def batch_analyse_risk(self, asset, ori_account, d, batch_size=20):
|
||||||
|
if asset is None:
|
||||||
|
if self.pending_add_risks:
|
||||||
|
self.perform_save_risks(self.pending_add_risks)
|
||||||
|
self.pending_add_risks = []
|
||||||
|
return
|
||||||
|
|
||||||
|
now = timezone.now().strftime('%Y-%m-%d %H:%M:%S')
|
||||||
|
basic = {'asset': asset, 'username': d['username']}
|
||||||
|
if ori_account:
|
||||||
|
self._analyse_item_changed(ori_account, d)
|
||||||
|
else:
|
||||||
|
self.pending_add_risks.append(
|
||||||
|
AccountRisk(**basic, risk='ghost', comment='{}'.format(now))
|
||||||
|
)
|
||||||
|
|
||||||
|
last_login = d.get('date_last_login')
|
||||||
|
if last_login and last_login < timezone.now() - self.long_time:
|
||||||
|
self.pending_add_risks.append(
|
||||||
|
AccountRisk(**basic, risk='zombie', comment='{}'.format(last_login))
|
||||||
|
)
|
||||||
|
|
||||||
|
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', comment='{}'.format(date_password_change))
|
||||||
|
)
|
||||||
|
|
||||||
|
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', comment='{}'.format(date_password_expired))
|
||||||
|
)
|
||||||
|
|
||||||
|
if len(self.pending_add_risks) > batch_size:
|
||||||
|
self.batch_analyse_risk(None, None, {})
|
||||||
|
|
||||||
|
def get_items_diff(self, ori_account, d):
|
||||||
|
if hasattr(ori_account, '_diff'):
|
||||||
|
return ori_account._diff
|
||||||
|
|
||||||
diff = {}
|
diff = {}
|
||||||
for item in self.diff_items:
|
for item in self.diff_items:
|
||||||
ori = getattr(ori_account, item)
|
ori = getattr(ori_account, item)
|
||||||
new = d.get(item, '')
|
new = d.get(item, '')
|
||||||
|
|
||||||
|
if not ori:
|
||||||
|
continue
|
||||||
|
|
||||||
|
if isinstance(new, timezone.datetime):
|
||||||
|
new = ori.strftime('%Y-%m-%d %H:%M:%S')
|
||||||
|
ori = ori.strftime('%Y-%m-%d %H:%M:%S')
|
||||||
|
|
||||||
if new != ori:
|
if new != ori:
|
||||||
setattr(ori_account, item, new)
|
|
||||||
diff[item] = get_text_diff(ori, new)
|
diff[item] = get_text_diff(ori, new)
|
||||||
|
|
||||||
|
ori_account._diff = diff
|
||||||
|
return diff
|
||||||
|
|
||||||
|
def batch_update_gathered_account(self, ori_account, d, batch_size=20):
|
||||||
|
if not ori_account or d is None:
|
||||||
|
if self.pending_update_accounts:
|
||||||
|
GatheredAccount.objects.bulk_update(self.pending_update_accounts, [*self.diff_items])
|
||||||
|
self.pending_update_accounts = []
|
||||||
|
return
|
||||||
|
|
||||||
|
diff = self.get_items_diff(ori_account, d)
|
||||||
if diff:
|
if diff:
|
||||||
|
for k in diff:
|
||||||
|
setattr(ori_account, k, d[k])
|
||||||
self.pending_update_accounts.append(ori_account)
|
self.pending_update_accounts.append(ori_account)
|
||||||
for k, v in diff.items():
|
|
||||||
self.pending_add_diffs.append(
|
|
||||||
GatheredAccountDiff(account=ori_account, item=k, diff=v)
|
|
||||||
)
|
|
||||||
|
|
||||||
if len(self.pending_update_accounts) > batch_size:
|
if len(self.pending_update_accounts) > batch_size:
|
||||||
self.batch_update_gathered_account(None, None)
|
self.batch_update_gathered_account(None, None)
|
||||||
|
@ -202,11 +277,14 @@ class GatherAccountsManager(AccountBasePlaybookManager):
|
||||||
else:
|
else:
|
||||||
self.batch_update_gathered_account(ori_account, d)
|
self.batch_update_gathered_account(ori_account, d)
|
||||||
|
|
||||||
|
self.batch_analyse_risk(asset, ori_account, d)
|
||||||
|
|
||||||
self.update_gather_accounts_status(asset)
|
self.update_gather_accounts_status(asset)
|
||||||
GatheredAccount.sync_accounts(gathered_accounts, self.is_sync_account)
|
GatheredAccount.sync_accounts(gathered_accounts, self.is_sync_account)
|
||||||
|
|
||||||
self.batch_create_gathered_account(None)
|
self.batch_create_gathered_account(None)
|
||||||
self.batch_update_gathered_account(None, None)
|
self.batch_update_gathered_account(None, None)
|
||||||
|
self.batch_analyse_risk(None, None, {})
|
||||||
|
|
||||||
def run(self, *args, **kwargs):
|
def run(self, *args, **kwargs):
|
||||||
super().run(*args, **kwargs)
|
super().run(*args, **kwargs)
|
||||||
|
|
|
@ -0,0 +1,58 @@
|
||||||
|
# Generated by Django 4.1.13 on 2024-11-08 09:04
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
("assets", "0006_baseautomation_start_time"),
|
||||||
|
("accounts", "0008_remove_accountrisk_confirmed_accountrisk_status_and_more"),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name="gatheredaccount",
|
||||||
|
name="date_change_password",
|
||||||
|
field=models.DateTimeField(null=True, verbose_name="Date change password"),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name="gatheredaccount",
|
||||||
|
name="date_password_expired",
|
||||||
|
field=models.DateTimeField(null=True, verbose_name="Date password expired"),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name="accountrisk",
|
||||||
|
name="comment",
|
||||||
|
field=models.TextField(default="", verbose_name="Comment"),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name="accountrisk",
|
||||||
|
name="risk",
|
||||||
|
field=models.CharField(
|
||||||
|
choices=[
|
||||||
|
("zombie", "Long time no login"),
|
||||||
|
("ghost", "Not managed"),
|
||||||
|
("groups_changed", "Groups change"),
|
||||||
|
("sudoers_changed", "Sudo changed"),
|
||||||
|
("authorized_keys_changed", "Authorized keys changed"),
|
||||||
|
("account_deleted", "Account delete"),
|
||||||
|
("password_expired", "Password expired"),
|
||||||
|
("long_time_password", "Long time no change"),
|
||||||
|
("weak_password", "Weak password"),
|
||||||
|
("password_error", "Password error"),
|
||||||
|
("no_admin_account", "No admin account"),
|
||||||
|
("others", "Others"),
|
||||||
|
],
|
||||||
|
max_length=128,
|
||||||
|
verbose_name="Risk",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
migrations.AlterUniqueTogether(
|
||||||
|
name="accountrisk",
|
||||||
|
unique_together={("asset", "username", "risk")},
|
||||||
|
),
|
||||||
|
migrations.DeleteModel(
|
||||||
|
name="GatheredAccountDiff",
|
||||||
|
),
|
||||||
|
]
|
|
@ -37,16 +37,18 @@ class AccountCheckAutomation(AccountBaseAutomation):
|
||||||
|
|
||||||
|
|
||||||
class RiskChoice(TextChoices):
|
class RiskChoice(TextChoices):
|
||||||
|
# 依赖自动发现的
|
||||||
zombie = 'zombie', _('Long time no login') # 好久没登录的账号, 禁用、删除
|
zombie = 'zombie', _('Long time no login') # 好久没登录的账号, 禁用、删除
|
||||||
ghost = 'ghost', _('Not managed') # 未被纳管的账号, 纳管, 删除, 禁用
|
ghost = 'ghost', _('Not managed') # 未被纳管的账号, 纳管, 删除, 禁用
|
||||||
long_time_password = 'long_time_password', _('Long time no change') # 好久没改密码的账号, 改密码
|
group_changed = 'groups_changed', _('Groups change') # 组变更, 确认
|
||||||
weak_password = 'weak_password', _('Weak password') # 弱密码, 改密
|
sudo_changed = 'sudoers_changed', _('Sudo changed') # sudo 变更, 确认
|
||||||
password_error = 'password_error', _('Password error') # 密码错误, 修改账号
|
|
||||||
password_expired = 'password_expired', _('Password expired') # 密码过期, 修改密码
|
|
||||||
group_changed = 'group_changed', _('Group change') # 组变更, 确认
|
|
||||||
sudo_changed = 'sudo_changed', _('Sudo changed') # sudo 变更, 确认
|
|
||||||
authorized_keys_changed = 'authorized_keys_changed', _('Authorized keys changed') # authorized_keys 变更, 确认
|
authorized_keys_changed = 'authorized_keys_changed', _('Authorized keys changed') # authorized_keys 变更, 确认
|
||||||
account_deleted = 'account_deleted', _('Account delete') # 账号被删除, 确认
|
account_deleted = 'account_deleted', _('Account delete') # 账号被删除, 确认
|
||||||
|
password_expired = 'password_expired', _('Password expired') # 密码过期, 修改密码
|
||||||
|
long_time_password = 'long_time_password', _('Long time no change') # 好久没改密码的账号, 改密码
|
||||||
|
|
||||||
|
weak_password = 'weak_password', _('Weak password') # 弱密码, 改密
|
||||||
|
password_error = 'password_error', _('Password error') # 密码错误, 修改账号
|
||||||
no_admin_account = 'no_admin_account', _('No admin account') # 无管理员账号, 设置账号
|
no_admin_account = 'no_admin_account', _('No admin account') # 无管理员账号, 设置账号
|
||||||
others = 'others', _('Others') # 其他风险, 确认
|
others = 'others', _('Others') # 其他风险, 确认
|
||||||
|
|
||||||
|
@ -56,25 +58,15 @@ class AccountRisk(JMSOrgBaseModel):
|
||||||
username = models.CharField(max_length=32, verbose_name=_('Username'))
|
username = models.CharField(max_length=32, verbose_name=_('Username'))
|
||||||
risk = models.CharField(max_length=128, verbose_name=_('Risk'), choices=RiskChoice.choices)
|
risk = models.CharField(max_length=128, verbose_name=_('Risk'), choices=RiskChoice.choices)
|
||||||
status = models.CharField(max_length=32, choices=ConfirmOrIgnore.choices, default='', blank=True, verbose_name=_('Status'))
|
status = models.CharField(max_length=32, choices=ConfirmOrIgnore.choices, default='', blank=True, verbose_name=_('Status'))
|
||||||
|
comment = models.TextField(default='', verbose_name=_('Comment'))
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
verbose_name = _('Account risk')
|
verbose_name = _('Account risk')
|
||||||
|
unique_together = ('asset', 'username', 'risk')
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return f"{self.username}@{self.asset} - {self.risk}"
|
return f"{self.username}@{self.asset} - {self.risk}"
|
||||||
|
|
||||||
def disable_account(self):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def remove_account(self):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def change_password(self):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def handle_risk(self):
|
|
||||||
pass
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def gen_fake_data(cls, count=1000, batch_size=50):
|
def gen_fake_data(cls, count=1000, batch_size=50):
|
||||||
from assets.models import Asset
|
from assets.models import Asset
|
||||||
|
|
|
@ -9,36 +9,27 @@ from common.utils.timezone import is_date_more_than
|
||||||
from orgs.mixins.models import JMSOrgBaseModel
|
from orgs.mixins.models import JMSOrgBaseModel
|
||||||
from .base import AccountBaseAutomation
|
from .base import AccountBaseAutomation
|
||||||
|
|
||||||
__all__ = ['GatherAccountsAutomation', 'GatheredAccount', 'GatheredAccountDiff']
|
__all__ = ['GatherAccountsAutomation', 'GatheredAccount', ]
|
||||||
|
|
||||||
|
|
||||||
class GatheredAccountDiff(models.Model):
|
|
||||||
account = models.ForeignKey('GatheredAccount', on_delete=models.CASCADE, verbose_name=_("Gathered account"))
|
|
||||||
diff = models.TextField(default='', verbose_name=_("Diff"))
|
|
||||||
item = models.CharField(max_length=32, default='', verbose_name=_("Item"))
|
|
||||||
date_created = models.DateTimeField(auto_now_add=True, verbose_name=_("Date created"))
|
|
||||||
|
|
||||||
|
|
||||||
class GatheredAccount(JMSOrgBaseModel):
|
class GatheredAccount(JMSOrgBaseModel):
|
||||||
remote_present = models.BooleanField(default=True, verbose_name=_("Remote present")) # 远端资产上是否还存在
|
|
||||||
present = models.BooleanField(default=False, verbose_name=_("Present")) # 系统资产上是否还存在
|
|
||||||
date_last_login = models.DateTimeField(null=True, verbose_name=_("Date login"))
|
|
||||||
asset = models.ForeignKey('assets.Asset', on_delete=models.CASCADE, verbose_name=_("Asset"))
|
asset = models.ForeignKey('assets.Asset', on_delete=models.CASCADE, verbose_name=_("Asset"))
|
||||||
username = models.CharField(max_length=32, blank=True, db_index=True, verbose_name=_('Username'))
|
username = models.CharField(max_length=32, blank=True, db_index=True, verbose_name=_('Username'))
|
||||||
address_last_login = models.CharField(max_length=39, default='', verbose_name=_("Address login"))
|
address_last_login = models.CharField(max_length=39, default='', verbose_name=_("Address login"))
|
||||||
status = models.CharField(max_length=32, default='', blank=True, choices=ConfirmOrIgnore.choices, verbose_name=_("Status"))
|
date_last_login = models.DateTimeField(null=True, verbose_name=_("Date login"))
|
||||||
authorized_keys = models.TextField(default='', blank=True, verbose_name=_("Authorized keys"))
|
authorized_keys = models.TextField(default='', blank=True, verbose_name=_("Authorized keys"))
|
||||||
sudoers = models.TextField(default='', verbose_name=_("Sudoers"), blank=True)
|
sudoers = models.TextField(default='', verbose_name=_("Sudoers"), blank=True)
|
||||||
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")) # 远端资产上是否还存在
|
||||||
|
present = models.BooleanField(default=False, verbose_name=_("Present")) # 系统资产上是否还存在
|
||||||
|
date_change_password = models.DateTimeField(null=True, verbose_name=_("Date change password"))
|
||||||
|
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"))
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def address(self):
|
def address(self):
|
||||||
return self.asset.address
|
return self.asset.address
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def find_account_risk(cls, gathered_account, accounts):
|
|
||||||
pass
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def update_exists_accounts(cls, gathered_account, accounts):
|
def update_exists_accounts(cls, gathered_account, accounts):
|
||||||
if not gathered_account.date_last_login:
|
if not gathered_account.date_last_login:
|
||||||
|
|
Loading…
Reference in New Issue