├── .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 | 
74 |
75 | 
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 | 
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 | 
118 |
119 | 如果颁发证书一直不成功,请检查80端口是否被占用。
120 |
121 | 部署成功后,浏览器访问域名,会展示伪装站点:
122 |
123 | 
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 |
--------------------------------------------------------------------------------