mirror of
https://github.com/eduardogsilva/routerfleet.git
synced 2025-08-31 23:40:46 +02:00
Allow SSH connection to different ports
This commit is contained in:
parent
a9215845bf
commit
25c700b0c1
5 changed files with 58 additions and 21 deletions
|
@ -2,7 +2,7 @@ from django import forms
|
||||||
from crispy_forms.helper import FormHelper
|
from crispy_forms.helper import FormHelper
|
||||||
from crispy_forms.layout import Layout, Submit, Row, Column, HTML
|
from crispy_forms.layout import Layout, Submit, Row, Column, HTML
|
||||||
from .models import Router, RouterGroup, SSHKey
|
from .models import Router, RouterGroup, SSHKey
|
||||||
from routerlib.functions import test_authentication
|
from routerlib.functions import test_authentication, connect_to_ssh
|
||||||
import ipaddress
|
import ipaddress
|
||||||
import socket
|
import socket
|
||||||
|
|
||||||
|
@ -12,7 +12,7 @@ class RouterForm(forms.ModelForm):
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Router
|
model = Router
|
||||||
fields = ['name', 'address', 'username', 'password', 'ssh_key', 'monitoring', 'router_type', 'enabled', 'backup_profile']
|
fields = ['name', 'port', 'address', 'username', 'password', 'ssh_key', 'monitoring', 'router_type', 'enabled', 'backup_profile']
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
super(RouterForm, self).__init__(*args, **kwargs)
|
super(RouterForm, self).__init__(*args, **kwargs)
|
||||||
|
@ -27,7 +27,7 @@ class RouterForm(forms.ModelForm):
|
||||||
self.helper.layout = Layout(
|
self.helper.layout = Layout(
|
||||||
Row(
|
Row(
|
||||||
Column('name', css_class='form-group col-md-6 mb-0'),
|
Column('name', css_class='form-group col-md-6 mb-0'),
|
||||||
Column('address', css_class='form-group col-md-6 mb-0'),
|
Column('ssh_key', css_class='form-group col-md-6 mb-0'),
|
||||||
css_class='form-row'
|
css_class='form-row'
|
||||||
),
|
),
|
||||||
Row(
|
Row(
|
||||||
|
@ -35,7 +35,12 @@ class RouterForm(forms.ModelForm):
|
||||||
Column('password', css_class='form-group col-md-6 mb-0'),
|
Column('password', css_class='form-group col-md-6 mb-0'),
|
||||||
css_class='form-row'
|
css_class='form-row'
|
||||||
),
|
),
|
||||||
'ssh_key',
|
Row(
|
||||||
|
Column('address', css_class='form-group col-md-6 mb-0'),
|
||||||
|
Column('port', css_class='form-group col-md-6 mb-0'),
|
||||||
|
css_class='form-row'
|
||||||
|
),
|
||||||
|
|
||||||
'backup_profile',
|
'backup_profile',
|
||||||
'router_type',
|
'router_type',
|
||||||
'monitoring',
|
'monitoring',
|
||||||
|
@ -59,6 +64,7 @@ class RouterForm(forms.ModelForm):
|
||||||
address = cleaned_data.get('address')
|
address = cleaned_data.get('address')
|
||||||
router_type = cleaned_data.get('router_type')
|
router_type = cleaned_data.get('router_type')
|
||||||
backup_profile = cleaned_data.get('backup_profile')
|
backup_profile = cleaned_data.get('backup_profile')
|
||||||
|
port = cleaned_data.get('port')
|
||||||
|
|
||||||
if name:
|
if name:
|
||||||
name = name.strip()
|
name = name.strip()
|
||||||
|
@ -82,6 +88,11 @@ class RouterForm(forms.ModelForm):
|
||||||
if backup_profile:
|
if backup_profile:
|
||||||
raise forms.ValidationError('Monitoring only routers cannot have a backup profile')
|
raise forms.ValidationError('Monitoring only routers cannot have a backup profile')
|
||||||
return cleaned_data
|
return cleaned_data
|
||||||
|
else:
|
||||||
|
if not port:
|
||||||
|
raise forms.ValidationError('You must provide a port')
|
||||||
|
if not 1 <= port <= 65535:
|
||||||
|
raise forms.ValidationError('Invalid port number')
|
||||||
|
|
||||||
if ssh_key and password:
|
if ssh_key and password:
|
||||||
raise forms.ValidationError('You must provide a password or an SSH Key, not both')
|
raise forms.ValidationError('You must provide a password or an SSH Key, not both')
|
||||||
|
@ -94,8 +105,9 @@ class RouterForm(forms.ModelForm):
|
||||||
if ssh_key and not password:
|
if ssh_key and not password:
|
||||||
cleaned_data['password'] = ''
|
cleaned_data['password'] = ''
|
||||||
|
|
||||||
|
|
||||||
test_authentication_success, test_authentication_message = test_authentication(
|
test_authentication_success, test_authentication_message = test_authentication(
|
||||||
router_type, cleaned_data['address'], username, cleaned_data['password'], ssh_key
|
router_type, cleaned_data['address'], port, username, cleaned_data['password'], ssh_key
|
||||||
)
|
)
|
||||||
if not test_authentication_success:
|
if not test_authentication_success:
|
||||||
if test_authentication_message:
|
if test_authentication_message:
|
||||||
|
|
18
router_manager/migrations/0016_router_port.py
Normal file
18
router_manager/migrations/0016_router_port.py
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
# Generated by Django 5.0.3 on 2024-04-12 17:52
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('router_manager', '0015_alter_routerstatus_status_online'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='router',
|
||||||
|
name='port',
|
||||||
|
field=models.IntegerField(default=22),
|
||||||
|
),
|
||||||
|
]
|
|
@ -26,6 +26,7 @@ class Router(models.Model):
|
||||||
name = models.CharField(max_length=100, unique=True)
|
name = models.CharField(max_length=100, unique=True)
|
||||||
internal_notes = models.TextField(null=True, blank=True)
|
internal_notes = models.TextField(null=True, blank=True)
|
||||||
address = models.CharField(max_length=100)
|
address = models.CharField(max_length=100)
|
||||||
|
port = models.IntegerField(default=22)
|
||||||
username = models.CharField(max_length=100, default='admin')
|
username = models.CharField(max_length=100, default='admin')
|
||||||
password = models.CharField(max_length=100, null=True, blank=True)
|
password = models.CharField(max_length=100, null=True, blank=True)
|
||||||
ssh_key = models.ForeignKey(SSHKey, on_delete=models.SET_NULL, null=True, blank=True)
|
ssh_key = models.ForeignKey(SSHKey, on_delete=models.SET_NULL, null=True, blank=True)
|
||||||
|
|
|
@ -75,14 +75,15 @@ def execute_backup(router_backup: RouterBackup):
|
||||||
router = router_backup.router
|
router = router_backup.router
|
||||||
backup_name = gen_backup_name(router_backup)
|
backup_name = gen_backup_name(router_backup)
|
||||||
file_extension = get_router_backup_file_extension(router.router_type)
|
file_extension = get_router_backup_file_extension(router.router_type)
|
||||||
|
ssh_client = None
|
||||||
try:
|
try:
|
||||||
if router_backup.router.router_type == 'routeros':
|
if router_backup.router.router_type == 'routeros':
|
||||||
ssh_client = connect_to_ssh(router.address, router.username, router.password, router.ssh_key)
|
ssh_client = connect_to_ssh(router.address, router.port, router.username, router.password, router.ssh_key)
|
||||||
ssh_client.exec_command(f'/system backup save name={backup_name}.{file_extension["binary"]}')
|
ssh_client.exec_command(f'/system backup save name={backup_name}.{file_extension["binary"]}')
|
||||||
ssh_client.exec_command(f'/export file={backup_name}.{file_extension["text"]}')
|
ssh_client.exec_command(f'/export file={backup_name}.{file_extension["text"]}')
|
||||||
return True, [f'{backup_name}.{file_extension["binary"]}', f'{backup_name}.{file_extension["text"]}'], error_message
|
return True, [f'{backup_name}.{file_extension["binary"]}', f'{backup_name}.{file_extension["text"]}'], error_message
|
||||||
elif router_backup.router.router_type == 'openwrt':
|
elif router_backup.router.router_type == 'openwrt':
|
||||||
ssh_client = connect_to_ssh(router.address, router.username, router.password, router.ssh_key)
|
ssh_client = connect_to_ssh(router.address, router.port, router.username, router.password, router.ssh_key)
|
||||||
stdin, stdout, stderr = ssh_client.exec_command('uci export')
|
stdin, stdout, stderr = ssh_client.exec_command('uci export')
|
||||||
backup_text = stdout.read().decode('utf-8')
|
backup_text = stdout.read().decode('utf-8')
|
||||||
if backup_text:
|
if backup_text:
|
||||||
|
@ -100,7 +101,8 @@ def execute_backup(router_backup: RouterBackup):
|
||||||
error_message = f"Failed to execute backup: {str(e)}"
|
error_message = f"Failed to execute backup: {str(e)}"
|
||||||
return False, [], error_message
|
return False, [], error_message
|
||||||
finally:
|
finally:
|
||||||
ssh_client.close()
|
if ssh_client:
|
||||||
|
ssh_client.close()
|
||||||
|
|
||||||
|
|
||||||
def retrieve_backup(router_backup: RouterBackup):
|
def retrieve_backup(router_backup: RouterBackup):
|
||||||
|
@ -109,12 +111,13 @@ def retrieve_backup(router_backup: RouterBackup):
|
||||||
backup_name = gen_backup_name(router_backup)
|
backup_name = gen_backup_name(router_backup)
|
||||||
success = False
|
success = False
|
||||||
file_extension = get_router_backup_file_extension(router.router_type)
|
file_extension = get_router_backup_file_extension(router.router_type)
|
||||||
|
ssh_client = None
|
||||||
|
|
||||||
try:
|
try:
|
||||||
if router_backup.router.router_type == 'routeros':
|
if router_backup.router.router_type == 'routeros':
|
||||||
rsc_file_path = f'/tmp/{backup_name}.{file_extension["text"]}'
|
rsc_file_path = f'/tmp/{backup_name}.{file_extension["text"]}'
|
||||||
backup_file_path = f'/tmp/{backup_name}.{file_extension["binary"]}'
|
backup_file_path = f'/tmp/{backup_name}.{file_extension["binary"]}'
|
||||||
ssh_client = connect_to_ssh(router.address, router.username, router.password, router.ssh_key)
|
ssh_client = connect_to_ssh(router.address, router.port, router.username, router.password, router.ssh_key)
|
||||||
scp_client = SCPClient(ssh_client.get_transport())
|
scp_client = SCPClient(ssh_client.get_transport())
|
||||||
scp_client.get(f'/{backup_name}.{file_extension["text"]}', rsc_file_path)
|
scp_client.get(f'/{backup_name}.{file_extension["text"]}', rsc_file_path)
|
||||||
scp_client.get(f'/{backup_name}.{file_extension["binary"]}', backup_file_path)
|
scp_client.get(f'/{backup_name}.{file_extension["binary"]}', backup_file_path)
|
||||||
|
@ -139,7 +142,7 @@ def retrieve_backup(router_backup: RouterBackup):
|
||||||
elif router_backup.router.router_type == 'openwrt':
|
elif router_backup.router.router_type == 'openwrt':
|
||||||
remote_backup_file_path = f'/tmp/{backup_name}.{file_extension["binary"]}'
|
remote_backup_file_path = f'/tmp/{backup_name}.{file_extension["binary"]}'
|
||||||
local_backup_file_path = f'/tmp/{backup_name}.{file_extension["binary"]}'
|
local_backup_file_path = f'/tmp/{backup_name}.{file_extension["binary"]}'
|
||||||
ssh_client = connect_to_ssh(router.address, router.username, router.password, router.ssh_key)
|
ssh_client = connect_to_ssh(router.address, router.port, router.username, router.password, router.ssh_key)
|
||||||
scp_client = SCPClient(ssh_client.get_transport())
|
scp_client = SCPClient(ssh_client.get_transport())
|
||||||
scp_client.get(remote_backup_file_path, local_backup_file_path)
|
scp_client.get(remote_backup_file_path, local_backup_file_path)
|
||||||
with open(local_backup_file_path, 'rb') as backup_file:
|
with open(local_backup_file_path, 'rb') as backup_file:
|
||||||
|
@ -156,23 +159,26 @@ def retrieve_backup(router_backup: RouterBackup):
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
return success, f"Failed to retrieve backup files: {str(e)}"
|
return success, f"Failed to retrieve backup files: {str(e)}"
|
||||||
finally:
|
finally:
|
||||||
ssh_client.close()
|
if ssh_client:
|
||||||
|
ssh_client.close()
|
||||||
|
|
||||||
return success, error_message
|
return success, error_message
|
||||||
|
|
||||||
|
|
||||||
def clean_up_backup_files(router_backup: RouterBackup):
|
def clean_up_backup_files(router_backup: RouterBackup):
|
||||||
router = router_backup.router
|
router = router_backup.router
|
||||||
|
ssh_client = None
|
||||||
try:
|
try:
|
||||||
if router_backup.router.router_type == 'routeros':
|
if router_backup.router.router_type == 'routeros':
|
||||||
ssh_client = connect_to_ssh(router.address, router.username, router.password, router.ssh_key)
|
ssh_client = connect_to_ssh(router.address, router.port, router.username, router.password, router.ssh_key)
|
||||||
ssh_client.exec_command('file remove [find where name~"routerfleet-backup-"]')
|
ssh_client.exec_command('file remove [find where name~"routerfleet-backup-"]')
|
||||||
elif router_backup.router.router_type == 'openwrt':
|
elif router_backup.router.router_type == 'openwrt':
|
||||||
ssh_client = connect_to_ssh(router.address, router.username, router.password, router.ssh_key)
|
ssh_client = connect_to_ssh(router.address, router.port, router.username, router.password, router.ssh_key)
|
||||||
ssh_client.exec_command('rm /tmp/routerfleet-backup-*')
|
ssh_client.exec_command('rm /tmp/routerfleet-backup-*')
|
||||||
else:
|
else:
|
||||||
print(f"Router type not supported: {router_backup.router.get_router_type_display()}")
|
print(f"Router type not supported: {router_backup.router.get_router_type_display()}")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"Failed to clean up backup files: {str(e)}")
|
print(f"Failed to clean up backup files: {str(e)}")
|
||||||
finally:
|
finally:
|
||||||
ssh_client.close()
|
if ssh_client:
|
||||||
|
ssh_client.close()
|
||||||
|
|
|
@ -40,18 +40,18 @@ def load_private_key_from_string(key_str):
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
def connect_to_ssh(address, username, password, sshkey=None):
|
def connect_to_ssh(address, port, username, password, sshkey=None):
|
||||||
ssh_client = paramiko.SSHClient()
|
ssh_client = paramiko.SSHClient()
|
||||||
ssh_client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
|
ssh_client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
|
||||||
if sshkey:
|
if sshkey:
|
||||||
private_key = load_private_key_from_string(sshkey.private_key)
|
private_key = load_private_key_from_string(sshkey.private_key)
|
||||||
ssh_client.connect(address, username=username, pkey=private_key, look_for_keys=False, timeout=10, allow_agent=False)
|
ssh_client.connect(address, port=port, username=username, pkey=private_key, look_for_keys=False, timeout=10, allow_agent=False)
|
||||||
else:
|
else:
|
||||||
ssh_client.connect(address, username=username, password=password, look_for_keys=False, timeout=10, allow_agent=False)
|
ssh_client.connect(address, port=port, username=username, password=password, look_for_keys=False, timeout=10, allow_agent=False)
|
||||||
return ssh_client
|
return ssh_client
|
||||||
|
|
||||||
|
|
||||||
def test_authentication(router_type, address, username, password, sshkey=None):
|
def test_authentication(router_type, address, port, username, password, sshkey=None):
|
||||||
router_features = get_router_features(router_type)
|
router_features = get_router_features(router_type)
|
||||||
if 'ssh' in router_features:
|
if 'ssh' in router_features:
|
||||||
connection_type = 'ssh'
|
connection_type = 'ssh'
|
||||||
|
@ -61,14 +61,14 @@ def test_authentication(router_type, address, username, password, sshkey=None):
|
||||||
return False, 'Router type not supported'
|
return False, 'Router type not supported'
|
||||||
|
|
||||||
if connection_type == 'ssh':
|
if connection_type == 'ssh':
|
||||||
return test_ssh_authentication(router_type, address, username, password, sshkey)
|
return test_ssh_authentication(router_type, address, port, username, password, sshkey)
|
||||||
elif connection_type == 'telnet':
|
elif connection_type == 'telnet':
|
||||||
return test_telnet_authentication(address, username, password, sshkey=None)
|
return test_telnet_authentication(address, username, password, sshkey=None)
|
||||||
|
|
||||||
|
|
||||||
def test_ssh_authentication(router_type, address, username, password, sshkey=None):
|
def test_ssh_authentication(router_type, address, port, username, password, sshkey=None):
|
||||||
try:
|
try:
|
||||||
ssh_client = connect_to_ssh(address, username, password, sshkey)
|
ssh_client = connect_to_ssh(address, port, username, password, sshkey)
|
||||||
if router_type == 'routeros':
|
if router_type == 'routeros':
|
||||||
stdin, stdout, stderr = ssh_client.exec_command('/system resource print')
|
stdin, stdout, stderr = ssh_client.exec_command('/system resource print')
|
||||||
output = stdout.read().decode()
|
output = stdout.read().decode()
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue