mirror of
https://github.com/eduardogsilva/routerfleet.git
synced 2025-08-31 07:10:14 +02:00
Support for SSH Key authentication
This commit is contained in:
parent
e2f9e8a8a6
commit
4af7d4ba01
4 changed files with 45 additions and 34 deletions
|
@ -1,11 +1,10 @@
|
||||||
import datetime
|
import datetime
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
from backup_data.models import RouterBackup
|
from backup_data.models import RouterBackup
|
||||||
import paramiko
|
|
||||||
import os
|
import os
|
||||||
from scp import SCPClient
|
from scp import SCPClient
|
||||||
from django.core.files.base import ContentFile
|
from django.core.files.base import ContentFile
|
||||||
from routerlib.functions import gen_backup_name
|
from routerlib.functions import gen_backup_name, connect_to_ssh
|
||||||
|
|
||||||
|
|
||||||
def perform_backup(router_backup: RouterBackup):
|
def perform_backup(router_backup: RouterBackup):
|
||||||
|
@ -73,14 +72,10 @@ def handle_backup_failure(router_backup: RouterBackup, error_message):
|
||||||
|
|
||||||
def execute_backup(router_backup: RouterBackup):
|
def execute_backup(router_backup: RouterBackup):
|
||||||
error_message = ""
|
error_message = ""
|
||||||
ssh_client = paramiko.SSHClient()
|
router = router_backup.router
|
||||||
ssh_client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
|
|
||||||
try:
|
try:
|
||||||
if router_backup.router.router_type == 'routeros':
|
if router_backup.router.router_type == 'routeros':
|
||||||
ssh_client.connect(
|
ssh_client = connect_to_ssh(router.address, router.username, router.password, router.ssh_key)
|
||||||
router_backup.router.address, username=router_backup.router.username,
|
|
||||||
password=router_backup.router.password, look_for_keys=False, allow_agent=False, timeout=10
|
|
||||||
)
|
|
||||||
backup_name = gen_backup_name(router_backup)
|
backup_name = gen_backup_name(router_backup)
|
||||||
ssh_client.exec_command(f'/system backup save name={backup_name}.backup')
|
ssh_client.exec_command(f'/system backup save name={backup_name}.backup')
|
||||||
ssh_client.exec_command(f'/export file={backup_name}.rsc')
|
ssh_client.exec_command(f'/export file={backup_name}.rsc')
|
||||||
|
@ -97,20 +92,15 @@ def execute_backup(router_backup: RouterBackup):
|
||||||
|
|
||||||
def retrieve_backup(router_backup: RouterBackup):
|
def retrieve_backup(router_backup: RouterBackup):
|
||||||
error_message = ""
|
error_message = ""
|
||||||
|
router = router_backup.router
|
||||||
backup_name = gen_backup_name(router_backup)
|
backup_name = gen_backup_name(router_backup)
|
||||||
|
|
||||||
success = False
|
success = False
|
||||||
ssh_client = paramiko.SSHClient()
|
|
||||||
ssh_client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
if router_backup.router.router_type == 'routeros':
|
if router_backup.router.router_type == 'routeros':
|
||||||
rsc_file_path = f"/tmp/{backup_name}.rsc"
|
rsc_file_path = f"/tmp/{backup_name}.rsc"
|
||||||
backup_file_path = f"/tmp/{backup_name}.backup"
|
backup_file_path = f"/tmp/{backup_name}.backup"
|
||||||
|
ssh_client = connect_to_ssh(router.address, router.username, router.password, router.ssh_key)
|
||||||
ssh_client.connect(router_backup.router.address, username=router_backup.router.username,
|
|
||||||
password=router_backup.router.password, look_for_keys=False, allow_agent=False,
|
|
||||||
timeout=10)
|
|
||||||
scp_client = SCPClient(ssh_client.get_transport())
|
scp_client = SCPClient(ssh_client.get_transport())
|
||||||
|
|
||||||
scp_client.get(f"/{backup_name}.rsc", rsc_file_path)
|
scp_client.get(f"/{backup_name}.rsc", rsc_file_path)
|
||||||
|
@ -145,14 +135,10 @@ def retrieve_backup(router_backup: RouterBackup):
|
||||||
|
|
||||||
|
|
||||||
def clean_up_backup_files(router_backup: RouterBackup):
|
def clean_up_backup_files(router_backup: RouterBackup):
|
||||||
ssh_client = paramiko.SSHClient()
|
router = router_backup.router
|
||||||
ssh_client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
|
|
||||||
try:
|
try:
|
||||||
if router_backup.router.router_type == 'routeros':
|
if router_backup.router.router_type == 'routeros':
|
||||||
ssh_client.connect(
|
ssh_client = connect_to_ssh(router.address, router.username, router.password, router.ssh_key)
|
||||||
router_backup.router.address, username=router_backup.router.username,
|
|
||||||
password=router_backup.router.password, look_for_keys=False, timeout=10, allow_agent=False
|
|
||||||
)
|
|
||||||
ssh_client.exec_command('file remove [find where name~"routerfleet-backup-"]')
|
ssh_client.exec_command('file remove [find where name~"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()}")
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
from io import StringIO
|
||||||
|
|
||||||
import paramiko
|
import paramiko
|
||||||
import telnetlib
|
import telnetlib
|
||||||
|
|
||||||
|
@ -20,6 +22,33 @@ def gen_backup_name(router_backup):
|
||||||
return f'routerfleet-backup-{router_backup.id}-{router_backup.schedule_type}-{router_backup.created.strftime("%Y-%m-%d_%H-%M")}'
|
return f'routerfleet-backup-{router_backup.id}-{router_backup.schedule_type}-{router_backup.created.strftime("%Y-%m-%d_%H-%M")}'
|
||||||
|
|
||||||
|
|
||||||
|
def load_private_key_from_string(key_str):
|
||||||
|
key_types = [
|
||||||
|
paramiko.RSAKey,
|
||||||
|
paramiko.DSSKey,
|
||||||
|
paramiko.ECDSAKey,
|
||||||
|
paramiko.Ed25519Key,
|
||||||
|
]
|
||||||
|
for key_type in key_types:
|
||||||
|
try:
|
||||||
|
key_file_obj = StringIO(key_str)
|
||||||
|
return key_type.from_private_key(key_file_obj)
|
||||||
|
except paramiko.ssh_exception.SSHException:
|
||||||
|
continue
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def connect_to_ssh(address, username, password, sshkey=None):
|
||||||
|
ssh_client = paramiko.SSHClient()
|
||||||
|
ssh_client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
|
||||||
|
if sshkey:
|
||||||
|
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)
|
||||||
|
else:
|
||||||
|
ssh_client.connect(address, username=username, password=password, look_for_keys=False, timeout=10, allow_agent=False)
|
||||||
|
return ssh_client
|
||||||
|
|
||||||
|
|
||||||
def test_authentication(router_type, address, username, password, sshkey=None):
|
def test_authentication(router_type, address, 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:
|
||||||
|
@ -37,19 +66,16 @@ def test_authentication(router_type, address, username, password, sshkey=None):
|
||||||
|
|
||||||
def test_ssh_authentication(router_type, address, username, password, sshkey=None):
|
def test_ssh_authentication(router_type, address, username, password, sshkey=None):
|
||||||
try:
|
try:
|
||||||
ssh = paramiko.SSHClient()
|
ssh_client = connect_to_ssh(address, username, password, sshkey)
|
||||||
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
|
|
||||||
ssh.connect(address, username=username, password=password, look_for_keys=False, timeout=10, allow_agent=False)
|
|
||||||
|
|
||||||
if router_type == 'routeros':
|
if router_type == 'routeros':
|
||||||
stdin, stdout, stderr = ssh.exec_command('/system resource print')
|
stdin, stdout, stderr = ssh_client.exec_command('/system resource print')
|
||||||
output = stdout.read().decode()
|
output = stdout.read().decode()
|
||||||
if 'platform: MikroTik' in output:
|
if 'platform: MikroTik' in output:
|
||||||
result = True, 'Success: MikroTik device confirmed'
|
result = True, 'Success: MikroTik device confirmed'
|
||||||
else:
|
else:
|
||||||
result = False, 'Device is not MikroTik'
|
result = False, 'Device is not MikroTik'
|
||||||
elif router_type == 'openwrt':
|
elif router_type == 'openwrt':
|
||||||
stdin, stdout, stderr = ssh.exec_command('ubus call system board')
|
stdin, stdout, stderr = ssh_client.exec_command('ubus call system board')
|
||||||
output = stdout.read().decode()
|
output = stdout.read().decode()
|
||||||
if 'OpenWrt' in output:
|
if 'OpenWrt' in output:
|
||||||
result = True, 'Success: OpenWRT device confirmed'
|
result = True, 'Success: OpenWRT device confirmed'
|
||||||
|
@ -58,7 +84,7 @@ def test_ssh_authentication(router_type, address, username, password, sshkey=Non
|
||||||
else:
|
else:
|
||||||
result = False, 'Unsupported device type'
|
result = False, 'Unsupported device type'
|
||||||
|
|
||||||
ssh.close()
|
ssh_client.close()
|
||||||
return result
|
return result
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
return False, str(e)
|
return False, str(e)
|
||||||
|
|
|
@ -21,7 +21,7 @@
|
||||||
<th>Router</th>
|
<th>Router</th>
|
||||||
<th>Address</th>
|
<th>Address</th>
|
||||||
<th>Schedule Type</th>
|
<th>Schedule Type</th>
|
||||||
<th>Schedule Time</th>
|
<th>Task Created</th>
|
||||||
<th>Finish Time</th>
|
<th>Finish Time</th>
|
||||||
<th class="min-width" title="Backup hash"><i class="fas fa-code-branch" title="Configuration has changed"></i></th>
|
<th class="min-width" title="Backup hash"><i class="fas fa-code-branch" title="Configuration has changed"></i></th>
|
||||||
<th class="min-width">Status</th>
|
<th class="min-width">Status</th>
|
||||||
|
@ -42,7 +42,7 @@
|
||||||
</td>
|
</td>
|
||||||
<td>{{ backup.router.address }}</td>
|
<td>{{ backup.router.address }}</td>
|
||||||
<td>{{ backup.get_schedule_type_display }}</td>
|
<td>{{ backup.get_schedule_type_display }}</td>
|
||||||
<td>{{ backup.schedule_time|default_if_none:"" }}</td>
|
<td>{{ backup.created|default_if_none:"" }}</td>
|
||||||
<td>{% if backup.finish_time %}{{ backup.finish_time }}{% else %}
|
<td>{% if backup.finish_time %}{{ backup.finish_time }}{% else %}
|
||||||
<i class="far fa-clock text-warning"></i>{% endif %}</td>
|
<i class="far fa-clock text-warning"></i>{% endif %}</td>
|
||||||
|
|
||||||
|
|
|
@ -25,7 +25,7 @@
|
||||||
{% for sshkey in sshkey_list %}
|
{% for sshkey in sshkey_list %}
|
||||||
<tr>
|
<tr>
|
||||||
<td>{{ sshkey.name }}</td>
|
<td>{{ sshkey.name }}</td>
|
||||||
<td>{{ sshkey.public_key }}</td>
|
<td>{{ sshkey.public_key|slice:40 }}...</td>
|
||||||
<td>{{ sshkey.router_set.count }}</td>
|
<td>{{ sshkey.router_set.count }}</td>
|
||||||
<td class="min-width">
|
<td class="min-width">
|
||||||
<a href="/router/manage_sshkey/?uuid={{ sshkey.uuid }}"><i
|
<a href="/router/manage_sshkey/?uuid={{ sshkey.uuid }}"><i
|
||||||
|
@ -40,9 +40,8 @@
|
||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-lg-12">
|
<div class="col-lg-12">
|
||||||
{% comment %}<a href="/router/manage_sshkey/" class="btn btn-primary">Add SSH Key</a>{% endcomment %}
|
<a href="/router/manage_sshkey/" class="btn btn-primary">Add SSH Key</a>
|
||||||
<a href="" class="btn btn-primary disabled">Add SSH Key</a>
|
|
||||||
<p>Sorry, SSH authentication using keys is still unsupported. It will be added soon</p>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue