├── .github └── workflows │ └── publish-image.yml ├── CHANGELOG.md ├── Dockerfile ├── README.md ├── data ├── Caddyfile └── entry.sh ├── docker-compose.yml ├── docs └── imgs │ ├── cert-suc.png │ ├── insatll-interaction-re.png │ ├── install-interaction.png │ ├── install-silence.png │ └── web.png └── install.sh /.github/workflows/publish-image.yml: -------------------------------------------------------------------------------- 1 | name: Publish image 2 | 3 | on: 4 | workflow_dispatch: 5 | inputs: 6 | manualTag: 7 | description: 'Manual Tag' 8 | required: true 9 | autoWithLatestTag: 10 | description: 'Auto Add Latest Tag' 11 | required: true 12 | default: true 13 | type: boolean 14 | release: 15 | types: [published] 16 | 17 | env: 18 | DOCKERHUB_USERNAME: ${{ secrets.DOCKERHUB_USERNAME }} 19 | DOCKERHUB_PASSWORD: ${{ secrets.DOCKERHUB_PASSWORD }} 20 | GHCR_USERNAME: ${{ github.repository_owner }} 21 | GHCR_PASSWORD: ${{ secrets.GITHUB_TOKEN }} 22 | 23 | jobs: 24 | PublishImage: 25 | runs-on: ubuntu-latest 26 | steps: 27 | - name: GetTargetVersion 28 | id: getTargetVersion 29 | run: | 30 | TargetVersion="" 31 | if [ "${{ github.event.release.tag_name }}" ] ; then 32 | TargetVersion=${{ github.event.release.tag_name }} 33 | fi 34 | if [ "${{ github.event.inputs.manualTag }}" ] ; then 35 | TargetVersion=${{ github.event.inputs.manualTag }} 36 | fi 37 | echo "TargetVersion: $TargetVersion" 38 | echo "TargetVersion=$TargetVersion" >> $GITHUB_OUTPUT 39 | 40 | - name: Checkout 41 | uses: actions/checkout@v3 42 | with: 43 | ref: ${{ steps.getTargetVersion.outputs.TargetVersion }} 44 | 45 | - name: Set up QEMU 46 | uses: docker/setup-qemu-action@v2 47 | 48 | - name: Set up Docker Buildx 49 | uses: docker/setup-buildx-action@v2 50 | 51 | - name: Login to DockerHub 52 | uses: docker/login-action@v2 53 | with: 54 | username: ${{ env.DOCKERHUB_USERNAME }} 55 | password: ${{ env.DOCKERHUB_PASSWORD }} 56 | 57 | - name: Log in to ghcr 58 | uses: docker/login-action@v2 59 | with: 60 | registry: ghcr.io 61 | username: ${{ env.GHCR_USERNAME }} 62 | password: ${{ env.GHCR_PASSWORD }} 63 | 64 | - name: Generate tags 65 | id: tags 66 | run: | 67 | targetVersion="${{ steps.getTargetVersion.outputs.TargetVersion }}" 68 | dockerImage="zai7lou/naiveproxy-docker" 69 | ghcrImage="ghcr.io/raywangqvq/naiveproxy-docker" 70 | dockerTagWithVersion="$dockerImage:$targetVersion" 71 | ghcrTagWithVersion="$ghcrImage:$targetVersion" 72 | dockerTagWithLatest="" 73 | ghcrTagWithLatest="" 74 | if [ "${{ github.event.inputs.autoWithLatestTag }}" == "true" ] ; then 75 | dockerTagWithLatest="$dockerImage:latest" 76 | ghcrTagWithLatest="$ghcrImage:latest" 77 | fi 78 | echo "dockerTagWithVersion=$dockerTagWithVersion" >> $GITHUB_OUTPUT 79 | echo "ghcrTagWithVersion=$ghcrTagWithVersion" >> $GITHUB_OUTPUT 80 | echo "dockerTagWithLatest=$dockerTagWithLatest" >> $GITHUB_OUTPUT 81 | echo "ghcrTagWithLatest=$ghcrTagWithLatest" >> $GITHUB_OUTPUT 82 | 83 | - name: Build and push 84 | uses: docker/build-push-action@v4 85 | with: 86 | context: . 87 | platforms: linux/amd64,linux/arm64 88 | push: true 89 | tags: | 90 | ${{ steps.tags.outputs.dockerTagWithVersion }} 91 | ${{ steps.tags.outputs.ghcrTagWithVersion }} 92 | ${{ steps.tags.outputs.dockerTagWithLatest }} 93 | ${{ steps.tags.outputs.ghcrTagWithLatest }} 94 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## 0.0.1 2 | - 初始化项目 3 | - 完成Dockerfile设计,并打出镜像 4 | - 安装脚本实现官网版naiveproxy创建 5 | 6 | ## 0.1.0 7 | - 完成使用现有证书功能 8 | - 实现更改端口功能(若不使用caddy颁发证书,可不开发80端口) 9 | - 无聊地为脚本添加一个logo 10 | - 重构优化脚本 11 | - 更新README 12 | 13 | ## 0.1.1 14 | - [Fix( #1 )]使用现有证书时设置`auto_https`为`disable_certs` 15 | 16 | ## 0.2.1 17 | - 发布镜像到ghcr -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM golang:1.19 AS build 2 | 3 | WORKDIR /go 4 | 5 | RUN go version \ 6 | && go install github.com/caddyserver/xcaddy/cmd/xcaddy@latest \ 7 | && /go/bin/xcaddy build --with github.com/caddyserver/forwardproxy@caddy2=github.com/klzgrad/forwardproxy@naive 8 | 9 | FROM debian AS final 10 | 11 | EXPOSE 80 12 | EXPOSE 443 13 | 14 | WORKDIR /app 15 | 16 | COPY --from=build /go/caddy ./caddy 17 | 18 | # https://github.com/abiosoft/caddy-docker/issues/173 19 | RUN apt-get update \ 20 | && apt-get install -y ca-certificates \ 21 | && apt-get clean \ 22 | && rm -rf /var/lib/apt/lists/* 23 | 24 | CMD ["bash"] 25 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # naiveproxy-docker 2 | 3 | 基于docker的naiveproxy。 4 | 5 | 6 | 7 | - [1. 说明](#1-说明) 8 | - [2. 预备工作](#2-预备工作) 9 | - [3. 部署服务端](#3-部署服务端) 10 | - [4. 客户端](#4-客户端) 11 | - [5. 自定义配置](#5-自定义配置) 12 | - [6. 版本变更](#6-版本变更) 13 | - [7. 常见问题](#7-常见问题) 14 | - [7.1. 端口可以自定义吗](#71-端口可以自定义吗) 15 | 16 | 17 | 18 | ## 1. 说明 19 | 20 | 镜像使用官方代码生成,利用`GitHub Actions`构建并上传到`DockerHub`。 21 | 22 | Dockerfile:[Dockerfile](Dockerfile) 23 | 24 | DockerHub: [DockerHub](https://hub.docker.com/repository/docker/zai7lou/naiveproxy-docker/general) 25 | 26 |
27 | 展开查看技术细节,不关心可以跳过 28 | 29 | - 关于镜像是怎么打的 30 | 31 | 镜像先是基于go的官方镜像,安装xcaddy,然后使用xcaddy编译naiveproxy插件版的caddy。然后将caddy拷贝到debian镜像中,最后发布这个debian镜像。 32 | 33 | 这样打出来的镜像只有65M,如果不使用docker而是直接在机器上装(go + xcaddy),要1G+。 34 | 35 | - 关于naiveproxy到底是什么 36 | 37 | naiveproxy有客户端和服务端,这里讲的是我们部署的服务端。 38 | 39 | naiveproxy服务端其实就是naiveproxy插件版caddy。 40 | 41 | naiveproxy插件版caddy指的是[https://github.com/klzgrad/forwardproxy](https://github.com/klzgrad/forwardproxy)。作者通过fork原版caddy,自己实现了`forward_proxy`功能,这个就是naiveproxy代理了。 42 | 43 | - 关于伪装 44 | 45 | `forward_proxy`里有个`probe_resistance`指令,我们请求会先进`forward_proxy`,如果用户名密码正确,则会正常实现naiveproxy代理功能;但如果认证失败,`probe_resistance`表明不会有异常产生,而是将当前请求继续往下仍,也就是扔到我们的伪装站点(可以是反代的站点也可以是本地的文件服务)。 46 | 47 | 所以就实现了我们客户端(能提供正确的用户名和密码)去访问就是naiveproxy代理,但其他人用户浏览器访问(或认证不通过),看到的就是一个正常站点。 48 | 49 |
50 | 51 | ## 2. 预备工作 52 | 53 | - 一个域名 54 | - 域名已DNS到当前服务器ip 55 | - 服务器已安装好docker环境 56 | 57 | P.S.不需要自己生成https证书,caddy会自动生成。 58 | 59 | ## 3. 部署服务端 60 | 61 | 一键安装脚本: 62 | 63 | ``` 64 | # create a dir 65 | mkdir -p ./naive && cd ./naive 66 | 67 | # install 68 | bash <(curl -sSL https://raw.githubusercontent.com/RayWangQvQ/naiveproxy-docker/main/install.sh) 69 | ``` 70 | 71 | 当不指定参数时,该脚本是互动式的,运行后会提示输入相关配置信息,输入后回车即可。 72 | 73 | ![install-interaction](docs/imgs/install-interaction.png) 74 | 75 | ![install-interaction-re](docs/imgs/insatll-interaction-re.png) 76 | 77 | 当然,你也可以像下面那样,直接将参数拼接好后立即执行: 78 | 79 | ``` 80 | # create a dir 81 | mkdir -p ./naive && cd ./naive 82 | 83 | # install 84 | curl -sSL -f -o ./install.sh https://raw.githubusercontent.com/RayWangQvQ/naiveproxy-docker/main/install.sh && chmod +x ./install.sh && ./install.sh -t demo.test.tk -m zhangsan@qq.com -u zhangsan -p 1qaz@wsx --verbose 85 | ``` 86 | 87 | ![install-silence](docs/imgs/install-silence.png) 88 | 89 | 参数说明: 90 | 91 | - `-t`:host,你的域名,如`demo.test.tk` 92 | - `-o`: cert-mode,证书模式,1为Caddy自动颁发,2为自己指定现有证书 93 | - `-c`: cert-file,证书文件绝对路径,如`/certs/test2.zai7lou.ml.crt` 94 | - `-k`, cert-key-file,证书key文件绝对路径,如`/certs/test2.zai7lou.ml.key` 95 | - `-m`:mail,你的邮箱,用于自动颁发证书,如`zhangsan@qq.com` 96 | - `-w`: http-port,http端口,默认80 97 | - `-s`: https-port,https端口,默认443 98 | - `-u`:user,proxy的用户名 99 | - `-p`:pwd,proxy的密码 100 | - `-f`:fakeHost,伪装域名,默认`https://demo.cloudreve.org` 101 | - `--verbose`,输出详细日志 102 | - `-h`:help,查看参数信息 103 | 104 | 容器run成功后,可以通过以下语句查看容器运行日志: 105 | 106 | ``` 107 | docker logs -f naiveproxy 108 | ``` 109 | 110 | `Ctrl + C` 可以退出日志追踪。 111 | 112 | 113 | 如果是第一次运行且选择自动颁发证书模式,颁发证书时日志可能会先ERROR飘红,别慌,等一会。 114 | 115 | 如果最后日志出现`certificate obtained successfully`字样,就是颁发成功了,可以去部署客户端了。 116 | 117 | ![success](docs/imgs/cert-suc.png) 118 | 119 | 如果颁发证书一直不成功,请检查80端口是否被占用。 120 | 121 | 部署成功后,浏览器访问域名,会展示伪装站点: 122 | 123 | ![web](docs/imgs/web.png) 124 | 125 | ## 4. 客户端 126 | 127 | 很多教程,就不说了。 128 | 129 | | 平台 | 客户端 | 130 | | :----: | :----: | 131 | | Win | V2RayN/Nekoray | 132 | | Linux | Nekoray | 133 | | MacOS | Nekoray | 134 | | Android | SagerNet | 135 | | iOS | Shadowrocket | 136 | 137 | ## 5. 自定义配置 138 | 139 | Caddy的配置文件`Caddyfile`已被挂载到宿主机的[./data/Caddyfile](data/Caddyfile),想要自定义配置,比如: 140 | 141 | - 添加proxy多用户 142 | - 修改proxy的用户名和密码 143 | - 更改端口 144 | - 修改伪装站点的host 145 | 146 | 等等,都可以直接在宿主机修改该文件: 147 | 148 | ``` 149 | vim ./data/Caddyfile 150 | ``` 151 | 152 | 修改完成并保存成功后,让Caddy热加载配置就可以了: 153 | 154 | ``` 155 | docker exec -it naiveproxy /app/caddy reload --config /data/Caddyfile 156 | ``` 157 | 158 | 举个栗子,多用户可以直接添加`forward_proxy`,像这样: 159 | 160 | ``` 161 | { 162 | debug 163 | http_port 80 164 | https_port 443 165 | order forward_proxy before file_server 166 | } 167 | :443, demo.test.tk { 168 | tls zhangsan@qq.com 169 | route { 170 | # proxy 171 | forward_proxy { 172 | basic_auth zhangsan 1qaz@wsx 173 | hide_ip 174 | hide_via 175 | probe_resistance 176 | } 177 | forward_proxy { 178 | basic_auth lisi 1234 179 | hide_ip 180 | hide_via 181 | probe_resistance 182 | } 183 | 184 | # 伪装网址 185 | reverse_proxy you.want.com { 186 | header_up Host {upstream_hostport} 187 | } 188 | } 189 | } 190 | ``` 191 | 192 | 详细的配置语法可以参考Caddy的官方文档:[Caddy Doc](https://caddyserver.com/docs/) 193 | 194 | P.S.我发现naiveproxy插件版地caddy,Caddyfile里不支持`demo.test.tk:443`的格式,必须像上面那样端口在域名前面,否则会报错。应该是适配有问题,需要注意下。 195 | 196 | ## 6. 版本变更 197 | 198 | [CHANGELOG](CHANGELOG.md) 199 | 200 | ## 7. 常见问题 201 | ### 7.1. 端口可以自定义吗 202 | 203 | 如果使用现有证书,可以自定义;如果要Caddy自动颁发,必须占有80端口。 204 | 205 | Caddy默认会占用80和443端口,用来管理证书,当前官方镜像并不支持更改默认端口,也就是一定需要占用80端口。 206 | 207 | 安装脚本可以选择证书模式,选择2使用现有证书,就可以不占用80和443端口了。 -------------------------------------------------------------------------------- /data/Caddyfile: -------------------------------------------------------------------------------- 1 | { 2 | 3 | http_port 4 | https_port 5 | 6 | order forward_proxy before file_server 7 | } 8 | :, { 9 | tls 10 | route { 11 | # proxy 12 | forward_proxy { 13 | basic_auth 14 | hide_ip 15 | hide_via 16 | probe_resistance 17 | } 18 | 19 | # 伪装网址 20 | reverse_proxy { 21 | header_up Host {upstream_hostport} 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /data/entry.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | echo "Formate the Caddyfile" 5 | /app/caddy fmt --overwrite /data/Caddyfile 6 | 7 | echo "Start server" 8 | /app/caddy start --config /data/Caddyfile 9 | 10 | tail -f -n 50 /data/Caddyfile -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3.4' 2 | 3 | services: 4 | naive: 5 | image: zai7lou/naiveproxy-docker 6 | container_name: naiveproxy 7 | tty: true 8 | restart: unless-stopped 9 | ports: 10 | - : 11 | - : 12 | volumes: 13 | - ./data:/data 14 | - ./share:/root/.local/share 15 | 16 | 17 | command: ["/bin/bash", "/data/entry.sh"] -------------------------------------------------------------------------------- /docs/imgs/cert-suc.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RayWangQvQ/naiveproxy-docker/d3c2763298acd234cbeb65cfc289e0ae6a8d5188/docs/imgs/cert-suc.png -------------------------------------------------------------------------------- /docs/imgs/insatll-interaction-re.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RayWangQvQ/naiveproxy-docker/d3c2763298acd234cbeb65cfc289e0ae6a8d5188/docs/imgs/insatll-interaction-re.png -------------------------------------------------------------------------------- /docs/imgs/install-interaction.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RayWangQvQ/naiveproxy-docker/d3c2763298acd234cbeb65cfc289e0ae6a8d5188/docs/imgs/install-interaction.png -------------------------------------------------------------------------------- /docs/imgs/install-silence.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RayWangQvQ/naiveproxy-docker/d3c2763298acd234cbeb65cfc289e0ae6a8d5188/docs/imgs/install-silence.png -------------------------------------------------------------------------------- /docs/imgs/web.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RayWangQvQ/naiveproxy-docker/d3c2763298acd234cbeb65cfc289e0ae6a8d5188/docs/imgs/web.png -------------------------------------------------------------------------------- /install.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -e 3 | set -u 4 | set -o pipefail 5 | 6 | # ------------share-------------- 7 | invocation='echo "" && say_verbose "Calling: ${yellow:-}${FUNCNAME[0]} ${green:-}$*${normal:-}"' 8 | exec 3>&1 9 | if [ -t 1 ] && command -v tput >/dev/null; then 10 | ncolors=$(tput colors || echo 0) 11 | if [ -n "$ncolors" ] && [ $ncolors -ge 8 ]; then 12 | bold="$(tput bold || echo)" 13 | normal="$(tput sgr0 || echo)" 14 | black="$(tput setaf 0 || echo)" 15 | red="$(tput setaf 1 || echo)" 16 | green="$(tput setaf 2 || echo)" 17 | yellow="$(tput setaf 3 || echo)" 18 | blue="$(tput setaf 4 || echo)" 19 | magenta="$(tput setaf 5 || echo)" 20 | cyan="$(tput setaf 6 || echo)" 21 | white="$(tput setaf 7 || echo)" 22 | fi 23 | fi 24 | 25 | say_warning() { 26 | printf "%b\n" "${yellow:-}ray_naive_install: Warning: $1${normal:-}" >&3 27 | } 28 | 29 | say_err() { 30 | printf "%b\n" "${red:-}ray_naive_install: Error: $1${normal:-}" >&2 31 | } 32 | 33 | say() { 34 | # using stream 3 (defined in the beginning) to not interfere with stdout of functions 35 | # which may be used as return value 36 | printf "%b\n" "${cyan:-}ray_naive_install:${normal:-} $1" >&3 37 | } 38 | 39 | say_verbose() { 40 | if [ "$verbose" = true ]; then 41 | say "$1" 42 | fi 43 | } 44 | 45 | machine_has() { 46 | eval $invocation 47 | 48 | command -v "$1" >/dev/null 2>&1 49 | return $? 50 | } 51 | 52 | check_docker() { 53 | eval $invocation 54 | 55 | if machine_has "docker"; then 56 | docker --version 57 | else 58 | say_err "Missing dependency: docker was not found, please install it first." 59 | exit 1 60 | fi 61 | } 62 | 63 | # args: 64 | # remote_path - $1 65 | get_http_header_curl() { 66 | eval $invocation 67 | 68 | local remote_path="$1" 69 | 70 | curl_options="-I -sSL --retry 5 --retry-delay 2 --connect-timeout 15 " 71 | curl $curl_options "$remote_path" 2>&1 || return 1 72 | return 0 73 | } 74 | 75 | # args: 76 | # remote_path - $1 77 | get_http_header_wget() { 78 | eval $invocation 79 | 80 | local remote_path="$1" 81 | local wget_options="-q -S --spider --tries 5 " 82 | # Store options that aren't supported on all wget implementations separately. 83 | local wget_options_extra="--waitretry 2 --connect-timeout 15 " 84 | local wget_result='' 85 | 86 | wget $wget_options $wget_options_extra "$remote_path" 2>&1 87 | wget_result=$? 88 | 89 | if [[ $wget_result == 2 ]]; then 90 | # Parsing of the command has failed. Exclude potentially unrecognized options and retry. 91 | wget $wget_options "$remote_path" 2>&1 92 | return $? 93 | fi 94 | 95 | return $wget_result 96 | } 97 | 98 | # Updates global variables $http_code and $download_error_msg 99 | downloadcurl() { 100 | eval $invocation 101 | 102 | unset http_code 103 | unset download_error_msg 104 | local remote_path="$1" 105 | local out_path="${2:-}" 106 | local remote_path_with_credential="${remote_path}" 107 | local curl_options="--retry 20 --retry-delay 2 --connect-timeout 15 -sSL -f --create-dirs " 108 | local failed=false 109 | if [ -z "$out_path" ]; then 110 | curl $curl_options "$remote_path_with_credential" 2>&1 || failed=true 111 | else 112 | curl $curl_options -o "$out_path" "$remote_path_with_credential" 2>&1 || failed=true 113 | fi 114 | if [ "$failed" = true ]; then 115 | local response=$(get_http_header_curl $remote_path) 116 | http_code=$(echo "$response" | awk '/^HTTP/{print $2}' | tail -1) 117 | download_error_msg="Unable to download $remote_path." 118 | if [[ $http_code != 2* ]]; then 119 | download_error_msg+=" Returned HTTP status code: $http_code." 120 | fi 121 | say_verbose "$download_error_msg" 122 | return 1 123 | fi 124 | return 0 125 | } 126 | 127 | # Updates global variables $http_code and $download_error_msg 128 | downloadwget() { 129 | eval $invocation 130 | 131 | unset http_code 132 | unset download_error_msg 133 | local remote_path="$1" 134 | local out_path="${2:-}" 135 | local remote_path_with_credential="${remote_path}" 136 | local wget_options="--tries 20 " 137 | # Store options that aren't supported on all wget implementations separately. 138 | local wget_options_extra="--waitretry 2 --connect-timeout 15 " 139 | local wget_result='' 140 | 141 | if [ -z "$out_path" ]; then 142 | wget -q $wget_options $wget_options_extra -O - "$remote_path_with_credential" 2>&1 143 | wget_result=$? 144 | else 145 | wget $wget_options $wget_options_extra -O "$out_path" "$remote_path_with_credential" 2>&1 146 | wget_result=$? 147 | fi 148 | 149 | if [[ $wget_result == 2 ]]; then 150 | # Parsing of the command has failed. Exclude potentially unrecognized options and retry. 151 | if [ -z "$out_path" ]; then 152 | wget -q $wget_options -O - "$remote_path_with_credential" 2>&1 153 | wget_result=$? 154 | else 155 | wget $wget_options -O "$out_path" "$remote_path_with_credential" 2>&1 156 | wget_result=$? 157 | fi 158 | fi 159 | 160 | if [[ $wget_result != 0 ]]; then 161 | local disable_feed_credential=false 162 | local response=$(get_http_header_wget $remote_path $disable_feed_credential) 163 | http_code=$(echo "$response" | awk '/^ HTTP/{print $2}' | tail -1) 164 | download_error_msg="Unable to download $remote_path." 165 | if [[ $http_code != 2* ]]; then 166 | download_error_msg+=" Returned HTTP status code: $http_code." 167 | fi 168 | say_verbose "$download_error_msg" 169 | return 1 170 | fi 171 | 172 | return 0 173 | } 174 | 175 | # args: 176 | # remote_path - $1 177 | # [out_path] - $2 - stdout if not provided 178 | download() { 179 | eval $invocation 180 | 181 | local remote_path="$1" 182 | local out_path="${2:-}" 183 | 184 | if [[ "$remote_path" != "http"* ]]; then 185 | cp "$remote_path" "$out_path" 186 | return $? 187 | fi 188 | 189 | local failed=false 190 | local attempts=0 191 | while [ $attempts -lt 3 ]; do 192 | attempts=$((attempts + 1)) 193 | failed=false 194 | if machine_has "curl"; then 195 | downloadcurl "$remote_path" "$out_path" || failed=true 196 | elif machine_has "wget"; then 197 | downloadwget "$remote_path" "$out_path" || failed=true 198 | else 199 | say_err "Missing dependency: neither curl nor wget was found." 200 | exit 1 201 | fi 202 | 203 | if [ "$failed" = false ] || [ $attempts -ge 3 ] || { [ ! -z $http_code ] && [ $http_code = "404" ]; }; then 204 | break 205 | fi 206 | 207 | say "Download attempt #$attempts has failed: $http_code $download_error_msg" 208 | say "Attempt #$((attempts + 1)) will start in $((attempts * 10)) seconds." 209 | sleep $((attempts * 10)) 210 | done 211 | 212 | if [ "$failed" = true ]; then 213 | say_verbose "Download failed: $remote_path" 214 | return 1 215 | fi 216 | return 0 217 | } 218 | # --------------------------------- 219 | 220 | echo ' ____ _ _ _ _ ' 221 | echo ' | _ \ __ _ _ _ | \ | | __ _(_)_(_)_ _____ ' 222 | echo ' | |_) / _` | | | | | \| |/ _` | | | \ \ / / _ \ ' 223 | echo ' | _ < (_| | |_| | | |\ | (_| | | | \ V / __/ ' 224 | echo ' |_| \_\__,_|\__, | |_| \_|\__,_| |_| \_/ \___| ' 225 | echo ' |___/ ' 226 | 227 | # ------------vars-----------、 228 | gitRowUrl="https://raw.githubusercontent.com/RayWangQvQ/naiveproxy-docker/main" 229 | 230 | host="" 231 | 232 | certMode="" 233 | certFile="" 234 | certKeyFile="" 235 | autoHttps="" 236 | 237 | mail="" 238 | 239 | httpPort="" 240 | httpsPort="" 241 | 242 | user="" 243 | pwd="" 244 | 245 | fakeHostDefault="https://demo.cloudreve.org" 246 | fakeHost="" 247 | 248 | verbose=false 249 | # -------------------------- 250 | 251 | # read params from init cmd 252 | while [ $# -ne 0 ]; do 253 | name="$1" 254 | case "$name" in 255 | -t | --host | -[Hh]ost) 256 | shift 257 | host="$1" 258 | ;; 259 | -o | --cert-mode | -[Cc]ert[Mm]ode) 260 | shift 261 | certMode="$1" 262 | ;; 263 | -c | --cert-file | -[Cc]ert[Ff]ile) 264 | shift 265 | certFile="$1" 266 | ;; 267 | -k | --cert-key-file | -[Cc]ert[Kk]ey[Ff]ile) 268 | shift 269 | certKeyFile="$1" 270 | ;; 271 | -m | --mail | -[Mm]ail) 272 | shift 273 | mail="$1" 274 | ;; 275 | -w | --http-port | -[Hh]ttp[Pp]ort) 276 | shift 277 | httpPort="$1" 278 | ;; 279 | -s | --http-port | -[Hh]ttp[Pp]ort) 280 | shift 281 | httpsPort="$1" 282 | ;; 283 | -u | --user | -[Uu]ser) 284 | shift 285 | user="$1" 286 | ;; 287 | -p | --pwd | -[Pp]wd) 288 | shift 289 | pwd="$1" 290 | ;; 291 | -f | --fake-host | -[Ff]ake[Hh]ost) 292 | shift 293 | fakeHost="$1" 294 | ;; 295 | --verbose | -[Vv]erbose) 296 | verbose=true 297 | ;; 298 | -? | --? | -h | --help | -[Hh]elp) 299 | script_name="$(basename "$0")" 300 | echo "Ray Naiveproxy in Docker" 301 | echo "Usage: $script_name [-t|--host ] [-m|--mail ]" 302 | echo " $script_name -h|-?|--help" 303 | echo "" 304 | echo "$script_name is a simple command line interface to install naiveproxy in docker." 305 | echo "" 306 | echo "Options:" 307 | echo " -t,--host Your host, Defaults to \`$host\`." 308 | echo " -Host" 309 | echo " Possible values:" 310 | echo " - xui.test.com" 311 | echo " -m,--mail Your mail, Defaults to \`$mail\`." 312 | echo " -Mail" 313 | echo " Possible values:" 314 | echo " - mail@qq.com" 315 | echo " -u,--user Your proxy user name, Defaults to \`$user\`." 316 | echo " -User" 317 | echo " Possible values:" 318 | echo " - user" 319 | echo " -p,--pwd Your proxy password, Defaults to \`$pwd\`." 320 | echo " -Pwd" 321 | echo " Possible values:" 322 | echo " - 1qaz@wsx" 323 | echo " -f,--fake-host Your fake host, Defaults to \`$fakeHost\`." 324 | echo " -FakeHost" 325 | echo " Possible values:" 326 | echo " - https://demo.cloudreve.org" 327 | echo " -?,--?,-h,--help,-Help Shows this help message" 328 | echo "" 329 | exit 0 330 | ;; 331 | *) 332 | say_err "Unknown argument \`$name\`" 333 | exit 1 334 | ;; 335 | esac 336 | shift 337 | done 338 | 339 | read_var_from_user() { 340 | eval $invocation 341 | 342 | # host 343 | if [ -z "$host" ]; then 344 | read -p "请输入域名(如demo.test.tk):" host 345 | else 346 | say "域名: $host" 347 | fi 348 | 349 | # cert 350 | if [ -z "$certMode" ]; then 351 | read -p "请输入证书模式(1.Caddy自动颁发;2.使用现有证书。默认1):" certMode 352 | if [ -z "$certMode" ]; then 353 | certMode="1" 354 | fi 355 | fi 356 | 357 | if [ "$certMode" == "1" ]; then 358 | # say "certMode: $certMode(由Caddy自动颁发)" 359 | say_warning "自动颁发证书需要开放80端口给Caddy使用,请确保80端口开放且未被占用" 360 | httpPort="80" 361 | # email 362 | if [ -z "$mail" ]; then 363 | read -p "请输入邮箱(如test@qq.com):" mail 364 | else 365 | say "邮箱: $mail" 366 | fi 367 | else 368 | # say "certMode: 2(使用现有证书)" 369 | autoHttps="auto_https disable_certs" 370 | if [ -z "$certKeyFile" ]; then 371 | read -p "请输入证书key文件路径:" certKeyFile 372 | else 373 | say "证书key: $certKeyFile" 374 | fi 375 | 376 | if [ -z "$certFile" ]; then 377 | read -p "请输入证书文件路径:" certFile 378 | else 379 | say "证书文件: $certFile" 380 | fi 381 | fi 382 | 383 | # port 384 | if [ -z "$httpPort" ]; then 385 | if [ $certMode == "2" ]; then 386 | say "使用现有证书模式允许使用非80的http端口" 387 | read -p "请输入Caddy的http端口(如8080, 默认80):" httpPort 388 | if [ -z "$httpPort" ]; then 389 | httpPort="80" 390 | fi 391 | else 392 | httpPort="80" 393 | say "Http端口: $httpPort" 394 | fi 395 | else 396 | say "httpPort: $httpPort" 397 | fi 398 | 399 | if [ -z "$httpsPort" ]; then 400 | read -p "请输入https端口(如8043, 默认443):" httpsPort 401 | if [ -z "$httpsPort" ]; then 402 | httpsPort="443" 403 | fi 404 | else 405 | say "Https端口: $httpsPort" 406 | fi 407 | 408 | if [ -z "$user" ]; then 409 | read -p "请输入节点用户名(如zhangsan):" user 410 | else 411 | say "节点用户名: $user" 412 | fi 413 | 414 | if [ -z "$pwd" ]; then 415 | read -p "请输入节点密码(如1qaz@wsx):" pwd 416 | else 417 | say "节点密码: $pwd" 418 | fi 419 | 420 | if [ -z "$fakeHost" ]; then 421 | read -p "请输入伪装站点地址(默认$fakeHostDefault):" fakeHost 422 | if [ -z "$fakeHost" ]; then 423 | fakeHost=$fakeHostDefault 424 | fi 425 | else 426 | say "伪装站点地址: $fakeHost" 427 | fi 428 | } 429 | 430 | # 下载docker-compose文件 431 | download_docker_compose_file() { 432 | eval $invocation 433 | 434 | rm -rf ./docker-compose.yml 435 | download $gitRowUrl/docker-compose.yml docker-compose.yml 436 | } 437 | 438 | # 配置docker-compose文件 439 | replace_docker_compose_configs() { 440 | eval $invocation 441 | 442 | # replace httpPort 443 | sed -i 's||'"$httpPort"'|g' ./docker-compose.yml 444 | 445 | # replace httpsPort 446 | sed -i 's||'"$httpsPort"'|g' ./docker-compose.yml 447 | 448 | # certs 449 | if [ "$certMode" == "2" ]; then 450 | sed -i 's||'-" $certFile":"$certFile"'|g' ./docker-compose.yml 451 | sed -i 's||'-" $certKeyFile":"$certKeyFile"'|g' ./docker-compose.yml 452 | else 453 | sed -i 's|| |g' ./docker-compose.yml 454 | sed -i 's|| |g' ./docker-compose.yml 455 | fi 456 | 457 | say "Docker compose file:" 458 | cat ./docker-compose.yml 459 | } 460 | 461 | # 下载data 462 | download_data_files() { 463 | eval $invocation 464 | 465 | mkdir -p ./data 466 | 467 | # entry 468 | rm -rf ./data/entry.sh 469 | download $gitRowUrl/data/entry.sh ./data/entry.sh 470 | 471 | # Caddyfile 472 | rm -rf ./data/Caddyfile 473 | download $gitRowUrl/data/Caddyfile ./data/Caddyfile 474 | } 475 | 476 | # 配置Caddyfile 477 | replace_caddyfile_configs() { 478 | eval $invocation 479 | 480 | # debug 481 | debug="" 482 | if [ $verbose = true ]; then 483 | debug="debug" 484 | fi 485 | sed -i 's||'"$debug"'|g' ./data/Caddyfile 486 | 487 | # replace host 488 | sed -i 's||'"$host"'|g' ./data/Caddyfile 489 | 490 | # replace mail 491 | sed -i 's||'"$mail"'|g' ./data/Caddyfile 492 | 493 | # cert_file 494 | sed -i 's||'"$certFile"'|g' ./data/Caddyfile 495 | 496 | # cert_key_file 497 | sed -i 's||'"$certKeyFile"'|g' ./data/Caddyfile 498 | 499 | # replace httpPort 500 | sed -i 's||'"$httpPort"'|g' ./data/Caddyfile 501 | 502 | # replace httpsPort 503 | sed -i 's||'"$httpsPort"'|g' ./data/Caddyfile 504 | 505 | # auto_https 506 | sed -i 's||'"$autoHttps"'|g' ./data/Caddyfile 507 | 508 | # replace user 509 | sed -i 's||'"$user"'|g' ./data/Caddyfile 510 | 511 | # replace pwd 512 | sed -i 's||'"$pwd"'|g' ./data/Caddyfile 513 | 514 | # replace fakeHost 515 | sed -i 's||'"$fakeHost"'|g' ./data/Caddyfile 516 | 517 | say "Caddyfile:" 518 | cat ./data/Caddyfile 519 | } 520 | 521 | # 运行容器 522 | runContainer() { 523 | eval $invocation 524 | 525 | say "Try to run docker container:" 526 | { 527 | docker compose version && docker compose up -d 528 | } || { 529 | docker-compose version && docker-compose up -d 530 | } || { 531 | certsV="" 532 | if [ "$certMode" == "2" ]; then 533 | certsV="-v $certFile:certFile -v $certKeyFile:$certKeyFile" 534 | fi 535 | docker run -itd --name naiveproxy \ 536 | --restart=unless-stopped \ 537 | -p $httpPort:$httpPort \ 538 | -p $httpsPort:$httpsPort \ 539 | -v $PWD/data:/data \ 540 | -v $PWD/share:/root/.local/share $certsV \ 541 | zai7lou/naiveproxy-docker bash /data/entry.sh 542 | } 543 | } 544 | 545 | # 检查容器运行状态 546 | check_result() { 547 | eval $invocation 548 | 549 | docker ps --filter "name=naiveproxy" 550 | 551 | containerId=$(docker ps -q --filter "name=^naiveproxy$") 552 | if [ -n "$containerId" ]; then 553 | echo "" 554 | echo "===============================================" 555 | echo "Congratulations! 恭喜!" 556 | echo "创建并运行naiveproxy容器成功。" 557 | echo "" 558 | echo "请使用浏览器访问'https://$host:$httpsPort',验证是否可正常访问伪装站点" 559 | echo "如果异常,请运行'docker logs -f naiveproxy'来追踪容器运行日志, 随后可以点击 Ctrl+c 退出日志追踪" 560 | echo "" 561 | echo "然后你可以使用客户端连接你的节点了:" 562 | echo "naive+https://$user:$pwd@$host:$httpsPort#naive" 563 | echo "Enjoy it~" 564 | echo "===============================================" 565 | else 566 | echo "" 567 | echo "请查看运行日志,确认容器是否正常运行,点击 Ctrl+c 退出日志追踪" 568 | echo "" 569 | docker logs -f naiveproxy 570 | fi 571 | } 572 | 573 | main() { 574 | check_docker 575 | read_var_from_user 576 | 577 | download_docker_compose_file 578 | replace_docker_compose_configs 579 | 580 | download_data_files 581 | replace_caddyfile_configs 582 | 583 | runContainer 584 | 585 | check_result 586 | } 587 | 588 | main 589 | --------------------------------------------------------------------------------