mirror of
https://github.com/jumpserver/jumpserver.git
synced 2025-05-03 21:56:46 +00:00
Merge pull request #10104 from O-Jiangweidong/pr@dev@feat_windows_winrm
feat: Windows类型资产增加winrm协议
This commit is contained in:
commit
9af2974bad
apps
assets
automations/base
const
migrations
serializers
ops/ansible
requirements
@ -1,11 +1,12 @@
|
|||||||
import json
|
import json
|
||||||
import os
|
import os
|
||||||
import shutil
|
import shutil
|
||||||
|
import yaml
|
||||||
|
|
||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
from hashlib import md5
|
from hashlib import md5
|
||||||
from socket import gethostname
|
from socket import gethostname
|
||||||
|
|
||||||
import yaml
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
from django.utils.translation import gettext as _
|
from django.utils.translation import gettext as _
|
||||||
@ -239,10 +240,12 @@ class BasePlaybookManager:
|
|||||||
jms_asset, jms_gateway = host['jms_asset'], host.get('gateway')
|
jms_asset, jms_gateway = host['jms_asset'], host.get('gateway')
|
||||||
if not jms_gateway:
|
if not jms_gateway:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
server = SSHTunnelForwarder(
|
server = SSHTunnelForwarder(
|
||||||
(jms_gateway['address'], jms_gateway['port']),
|
(jms_gateway['address'], jms_gateway['port']),
|
||||||
ssh_username=jms_gateway['username'],
|
ssh_username=jms_gateway['username'],
|
||||||
ssh_password=jms_gateway['secret'],
|
ssh_password=jms_gateway['secret'],
|
||||||
|
ssh_pkey=jms_gateway['private_key_path'],
|
||||||
remote_bind_address=(jms_asset['address'], jms_asset['port'])
|
remote_bind_address=(jms_asset['address'], jms_asset['port'])
|
||||||
)
|
)
|
||||||
try:
|
try:
|
||||||
@ -252,8 +255,8 @@ class BasePlaybookManager:
|
|||||||
print('\033[31m %s \033[0m\n' % err_msg)
|
print('\033[31m %s \033[0m\n' % err_msg)
|
||||||
not_valid.append(k)
|
not_valid.append(k)
|
||||||
else:
|
else:
|
||||||
jms_asset['address'] = '127.0.0.1'
|
host['ansible_host'] = jms_asset['address'] = '127.0.0.1'
|
||||||
jms_asset['port'] = server.local_bind_port
|
host['ansible_port'] = jms_asset['port'] = server.local_bind_port
|
||||||
servers.append(server)
|
servers.append(server)
|
||||||
|
|
||||||
# 网域不可连接的,就不继续执行此资源的后续任务了
|
# 网域不可连接的,就不继续执行此资源的后续任务了
|
||||||
|
@ -36,7 +36,7 @@ class HostTypes(BaseType):
|
|||||||
'choices': ['ssh', 'telnet', 'vnc', 'rdp']
|
'choices': ['ssh', 'telnet', 'vnc', 'rdp']
|
||||||
},
|
},
|
||||||
cls.WINDOWS: {
|
cls.WINDOWS: {
|
||||||
'choices': ['rdp', 'ssh', 'vnc']
|
'choices': ['rdp', 'ssh', 'vnc', 'winrm']
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -58,7 +58,7 @@ class HostTypes(BaseType):
|
|||||||
cls.WINDOWS: {
|
cls.WINDOWS: {
|
||||||
'ansible_config': {
|
'ansible_config': {
|
||||||
'ansible_shell_type': 'cmd',
|
'ansible_shell_type': 'cmd',
|
||||||
'ansible_connection': 'ssh',
|
'ansible_connection': 'smart',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
cls.OTHER_HOST: {
|
cls.OTHER_HOST: {
|
||||||
|
@ -10,6 +10,7 @@ class Protocol(ChoicesMixin, models.TextChoices):
|
|||||||
rdp = 'rdp', 'RDP'
|
rdp = 'rdp', 'RDP'
|
||||||
telnet = 'telnet', 'Telnet'
|
telnet = 'telnet', 'Telnet'
|
||||||
vnc = 'vnc', 'VNC'
|
vnc = 'vnc', 'VNC'
|
||||||
|
winrm = 'winrm', 'WinRM'
|
||||||
|
|
||||||
mysql = 'mysql', 'MySQL'
|
mysql = 'mysql', 'MySQL'
|
||||||
mariadb = 'mariadb', 'MariaDB'
|
mariadb = 'mariadb', 'MariaDB'
|
||||||
@ -51,6 +52,13 @@ class Protocol(ChoicesMixin, models.TextChoices):
|
|||||||
'port': 23,
|
'port': 23,
|
||||||
'secret_types': ['password'],
|
'secret_types': ['password'],
|
||||||
},
|
},
|
||||||
|
cls.winrm: {
|
||||||
|
'port': 5985,
|
||||||
|
'secret_types': ['password'],
|
||||||
|
'setting': {
|
||||||
|
'use_ssl': False,
|
||||||
|
}
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
|
@ -2,6 +2,8 @@
|
|||||||
|
|
||||||
from django.db import migrations, models
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
from assets.const import AllTypes
|
||||||
|
|
||||||
|
|
||||||
def migrate_platform_charset(apps, schema_editor):
|
def migrate_platform_charset(apps, schema_editor):
|
||||||
platform_model = apps.get_model('assets', 'Platform')
|
platform_model = apps.get_model('assets', 'Platform')
|
||||||
@ -20,6 +22,11 @@ def migrate_platform_protocol_primary(apps, schema_editor):
|
|||||||
p.save()
|
p.save()
|
||||||
|
|
||||||
|
|
||||||
|
def migrate_internal_platforms(apps, schema_editor):
|
||||||
|
platform_cls = apps.get_model('assets', 'Platform')
|
||||||
|
AllTypes.create_or_update_internal_platforms(platform_cls)
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
@ -34,4 +41,5 @@ class Migration(migrations.Migration):
|
|||||||
),
|
),
|
||||||
migrations.RunPython(migrate_platform_charset),
|
migrations.RunPython(migrate_platform_charset),
|
||||||
migrations.RunPython(migrate_platform_protocol_primary),
|
migrations.RunPython(migrate_platform_protocol_primary),
|
||||||
|
migrations.RunPython(migrate_internal_platforms),
|
||||||
]
|
]
|
||||||
|
@ -41,6 +41,9 @@ class ProtocolSettingSerializer(serializers.Serializer):
|
|||||||
# Redis
|
# Redis
|
||||||
auth_username = serializers.BooleanField(default=False, label=_("Auth with username"))
|
auth_username = serializers.BooleanField(default=False, label=_("Auth with username"))
|
||||||
|
|
||||||
|
# WinRM
|
||||||
|
use_ssl = serializers.BooleanField(default=False, label=_("Use SSL"))
|
||||||
|
|
||||||
|
|
||||||
class PlatformAutomationSerializer(serializers.ModelSerializer):
|
class PlatformAutomationSerializer(serializers.ModelSerializer):
|
||||||
class Meta:
|
class Meta:
|
||||||
|
@ -72,15 +72,14 @@ class JMSInventory:
|
|||||||
var['ansible_ssh_private_key_file'] = account.private_key_path
|
var['ansible_ssh_private_key_file'] = account.private_key_path
|
||||||
return var
|
return var
|
||||||
|
|
||||||
def make_ssh_account_vars(self, host, asset, account, automation, protocols, platform, gateway):
|
def make_account_vars(self, host, asset, account, automation, protocol, platform, gateway):
|
||||||
if not account:
|
if not account:
|
||||||
host['error'] = _("No account available")
|
host['error'] = _("No account available")
|
||||||
return host
|
return host
|
||||||
|
|
||||||
ssh_protocol_matched = list(filter(lambda x: x.name == 'ssh', protocols))
|
port = protocol.port if protocol else 22
|
||||||
ssh_protocol = ssh_protocol_matched[0] if ssh_protocol_matched else None
|
|
||||||
host['ansible_host'] = asset.address
|
host['ansible_host'] = asset.address
|
||||||
host['ansible_port'] = ssh_protocol.port if ssh_protocol else 22
|
host['ansible_port'] = port
|
||||||
|
|
||||||
su_from = account.su_from
|
su_from = account.su_from
|
||||||
if platform.su_enabled and su_from:
|
if platform.su_enabled and su_from:
|
||||||
@ -97,28 +96,55 @@ class JMSInventory:
|
|||||||
host.update(self.make_account_ansible_vars(account))
|
host.update(self.make_account_ansible_vars(account))
|
||||||
|
|
||||||
if gateway:
|
if gateway:
|
||||||
host.update(self.make_proxy_command(gateway))
|
ansible_connection = host.get('ansible_connection', 'ssh')
|
||||||
|
if ansible_connection in ('local', 'winrm'):
|
||||||
|
host['gateway'] = {
|
||||||
|
'address': gateway.address, 'port': gateway.port,
|
||||||
|
'username': gateway.username, 'secret': gateway.password,
|
||||||
|
'private_key_path': gateway.private_key_path
|
||||||
|
}
|
||||||
|
host['jms_asset']['port'] = port
|
||||||
|
else:
|
||||||
|
host.update(self.make_proxy_command(gateway))
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_primary_protocol(protocols):
|
def get_primary_protocol(ansible_config, protocols):
|
||||||
if protocols:
|
invalid_protocol = type('protocol', (), {'name': 'null', 'port': 0})
|
||||||
primary = protocols[0]
|
ansible_connection = ansible_config.get('ansible_connection')
|
||||||
protocol = primary.name
|
# 数值越小,优先级越高,若用户在 ansible_config 中配置了,则提高用户配置方式的优先级
|
||||||
port = primary.port
|
protocol_priority = {'ssh': 10, 'winrm': 9, ansible_connection: 1}
|
||||||
else:
|
protocol_sorted = sorted(protocols, key=lambda x: protocol_priority.get(x.name, 999))
|
||||||
protocol = 'null'
|
protocol = protocol_sorted[0] if protocol_sorted else invalid_protocol
|
||||||
port = 0
|
return protocol
|
||||||
return protocol, port
|
|
||||||
|
@staticmethod
|
||||||
|
def fill_ansible_config(ansible_config, protocol):
|
||||||
|
if protocol.name in ('ssh', 'winrm'):
|
||||||
|
ansible_config['ansible_connection'] = protocol.name
|
||||||
|
if protocol.name == 'winrm':
|
||||||
|
if protocol.setting.get('use_ssl', False):
|
||||||
|
ansible_config['ansible_winrm_scheme'] = 'https'
|
||||||
|
ansible_config['ansible_winrm_transport'] = 'ssl'
|
||||||
|
ansible_config['ansible_winrm_server_cert_validation'] = 'ignore'
|
||||||
|
else:
|
||||||
|
ansible_config['ansible_winrm_scheme'] = 'http'
|
||||||
|
ansible_config['ansible_winrm_transport'] = 'plaintext'
|
||||||
|
return ansible_config
|
||||||
|
|
||||||
def asset_to_host(self, asset, account, automation, protocols, platform):
|
def asset_to_host(self, asset, account, automation, protocols, platform):
|
||||||
protocol, port = self.get_primary_protocol(protocols)
|
try:
|
||||||
|
ansible_config = dict(automation.ansible_config)
|
||||||
|
except (AttributeError, TypeError):
|
||||||
|
ansible_config = {}
|
||||||
|
|
||||||
|
protocol = self.get_primary_protocol(ansible_config, protocols)
|
||||||
|
|
||||||
host = {
|
host = {
|
||||||
'name': '{}'.format(asset.name.replace(' ', '_')),
|
'name': '{}'.format(asset.name.replace(' ', '_')),
|
||||||
'jms_asset': {
|
'jms_asset': {
|
||||||
'id': str(asset.id), 'name': asset.name, 'address': asset.address,
|
'id': str(asset.id), 'name': asset.name, 'address': asset.address,
|
||||||
'type': asset.type, 'category': asset.category,
|
'type': asset.type, 'category': asset.category,
|
||||||
'protocol': protocol, 'port': port,
|
'protocol': protocol.name, 'port': protocol.port,
|
||||||
'spec_info': asset.spec_info, 'secret_info': asset.secret_info,
|
'spec_info': asset.spec_info, 'secret_info': asset.secret_info,
|
||||||
'protocols': [{'name': p.name, 'port': p.port} for p in protocols],
|
'protocols': [{'name': p.name, 'port': p.port} for p in protocols],
|
||||||
},
|
},
|
||||||
@ -131,25 +157,16 @@ class JMSInventory:
|
|||||||
if host['jms_account'] and asset.platform.type == 'oracle':
|
if host['jms_account'] and asset.platform.type == 'oracle':
|
||||||
host['jms_account']['mode'] = 'sysdba' if account.privileged else None
|
host['jms_account']['mode'] = 'sysdba' if account.privileged else None
|
||||||
|
|
||||||
try:
|
ansible_config = self.fill_ansible_config(ansible_config, protocol)
|
||||||
ansible_config = dict(automation.ansible_config)
|
|
||||||
except Exception as e:
|
|
||||||
ansible_config = {}
|
|
||||||
ansible_connection = ansible_config.get('ansible_connection', 'ssh')
|
|
||||||
host.update(ansible_config)
|
host.update(ansible_config)
|
||||||
|
|
||||||
gateway = None
|
gateway = None
|
||||||
if not asset.is_gateway and asset.domain:
|
if not asset.is_gateway and asset.domain:
|
||||||
gateway = asset.domain.select_gateway()
|
gateway = asset.domain.select_gateway()
|
||||||
|
|
||||||
if ansible_connection == 'local':
|
self.make_account_vars(
|
||||||
if gateway:
|
host, asset, account, automation, protocol, platform, gateway
|
||||||
host['gateway'] = {
|
)
|
||||||
'address': gateway.address, 'port': gateway.port,
|
|
||||||
'username': gateway.username, 'secret': gateway.password
|
|
||||||
}
|
|
||||||
else:
|
|
||||||
self.make_ssh_account_vars(host, asset, account, automation, protocols, platform, gateway)
|
|
||||||
return host
|
return host
|
||||||
|
|
||||||
def get_asset_sorted_accounts(self, asset):
|
def get_asset_sorted_accounts(self, asset):
|
||||||
@ -194,14 +211,23 @@ class JMSInventory:
|
|||||||
else:
|
else:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def set_platform_protocol_setting_to_asset(asset, platform_protocols):
|
||||||
|
asset_protocols = asset.protocols.all()
|
||||||
|
for p in asset_protocols:
|
||||||
|
setattr(p, 'setting', platform_protocols.get(p.name, {}))
|
||||||
|
return asset_protocols
|
||||||
|
|
||||||
def generate(self, path_dir):
|
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():
|
||||||
automation = platform.automation
|
automation = platform.automation
|
||||||
|
platform_protocols = {
|
||||||
|
p['name']: p['setting'] for p in platform.protocols.values('name', 'setting')
|
||||||
|
}
|
||||||
for asset in assets:
|
for asset in assets:
|
||||||
protocols = asset.protocols.all()
|
protocols = self.set_platform_protocol_setting_to_asset(asset, platform_protocols)
|
||||||
account = self.select_account(asset)
|
account = self.select_account(asset)
|
||||||
host = self.asset_to_host(asset, account, automation, protocols, platform)
|
host = self.asset_to_host(asset, account, automation, protocols, platform)
|
||||||
|
|
||||||
|
@ -65,6 +65,7 @@ pyjwkest==1.4.2
|
|||||||
jsonfield2==4.0.0.post0
|
jsonfield2==4.0.0.post0
|
||||||
geoip2==4.5.0
|
geoip2==4.5.0
|
||||||
ipip-ipdb==1.6.1
|
ipip-ipdb==1.6.1
|
||||||
|
pywinrm==0.4.3
|
||||||
# Django environment
|
# Django environment
|
||||||
Django==3.2.17
|
Django==3.2.17
|
||||||
django-bootstrap3==14.2.0
|
django-bootstrap3==14.2.0
|
||||||
|
Loading…
Reference in New Issue
Block a user