Merge branch 'v3' of github.com:jumpserver/jumpserver into v3

pull/8991/head
ibuler 2 years ago
commit 3e7004d784

@ -6,21 +6,33 @@
# #
# - name: print variables # - name: print variables
# debug: # debug:
# msg: "Username: {{ account.username }}, Secret: {{ account.secret }}, Secret type: {{ account.secret_type }}" # msg: "Username: {{ account.username }}, Secret: {{ account.secret }}, Secret type: {{ secret_type }}"
- name: Change password - name: Change password
ansible.builtin.user: ansible.builtin.user:
name: "{{ account.username }}" name: "{{ account.username }}"
password: "{{ account.secret | password_hash('sha512') }}" password: "{{ account.secret | password_hash('sha512') }}"
update_password: always update_password: always
when: account.secret_type == "password" when: "{{ secret_type == 'password' }}"
- name: Change public key - name: create user If it already exists, no operation will be performed
ansible.builtin.user:
name: "{{ account.username }}"
when: "{{ secret_type == 'ssh_key' }}"
- name: remove jumpserver ssh key
ansible.builtin.lineinfile:
dest: "{{ kwargs.dest }}"
regexp: "{{ kwargs.regexp }}"
state: absent
when: "{{ secret_type == 'ssh_key' and kwargs.strategy == 'set_jms' }}"
- name: Change SSH key
ansible.builtin.authorized_key: ansible.builtin.authorized_key:
user: "{{ account.username }}" user: "{{ account.username }}"
key: "{{ account.public_key }}" key: "{{ account.secret }}"
state: present exclusive: "{{ kwargs.exclusive }}"
when: account.secret_type == "public_key" when: "{{ secret_type == 'ssh_key' }}"
- name: Refresh connection - name: Refresh connection
ansible.builtin.meta: reset_connection ansible.builtin.meta: reset_connection
@ -32,3 +44,13 @@
ansible_user: "{{ account.username }}" ansible_user: "{{ account.username }}"
ansible_password: "{{ account.secret }}" ansible_password: "{{ account.secret }}"
ansible_become: no ansible_become: no
when: "{{ secret_type == 'password' }}"
- name: Verify SSH key
ansible.builtin.ping:
become: no
vars:
ansible_user: "{{ account.username }}"
ansible_ssh_private_key_file: "{{ account.private_key_path }}"
ansible_become: no
when: "{{ secret_type == 'ssh_key' }}"

