# socks技术方案
- Socket Secure,网络协议,处于会话层,用于管理网络连接并提供安全性和隐私保护;
- 通过Socks代理服务器,实现隐藏真实ip和其他身份信息,匿名访问互联网资源。
- 防火墙系统作为应用层网关,将内网与外网隔离开来,管理网络之间的通信。socks5 协议位于应用层和传输层之间,提供了一种能够在防火墙上透明且安全地通信的通用方式。
# 与VPN的对比
* 与VPN的技术路线不同;VPN基于stun技术路线;socks基于代理;
# 参考文档
- socks5协议-https://www.cnblogs.com/chr1ce/articles/16246884.html
- linux设置socks代理-https://blog.csdn.net/weixin_39712991/article/details/143665010
# socks的简易流程
- 参考文档: socks5 代理服务器原理及实现--https://zhuanlan.zhihu.com/p/703624588
# 协商验证阶段
- socks5客户端与服务端通信,进行认证;
- 类似于stun的过程,本地启动客户端代理;
# 传输阶段
- 宿主机某个应用正常的请求经过socks客户端报文封装后,传递给socks服务端
- socks服务端解析地址,并创建stun类型的tcp连接到目标地址;
- 宿主机应用通过socks客户端发送报文,经过服务端的stun完成整个链路;
# socks的关键
- socks客户端代理与socks服务端;
- socks服务端,即常说的节点;
- 某个应用需要使用代理,应用本身需要支持支持客户端代理才行;
# 客户端代理
- 大多数主流软件都提供了客户端代理功能,常用的代理参数:http_proxy,https_proxy;
- 也有直接用proxy的,常见的还有ftp_proxy,需要注意,具体的代理协议支持程度由应用本身决定,而不是通过这个参数名称判断,如http_proxy不只是能代理http协议;
- 代理机制是跟应用绑定的,比如: 开启魔法后,浏览器能访问google,youtube等,但是直接ping或者telnet不能通;这与vpn是明显不同的;
# 常用的socks客户端代理
- ShadowSocksR
- ClashForWindows,ClashForLinux
- AriConnect
- V2ray
# 备注
- 大部分已经跑路
- 通常被称之为机场
# linux常见应用代理配置
# 参考文档
- linux常用配置代理proxy--https://www.cnblogs.com/mq0036/p/17351647.html
# 备注
- linux通过设置http_proxy,https_proxy变量,可以设置全局代理;
- yum,wget,curl配置代理都是有针对性的代理,即时配置了全局代理,仍然不能生效;
# docker配置代理
- 全局代理配置后,docker不能生效,要单独配置!
- 具体在
/etc/docker/daemon.json
中配置即可.参考:给docker配置socks代理--https://www.cnblogs.com/lrui1/p/18353634 - 注意,代理最好使用局域网ip,使用127.0.0.1可能会踩坑
{
"registry-mirrors": ["https://registry.devops-engineer.com.cn/"],
"data-root":"/hd01/docker-root",
"proxies":{
"http-proxy":"http://127.0.0.1:7890",
"https-proxy":"http://127.0.0.1:7890"
},
"dns": ["8.8.8.8", "8.8.4.4"]
}
systemctl daemon-reload
systemctl restart docker
- 注意,如果daemon.json没有配置dns,内部将无法正常访问域名(如Dockerfile中安装文件将失败);下方配置示例
{
"dns": ["8.8.8.8", "8.8.4.4"]
}
# yum设置代理
- 在
/etc/yum.conf
中配置 - 添加proxy变量
# wget配置代理
- 在
/etc/wgetrc
中进行配置 - 添加http_proxy,https_proxy配置;
- 注意,实际使用中可能有出入,新版本可能已经支持全局代理;
# curl配置代理
- 在使用过程中,使用参数 http_proxy=socks客户端;
- 注意,实际使用中可能有出入,新版本可能已经支持全局代理;
# git使用代理
- 在使用命令中,使用参数 http_proxy=socks客户端;
- 注意,实际使用中可能有出入,新版本可能已经支持全局代理;
git config --global http.proxy http://127.0.0.1:7890 #代理
git config --global --unset http.proxy #取消代理
git config --global http.proxy #查询是否使用代理
# 通过ng代理外网API接口方案
- ng默认是直连目标地址,可以通过ng代理本地的地址,然后本地通过客户端代理再访问外网地址
- 以openai为例:
# ng配置
location /openai/ {
proxy_pass http://127.0.0.1:8089/;
# 保留原始 Host 头
proxy_set_header Host api.openai.com;
proxy_set_header Connection "";
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
# 启用 SSL 代理
proxy_ssl_server_name on; # 重要:启用 SNI,让目标服务器知道是 api.openai.com
# 传递原始请求头(OpenAI 需要 Authorization)
proxy_set_header Authorization $http_authorization;
proxy_pass_request_headers on;
# 超时设置
proxy_connect_timeout 30s;
proxy_send_timeout 60s;
proxy_read_timeout 60s;
# 缓冲
proxy_buffering off;
}
# 代理客户端
package com.automannn.demo.proxy;
import com.sun.net.httpserver.*;
import java.io.*;
import java.net.*;
import java.net.http.*;
import java.net.http.HttpClient.Redirect;
import java.net.http.HttpRequest.BodyPublishers;
import java.net.http.HttpResponse.BodyHandlers;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class SimpleReverseProxy {
private final int listenPort;
private final String targetHost;
private final Proxy proxy;
private HttpServer server;
private final ExecutorService executor = Executors.newCachedThreadPool();
public SimpleReverseProxy(int listenPort, String targetHost, String proxyHost, int proxyPort) {
this.listenPort = listenPort;
this.targetHost = targetHost;
this.proxy = proxyHost != null ?
new Proxy(Proxy.Type.HTTP, new InetSocketAddress(proxyHost, proxyPort)) :
Proxy.NO_PROXY;
}
public void start() throws IOException {
server = HttpServer.create(new InetSocketAddress(listenPort), 0);
server.createContext("/", this::handleRequest);
server.setExecutor(executor);
server.start();
System.out.println("Proxy server started on port " + listenPort);
System.out.println("Forwarding requests to: " + targetHost);
System.out.println("Using proxy: " + (proxy.address() != null ? proxy.address() : "None"));
}
private void handleRequest(HttpExchange exchange) throws IOException {
try {
// 创建 HTTP 客户端(使用代理)
HttpClient client = HttpClient.newBuilder()
.proxy(ProxySelector.of((InetSocketAddress) proxy.address()))
.followRedirects(Redirect.NORMAL)
.build();
// 构建目标 URL
String path = exchange.getRequestURI().getPath();
String query = exchange.getRequestURI().getQuery();
String targetUrl = targetHost + path + (query != null ? "?" + query : "");
// 准备转发请求
HttpRequest.Builder requestBuilder = HttpRequest.newBuilder()
.uri(URI.create(targetUrl))
.method(exchange.getRequestMethod(), getBodyPublisher(exchange));
// 复制请求头
for (Map.Entry<String, List<String>> entry : exchange.getRequestHeaders().entrySet()) {
String headerName = entry.getKey();
// 跳过代理相关头
if ("Proxy-Connection".equalsIgnoreCase(headerName) ||
"Connection".equalsIgnoreCase(headerName)) {
continue;
}
for (String value : entry.getValue()) {
requestBuilder.header(headerName, value);
}
}
// 发送请求
HttpResponse<byte[]> response = client.send(
requestBuilder.build(),
BodyHandlers.ofByteArray()
);
// 设置响应头
response.headers().map().forEach((key, values) -> {
if (!"Content-Length".equalsIgnoreCase(key)) {
exchange.getResponseHeaders().put(key, values);
}
});
// 发送响应
exchange.sendResponseHeaders(response.statusCode(), response.body().length);
try (OutputStream os = exchange.getResponseBody()) {
os.write(response.body());
}
} catch (Exception e) {
e.printStackTrace();
sendErrorResponse(exchange, 500, "Proxy error: " + e.getMessage());
}
}
private HttpRequest.BodyPublisher getBodyPublisher(HttpExchange exchange) {
if ("GET".equalsIgnoreCase(exchange.getRequestMethod()) ||
exchange.getRequestBody() == null) {
return BodyPublishers.noBody();
}
try {
return BodyPublishers.ofByteArray(exchange.getRequestBody().readAllBytes());
} catch (IOException e) {
return BodyPublishers.noBody();
}
}
private void sendErrorResponse(HttpExchange exchange, int code, String message) {
try {
byte[] response = message.getBytes();
exchange.sendResponseHeaders(code, response.length);
exchange.getResponseBody().write(response);
} catch (IOException ex) {
ex.printStackTrace();
} finally {
exchange.close();
}
}
public void stop() {
if (server != null) {
server.stop(0);
executor.shutdown();
System.out.println("Proxy server stopped");
}
}
public static void main(String[] args) throws IOException {
// 允许设置受限制的 Host 头
System.setProperty("jdk.httpclient.allowRestrictedHeaders", "host,Content-length");
// 配置示例:
// 监听端口: 8080
// 目标服务器: https://api.openai.com
// VPN 代理: 127.0.0.1:7890 (Clash 默认端口)
SimpleReverseProxy proxy = new SimpleReverseProxy(
8089,
"https://api.openai.com",
"127.0.0.1",
7890
);
proxy.start();
// 添加关闭钩子
Runtime.getRuntime().addShutdownHook(new Thread(proxy::stop));
}
}