[GH-ISSUE #176] HTTP over TLS 二级代理无法工作 #106

Open
opened 2026-02-27 23:15:28 +03:00 by kerem · 7 comments
Owner

Originally created by @wzxjohn on GitHub (Nov 16, 2018).
Original GitHub issue: https://github.com/snail007/goproxy/issues/176

配置流程

proxy keygen -C ca
proxy keygen -C ca -s -c win_proxy

机器 A 启动:
proxy.exe http -t tls -p ":8080" -C win_proxy.crt -K win_proxy.key
客户端启动:
proxy http -t tcp -p ":8080" -T tls -P "a.b.c.d:8080" -C win_proxy.crt -K win_proxy.key

客户端报错

err:remote error: tls: bad certificate

服务端报错

ERR:http decoder read err:tls: client didn't provide a certificate

用 curl 测试可以连通,测试命令:
curl -i https://a.b.c.d:8080/ --insecure --cert win_proxy.crt --key win_proxy.key

Originally created by @wzxjohn on GitHub (Nov 16, 2018). Original GitHub issue: https://github.com/snail007/goproxy/issues/176 ### 配置流程 ``` proxy keygen -C ca proxy keygen -C ca -s -c win_proxy ``` 机器 A 启动: `proxy.exe http -t tls -p ":8080" -C win_proxy.crt -K win_proxy.key` 客户端启动: `proxy http -t tcp -p ":8080" -T tls -P "a.b.c.d:8080" -C win_proxy.crt -K win_proxy.key` ### 客户端报错 `err:remote error: tls: bad certificate` ### 服务端报错 `ERR:http decoder read err:tls: client didn't provide a certificate` 用 curl 测试可以连通,测试命令: `curl -i https://a.b.c.d:8080/ --insecure --cert win_proxy.crt --key win_proxy.key`
Author
Owner

@snail007 commented on GitHub (Nov 16, 2018):

使用证书错误,签发方式必须指定--ca.

<!-- gh-comment-id:439327342 --> @snail007 commented on GitHub (Nov 16, 2018): 使用证书错误,签发方式必须指定--ca.
Author
Owner

@wzxjohn commented on GitHub (Nov 16, 2018):

@snail007
-C, --ca="" ca name
根据文档, -C 和 --ca 是同样的意思呀

<!-- gh-comment-id:439329841 --> @wzxjohn commented on GitHub (Nov 16, 2018): @snail007 -C, --ca="" ca name 根据文档, -C 和 --ca 是同样的意思呀
Author
Owner

@snail007 commented on GitHub (Nov 16, 2018):

仔细看命令帮助,
--ca="" ca cert file for tls
-C, --cert="proxy.crt" cert file for tls

<!-- gh-comment-id:439330337 --> @snail007 commented on GitHub (Nov 16, 2018): 仔细看命令帮助, --ca="" ca cert file for tls -C, --cert="proxy.crt" cert file for tls
Author
Owner

@wzxjohn commented on GitHub (Nov 16, 2018):

@snail007
这是我看到的 help 信息:

$ proxy keygen --help
usage: proxy keygen [<flags>]

create certificate for proxy

Flags:
      --help                 Show context-sensitive help (also try --help-long and --help-man).
      --version              Show application version.
      --debug                debug log output
      --daemon               run proxy in background
      --forever              run proxy in forever,fail and retry
......
  -n, --cn=""                common name
  -C, --ca=""                ca name
  -c, --cert=""              cert name of sign to create
  -d, --days=365             days of sign
  -s, --sign                 cert is to signin

$ proxy http --help
usage: proxy http [<flags>]

proxy on http mode

Flags:
      --help                     Show context-sensitive help (also try --help-long and --help-man).
      --version                  Show application version.
      --debug                    debug log output
      --daemon                   run proxy in background
      --forever                  run proxy in forever,fail and retry
......
  -P, --parent= ...              parent address, such as: "23.32.32.19:28008"
      --ca=""                    ca cert file for tls
  -C, --cert="proxy.crt"         cert file for tls
  -K, --key="proxy.key"          key file for tls
  -t, --local-type=tcp           local protocol type <tls|tcp|kcp>
  -T, --parent-type=PARENT-TYPE  parent protocol type <tls|tcp|ssh|kcp>
      --always                   always use parent proxy
      --timeout=2000             tcp timeout milliseconds when connect to real server or parent proxy
      --http-timeout=3000        check domain if blocked , http request timeout milliseconds when connect to host
      --interval=10              check domain if blocked every interval seconds
  -b, --blocked="blocked"        blocked domain file , one domain each line
  -d, --direct="direct"          direct domain file , one domain each line
  -F, --auth-file=AUTH-FILE      http basic auth file,"username:password" each line in file
  -a, --auth=AUTH ...            http basic auth username and password, mutiple user repeat -a ,such as: -a user1:pass1 -a user2:pass2
  -I, --check-parent-interval=3  check if proxy is okay every interval seconds,zero: means no check
  -p, --local=":33080"           local ip:port to listen,multiple address use comma split,such as: 0.0.0.0:80,0.0.0.0:443
......

我看到的 help 信息告诉我,证书签发时,-C = --ca,证书使用时,-C = --cert

<!-- gh-comment-id:439332183 --> @wzxjohn commented on GitHub (Nov 16, 2018): @snail007 这是我看到的 help 信息: ``` $ proxy keygen --help usage: proxy keygen [<flags>] create certificate for proxy Flags: --help Show context-sensitive help (also try --help-long and --help-man). --version Show application version. --debug debug log output --daemon run proxy in background --forever run proxy in forever,fail and retry ...... -n, --cn="" common name -C, --ca="" ca name -c, --cert="" cert name of sign to create -d, --days=365 days of sign -s, --sign cert is to signin $ proxy http --help usage: proxy http [<flags>] proxy on http mode Flags: --help Show context-sensitive help (also try --help-long and --help-man). --version Show application version. --debug debug log output --daemon run proxy in background --forever run proxy in forever,fail and retry ...... -P, --parent= ... parent address, such as: "23.32.32.19:28008" --ca="" ca cert file for tls -C, --cert="proxy.crt" cert file for tls -K, --key="proxy.key" key file for tls -t, --local-type=tcp local protocol type <tls|tcp|kcp> -T, --parent-type=PARENT-TYPE parent protocol type <tls|tcp|ssh|kcp> --always always use parent proxy --timeout=2000 tcp timeout milliseconds when connect to real server or parent proxy --http-timeout=3000 check domain if blocked , http request timeout milliseconds when connect to host --interval=10 check domain if blocked every interval seconds -b, --blocked="blocked" blocked domain file , one domain each line -d, --direct="direct" direct domain file , one domain each line -F, --auth-file=AUTH-FILE http basic auth file,"username:password" each line in file -a, --auth=AUTH ... http basic auth username and password, mutiple user repeat -a ,such as: -a user1:pass1 -a user2:pass2 -I, --check-parent-interval=3 check if proxy is okay every interval seconds,zero: means no check -p, --local=":33080" local ip:port to listen,multiple address use comma split,such as: 0.0.0.0:80,0.0.0.0:443 ...... ``` 我看到的 help 信息告诉我,证书签发时,-C = --ca,证书使用时,-C = --cert
Author
Owner

@wzxjohn commented on GitHub (Nov 16, 2018):

使用 --ca="" 的方式进行 keygen 证书签发, 报错相同。

<!-- gh-comment-id:439337757 --> @wzxjohn commented on GitHub (Nov 16, 2018): 使用 --ca="" 的方式进行 keygen 证书签发, 报错相同。
Author
Owner

@wzxjohn commented on GitHub (Nov 16, 2018):

使用 Wireshark 抓包分析了 curl 和 goproxy 发出的包,发现在 TLS 握手阶段,goproxy 并没有给上游 server 发送正确的 client certificate,导致上游 server 发送了 Alert 拒绝握手。

<!-- gh-comment-id:439436459 --> @wzxjohn commented on GitHub (Nov 16, 2018): 使用 Wireshark 抓包分析了 curl 和 goproxy 发出的包,发现在 TLS 握手阶段,goproxy 并没有给上游 server 发送正确的 client certificate,导致上游 server 发送了 Alert 拒绝握手。
Author
Owner

@wzxjohn commented on GitHub (Nov 16, 2018):

@snail007 找到原因了。
上游 Server 会读取 -C 参数的证书,取 Subject 作为 Issuer 去发 Certification Request,然后 Client 会去使用自身的证书的 Issuer 去做匹配。
这个流程本身是没有问题的,问题在于,文档中说需要先生成 CA,再用 CA 签发上游 Server 和 Client 使用的证书。这一步说明存在问题。实际上,在目前的代码中,上游 Server 和 Client 都必须使用 CA 证书本身才可以通信成功。
也就是说,Server 和 Client 必须使用 Subject 和 Issuer 相同的证书才可以进行通信。

建议这里修改一下这个流程,上游 Server 向客户端发送的证书应该使用 -C -K 参数指定的证书,但是用来验证客户端的证书应该是 --ca 参数指定的证书。否则客户端和服务端就永远只能使用 CA 证书本身进行通信了。

经过测试,目前的代码是支持上游 Server 指定单独的 CA 证书的,只是文档中没有说明。
建议在文档中强调这点,明确一下各个证书在各级的作用。这样配置起来会更加清晰。

<!-- gh-comment-id:439441399 --> @wzxjohn commented on GitHub (Nov 16, 2018): @snail007 找到原因了。 上游 Server 会读取 -C 参数的证书,取 Subject 作为 Issuer 去发 Certification Request,然后 Client 会去使用自身的证书的 Issuer 去做匹配。 这个流程本身是没有问题的,问题在于,文档中说需要先生成 CA,再用 CA 签发上游 Server 和 Client 使用的证书。这一步说明存在问题。~~实际上,在目前的代码中,上游 Server 和 Client 都必须使用 CA 证书本身才可以通信成功。~~ ~~也就是说,Server 和 Client 必须使用 Subject 和 Issuer 相同的证书才可以进行通信。~~ ~~建议这里修改一下这个流程,上游 Server 向客户端发送的证书应该使用 -C -K 参数指定的证书,但是用来验证客户端的证书应该是 --ca 参数指定的证书。否则客户端和服务端就永远只能使用 CA 证书本身进行通信了。~~ **经过测试,目前的代码是支持上游 Server 指定单独的 CA 证书的,只是文档中没有说明。 建议在文档中强调这点,明确一下各个证书在各级的作用。这样配置起来会更加清晰。**
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
starred/goproxy#106
No description provided.