diff --git a/.github/workflows/dockerimage.yml b/.github/workflows/dockerimage.yml new file mode 100644 index 0000000..f4961b4 --- /dev/null +++ b/.github/workflows/dockerimage.yml @@ -0,0 +1,38 @@ +name: V2ray Heroku Docker Image + +on: + push: + schedule: + - cron: "0 0 * * 5" + +jobs: + + build: + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v1 + - name: Build the Docker image + run: docker build . --file Dockerfile --tag v2heroku:$GITHUB_SHA + + run: + + needs: build + runs-on: ubuntu-latest + + env: + AppName: test + + steps: + - uses: actions/checkout@v1 + - name: Prepare environment + run: | + sudo apt-get update -y + sudo apt-get install unzip python3 python3-pip -y + sudo pip3 install requests + + - name: Run the program + run: | + cd ./worker + sudo -E python3 ./deploy.py \ No newline at end of file diff --git a/Dockerfile b/Dockerfile index c48cfd6..4c1002a 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,9 +1,12 @@ -FROM debian:sid +FROM python:3.7-stretch -RUN apt update -y \ - && apt upgrade -y \ - && apt install -y wget unzip qrencode +RUN apt-get update -y \ + && apt-get install -y unzip \ + && python3 -V \ + && pip3 install requests -U -ADD entrypoint.sh /entrypoint.sh -RUN chmod +x /entrypoint.sh -CMD /entrypoint.sh +ADD worker /worker + +CMD cd /worker \ + && python3 ./deploy.py \ + && bash ./run.sh \ No newline at end of file diff --git a/README.md b/README.md index 78adc4f..ba6a5fc 100644 --- a/README.md +++ b/README.md @@ -1,20 +1,32 @@ -# 一键部署 v2ray 到 heroku -[![Deploy](https://www.herokucdn.com/deploy/button.png)](https://heroku.com/deploy) -- - - -- - - -1.部署时配置 v2ray core 的版本、Vmess协议的UUID、AlterId、Path和连接缓存。 +#
一键部署 v2ray 到 Heroku
+**本程序[以GPL-3.0开源许可开源](https://github.com/mnixry/v2ray-heroku-fix/blob/master/LICENSE#L591),仅供学习交流参考使用,对于使用本程序造成的一切后果作者概不承担!** -2.如果输入AppName变量,则自动生成订阅地址和二维码,通过配置V2_QR_Path变量修改地址 -二维码地址:https://test.herokuapp.com/1234/v2.png -订阅地址:https://test.herokuapp.com/1234 (test改成自己的app名称,如果更改了V2_QR_Path,同时也要将对应的1234改成修改后的) +![](https://github.com/mnixry/v2ray-heroku-fix/workflows/V2ray%20Heroku%20Docker%20Image/badge.svg) -3.服务端部署后,点 open app ,能正常显示网页,地址补上path后访问显示 Bad Request,表示部署成功。 +--- -4.更新 v2ray 版本,访问 https://dashboard.heroku.com/apps 选择部署好v2ray的app,如果VER变量为 latest。直接选择More --> Restart all dynos, 程序自动重启,可通过view Logs确认进度。(更新指定版本: Settings --> Reveal Config Varsapp -->VER,修改成需要的版本号,例如 3.21) +## 停止维护 -# 参考 -https://github.com/v2ray/v2ray-core +**由于Heroku的用户协议禁止作为代理使用,本项目停止更新** -https://github.com/wangyi2005/v2ray-heroku +**正在研究采用kubesail提供的服务实现类似功能** -https://github.com/1715173329/v2ray-heroku-undone + +## 部署方法 +1. 点击下方按钮跳转Heroku部署(需要注册账号) + - [![Deploy](https://www.herokucdn.com/deploy/button.png)](https://dashboard.heroku.com/new?template=https://github.com/mnixry/v2ray-heroku-fix) + +2. 跟着提示走吧(逃 + +3. 服务端部署后,点 `open app`,能正常显示反代理的网页,地址补上V2ray路径后访问显示`Bad Request`,表示部署成功。 + +4. 更新 v2ray 版本 + - 访问 https://dashboard.heroku.com/apps 选择部署好v2ray的app + - 直接选择`More` --> `Restart all dynos` + +## 参考链接: +> [V2ray-Core](https://github.com/v2ray/v2ray-core) + +> [v2ray-heroku](https://github.com/wangyi2005/v2ray-heroku) + +> [v2ray-heroku-undone](https://github.com/1715173329/v2ray-heroku-undone) diff --git a/app.json b/app.json index aa7e2c1..3dded51 100644 --- a/app.json +++ b/app.json @@ -1,38 +1,40 @@ { - "name": "V2ray简易部署", + "name": "V2ray-Heroku部署", "description": "Deploy V2ray to Heroku.", - "keywords": ["V2ray"], + "keywords": [ + "V2ray" + ], "env": { "AppName": { - "description": "请输入最上方填写的App Name,用于生成二维码和订阅链接,如果不想生成输入no", - "value": "no" - }, - "VER": { - "description": "默认latest安装最新版本V2Ray (输入V2Ray 版本号 进行指定版本安装。例如:3.22)", - "value": "latest" - }, + "description": "请输入最上方填写的App Name,用于订阅链接,必填!", + "value": "" + }, + "Subscribe_Address": { + "description": "请输入查看订阅的地址,前面不用加`/`", + "value": "subscribe" + }, "UUID": { - "description": "Vmess协议默认UUID,请输入自己的UUID", - "value": "4890bd47-5180-4b1c-9a5d-3ef686543112" - }, + "description": "请输入自己的UUID,如无程序会自动随机一份", + "value": "", + "required": false + }, "AlterID": { - "description": "AlterID大小,推荐10,数值越大内存占用越高", - "value": "10" - }, + "description": "AlterID大小,推荐16,数值越大内存占用越高", + "value": "16", + "required": false + }, "V2_Path": { - "description": "Path路径,默认/FreeApp", - "value": "/FreeApp" - }, - "V2_QR_Path": { - "description": "二维码和订阅地址路径,默认1234。如AppName变量为no,此变量没有作用", - "value": "1234" - }, - "V2RAY_RAY_BUFFER_SIZE": { - "description": "每个连接的缓存大小,单位MB,默认10。如果感觉有断流现象,请考虑将缓存改成1", - "value": "10" - } + "description": "Path路径,默认留空则随机生成一份", + "value": "", + "required": false + }, + "Anti_Proxy_Path": { + "description": "反代理的网站地址,默认为百度", + "value": "https://www.baidu.com", + "required": true + } }, - "website": "https://github.com/ki8852/v2ray-heroku-undone", - "repository": "https://github.com/ki8852/v2ray-heroku-undone", + "website": "https://github.com/mnixry/v2ray-heroku-fix", + "repository": "https://github.com/mnixry/v2ray-heroku-fix", "stack": "container" - } +} \ No newline at end of file diff --git a/demo.tar.gz b/demo.tar.gz deleted file mode 100644 index 42cfb19..0000000 Binary files a/demo.tar.gz and /dev/null differ diff --git a/entrypoint.sh b/entrypoint.sh deleted file mode 100644 index 11d33a8..0000000 --- a/entrypoint.sh +++ /dev/null @@ -1,129 +0,0 @@ -#! /bin/bash -if [[ -z "${UUID}" ]]; then - UUID="4890bd47-5180-4b1c-9a5d-3ef686543112" -fi - -if [[ -z "${AlterID}" ]]; then - AlterID="10" -fi - -if [[ -z "${V2_Path}" ]]; then - V2_Path="/FreeApp" -fi - -if [[ -z "${V2_QR_Path}" ]]; then - V2_QR_Code="1234" -fi - -rm -rf /etc/localtime -ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime -date -R - -SYS_Bit="$(getconf LONG_BIT)" -[[ "$SYS_Bit" == '32' ]] && BitVer='_linux_386.tar.gz' -[[ "$SYS_Bit" == '64' ]] && BitVer='_linux_amd64.tar.gz' - -if [ "$VER" = "latest" ]; then - V_VER=`wget -qO- "https://api.github.com/repos/v2ray/v2ray-core/releases/latest" | grep 'tag_name' | cut -d\" -f4` -else - V_VER="v$VER" -fi - -mkdir /v2raybin -cd /v2raybin -wget --no-check-certificate -qO 'v2ray.zip' "https://github.com/v2ray/v2ray-core/releases/download/$V_VER/v2ray-linux-$SYS_Bit.zip" -unzip v2ray.zip -rm -rf v2ray.zip -chmod +x /v2raybin/v2ray-$V_VER-linux-$SYS_Bit/* - -C_VER=`wget -qO- "https://api.github.com/repos/mholt/caddy/releases/latest" | grep 'tag_name' | cut -d\" -f4` -mkdir /caddybin -cd /caddybin -wget --no-check-certificate -qO 'caddy.tar.gz' "https://github.com/mholt/caddy/releases/download/$C_VER/caddy_$C_VER$BitVer" -tar xvf caddy.tar.gz -rm -rf caddy.tar.gz -chmod +x caddy -cd /root -mkdir /wwwroot -cd /wwwroot - -wget --no-check-certificate -qO 'demo.tar.gz' "https://github.com/ki8852/v2ray-heroku-undone/raw/master/demo.tar.gz" -tar xvf demo.tar.gz -rm -rf demo.tar.gz - -cat <<-EOF > /v2raybin/v2ray-$V_VER-linux-$SYS_Bit/config.json -{ - "log":{ - "loglevel":"warning" - }, - "inbound":{ - "protocol":"vmess", - "listen":"127.0.0.1", - "port":2333, - "settings":{ - "clients":[ - { - "id":"${UUID}", - "level":1, - "alterId":${AlterID} - } - ] - }, - "streamSettings":{ - "network":"ws", - "wsSettings":{ - "path":"${V2_Path}" - } - } - }, - "outbound":{ - "protocol":"freedom", - "settings":{ - } - } -} -EOF - -cat <<-EOF > /caddybin/Caddyfile -http://0.0.0.0:${PORT} -{ - root /wwwroot - index index.html - timeouts none - proxy ${V2_Path} localhost:2333 { - websocket - header_upstream -Origin - } -} -EOF - -cat <<-EOF > /v2raybin/vmess.json -{ - "v": "2", - "ps": "${AppName}.herokuapp.com", - "add": "${AppName}.herokuapp.com", - "port": "443", - "id": "${UUID}", - "aid": "${AlterID}", - "net": "ws", - "type": "none", - "host": "", - "path": "${V2_Path}", - "tls": "tls" -} -EOF - -if [ "$AppName" = "no" ]; then - echo "不生成二维码" -else - mkdir /wwwroot/$V2_QR_Path - vmess="vmess://$(cat /v2raybin/vmess.json | base64 -w 0)" - Linkbase64=$(echo -n "${vmess}" | tr -d '\n' | base64 -w 0) - echo "${Linkbase64}" | tr -d '\n' > /wwwroot/$V2_QR_Path/index.html - echo -n "${vmess}" | qrencode -s 6 -o /wwwroot/$V2_QR_Path/v2.png -fi - -cd /v2raybin/v2ray-$V_VER-linux-$SYS_Bit -./v2ray & -cd /caddybin -./caddy -conf="Caddyfile" diff --git a/worker/deploy.py b/worker/deploy.py new file mode 100644 index 0000000..b115d9f --- /dev/null +++ b/worker/deploy.py @@ -0,0 +1,181 @@ +import json +import os +import platform +import subprocess +from base64 import urlsafe_b64encode +from logging import basicConfig, getLogger +from random import randint +from secrets import token_urlsafe +from shutil import rmtree +from uuid import uuid4 + +import requests + +assert os.getenv('AppName') + +basicConfig(level=20) + +def GetEnv(key:str,default=None): + envGet = os.getenv(key) + if not envGet: + return default + else: + return envGet + +LOGGER = getLogger('worker') +LOGGER_CONFIG = LOGGER.getChild('config') +LOGGER_COMMAND = LOGGER.getChild('command') +WORK_DIR = os.path.abspath(os.getcwd()) +SETTINGS: dict = { + 'name': GetEnv('AppName'), + 'subscribe_path': GetEnv('Subscribe_Address', token_urlsafe(16)), + 'uuid': GetEnv('UUID', str(uuid4())), + 'port': randint(1000, 60000), + 'alter_id': GetEnv('AlterID', 16), + 'v2ray_path': GetEnv('V2_Path', f'/{token_urlsafe(8)}'), + 'reverse_proxy': GetEnv('Anti_Proxy_Path', 'https://www.baidu.com') +} + + +def execute(command: str, block: bool = True) -> int: + LOGGER_COMMAND.info(f'Execute command "{command}"') + if block: + return subprocess.run(command, shell=True, check=True).returncode + else: + return subprocess.Popen(command, shell=True).returncode + + +def mkdir(dir: str): + if os.path.exists(dir): + rmtree(dir) + os.mkdir(dir) + + +LOGGER_CONFIG.info('Start setting the system time zone') +execute(r"""rm -rf /etc/localtime \ + && ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime \ + && date -R""") + +LOGGER_CONFIG.info( + f'The platform information is as follows: {platform.uname()}') +if platform.architecture()[0] == '32bit': + SYS_BIT = 32 +elif platform.architecture()[0] == '64bit': + SYS_BIT = 64 +else: + raise RuntimeError( + f'Unrecognized operating platform: {platform.architecture()}') + +LOGGER_CONFIG.info('Start getting V2ray version list') +RELEASE_INFO = requests.get( + 'https://api.github.com/repos/v2ray/v2ray-core/releases/latest').json() +for perAsset in RELEASE_INFO['assets']: + LOGGER_CONFIG.debug(f'Check asset {perAsset["name"]}') + if perAsset['name'] == f'v2ray-linux-{SYS_BIT}.zip': + DOWNLOAD_LINK = perAsset['browser_download_url'] + break +else: + raise FileNotFoundError('No suitable version found') + +LOGGER_CONFIG.info('Start downloading V2ray core files') +LOGGER_CONFIG.debug(f'Download link: {DOWNLOAD_LINK}') +with open(os.path.join(WORK_DIR, 'v2ray.zip'), 'wb') as f: + f.write(requests.get(DOWNLOAD_LINK).content) + +LOGGER_CONFIG.info('Start downloading Caddy files') +CADDY_URL = 'https://github.com/caddyserver/caddy/releases/download/v1.0.4/caddy_v1.0.4_linux_amd64.tar.gz' if ( + SYS_BIT == 64 +) else 'https://github.com/caddyserver/caddy/releases/download/v1.0.4/caddy_v1.0.4_linux_386.tar.gz' +LOGGER_CONFIG.debug(f'Download link: {CADDY_URL}') +with open(os.path.join(WORK_DIR, 'caddy.tar.gz'), 'wb') as f: + f.write(requests.get(CADDY_URL).content) + +LOGGER_CONFIG.info('Extract the downloaded file') +mkdir('v2ray') +execute( + f'unzip "{os.path.join(WORK_DIR,"v2ray.zip")}" -d "{os.path.join(WORK_DIR,"v2ray")}"' +) +mkdir('caddy') +execute( + f'tar -xvf "{os.path.join(WORK_DIR,"caddy.tar.gz")}" -C "{os.path.join(WORK_DIR,"caddy")}"' +) + +LOGGER_CONFIG.info('Start writing configuration files') +V2_CONF = { + "log": { + "loglevel": "warning" + }, + "inbound": { + "protocol": "vmess", + "listen": "127.0.0.1", + "port": SETTINGS['port'], + "settings": { + "clients": [{ + "id": SETTINGS['uuid'], + "level": 1, + "alterId": int(SETTINGS['alter_id']) + }] + }, + "streamSettings": { + "network": "ws", + "wsSettings": { + "path": SETTINGS['v2ray_path'] + } + } + }, + "outbound": { + "protocol": "freedom", + "settings": {} + } +} +with open(os.path.join(WORK_DIR, './v2ray/config.json'), + 'wt', + encoding='utf-8') as f: + f.write(json.dumps(V2_CONF, indent=4, sort_keys=True)) + +SHARE_CONF = { + "v": "2", + "ps": f"{SETTINGS['name']}.herokuapp.com", + "add": f"{SETTINGS['name']}.herokuapp.com", + "port": "443", + "id": SETTINGS['uuid'], + "aid": SETTINGS['alter_id'], + "net": "ws", + "type": "none", + "host": f"{SETTINGS['name']}.herokuapp.com", + "path": SETTINGS['v2ray_path'], + "tls": "tls" +} + +V2_LINK = urlsafe_b64encode(json.dumps(SHARE_CONF).encode()).decode() +mkdir('subscribe') +with open(os.path.join(WORK_DIR, 'subscribe', 'index.html'), + 'wt', + encoding='utf-8') as f: + f.write(f'vmess://{V2_LINK}') + +CADDY_CONF = f""":{GetEnv('PORT',80)} {{ + gzip + log stdout + timeouts none + + proxy / {SETTINGS['reverse_proxy']} {{ + except /{SETTINGS['subscribe_path']} + }} + + proxy {SETTINGS['v2ray_path']} 127.0.0.1:{SETTINGS['port']} {{ + websocket + header_upstream -Origin + }} + +}} +:{GetEnv('PORT',80)}/{SETTINGS['subscribe_path']} {{ + gzip + log stdout + root "{os.path.join(WORK_DIR,'subscribe')}" +}}""" +with open(os.path.join(WORK_DIR, './caddy/Caddyfile'), 'wt', + encoding='utf-8') as f: + f.write(CADDY_CONF) + +LOGGER_CONFIG.info(f'The V2ray link is vmess://{V2_LINK}') \ No newline at end of file diff --git a/worker/run.sh b/worker/run.sh new file mode 100644 index 0000000..09b9b77 --- /dev/null +++ b/worker/run.sh @@ -0,0 +1,9 @@ +cd /worker +chmod -R +x /worker +echo "v2ray config:" +cat ./v2ray/config.json +echo "caddy config:" +cat ./caddy/Caddyfile + +./v2ray/v2ray & +./caddy/caddy -conf ./caddy/Caddyfile \ No newline at end of file