perf: 解决冲突

pull/8873/head
ibuler 2022-09-15 21:23:00 +08:00
commit 1d1b252e58
37 changed files with 294 additions and 346 deletions

View File

@ -49,10 +49,10 @@ class AccountViewSet(OrgBulkModelViewSet):
filterset_class = AccountFilterSet
serializer_classes = {
'default': serializers.AccountSerializer,
'verify_account': serializers.AssetTaskSerializer
'verify': serializers.AssetTaskSerializer
}
rbac_perms = {
'verify_account': 'assets.test_authbook',
'verify': 'assets.test_authbook',
'partial_update': 'assets.change_assetaccountsecret',
}

View File

@ -95,6 +95,10 @@ class Asset(AbsConnectivity, NodesRelationMixin, JMSOrgBaseModel):
def get_target_ip(self):
return self.ip
def get_target_ssh_port(self):
protocol = self.protocols.all().filter(name='ssh').first()
return protocol.port if protocol else 22
@property
def is_valid(self):
warning = ''

View File

@ -1,10 +0,0 @@
{% for account in accounts %}
- hosts: {{ account.asset.name }}
vars:
account:
username: {{ account.username }}
password: {{ account.password }}
public_key: {{ account.public_key }}
roles:
- change_password
{% endfor %}

View File

@ -1,6 +0,0 @@
id: change_password_mysql
name: Change password for MySQL
category: database
type:
- mysql
method: change_password

View File

@ -1,27 +0,0 @@
- name: ping
ping:
#- name: print variables
# debug:
# msg: "Username: {{ account.username }}, Password: {{ account.password }}"
- name: Change password
user:
name: "{{ account.username }}"
password: "{{ account.password | password_hash('des') }}"
update_password: always
when: account.password
- name: Change public key
authorized_key:
user: "{{ account.username }}"
key: "{{ account.public_key }}"
state: present
when: account.public_key
- name: Verify password
ping:
vars:
ansible_user: "{{ account.username }}"
ansible_pass: "{{ account.password }}"
ansible_ssh_connection: paramiko

View File

@ -1,10 +0,0 @@
{% for account in accounts %}
- hosts: {{ account.asset.name }}
vars:
account:
username: {{ account.username }}
password: {{ account.password }}
public_key: {{ account.public_key }}
roles:
- change_password
{% endfor %}

View File

@ -1,6 +0,0 @@
id: change_password_oracle
name: Change password for Oracle
method: change_password
category: database
type:
- oracle

View File

@ -1,27 +0,0 @@
- name: ping
ping:
#- name: print variables
# debug:
# msg: "Username: {{ account.username }}, Password: {{ account.password }}"
- name: Change password
user:
name: "{{ account.username }}"
password: "{{ account.password | password_hash('des') }}"
update_password: always
when: account.password
- name: Change public key
authorized_key:
user: "{{ account.username }}"
key: "{{ account.public_key }}"
state: present
when: account.public_key
- name: Verify password
ping:
vars:
ansible_user: "{{ account.username }}"
ansible_pass: "{{ account.password }}"
ansible_ssh_connection: paramiko

View File

@ -1,10 +0,0 @@
{% for account in accounts %}
- hosts: {{ account.asset.name }}
vars:
account:
username: {{ account.username }}
password: {{ account.password }}
public_key: {{ account.public_key }}
roles:
- change_password
{% endfor %}

View File

@ -1,6 +0,0 @@
id: change_password_postgresql
name: Change password for PostgreSQL
category: database
type:
- postgresql
method: change_password

View File

@ -1,27 +0,0 @@
- name: ping
ping:
#- name: print variables
# debug:
# msg: "Username: {{ account.username }}, Password: {{ account.password }}"
- name: Change password
user:
name: "{{ account.username }}"
password: "{{ account.password | password_hash('des') }}"
update_password: always
when: account.password
- name: Change public key
authorized_key:
user: "{{ account.username }}"
key: "{{ account.public_key }}"
state: present
when: account.public_key
- name: Verify password
ping:
vars:
ansible_user: "{{ account.username }}"
ansible_pass: "{{ account.password }}"
ansible_ssh_connection: paramiko

View File

@ -1,10 +0,0 @@
{% for account in accounts %}
- hosts: {{ account.asset.name }}
vars:
account:
username: {{ account.username }}
password: {{ account.password }}
public_key: {{ account.public_key }}
roles:
- change_password
{% endfor %}

View File

@ -1,8 +0,0 @@
id: change_password_sqlserver
name: Change password for SQLServer
version: 1
category: database
type:
- sqlserver
method: change_password

View File

