cpolar隧道域名自动化获取脚本
亦念远方
2024年10月03日 23:27
收录于文集
共2篇

前言:需要通过外网远程访问香橙派上的资源,遂寻得 cpolar 这个穿透工具。(其实是找了一圈无意间发现的)目前注册仅需邮箱即可,具体流程请移步官网(https://www.cpolar.com/)

本文参考文献详见参考文献部分


前置条件:安装 python3,并且确保安装了 requests, BeautifulSoup4 以及邮件发送相关模块。

Ubuntu:

sudo apt update && sudo apt upgrade

sudo apt install python3

sudo pip install requests beautifulsoup4

Arch:

sudo pacman -Syu

sudo pacman -S python python-requests python-beautifulsoup4

1.即时脚本

这个脚本适用于即时获取隧道地址,可以在运行脚本后输入需要查询的隧道名称,也可以直接传入一个参数。记得把必要的(代码中的斜体内容)内容换成你自己的。嫌麻烦也可以看最后的注意事项。

代码示例:

import requests

from bs4 import BeautifulSoup

import sys

# 登录网站并获取页面内容

def fetch_info_from_website(login_url, info_url, credentials, tunnel_name):

  with requests.Session() as session:

    try:

      # 获取登录页面以抓取csrf token

      login_page = session.get(login_url)

      login_page.raise_for_status() # 检查请求是否成功

      login_page_soup = BeautifulSoup(login_page.text, 'html.parser')

      # 提取csrf token

      csrf_token = login_page_soup.find('input', {'name': 'csrf_token'})['value']

      credentials['csrf_token'] = csrf_token

      # 登录

      print("登录中,请等待。")

      login_response = session.post(login_url, data=credentials)

      # 检查是否登录成功

      if login_response.status_code != 200 or login_response.url == login_url:

        print("登录失败,请检查您的凭据。")

        return []

      else:

        print("登录成功。")

      # 获取信息页面

      response = session.get(info_url)

      response.raise_for_status()

      # 解析页面

      soup = BeautifulSoup(response.text, 'html.parser')

      table = soup.find('table')

      if not table:

        print("未找到隧道列表,请检查对应设备的cpolar服务和网络连接。")

        return []

      links = [] # 用于存储找到的链接

      for row in table.find_all('tr')[1:]: # 跳过表头

        cells = row.find_all('td')

        if len(cells) > 1:

          tunnel = cells[0].get_text().strip()

          url_cell = row.find('a', href=True) # 直接在行中查找<a>标签

          if tunnel == tunnel_name and url_cell:

            links.append(url_cell['href']) # 添加匹配的链接

            print(f"找到隧道 {tunnel} 的链接: {url_cell['href']}")

      return links

    except requests.RequestException as e:

      print(f"请求异常: {e}")

    except Exception as e:

      print(f"发生错误: {e}")

if __name__ == '__main__':

  login_url = "https://dashboard.cpolar.com/login"

  info_url = "https://dashboard.cpolar.com/status"

  credentials = {

    'login': 'example@example.com',

    'password': 'yourpassword

  }

   

  # 检查是否有命令行参数传入

  if len(sys.argv) > 1:

    tunnel_name = sys.argv[1] # 第一个命令行参数作为隧道名称

  else:

    print("请提供一个隧道名称作为参数,或者留空以自动提示输入。")

    tunnel_name = input("请输入隧道名称: ")

    if not tunnel_name:

      print("隧道名称不能为空。")

      sys.exit(1)

  links = fetch_info_from_website(login_url, info_url, credentials, tunnel_name)

  if links:

    print()

  else:

    print(f"没有找到名为 {tunnel_name} 的隧道链接。")

运行命令示例(文件名可自取):

python collectaddresswithoutsending.py

python collectaddresswithoutsending.py tunnel_name

2.邮件脚本

这个脚本适用于定时获取隧道地址并发送至指定邮箱,一般在脚本中指定隧道名称。

代码示例:

import requests

from bs4 import BeautifulSoup

from email.mime.multipart import MIMEMultipart

from email.mime.text import MIMEText

from email.header import Header

import smtplib

# 登录网站并获取页面内容

def fetch_info_from_website(login_url, info_url, credentials, tunnel_name):

  with requests.Session() as session:

    try:

      # 获取登录页面以抓取csrf token

      login_page = session.get(login_url)

      login_page.raise_for_status() # 检查请求是否成功

      login_page_soup = BeautifulSoup(login_page.text, 'html.parser')

      # 提取csrf token

      csrf_token = login_page_soup.find('input', {'name': 'csrf_token'})['value']

      credentials['csrf_token'] = csrf_token

      # 登录

      print("登录中,请等待。")

      login_response = session.post(login_url, data=credentials)

      # 检查是否登录成功

      if login_response.status_code != 200 or login_response.url == login_url:

        print("登录失败,请检查您的凭据。")

        return []

      else:

        print("登录成功。")

      # 获取信息页面

      response = session.get(info_url)

      response.raise_for_status()

      # 解析页面

      soup = BeautifulSoup(response.text, 'html.parser')

      table = soup.find('table')

      if not table:

        print("未找到隧道列表,请检查对应设备的cpolar服务和网络连接。")

        return []

      links = [] # 用于存储找到的链接

      for row in table.find_all('tr')[1:]: # 跳过表头

        cells = row.find_all('td')

        if len(cells) > 1:

          tunnel = cells[0].get_text().strip()

          url_cell = row.find('a', href=True) # 直接在行中查找<a>标签

          if tunnel == tunnel_name and url_cell:

            links.append(url_cell['href']) # 添加匹配的链接

            print(f"找到隧道 {tunnel} 的链接: {url_cell['href']}")

      return links

    except requests.RequestException as e:

      print(f"请求异常: {e}")

    except Exception as e:

      print(f"发生错误: {e}")

# 发送邮件

def send_email(subject, body, to_email):

  from_email = 'from_email@example.com'

  auth_password = 'yourauth_password'

  msg = MIMEMultipart('alternative')

  msg['From'] = Header(from_email)

  msg['To'] = Header(to_email)

  msg['Subject'] = Header(subject)

  # 邮件正文的纯文本版本

  text = "这是邮件的纯文本版本,用于不支持HTML的邮件客户端。\n" + body

  msg.attach(MIMEText(text, 'plain', 'utf-8'))

  # 邮件正文的HTML版本

  msg.attach(MIMEText(body, 'html', 'utf-8'))

  try:

    with smtplib.SMTP('yourSMTPserver', 587) as server:

      server.starttls()

      server.login(from_email, auth_password)

      server.sendmail(from_email, [to_email], msg.as_string())

      print("邮件发送成功")

  except smtplib.SMTPException as e:

    print("邮件发送失败:", e)

  except Exception as e:

    print("发送邮件时发生未知错误:", e)

if __name__ == '__main__':

  login_url = "https://dashboard.cpolar.com/login"

  info_url = "https://dashboard.cpolar.com/status"

  credentials = {

    'login': 'example@example.com',

    'password': 'yourpassword'

  }

  tunnel_name = "tunnel_name" # 指定的隧道名称

  links = fetch_info_from_website(login_url, info_url, credentials, tunnel_name)

  if links:

    to_email = "to_email@example.com"

    subject = "Tunnel Links Notification"

    html_links = "<br>".join([f"<a href='{link}'>{link}</a>" for link in links])

    html_body = "<html><body><p>以下是更新后的链接:</p>" + html_links + "</body></html>"

    send_email(subject, html_body, to_email)

  else:

    print(f"没有找到名为 {tunnel_name} 的隧道链接。")

运行:

在 crontab 里添加计划任务即可,这里就不重复造轮子了。


其他注意事项:

发件邮箱:

作者最终使用的发件邮箱是 icloud邮箱

使用 outlook 邮箱。发件第一次一切正常,之后却没法正常发送邮件了,原因是微软在逐步淘汰Basic Auth,故使用 outlook 邮箱并不理想。本人能力有限没法写出用 OAuth2 协议的发件函数,大佬请自便。

使用 网易邮箱。也许是本人的问题,一次都未成功。

使用 QQ邮箱。本人尝试了一下发送没有问题,只是邮件发送成功后会有一条莫名其妙的报错信息。网上资料很少只能推测是 python 的邮件模块收到了 QQ邮箱 服务器发来的无法理解的信息故而报错,但实际上邮件是成功发送了的。虽然可以忽略,但是不够完美。

使用 icloud邮箱。没有次数限制,写脚本测试时没有失败过一次,Basic Auth 依旧可以正常使用。本人建议开启双重认证后生成专属的应用密码。

关于密码安全:

将邮箱密码明文编写到脚本中是绝对不安全的。本人使用生成的应用密码依然存在风险,如有需要建议使用环境变量或钥匙串提高安全等级。

关于 cpolar 的账号密码。该账号泄漏会泄漏你的通道地址。即使是面板通道,增加访问后缀,开启防火墙和防暴力破解软件,限制登录错误次数等等手段对个人内用户基本足够。对于 docker 应用等等应尽量避免使用特权模式的容器暴露等等。

总之不建议把密码硬编写入脚本。安全方面各位见仁见智,评估风险后决定采取相应的安全措施。

代码中需要更改的部分:

cpolar 账号与密码

发件邮箱地址与密码和对应的服务器地址与端口

收件邮箱地址


参考文献:

https://blog.csdn.net/qq_28319843/article/details/140876888