@ -1,14 +1,17 @@
import os
import random import random
import string import string
from hashlib import md5
from copy import deepcopy from copy import deepcopy
from socket import gethostname
from collections import defaultdict from collections import defaultdict
from django.utils import timezone from django.utils import timezone
from common.utils import lazyproperty, gen_key_pair from common.utils import lazyproperty, gen_key_pair, ssh_pubkey_gen, ssh_key_string_to_obj
from assets.models import ChangeSecretRecord from assets.models import ChangeSecretRecord
from assets.const import ( from assets.const import (
AutomationTypes, SecretType, SecretStrategy, DEFAULT_PASSWORD_RULES AutomationTypes, SecretType, SecretStrategy, SSHKeyStrategy, DEFAULT_PASSWORD_RULES
) )
from ..base.manager import BasePlaybookManager from ..base.manager import BasePlaybookManager
@ -17,15 +20,15 @@ class ChangeSecretManager(BasePlaybookManager):
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs) super().__init__(*args, **kwargs)
self.method_hosts_mapper = defaultdict(list) self.method_hosts_mapper = defaultdict(list)
self.secret_type = self.execution.plan_snapshot.get('secret_type')
self.secret_strategy = self.execution.plan_snapshot['secret_strategy'] self.secret_strategy = self.execution.plan_snapshot['secret_strategy']
self.ssh_key_change_strategy = self.execution.plan_snapshot['ssh_key_change_strategy']
self._password_generated = None self._password_generated = None
self._ssh_key_generated = None self._ssh_key_generated = None
self.name_recorder_mapper = {} # 做个映射,方便后面处理 self.name_recorder_mapper = {} # 做个映射,方便后面处理
@classmethod @classmethod
def method_type(cls): def method_type(cls):
return AutomationTypes.change_secret return AutomationTypes.method_id_meta_mapper
@lazyproperty @lazyproperty
def related_accounts(self): def related_accounts(self):
@ -36,6 +39,19 @@ class ChangeSecretManager(BasePlaybookManager):
private_key, public_key = gen_key_pair() private_key, public_key = gen_key_pair()
return private_key return private_key
@staticmethod
def generate_public_key(private_key):
return ssh_pubkey_gen(private_key=private_key, hostname=gethostname())
@staticmethod
def generate_private_key_path(secret, path_dir):
key_name = '.' + md5(secret.encode('utf-8')).hexdigest()
key_path = os.path.join(path_dir, key_name)
if not os.path.exists(key_path):
ssh_key_string_to_obj(secret, password=None).write_private_key_file(key_path)
os.chmod(key_path, 0o400)
return key_path
def generate_password(self): def generate_password(self):
kwargs = self.automation.plan_snapshot['password_rules'] or {} kwargs = self.automation.plan_snapshot['password_rules'] or {}
length = int(kwargs.get('length', DEFAULT_PASSWORD_RULES['length'])) length = int(kwargs.get('length', DEFAULT_PASSWORD_RULES['length']))
@ -77,16 +93,29 @@ class ChangeSecretManager(BasePlaybookManager):
else: else:
return self.generate_password() return self.generate_password()
def get_secret(self, account): def get_secret(self):
if account.secret_type == SecretType.ssh_key: if self.secret_type == SecretType.ssh_key:
secret = self.get_ssh_key() secret = self.get_ssh_key()
elif account.secret_type == SecretType.password: elif self.secret_type == SecretType.password:
secret = self.get_password() secret = self.get_password()
else: else:
raise ValueError("Secret must be set") raise ValueError("Secret must be set")
return secret return secret
def host_callback(self, host, asset=None, account=None, automation=None, **kwargs): def get_kwargs(self, account, secret):
kwargs = {}
if self.secret_type != SecretType.ssh_key:
return kwargs
kwargs['strategy'] = self.automation.plan_snapshot['ssh_key_change_strategy']
kwargs['exclusive'] = 'yes' if kwargs['strategy'] == SSHKeyStrategy.set else 'no'
if kwargs['strategy'] == SSHKeyStrategy.set_jms:
kwargs['dest'] = '/home/{}/.ssh/authorized_keys'.format(account.username)
kwargs['regexp'] = '.*{}$'.format(secret.split()[2].strip())
return kwargs
def host_callback(self, host, asset=None, account=None, automation=None, path_dir=None, **kwargs):
host = super().host_callback(host, asset=asset, account=account, automation=automation, **kwargs) host = super().host_callback(host, asset=asset, account=account, automation=automation, **kwargs)
if host.get('error'): if host.get('error'):
return host return host
@ -95,7 +124,9 @@ class ChangeSecretManager(BasePlaybookManager):
if account: if account:
accounts = accounts.exclude(id=account.id) accounts = accounts.exclude(id=account.id)
if '*' not in self.automation.accounts: if '*' not in self.automation.accounts:
accounts = accounts.filter(username__in=self.automation.accounts) accounts = accounts.filter(
username__in=self.automation.accounts, secret_type=self.secret_type
)
method_attr = getattr(automation, self.method_type() + '_method') method_attr = getattr(automation, self.method_type() + '_method')
method_hosts = self.method_hosts_mapper[method_attr] method_hosts = self.method_hosts_mapper[method_attr]
@ -103,11 +134,12 @@ class ChangeSecretManager(BasePlaybookManager):
inventory_hosts = [] inventory_hosts = []
records = [] records = []
host['secret_type'] = self.secret_type
for account in accounts: for account in accounts:
h = deepcopy(host) h = deepcopy(host)
h['name'] += '_' + account.username h['name'] += '_' + account.username
new_secret = self.get_secret()
new_secret = self.get_secret(account)
recorder = ChangeSecretRecord( recorder = ChangeSecretRecord(
account=account, execution=self.execution, account=account, execution=self.execution,
old_secret=account.secret, new_secret=new_secret, old_secret=account.secret, new_secret=new_secret,
@ -115,11 +147,19 @@ class ChangeSecretManager(BasePlaybookManager):
records.append(recorder) records.append(recorder)
self.name_recorder_mapper[h['name']] = recorder self.name_recorder_mapper[h['name']] = recorder
private_key_path = None
if self.secret_type == SecretType.ssh_key:
private_key_path = self.generate_private_key_path(new_secret, path_dir)
new_secret = self.generate_public_key(new_secret)
h['kwargs'] = self.get_kwargs(account, new_secret)
h['account'] = { h['account'] = {
'name': account.name, 'name': account.name,
'username': account.username, 'username': account.username,
'secret_type': account.secret_type, 'secret_type': account.secret_type,
'secret': new_secret, 'secret': new_secret,
'private_key_path': private_key_path
} }
inventory_hosts.append(h) inventory_hosts.append(h)
method_hosts.append(h['name']) method_hosts.append(h['name'])

@ -156,7 +156,7 @@ class JMSInventory:
account_selected = accounts[0] if accounts else None account_selected = accounts[0] if accounts else None
return account_selected return account_selected
def generate(self): def generate(self, path_dir):
hosts = [] hosts = []
platform_assets = self.group_by_platform(self.assets) platform_assets = self.group_by_platform(self.assets)
for platform, assets in platform_assets.items(): for platform, assets in platform_assets.items():
@ -173,7 +173,8 @@ class JMSInventory:
if self.host_callback is not None: if self.host_callback is not None:
host = self.host_callback( host = self.host_callback(
host, asset=asset, account=account, host, asset=asset, account=account,
platform=platform, automation=automation platform=platform, automation=automation,
path_dir=path_dir
) )
if isinstance(host, list): if isinstance(host, list):
@ -195,8 +196,8 @@ class JMSInventory:
return data return data
def write_to_file(self, path): def write_to_file(self, path):
data = self.generate()
path_dir = os.path.dirname(path) path_dir = os.path.dirname(path)
data = self.generate(path_dir)
if not os.path.exists(path_dir): if not os.path.exists(path_dir):
os.makedirs(path_dir, 0o700, True) os.makedirs(path_dir, 0o700, True)
with open(path, 'w') as f: with open(path, 'w') as f:

Loading…
Cancel
Save