@ -1,27 +0,0 @@
- name: ping
ping:
#- name: print variables
# debug:
# msg: "Username: {{ account.username }}, Password: {{ account.password }}"
- name: Change password
user:
name: "{{ account.username }}"
password: "{{ account.password | password_hash('des') }}"
update_password: always
when: account.password
- name: Change public key
authorized_key:
user: "{{ account.username }}"
key: "{{ account.public_key }}"
state: present
when: account.public_key
- name: Verify password
ping:
vars:
ansible_user: "{{ account.username }}"
ansible_pass: "{{ account.password }}"
ansible_ssh_connection: paramiko

View File

@ -1,10 +0,0 @@
{% for account in accounts %}
- hosts: {{ account.asset.name }}
vars:
account:
username: {{ account.username }}
password: {{ account.password }}
public_key: {{ account.public_key }}
roles:
- change_password
{% endfor %}

View File

@ -1,6 +0,0 @@
id: change_password_aix
name: Change password for AIX
category: host
type:
- aix
method: change_password

View File

@ -1,27 +0,0 @@
- name: ping
ping:
#- name: print variables
# debug:
# msg: "Username: {{ account.username }}, Password: {{ account.password }}"
- name: Change password
user:
name: "{{ account.username }}"
password: "{{ account.password | password_hash('des') }}"
update_password: always
when: account.password
- name: Change public key
authorized_key:
user: "{{ account.username }}"
key: "{{ account.public_key }}"
state: present
when: account.public_key
- name: Verify password
ping:
vars:
ansible_user: "{{ account.username }}"
ansible_pass: "{{ account.password }}"
ansible_ssh_connection: paramiko

View File

@ -1,8 +0,0 @@
- hosts: all
vars:
account:
username: {{ account.username }}
password: {{ account.password }}
public_key: {{ account.public_key }}
roles:
- change_password

View File

@ -1,7 +0,0 @@
id: change_password_linux
name: Change password for Linux
category: host
type:
- unix
- linux
method: change_password

View File

@ -1,23 +0,0 @@
- name: Check connection
ping:
- name: Change password
user:
name: "{{ account.username }}"
password: "{{ account.password | password_hash('sha512') }}"
update_password: always
when: account.password
- name: Change public key
authorized_key:
user: "{{ account.username }}"
key: "{{ account.public_key }}"
state: present
when: account.public_key
- name: Verify password
ping:
vars:
ansible_user: "{{ account.username }}"
ansible_pass: "{{ account.password }}"
ansible_ssh_connection: paramiko

View File

@ -1,10 +0,0 @@
{% for account in accounts %}
- hosts: {{ account.asset.name }}
vars:
account:
username: {{ account.username }}
password: {{ account.password }}
public_key: {{ account.public_key }}
roles:
- change_password
{% endfor %}

View File

@ -1,7 +0,0 @@
id: change_password_local_windows
name: Change password local account for Windows
version: 1
method: change_password
category: host
type:
- windows

View File

@ -1,27 +0,0 @@
- name: ping
ping:
#- name: print variables
# debug:
# msg: "Username: {{ account.username }}, Password: {{ account.password }}"
- name: Change password
user:
name: "{{ account.username }}"
password: "{{ account.password | password_hash('des') }}"
update_password: always
when: account.password
- name: Change public key
authorized_key:
user: "{{ account.username }}"
key: "{{ account.public_key }}"
state: present
when: account.public_key
- name: Verify password
ping:
vars:
ansible_user: "{{ account.username }}"
ansible_pass: "{{ account.password }}"
ansible_ssh_connection: paramiko

View File

@ -0,0 +1,32 @@
import os
import time
import shutil
from typing import List
from django.conf import settings
from assets.models import Asset
class BaseGeneratePlaybook:
src_filepath: str
def __init__(self, assets: List[Asset], strategy):
self.assets = assets
self.strategy = strategy
self.temp_folder = self.temp_folder_path()
@staticmethod
def temp_folder_path():
project_dir = settings.PROJECT_DIR
tmp_dir = os.path.join(project_dir, 'tmp')
filepath = os.path.join(tmp_dir, str(time.time()))
return filepath
def del_temp_folder(self):
shutil.rmtree(self.temp_folder)
def generate_temp_playbook(self):
src = self.src_filepath
dst = os.path.join(self.temp_folder, self.strategy)
shutil.copytree(src, dst)
return dst

View File

