首页 > 编程问答 >如何使用 Paramiko 进行 Tailscale 身份验证?

如何使用 Paramiko 进行 Tailscale 身份验证?

时间:2024-08-01 08:32:26浏览次数:9  
标签:python logging paramiko tailscale

我正在编写一个脚本,该脚本使用 Tailscale API 获取设备列表,然后使用 Paramiko 通过 SSH 连接并查询设备上的一些数据。昨天一切正常,但今天所有连接都失败了。我意识到,在幕后,SSH 失败了,因为它由 Tailscale 管理,需要重新身份验证:

ssh root@MyDevice
# Tailscale SSH requires an additional check.
# To authenticate, visit: https://login.tailscale.com/a/XXXXXX

但是我如何使用 Paramiko 检测并解决此问题?我当前的代码非常基本,但我能够添加日志记录:

import requests
from datetime import datetime, timedelta
import paramiko
import logging

url = "https://api.tailscale.com/api/v2/tailnet/-/devices"

# https://tailscale.com/kb/1085/auth-keys#generate-an-auth-key
headers = {"Authorization": "Bearer GENERATED AUTH KEY"}

response = requests.get(url, headers=headers)

# print(response.json())
data = response.json()
devices = data['devices']

current = datetime.utcnow() - timedelta(minutes = 1)

class OnlyTailscaleFilter(logging.Filter):
    def filter(self, record):
        return not record.getMessage().contains('Tailscale')

logging.basicConfig()
logger = logging.getLogger("paramiko")
logger.setLevel(logging.INFO)
logger.addFilter(OnlyTailscaleFilter())

for device in devices:
    if 'lastSeen' in device:
        last_seen = datetime.strptime(device['lastSeen'], '%Y-%m-%dT%H:%M:%SZ')
        if last_seen > current:
            hostname = ''
            uptime_seconds = ''
                    
            if 'hostname' in device:
                hostname = device['hostname']

            ssh = paramiko.SSHClient()
            ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
            try:
                ssh.connect(device['addresses'][0], username='root')
            except paramiko.AuthenticationException as ex:
                print('{}, An authentication exception occurred: {}'.format(hostname, ex))
                exit(1)
            else:
                ssh_stdin, ssh_stdout, ssh_stderr = ssh.exec_command('cat /proc/uptime')
                if ssh_stdout.channel.recv_exit_status() == 0:
                    temp = ssh_stdout.readlines()
                    if temp:
                        temp = temp[0].strip('\n').strip('\"')
                        uptime_seconds = temp.split()[0]

                print(hostname, ',', uptime_seconds)
                ssh.close()

当我运行它时,我可以在异常之前的日志中看到这一点:

INFO:paramiko.transport:Connected (version 2.0, client Tailscale)
INFO:paramiko.transport:Auth banner: b'# Tailscale SSH requires an additional check.\n# To authenticate, visit: https://login.tailscale.com/a/XXXXX\n'

当然可以使用 Paramiko 检测并处理此 Tailscale 身份验证挑战。诀窍在于识别 Tailscale 的身份验证提示并使用 Tailscale 提供的 URL 进行身份验证。由于此身份验证流在浏览器中发生,因此无法在 Paramiko 中直接处理它。

以下是调整代码以解决此问题的步骤:

  1. 检测 Tailscale 身份验证提示: 修改的代码以识别 Paramiko 捕获的 SSH 横幅中的 Tailscale 特定消息。
  2. 提取身份验证 URL: 从 SSH 横幅中解析 Tailscale 提供的 URL。
  3. 提示用户进行身份验证: 指示用户访问提取的 URL 以完成身份验证过程。
  4. 重试 SSH 连接: 用户完成身份验证后,重试 SSH 连接。

以下是使用这些修改更新后的代码:

import requests
from datetime import datetime, timedelta
import paramiko
import logging
import re

# ...(现有的代码)...

for device in devices:
    if 'lastSeen' in device:
        # ...(现有的代码)...

            try:
                ssh.connect(device['addresses'][0], username='root')
            except paramiko.AuthenticationException as ex:
                # 检查 Tailscale 身份验证提示
                if "Tailscale SSH requires an additional check" in str(ex):
                    # 从异常中提取身份验证 URL
                    match = re.search(r"(https://login\.tailscale\.com/a/\w+)", str(ex))
                    if match:
                        auth_url = match.group(1)
                        print(f"需要 Tailscale 身份验证。请访问:{auth_url}")
                        input("按 Enter 键继续...")  # 在用户完成身份验证后等待
                        try:
                            ssh.connect(device['addresses'][0], username='root')
                        except paramiko.AuthenticationException as ex:
                            print(f"{hostname}, 身份验证失败:{ex}")
                            exit(1)
                    else:
                        print(f"{hostname}, 无法提取 Tailscale 身份验证 URL")
                        exit(1)
                else:
                    print(f"{hostname}, 发生身份验证异常:{ex}")
                    exit(1)
            else:
                # ...(现有的代码)... 

