V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
fl0w1nd
V2EX  ›  宽带症候群

NAT1 利用 LUCKY+自动化脚本实现无感化 PLEX 外网访问

  •  
  •   fl0w1nd · 59 天前 · 1180 次点击
    这是一个创建于 59 天前的主题,其中的信息可能已经有所发展或是发生改变。

    效果

    能在没有公网,仅拥有 NAT1 的情况下,做到无感 PLEX 外网访问

    前提

    • 确保路由器的 NAT 类型为 NAT1,并且要求 TCP 的 NAT 类型也为 NAT1
      • 主路由场景下基本没什么大问题,如果在旁路由场景下可能需要为旁路由开启 DMZ ,此外,Openclash 等科学插件也有影响
    • 安装 Lucky ,这里不多赘述,自行解决

    相关链接

    Lucky 项目地址 Python-plexapi

    配置

    脚本配置

    • Openwrt 为例,首先确保安装 Python ,随后安装下列库

    pip3 install plexapi

    这是需要修改的内容

    # Plex 账号信息
    PLEX_USERNAME = "[email protected]"
    PLEX_PASSWORD = "password"
    
    PLEX_SERVER_BASEURL = "http://192.168.3.6:32400"  # 替换为你的 Plex 服务器 URL
    
    TOKEN_FILE = "/root/plex_token.json"  # 保存 PLEX Token 的路径
    

    这是脚本代码

    import os
    import sys
    import json
    import requests
    from plexapi.server import PlexServer
    
    # Plex 账号信息
    PLEX_USERNAME = "[email protected]"
    PLEX_PASSWORD = "password"
    
    # Plex API URL
    LOGIN_URL = "https://plex.tv/users/sign_in.json"
    PLEX_SERVER_BASEURL = "http://192.168.3.6:32400"  # 替换为你的 Plex 服务器 URL
    
    # 登录请求的头信息
    HEADERS = {
        "Content-Type": "application/json",
        "Accept": "application/json",
        "X-Plex-Client-Identifier": "MyPlexApp"
    }
    
    # 获取新令牌的登录请求数据
    data = {
        "user": {
            "login": PLEX_USERNAME,
            "password": PLEX_PASSWORD
        }
    }
    
    def get_plex_token():
        """
        通过向 Plex API 发送登录请求获取 Plex 认证令牌。
        """
        try:
            response = requests.post(LOGIN_URL, headers=HEADERS, data=json.dumps(data))
            if response.status_code == 201:  # 状态码 201 表示登录成功
                return response.json()['user']['authentication_token']
            else:
                print(f"获取令牌失败。状态码: {response.status_code}")
                return None
        except Exception as e:
            print(f"认证过程中发生错误: {str(e)}")
            return None
    
    def read_token_from_file(token_file):
        """
        从指定的 JSON 文件中读取 Plex 令牌。
        """
        if os.path.exists(token_file):
            try:
                with open(token_file, 'r') as file:
                    data = json.load(file)
                    return data.get('token')
            except Exception as e:
                print(f"读取令牌文件时发生错误: {str(e)}")
                return None
        else:
            return None
    
    def save_token_to_file(token_file, token):
        """
        将 Plex 令牌保存到指定的 JSON 文件中。
        """
        try:
            with open(token_file, 'w') as file:
                json.dump({"token": token}, file)
        except Exception as e:
            print(f"保存令牌到文件时发生错误: {str(e)}")
    
    def check_token_validity(baseurl, token):
        """
        通过向服务器发送验证请求来检查 Plex 令牌的有效性。
        """
        try:
            check_url = f"{baseurl}/library/sections?X-Plex-Token={token}"
            response = requests.get(check_url)
            return response.status_code == 200
        except Exception as e:
            print(f"检查令牌有效性时发生错误: {str(e)}")
            return False
    
    def modify_port(plex, new_port):
        """
        修改 Plex 服务器设置中的手动端口映射。
        """
        manual_port_setting = plex.settings.get('manualPortMappingPort')
        
        if manual_port_setting:
            try:
                manual_port_setting.set(new_port)
                plex.settings.save()
                print(f"手动端口映射已成功更改为: {new_port}")
            except Exception as e:
                print(f"修改端口或保存设置时发生错误: {str(e)}")
        else:
            print("无法找到手动端口映射设置。")
    
    if __name__ == "__main__":
        # 第一步:解析命令行参数,获取新的端口号
        if len(sys.argv) < 2:
            print("用法: python3 modify.py <new_port>")
            sys.exit(1)
    
        try:
            NEW_PORT = int(sys.argv[1])  # 将提供的端口号转换为整数
        except ValueError:
            print("无效的端口号。请提供有效的整数。")
            sys.exit(1)
    
        # 第二步:定义令牌存储文件
        TOKEN_FILE = "/root/plex_token.json"  # Token 路径
    
        # 第三步:从文件中读取令牌或生成新令牌
        PLEX_TOKEN = read_token_from_file(TOKEN_FILE)
    
        # 第四步:如果文件中没有令牌,或者令牌无效,则生成新令牌
        if PLEX_TOKEN is None or not check_token_validity(PLEX_SERVER_BASEURL, PLEX_TOKEN):
            PLEX_TOKEN = get_plex_token()
            if PLEX_TOKEN:
                save_token_to_file(TOKEN_FILE, PLEX_TOKEN)
            else:
                print("获取有效令牌失败。")
                sys.exit(1)
    
        # 第五步:使用 plexapi 连接到 Plex 服务器并修改手动端口
        try:
            plex = PlexServer(PLEX_SERVER_BASEURL, PLEX_TOKEN)
            print("成功连接到 Plex 服务器!")
            
            # 修改手动端口映射(使用传入的端口号)
            modify_port(plex, NEW_PORT)
        except Exception as e:
            print(f"连接 Plex 服务器或修改设置时发生错误: {str(e)}")
    

    Lucky 配置 CleanShot 2024-10-28 at 15.23.49@2x.png

    创建一个 TCP 穿透规则,这里我指定了一个空闲端口为 13333 ,并且不使用 LUCKY 的内置端口转发。开启自定义脚本触发,代码如下:

    python3 /root/modify_port.py ${port}
    
    if [ $? -ne 0 ]; then
        echo "Python script execution failed with exit code $?."
    fi
    

    注意此处代码的 /root/modify_port.py 就是刚刚的 python 执行脚本的实际路径。

    随后确认即可,规则创建关闭后稍时即可打洞成功。

    端口转发

    由于我们刚刚指定了端口,并且没有使用 Lucky 内置的端口转发,因此我们需要用防火墙配置手动端口转发 CleanShot 2024-10-28 at 15.35.16@2x.png

    如果你是主路由,源区域应该是防火墙的“WAN”区域,我是旁路由,所以源区域为 LAN

    外部端口就是刚刚打洞绑定的端口,例如我刚刚在 LUCKY 配置了 13333 端口

    目前区域即可 LAN ,内部 IP 地址即为 PLEX 所在主机 IP ,内部端口通常为 PLEX Server 默认端口 32400

    常见问题

    • 在配置前可单独调试脚本,确保脚本正常运作,脚本调试命令为: python3 modify_port.py <port>

    • 旁路由的可能额外操作 LUCKY 位于旁路由,并且使用了 Openclash ,即使配置了旁路由为 DMZ ,但是 TCP 流量经过了 Openclash ,因此导致了 TCP 的 NAT 类型不为 NAT1 。

    解决方案有下列几种:

    1. 通过 Openclash 的仅允许常用端口流量功能,可以让打洞的流量不经过 Openclash ,即不包含 3478 端口流量
    2. 通过 Openclash 的黑白名单功能中的绕过核心的来源端口功能 CleanShot 2024-10-28 at 15.49.18@2x.png

    设置几个打洞时绑定的预设端口,这些端口流量就不会经过 Openclash 了,但配置中也指出了,Fake ip 模式下只能过滤纯 IP 类型请求。

    其它请自行发挥,包括但不限于使用 Openclash 的开发者选项,或者修改防火墙来实现

    目前尚无回复
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   5455 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 28ms · UTC 01:30 · PVG 09:30 · LAX 17:30 · JFK 20:30
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.