@ -0,0 +1,106 @@
import os
import yaml
import jinja2
from typing import List
from django.conf import settings
from assets.models import Asset
from .base import BaseGeneratePlaybook
class GenerateChangePasswordPlaybook(BaseGeneratePlaybook):
def __init__(
self, assets: List[Asset], strategy, usernames, password='',
private_key='', public_key='', key_strategy=''
):
super().__init__(assets, strategy)
self.password = password
self.public_key = public_key
self.private_key = private_key
self.key_strategy = key_strategy
self.relation_asset_map = self.get_username_relation_asset_map(usernames)
def get_username_relation_asset_map(self, usernames):
# TODO 没特权用户的资产 要考虑网关
complete_map = {
asset: list(asset.accounts.value_list('username', flat=True))
for asset in self.assets
}
if '*' in usernames:
return complete_map
relation_map = {}
for asset, usernames in complete_map.items():
usernames = list(set(usernames) & set(usernames))
if not usernames:
continue
relation_map[asset] = list(set(usernames) & set(usernames))
return relation_map
@property
def src_filepath(self):
return os.path.join(
settings.BASE_DIR, 'assets', 'playbooks', 'strategy',
'change_password', 'roles', self.strategy
)
def generate_hosts(self):
host_pathname = os.path.join(self.temp_folder, 'hosts')
with open(host_pathname, 'w', encoding='utf8') as f:
for asset in self.relation_asset_map.keys():
f.write(f'{asset.name}\n')
def generate_host_vars(self):
host_vars_pathname = os.path.join(self.temp_folder, 'hosts', 'host_vars')
os.makedirs(host_vars_pathname, exist_ok=True)
for asset, usernames in self.relation_asset_map.items():
host_vars = {
'ansible_host': asset.get_target_ip(),
'ansible_port': asset.get_target_ssh_port(), # TODO 需要根绝协议取端口号
'ansible_user': asset.admin_user.username,
'ansible_pass': asset.admin_user.username,
'usernames': usernames,
}
pathname = os.path.join(host_vars_pathname, f'{asset.name}.yml')
with open(pathname, 'w', encoding='utf8') as f:
f.write(yaml.dump(host_vars, allow_unicode=True))
def generate_secret_key_files(self):
if not self.private_key and not self.public_key:
return
file_pathname = os.path.join(self.temp_folder, self.strategy, 'files')
public_pathname = os.path.join(file_pathname, 'id_rsa.pub')
private_pathname = os.path.join(file_pathname, 'id_rsa')
os.makedirs(file_pathname, exist_ok=True)
with open(public_pathname, 'w', encoding='utf8') as f:
f.write(self.public_key)
with open(private_pathname, 'w', encoding='utf8') as f:
f.write(self.private_key)
def generate_role_main(self):
task_main_pathname = os.path.join(self.temp_folder, 'main.yaml')
context = {
'password': self.password,
'key_strategy': self.key_strategy,
'private_key_file': 'id_rsa' if self.private_key else '',
'exclusive': 'no' if self.key_strategy == 'all' else 'yes',
'jms_key': self.public_key.split()[2].strip() if self.public_key else '',
}
with open(task_main_pathname, 'r+', encoding='utf8') as f:
string_var = f.read()
f.seek(0, 0)
response = jinja2.Template(string_var).render(context)
results = yaml.safe_load(response)
f.write(yaml.dump(results, allow_unicode=True))
def execute(self):
self.generate_temp_playbook()
self.generate_hosts()
self.generate_host_vars()
self.generate_secret_key_files()
self.generate_role_main()

View File

@ -0,0 +1,86 @@
import os
import yaml
from typing import List
from django.conf import settings
from assets.models import Asset
from .base import BaseGeneratePlaybook
class GenerateVerifyPlaybook(BaseGeneratePlaybook):
def __init__(
self, assets: List[Asset], strategy, usernames
):
super().__init__(assets, strategy)
self.relation_asset_map = self.get_account_relation_asset_map(usernames)
def get_account_relation_asset_map(self, usernames):
# TODO 没特权用户的资产 要考虑网关
complete_map = {
asset: list(asset.accounts.all())
for asset in self.assets
}
if '*' in usernames:
return complete_map
relation_map = {}
for asset, accounts in complete_map.items():
account_map = {account.username: account for account in accounts}
accounts = [account_map[i] for i in (set(usernames) & set(account_map))]
if not accounts:
continue
relation_map[asset] = accounts
return relation_map
@property
def src_filepath(self):
return os.path.join(
settings.BASE_DIR, 'assets', 'playbooks', 'strategy',
'verify', 'roles', self.strategy
)
def generate_hosts(self):
host_pathname = os.path.join(self.temp_folder, 'hosts')
with open(host_pathname, 'w', encoding='utf8') as f:
for asset in self.relation_asset_map.keys():
f.write(f'{asset.name}\n')
def generate_host_vars(self):
host_vars_pathname = os.path.join(self.temp_folder, 'hosts', 'host_vars')
os.makedirs(host_vars_pathname, exist_ok=True)
for asset, accounts in self.relation_asset_map.items():
account_info = []
for account in accounts:
private_key_filename = f'{asset.name}_{account.username}' if account.private_key else ''
account_info.append({
'username': account.username,
'password': account.password,
'private_key_filename': private_key_filename,
})
host_vars = {
'ansible_host': asset.get_target_ip(),
'ansible_port': asset.get_target_ssh_port(), # TODO 需要根绝协议取端口号
'account_info': account_info,
}
pathname = os.path.join(host_vars_pathname, f'{asset.name}.yml')
with open(pathname, 'w', encoding='utf8') as f:
f.write(yaml.dump(host_vars, allow_unicode=True))
def generate_secret_key_files(self):
file_pathname = os.path.join(self.temp_folder, self.strategy, 'files')
os.makedirs(file_pathname, exist_ok=True)
for asset, accounts in self.relation_asset_map.items():
for account in accounts:
if account.private_key:
path_name = os.path.join(file_pathname, f'{asset.name}_{account.username}')
with open(path_name, 'w', encoding='utf8') as f:
f.write(account.private_key)
def execute(self):
self.generate_temp_playbook()
self.generate_hosts()
self.generate_host_vars()
self.generate_secret_key_files()
# self.generate_role_main() # TODO Linux 暂时不需要