此代码执行以下操作:

  • 它会在 paramiko.AuthenticationException 中捕获 Tailscale 特定的消息。
  • 它使用正则表达式从异常消息中提取身份验证 URL。
  • 它提示用户访问提取的 URL 以完成身份验证。
  • 在用户按下 Enter 键后,它会尝试重新建立 SSH 连接。

此方法提供了一个交互式流程,供在使用 Paramiko 和 Tailscale 时处理必要的身份验证步骤。请记住,此方法依赖用户交互来完成身份验证。

标签:python,logging,paramiko,tailscale
From: 78813596

相关文章

  • 跟踪 VScode 中的 python 警告
    我想知道哪一行代码导致我的代码中引发警告。可以在VScode中实现吗?例如,我在终端中看到警告:目前,我只能看到在python终端中引发警告的导入模块,但我无法将其跟踪到代码的确切行,只能跟踪到该行导入模块的。也许,可以在警告上添加断点或更改某些设置,或使用扩展?以下......
  • ffmpeg python 导致死锁
    我在使用ffmpegpython处理相机帧时遇到问题。我使用process.communicate()的第一种方法效果很好,但存在延迟问题。process=(ffmpeg.input('pipe:',format='rawvideo',pix_fmt='rgb24',s='{}x{}'.format(width,height))......
  • 将 HTTP 分块编码数据流代码片段从 Node.js 转换为 Python
    我有一个Node.js客户端代码,它将请求发送到HTTP服务器,然后连续接收分块编码数据。这是带有一些流量数据输出的Node.js代码。consthttp=require('http');constoptions={hostname:'...',path:'...',port:...,...};constreq=http.request(......
  • vsc python 调试器和 pylance 无法识别已安装的包
    我最近使用snowflake-connector-python在我的虚拟环境中安装了pipinstallsnowflake-connector-python[pandas]==2.7.6,当我在激活虚拟环境的情况下从命令行运行我的脚本时,它工作正常。我设置了与VSC解释器相同的虚拟环境,但尝试运行python调试器会引发异常......
  • 如何从python读取matlab持续时间对象
    我创建一个matlab持续时间对象并将其保存到.mat文件:timeend=seconds(123);save('time.mat',timeend,'-v7.3');然后我从python读取它:withh5py.File('time.mat','r')asf:var=f['timeend'][:]print(list(var))......
  • 通过 python 连接到 Snowflake 时出错“UnpicklingError: invalid load key, '\x00'
    我在使用snowflake.connector.connect通过python连接到snowflake时遇到以下错误importsnowflake.connector#pipinstallsnowflake-connector-python#iamgettingtheenvfrom.envfileistoredlocallycnx=snowflake.connector.connect(user=os.getenv('USER'),pass......
  • Python Selenium 单击 webdriverwait 与 find_element
    我无法理解这两个代码块之间的区别。发送点击在webdriverwait和find_elements中都有效。代码1fromseleniumimportwebdriverfromselenium.webdriver.common.byimportByfromselenium.webdriver.support.uiimportWebDriverWaitfromselenium.webdriver.suppo......
  • Python 问题 如何创建在 PDF 中注册为剪切线的专色?
    我正在开发一个项目,需要我在图像周围创建一条剪切线,但在任何RIP程序(例如Versaworks或Flexi)上将其注册为实际剪切线时遇到困难。我尝试了很多不同的方法python库可以帮助解决这个问题,但我无法让它工作。我希望它像我们在Illustrator中所做的那样,创建一条名为CutConto......
  • 使用Python时如何避免`setattr`(和`getattr`)?以及是否有必要避免
    如果我想向协议缓冲区中的字段添加一个在编译时未知的值,我目前正在做setattr我通常不喜欢使用setattr,因为它看起来不太安全。但是当我知道该对象是protobuf时,我认为这很好,因为我设置它的值必须是protobuf允许的类型。所以也许它并不是真的不安全?让我举......
  • Java sshtools 生成的 EDDSA 签名与 Python 的 pycryptome 生成的签名不匹配
    我有一个python库,它使用pycryptodomelibrary使用openssh格式的ED25519私钥使用Ed25519算法对数据进行签名。然后需要使用sshtools库和相应的公钥在Java应用程序中验证签名。但是签名验证失败。约束:从文件中读取私钥/公钥很重要。我无法......