Merge pull request #10104 from O-Jiangweidong/pr@dev@feat_windows_winrm

feat: Windows类型资产增加winrm协议
pull/10135/head^2
老广 2023-04-04 14:08:34 +08:00 committed by GitHub
commit 9af2974bad
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 85 additions and 36 deletions

View File

@ -1,11 +1,12 @@
import json
import os
import shutil
import yaml
from collections import defaultdict
from hashlib import md5
from socket import gethostname
import yaml
from django.conf import settings
from django.utils import timezone
from django.utils.translation import gettext as _
@ -239,10 +240,12 @@ class BasePlaybookManager:
jms_asset, jms_gateway = host['jms_asset'], host.get('gateway')
if not jms_gateway:
continue
server = SSHTunnelForwarder(
(jms_gateway['address'], jms_gateway['port']),
ssh_username=jms_gateway['username'],
ssh_password=jms_gateway['secret'],
ssh_pkey=jms_gateway['private_key_path'],
remote_bind_address=(jms_asset['address'], jms_asset['port'])
)
try:
@ -252,8 +255,8 @@ class BasePlaybookManager:
print('\033[31m %s \033[0m\n' % err_msg)
not_valid.append(k)
else:
jms_asset['address'] = '127.0.0.1'
jms_asset['port'] = server.local_bind_port
host['ansible_host'] = jms_asset['address'] = '127.0.0.1'
host['ansible_port'] = jms_asset['port'] = server.local_bind_port
servers.append(server)
# 网域不可连接的,就不继续执行此资源的后续任务了

View File

@ -36,7 +36,7 @@ class HostTypes(BaseType):
'choices': ['ssh', 'telnet', 'vnc', 'rdp']
},
cls.WINDOWS: {
'choices': ['rdp', 'ssh', 'vnc']
'choices': ['rdp', 'ssh', 'vnc', 'winrm']
}
}
@ -58,7 +58,7 @@ class HostTypes(BaseType):
cls.WINDOWS: {
'ansible_config': {
'ansible_shell_type': 'cmd',
'ansible_connection': 'ssh',
'ansible_connection': 'smart',
},
},
cls.OTHER_HOST: {

View File

@ -10,6 +10,7 @@ class Protocol(ChoicesMixin, models.TextChoices):
rdp = 'rdp', 'RDP'
telnet = 'telnet', 'Telnet'
vnc = 'vnc', 'VNC'
winrm = 'winrm', 'WinRM'
mysql = 'mysql', 'MySQL'
mariadb = 'mariadb', 'MariaDB'
@ -51,6 +52,13 @@ class Protocol(ChoicesMixin, models.TextChoices):
'port': 23,
'secret_types': ['password'],
},
cls.winrm: {
'port': 5985,
'secret_types': ['password'],
'setting': {
'use_ssl': False,
}
},
}
@classmethod

View File

@ -2,6 +2,8 @@
from django.db import migrations, models
from assets.const import AllTypes
def migrate_platform_charset(apps, schema_editor):
platform_model = apps.get_model('assets', 'Platform')
@ -20,6 +22,11 @@ def migrate_platform_protocol_primary(apps, schema_editor):
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):
dependencies = [
@ -34,4 +41,5 @@ class Migration(migrations.Migration):
),
migrations.RunPython(migrate_platform_charset),
migrations.RunPython(migrate_platform_protocol_primary),
migrations.RunPython(migrate_internal_platforms),
]

View File

@ -41,6 +41,9 @@ class ProtocolSettingSerializer(serializers.Serializer):
# Redis
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 Meta:

View File

@ -72,15 +72,14 @@ class JMSInventory:
var['ansible_ssh_private_key_file'] = account.private_key_path
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:
host['error'] = _("No account available")
return host
ssh_protocol_matched = list(filter(lambda x: x.name == 'ssh', protocols))
ssh_protocol = ssh_protocol_matched[0] if ssh_protocol_matched else None
port = protocol.port if protocol else 22
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
if platform.su_enabled and su_from:
@ -97,28 +96,55 @@ class JMSInventory:
host.update(self.make_account_ansible_vars(account))
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
def get_primary_protocol(protocols):
if protocols:
primary = protocols[0]
protocol = primary.name
port = primary.port
else:
protocol = 'null'
port = 0
return protocol, port
def get_primary_protocol(ansible_config, protocols):
invalid_protocol = type('protocol', (), {'name': 'null', 'port': 0})
ansible_connection = ansible_config.get('ansible_connection')
# 数值越小,优先级越高,若用户在 ansible_config 中配置了,则提高用户配置方式的优先级
protocol_priority = {'ssh': 10, 'winrm': 9, ansible_connection: 1}
protocol_sorted = sorted(protocols, key=lambda x: protocol_priority.get(x.name, 999))
protocol = protocol_sorted[0] if protocol_sorted else invalid_protocol
return protocol
@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):
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 = {
'name': '{}'.format(asset.name.replace(' ', '_')),
'jms_asset': {
'id': str(asset.id), 'name': asset.name, 'address': asset.address,
'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,
'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':
host['jms_account']['mode'] = 'sysdba' if account.privileged else None
try:
ansible_config = dict(automation.ansible_config)
except Exception as e:
ansible_config = {}
ansible_connection = ansible_config.get('ansible_connection', 'ssh')
ansible_config = self.fill_ansible_config(ansible_config, protocol)
host.update(ansible_config)
gateway = None
if not asset.is_gateway and asset.domain:
gateway = asset.domain.select_gateway()
if ansible_connection == 'local':
if 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)
self.make_account_vars(
host, asset, account, automation, protocol, platform, gateway
)
return host
def get_asset_sorted_accounts(self, asset):
@ -194,14 +211,23 @@ class JMSInventory:
else:
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):
hosts = []
platform_assets = self.group_by_platform(self.assets)
for platform, assets in platform_assets.items():
automation = platform.automation
platform_protocols = {
p['name']: p['setting'] for p in platform.protocols.values('name', 'setting')
}
for asset in assets:
protocols = asset.protocols.all()
protocols = self.set_platform_protocol_setting_to_asset(asset, platform_protocols)
account = self.select_account(asset)
host = self.asset_to_host(asset, account, automation, protocols, platform)

View File

@ -65,6 +65,7 @@ pyjwkest==1.4.2
jsonfield2==4.0.0.post0
geoip2==4.5.0
ipip-ipdb==1.6.1
pywinrm==0.4.3
# Django environment
Django==3.2.17
django-bootstrap3==14.2.0