View File

@ -0,0 +1,12 @@
- hosts: all
vars:
connection_type: ssh
password:
value: {{ password}}
public_key:
value: {{ jms_key }}
exclusive: {{ exclusive }}
key_strategy: {{ key_strategy }}
private_key_file: {{ private_key_file }}
roles:
- linux

View File

@ -0,0 +1,36 @@
- name: Check connection
ping:
- name: Change password
user:
name: "{{ item }}"
password: "{{ password.value | password_hash('sha512') }}"
update_password: always
with_items: "{{ usernames }}"
when: "{{ password.value }}"
- name: Change public key
authorized_key:
user: "{{ item }}"
key: "{{ lookup('file', id_rsa.pub) }}"
state: present
exclusive: "{{ public_key.exclusive }}"
with_items: "{{ usernames }}"
when: "{{ public_key.value and key_strategy != 'set_jms' }}"
- name: Change public key
lineinfile:
user: "{{ item }}"
dest: /home/{{ item }}/.ssh/authorized_keys regexp='.*{{ public_key.value }}$
state: absent
with_items: "{{ usernames }}"
when: "{{ public_key.value and key_strategy == 'set_jms' }}"
- name: Verify user
ping:
vars:
ansible_user: "{{ item }}"
ansible_pass: "{{ password.value }}"
ansible_ssh_private_key_file: "{{ private_key_file }}"
ansible_connection: "{{ connection_type | default('ssh') }}"
with_items: "{{ usernames }}"

View File

@ -0,0 +1,5 @@
- hosts: all
vars:
connection_type: ssh
roles:
- linux

View File

@ -0,0 +1,8 @@
- name: Verify user
ping:
vars:
ansible_user: "{{ item.username }}"
ansible_pass: "{{ item.username }}"
ansible_connection: "{{ connection_type | default('ssh') }}"
ansible_ssh_private_key_file: "{{ item.private_key_file }}"
with_items: "{{ account_info }}"

View File

@ -1,13 +0,0 @@
- hosts: centos
gather_facts: no
vars:
account:
username: web
password: test123
tasks:
- name: Verify password
ping:
vars:
ansible_user: "{{ account.username }}"
ansible_pass: "{{ account.password }}"

View File

@ -1,10 +0,0 @@
id: ansible_posix_ping
name: Ansible posix ping
description: Ansible ping
category: host
type:
- linux
- unix
- macos
- bsd
method: verify_account

View File

@ -1,13 +0,0 @@
- hosts: centos
gather_facts: no
vars:
account:
username: web
password: test123
tasks:
- name: Verify password
win_ping:
vars:
ansible_user: "{{ account.username }}"
ansible_pass: "{{ account.password }}"

View File

@ -1,6 +0,0 @@
id: ansible_win_ping
name: Ansible win ping
category: host
type:
- windows
method: verify_account

View File

@ -6,7 +6,7 @@ class StrategyChoice(models.TextChoices):
push = 'push', _('Push')
verify = 'verify', _('Verify')
collect = 'collect', _('Collect')
change_auth = 'change_auth', _('Change auth')
change_password = 'change_password', _('Change password')
class SSHKeyStrategy(models.TextChoices):

View File

@ -10,7 +10,7 @@ class ExecutionManager:
StrategyChoice.push: PushExecutionManager,
StrategyChoice.verify: VerifyExecutionManager,
StrategyChoice.collect: CollectExecutionManager,
StrategyChoice.change_auth: ChangeAuthExecutionManager,
StrategyChoice.change_password: ChangeAuthExecutionManager,
}
def __new__(cls, execution):
@ -23,7 +23,7 @@ class TaskHandler:
StrategyChoice.push: PushHandler,
StrategyChoice.verify: VerifyHandler,
StrategyChoice.collect: CollectHandler,
StrategyChoice.change_auth: ChangeAuthHandler,
StrategyChoice.change_password: ChangeAuthHandler,
}
def __new__(cls, task, show_step_info):