mirror of https://github.com/jumpserver/jumpserver
Merge branch 'v3' of github.com:jumpserver/jumpserver into v3
commit
3e7004d784
|
@ -3,24 +3,36 @@
|
|||
tasks:
|
||||
- name: Test privileged account
|
||||
ansible.builtin.ping:
|
||||
#
|
||||
# - name: print variables
|
||||
# debug:
|
||||
# msg: "Username: {{ account.username }}, Secret: {{ account.secret }}, Secret type: {{ account.secret_type }}"
|
||||
#
|
||||
# - name: print variables
|
||||
# debug:
|
||||
# msg: "Username: {{ account.username }}, Secret: {{ account.secret }}, Secret type: {{ secret_type }}"
|
||||
|
||||
- name: Change password
|
||||
ansible.builtin.user:
|
||||
name: "{{ account.username }}"
|
||||
password: "{{ account.secret | password_hash('sha512') }}"
|
||||
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:
|
||||
user: "{{ account.username }}"
|
||||
key: "{{ account.public_key }}"
|
||||
state: present
|
||||
when: account.secret_type == "public_key"
|
||||
key: "{{ account.secret }}"
|
||||
exclusive: "{{ kwargs.exclusive }}"
|
||||
when: "{{ secret_type == 'ssh_key' }}"
|
||||
|
||||
- name: Refresh connection
|
||||
ansible.builtin.meta: reset_connection
|
||||
|
@ -32,3 +44,13 @@
|
|||
ansible_user: "{{ account.username }}"
|
||||
ansible_password: "{{ account.secret }}"
|
||||
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 string
|
||||
from hashlib import md5
|
||||
from copy import deepcopy
|
||||
from socket import gethostname
|
||||
from collections import defaultdict
|
||||
|
||||
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.const import (
|
||||
AutomationTypes, SecretType, SecretStrategy, DEFAULT_PASSWORD_RULES
|
||||
AutomationTypes, SecretType, SecretStrategy, SSHKeyStrategy, DEFAULT_PASSWORD_RULES
|
||||
)
|
||||
from ..base.manager import BasePlaybookManager
|
||||
|
||||
|
@ -17,15 +20,15 @@ class ChangeSecretManager(BasePlaybookManager):
|
|||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
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.ssh_key_change_strategy = self.execution.plan_snapshot['ssh_key_change_strategy']
|
||||
self._password_generated = None
|
||||
self._ssh_key_generated = None
|
||||
self.name_recorder_mapper = {} # 做个映射,方便后面处理
|
||||
|
||||
@classmethod
|
||||
def method_type(cls):
|
||||
return AutomationTypes.change_secret
|
||||
return AutomationTypes.method_id_meta_mapper
|
||||
|
||||
@lazyproperty
|
||||
def related_accounts(self):
|
||||
|
@ -36,6 +39,19 @@ class ChangeSecretManager(BasePlaybookManager):
|
|||
private_key, public_key = gen_key_pair()
|
||||
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):
|
||||
kwargs = self.automation.plan_snapshot['password_rules'] or {}
|
||||
length = int(kwargs.get('length', DEFAULT_PASSWORD_RULES['length']))
|
||||
|
@ -77,16 +93,29 @@ class ChangeSecretManager(BasePlaybookManager):
|
|||
else:
|
||||
return self.generate_password()
|
||||
|
||||
def get_secret(self, account):
|
||||
if account.secret_type == SecretType.ssh_key:
|
||||
def get_secret(self):
|
||||
if self.secret_type == SecretType.ssh_key:
|
||||
secret = self.get_ssh_key()
|
||||
elif account.secret_type == SecretType.password:
|
||||
elif self.secret_type == SecretType.password:
|
||||
secret = self.get_password()
|
||||
else:
|
||||
raise ValueError("Secret must be set")
|
||||
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)
|
||||
if host.get('error'):
|
||||
return host
|
||||
|
@ -95,7 +124,9 @@ class ChangeSecretManager(BasePlaybookManager):
|
|||
if account:
|
||||
accounts = accounts.exclude(id=account.id)
|
||||
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_hosts = self.method_hosts_mapper[method_attr]
|
||||
|
@ -103,11 +134,12 @@ class ChangeSecretManager(BasePlaybookManager):
|
|||
inventory_hosts = []
|
||||
records = []
|
||||
|
||||
host['secret_type'] = self.secret_type
|
||||
for account in accounts:
|
||||
h = deepcopy(host)
|
||||
h['name'] += '_' + account.username
|
||||
new_secret = self.get_secret()
|
||||
|
||||
new_secret = self.get_secret(account)
|
||||
recorder = ChangeSecretRecord(
|
||||
account=account, execution=self.execution,
|
||||
old_secret=account.secret, new_secret=new_secret,
|
||||
|
@ -115,11 +147,19 @@ class ChangeSecretManager(BasePlaybookManager):
|
|||
records.append(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'] = {
|
||||
'name': account.name,
|
||||
'username': account.username,
|
||||
'secret_type': account.secret_type,
|
||||
'secret': new_secret,
|
||||
'private_key_path': private_key_path
|
||||
}
|
||||
inventory_hosts.append(h)
|
||||
method_hosts.append(h['name'])
|
||||
|
|
|
@ -156,7 +156,7 @@ class JMSInventory:
|
|||
account_selected = accounts[0] if accounts else None
|
||||
return account_selected
|
||||
|
||||
def generate(self):
|
||||
def generate(self, path_dir):
|
||||
hosts = []
|
||||
platform_assets = self.group_by_platform(self.assets)
|
||||
for platform, assets in platform_assets.items():
|
||||
|
@ -173,7 +173,8 @@ class JMSInventory:
|
|||
if self.host_callback is not None:
|
||||
host = self.host_callback(
|
||||
host, asset=asset, account=account,
|
||||
platform=platform, automation=automation
|
||||
platform=platform, automation=automation,
|
||||
path_dir=path_dir
|
||||
)
|
||||
|
||||
if isinstance(host, list):
|
||||
|
@ -195,8 +196,8 @@ class JMSInventory:
|
|||
return data
|
||||
|
||||
def write_to_file(self, path):
|
||||
data = self.generate()
|
||||
path_dir = os.path.dirname(path)
|
||||
data = self.generate(path_dir)
|
||||
if not os.path.exists(path_dir):
|
||||
os.makedirs(path_dir, 0o700, True)
|
||||
with open(path, 'w') as f:
|
||||
|
|
Loading…
Reference in New Issue