diff --git a/archery/settings.py b/archery/settings.py index 69387e605e..7780d26b49 100644 --- a/archery/settings.py +++ b/archery/settings.py @@ -257,3 +257,11 @@ # }, } } + +MEDIA_ROOT = os.path.join(BASE_DIR, 'media') +if not os.path.exists(MEDIA_ROOT): + os.mkdir(MEDIA_ROOT) + +PKEY_ROOT = os.path.join(MEDIA_ROOT, 'keys') +if not os.path.exists(PKEY_ROOT): + os.mkdir(PKEY_ROOT) \ No newline at end of file diff --git a/sql/admin.py b/sql/admin.py index ff73925934..ec57f68462 100755 --- a/sql/admin.py +++ b/sql/admin.py @@ -11,6 +11,8 @@ WorkflowAudit, WorkflowLog, ParamTemplate, ParamHistory, InstanceTag, \ Tunnel, AuditEntry +from sql.form import TunnelForm + # 用户管理 @admin.register(Users) @@ -87,13 +89,14 @@ class TunnelAdmin(admin.ModelAdmin): search_fields = ('id', 'tunnel_name') fieldsets = ( None, - {'fields': ('tunnel_name', 'host', 'port', 'user', 'password', 'pkey_path', 'pkey_password',), }), + {'fields': ('tunnel_name', 'host', 'port', 'user', 'password', 'pkey_path', 'pkey_password', 'pkey'), }), ordering = ('id',) # 添加页显示内容 add_fieldsets = ( ('隧道信息', {'fields': ('tunnel_name', 'host', 'port')}), - ('连接信息', {'fields': ('user', 'password', 'pkey_path', 'pkey_password')}), + ('连接信息', {'fields': ('user', 'password', 'pkey_path', 'pkey_password', 'pkey')}), ) + form = TunnelForm def formfield_for_dbfield(self, db_field, **kwargs): if db_field.name in ['password', 'pkey_password']: diff --git a/sql/engines/__init__.py b/sql/engines/__init__.py index 21bf69033e..97cf8ee434 100644 --- a/sql/engines/__init__.py +++ b/sql/engines/__init__.py @@ -27,7 +27,7 @@ def __init__(self, instance=None): instance.tunnel.port, instance.tunnel.user, instance.tunnel.password, - instance.tunnel.pkey_path, + instance.tunnel.pkey, instance.tunnel.pkey_password, ) self.host,self.port = self.ssh.get_ssh() @@ -48,7 +48,7 @@ def remote_instance_conn(self, instance=None): instance.tunnel.port, instance.tunnel.user, instance.tunnel.password, - instance.tunnel.pkey_path, + instance.tunnel.pkey, instance.tunnel.pkey_password, ) self.remote_host, self.remote_port = self.remotessh.get_ssh() diff --git a/sql/form.py b/sql/form.py new file mode 100644 index 0000000000..9b8280bb37 --- /dev/null +++ b/sql/form.py @@ -0,0 +1,32 @@ +#!/usr/bin/python +# -*- coding:utf-8 -*- +""" +--------------------------------------------------------- +@project: issacmarkArchery +@file: form +@date: 2021/12/30 17:43 +@author: mayp +--------------------------------------------------------- +""" +from django.forms import ModelForm, Textarea +from sql.models import Tunnel +from django.core.exceptions import ValidationError + + +class TunnelForm(ModelForm): + class Meta: + model = Tunnel + fields = "__all__" + widgets = { + 'PKey': Textarea(attrs={'cols': 40, 'rows': 8}), + } + + def clean(self): + cleaned_data = super().clean() + if cleaned_data.get('pkey_path'): + try: + pkey_path = cleaned_data.get('pkey_path').read() + if pkey_path: + cleaned_data['pkey'] = str(pkey_path, 'utf-8').replace(r'\r', '').replace(r'\n', '') + except IOError: + raise ValidationError("秘钥文件不存在, 请勾选秘钥路径的清除选项再进行保存") diff --git a/sql/models.py b/sql/models.py index 11920ce72a..98f85731c6 100755 --- a/sql/models.py +++ b/sql/models.py @@ -99,7 +99,8 @@ class Tunnel(models.Model): port = models.IntegerField('端口', default=0) user = fields.EncryptedCharField(verbose_name='用户名', max_length=200, default='', blank=True, null=True) password = fields.EncryptedCharField(verbose_name='密码', max_length=300, default='', blank=True, null=True) - pkey_path = fields.EncryptedCharField(verbose_name='密钥地址', max_length=300, default='', blank=True, null=True) + pkey = fields.EncryptedTextField(verbose_name="密钥", blank=True, null=True) + pkey_path = models.FileField(verbose_name="密钥地址", blank=True, null=True, upload_to='keys/') pkey_password = fields.EncryptedCharField(verbose_name='密钥密码', max_length=300, default='', blank=True, null=True) create_time = models.DateTimeField('创建时间', auto_now_add=True) update_time = models.DateTimeField('更新时间', auto_now=True) @@ -107,6 +108,12 @@ class Tunnel(models.Model): def __str__(self): return self.tunnel_name + def short_pkey(self): + if len(str(self.pkey)) > 20: + return '{}...'.format(str(self.pkey)[0:19]) + else: + return str(self.pkey) + class Meta: managed = True db_table = 'ssh_tunnel' @@ -878,4 +885,4 @@ def __unicode__(self): def __str__(self): return '{0} - {1} - {2} - {3} - {4}'.format(self.user_id, self.user_name, self.ip - , self.action, self.action_time) \ No newline at end of file + , self.action, self.action_time) diff --git a/sql/utils/ssh_tunnel.py b/sql/utils/ssh_tunnel.py index fe58923ed0..e40c9861e7 100644 --- a/sql/utils/ssh_tunnel.py +++ b/sql/utils/ssh_tunnel.py @@ -7,12 +7,14 @@ """ from sshtunnel import SSHTunnelForwarder from paramiko import RSAKey +import io + class SSHConnection(object): """ ssh隧道连接类,用于映射ssh隧道端口到本地,连接结束时需要清理 """ - def __init__(self, host, port, tun_host, tun_port, tun_user, tun_password, pkey_path, pkey_password): + def __init__(self, host, port, tun_host, tun_port, tun_user, tun_password, pkey, pkey_password): self.host = host self.port = int(port) self.tun_host = tun_host @@ -20,8 +22,11 @@ def __init__(self, host, port, tun_host, tun_port, tun_user, tun_password, pkey_ self.tun_user = tun_user self.tun_password = tun_password - if pkey_path: - self.private_key = RSAKey.from_private_key_file(pkey_path, password=pkey_password) + if pkey: + private_key_file_obj = io.StringIO() + private_key_file_obj.write(pkey) + private_key_file_obj.seek(0) + self.private_key = RSAKey.from_private_key(private_key_file_obj, password=pkey_password) self.server = SSHTunnelForwarder( ssh_address_or_host=(self.tun_host, self.tun_port), ssh_username=self.tun_user, diff --git a/src/docker-compose/docker-compose.yml b/src/docker-compose/docker-compose.yml index 605ef44df1..88a92321e4 100644 --- a/src/docker-compose/docker-compose.yml +++ b/src/docker-compose/docker-compose.yml @@ -53,6 +53,7 @@ services: - "./archery/downloads:/opt/archery/downloads" - "./archery/sql/migrations:/opt/archery/sql/migrations" - "./archery/logs:/opt/archery/logs" + - "./archery/keys:/opt/archery/keys" entrypoint: "dockerize -wait tcp://mysql:3306 -wait tcp://redis:6379 -timeout 60s /opt/archery/src/docker/startup.sh" environment: NGINX_PORT: 9123 diff --git a/src/init_sql/v1.8.3.sql b/src/init_sql/v1.8.3.sql index 8a749e786a..0416804bdd 100644 --- a/src/init_sql/v1.8.3.sql +++ b/src/init_sql/v1.8.3.sql @@ -13,3 +13,6 @@ CREATE TABLE `audit_log` ( -- 新增my2sql菜单权限 set @content_type_id=(select id from django_content_type where app_label='sql' and model='permission'); INSERT INTO auth_permission (name, content_type_id, codename) VALUES ('菜单 My2SQL', @content_type_id, 'menu_my2sql'); + +-- ssh 隧道功能修改 +ALTER TABLE `ssh_tunnel` ADD COLUMN pkey longtext NULL AFTER password DEFAULT CHARSET=utf8mb4 COMMENT='密钥信息'; \ No newline at end of file