反向代理¶
反向代理是代理服务的一种。服务器根据客户端的请求,从其关系的一组或多组后端服务器(如Web服务器)上获取资源,然后再将这些资源返回给客户端,客户端只会得知反向代理的IP地址,而不知道在代理服务器后面的服务器集群的存在。
GOST中的端口转发服务也可以被当作是一种功能受限的反向代理,因其只能转发到固定的一个或一组后端服务。
反向代理是端口转发服务的一个扩展,其依托于端口转发功能,并通过嗅探转发的请求数据来获取特定协议(目前支持HTTP/HTTPS)中的目标主机信息。
关于反向代理更详细的说明可以参考这篇博文。
本地端口转发¶
services:
- name: https
addr: :443
handler:
type: tcp
metadata:
sniffing: true
listener:
type: tcp
forwarder:
nodes:
- name: google
addr: www.google.com:443
# filter:
# host: www.google.com
matcher:
rule: Host(`www.google.com`)
- name: github
addr: github.com:443
# filter:
# host: *.github.com
matcher:
rule: Host(`*.github.com`)
- name: http
addr: :80
handler:
type: tcp
metadata:
sniffing: true
listener:
type: tcp
forwarder:
nodes:
- name: example-com
addr: example.com:80
# filter:
# host: example.com
matcher:
rule: Host(`example.com`)
- name: example-org
addr: example.org:80
# filter:
# host: example.org
# path: /
matcher:
rule: Host(`example.org`) && PathPrefix(`/`)
通过sniffing
选项来开启流量嗅探,并在forwarder.nodes
中通过filter
或matcher.rule
选项对节点设置路由条件或规则。
当开启流量嗅探后,转发服务会对客户端的请求信息应用转发器(forwarder)中节点设置的匹配条件(filter)或匹配规则(matcher.rule)过滤出最终转发的目标节点。
此时可以将对应的域名解析到本地通过反向代理来访问:
远程端口转发¶
远程端口转发服务同样也可以对流量进行嗅探。
services:
- name: https
addr: :443
handler:
type: rtcp
metadata:
sniffing: true
listener:
type: rtcp
chain: chain-0
forwarder:
nodes:
- name: local-0
addr: 192.168.1.1:443
# filter:
# host: srv-0.local
matcher:
rule: Host(`srv-0.local`)
- name: local-1
addr: 192.168.1.2:443
# filter:
# host: srv-1.local
matcher:
rule: Host(`srv-1.local`)
- name: fallback
addr: 192.168.2.1:443
- name: http
addr: :80
handler:
type: rtcp
metadata:
sniffing: true
listener:
type: rtcp
chain: chain-0
forwarder:
nodes:
- name: local-0
addr: 192.168.1.1:80
# filter:
# host: srv-0.local
matcher:
rule: Host(`srv-0.local`)
- name: local-1
addr: 192.168.1.2:80
# filter:
# host: srv-1.local
matcher:
rule: Host(`srv-1.local`)
chains:
- name: chain-0
hops:
- name: hop-0
nodes:
- name: node-0
addr: SERVER_IP:8443
connector:
type: relay
dialer:
type: wss
此时可以将对应的域名解析到服务器地址通过反向代理来访问内网服务:
如果访问的目标主机没有与转发器中的节点设定的主机名匹配上,当存在没有设置主机名的节点,则会在这些节点中选择一个使用。
由于srv-2.local没有匹配到节点,因此会被转发到fallback节点(192.168.2.1:443)。
请求路由¶
反向代理中请求到目标节点的路由有两种模式:条件过滤和规则匹配。两种模式二选一,规则匹配优先。
条件过滤¶
在节点上通过filter
选项来设置过滤条件,当请求满足此过滤条件时此节点为合格结点,将参与下一步的目标节点选择。
主机名过滤¶
通过filter.host
选项为节点设置主机名过滤。
filter.host
也支持通配符,*.example.com
或.example.com
匹配example.com及其子域名abc.example.com,def.abc.example.com等。
services:
- name: http
addr: :80
handler:
type: tcp
metadata:
sniffing: true
listener:
type: tcp
forwarder:
nodes:
- name: example-com
addr: example.com:80
filter:
host: example.com
- name: example-org
addr: example.org:80
filter:
host: *.example.org
应用协议过滤¶
通过filter.protocol
选项设置协议类型过滤,当嗅探到对应类型流量则会转发到此节点。
目前支持的应用协议有:
http
- HTTP流量数据。tls
- TLS流量数据。ssh
- SSH流量数据。
services:
- name: service-0
addr: :8000
handler:
type: tcp
metadata:
sniffing: true
listener:
type: tcp
forwarder:
nodes:
- name: http-server
addr: example.com:80
filter:
host: example.com
protocol: http
- name: https-server
addr: example.com:443
filter:
host: example.com
protocol: tls
- name: ssh-server
addr: example.com:22
filter:
protocol: ssh
URL路径过滤¶
通过filter.path
选项为节点设置路径前缀过滤。当嗅探到HTTP流量后,会使用URL路径前缀匹配模式来选择节点。
services:
- name: http
addr: :80
handler:
type: tcp
metadata:
sniffing: true
listener:
type: tcp
forwarder:
nodes:
- name: target-0
addr: 192.168.1.1:80
filter:
path: /
- name: target-1
addr: 192.168.1.2:80
filter:
path: /test
规则匹配¶
除了简单的条件过滤外,请求路由同时也集成了Traefik中更加灵活的规则路由功能。
通过matcher.rule
选项设置节点的匹配规则,当设置了规则后,filter
将会被忽略。
services:
- name: http
addr: :80
handler:
type: tcp
metadata:
sniffing: true
listener:
type: tcp
forwarder:
nodes:
- name: target-0
addr: 192.168.1.1:80
matcher:
rule: Host(`www.example.com`) || Host(`www.example.org`)
- name: target-1
addr: 192.168.1.2:80
matcher:
rule: Host(`*.example.com`)
规则¶
目前支持的匹配规则列表
规则 | 说明 | 示例 |
---|---|---|
Host(`domain`) | HTTP请求的主机名或TLS请求的SNI匹配domain ,等效于filter.host 。 | Host(`example.com`) ,Host(`*.example.org`) |
HostRegexp(`regexp`) | HTTP请求的主机名或TLS请求的SNI匹配正则表达式regexp 。 | HostRegexp(`^.+\.example\.com$`) |
Method(`method`) | HTTP请求的方法匹配method 。 | Method(`POST`) |
Path(`path`) | HTTP请求的URI路径匹配path 。 | Path(`/products/1234`) |
PathPrefix(`prefix`) | HTTP请求的URI路径匹配前缀prefix ,等效于filter.path 。 | PathPrefix(`/products`) |
PathRegexp(`regexp`) | HTTP请求的URI路径匹配正则表达式regexp 。 | PathRegexp(`\.(jpeg|jpg|png)$`) |
Query(`key`) | HTTP请求的Query参数名包含key 。 | Query(`foo`) |
Query(`key`, `value`) | HTTP请求的Query参数名包含key ,对应的参数值匹配value 。 | Query(`foo`, `bar`) |
QueryRegexp(`key`, `regexp`) | HTTP请求的Query参数名包含key ,对应的参数值匹配正则表达式regexp 。 | QueryRegexp(`foo`, `^.*$`) |
Header(`key`) | HTTP请求的Header中包含key 。 | Header(`Content-Type`) |
Header(`key`, `value`) | HTTP请求的Header中包含key ,对应的值匹配value 。 | Header(`Content-Type`, `application/json`) |
HeaderRegexp(`key`, `regexp`) | HTTP请求的Header中包含key ,对应的值匹配正则表达式regexp 。 | HeaderRegexp(`Content-Type`, `^application/(json|yaml)$`) |
ClientIP(`ip`) | 请求的客户端IP匹配ip ,ip 格式为IPv4,IPv6或CIDR。 | ClientIP(`192.168.0.1`) ,ClientIP(`::1`) ,ClientIP(`192.168.1.0/24`) ,ClientIP(`fe80::/10`) |
Proto(`proto`) | 匹配协议类型,等效于filter.protocol 。 | Proto(`http`) |
正则表达式
接受正则表达式作为其值的匹配器使用Go风格的语法。
使用运算符和括号表达复杂规则
可以使用常见的 AND (&&
) 和 OR (||
) 逻辑运算符,以及预期的优先规则和括号。
可以使用 NOT (!
) 运算符反转匹配器。
上面的规则匹配:
- 主机名是
example.com
或, - 主机名是
example.org
且 路径不是/path
。
优先级¶
为了避免路径重叠,默认情况下,路由按规则长度降序排序。优先级直接等于规则的长度,因此最长的长度具有最高优先级。
通过matcher.priority
选项可以设置节点的权重,从而改变节点选择的优先级。
services:
- name: http
addr: :80
handler:
type: tcp
metadata:
sniffing: true
listener:
type: tcp
forwarder:
nodes:
- name: target-0
addr: 192.168.1.1:80
matcher:
rule: Host(`www.example.com`)
priority: 100
- name: target-1
addr: 192.168.1.2:80
matcher:
rule: Host(`*.example.com`)
priority: 50
当请求的Host为www.example.com
时,会优先选择target-0
节点。
HTTP请求设置¶
当嗅探到HTTP流量时,可以在目标节点上通过forwarder.nodes.http
选项对HTTP的请求信息进行设置,包括Host头重写,自定义头部信息,开启Basic Auth,URL路径重写。对本地和远程端口转发均适用。
重写Host头¶
通过设置http.host
选项可以重写原始请求头中的Host。
services:
- name: http
addr: :80
handler:
type: tcp
metadata:
sniffing: true
listener:
type: tcp
forwarder:
nodes:
- name: example-com
addr: example.com:80
filter:
host: example.com
http:
host: test.example.com
- name: example-org
addr: example.org:80
filter:
host: example.org
http:
host: test.example.org:80
当请求http://example.com时,最终发送给example.com:80的HTTP请求头中Host为test.example.com。
自定义请求头¶
通过设置http.header
选项可以自定义请求头部信息,如果所设置的头部字段已存在则会被覆盖。
services:
- name: http
addr: :80
handler:
type: tcp
metadata:
sniffing: true
listener:
type: tcp
forwarder:
nodes:
- name: example-com
addr: example.com:80
filter:
host: example.com
http:
header:
User-Agent: gost/3.0.0
foo: bar
bar: 123
# host: test.example.com
- name: example-org
addr: example.org:80
filter:
host: example.org
http:
header:
User-Agent: curl/7.81.0
foo: bar
bar: baz
# host: test.example.org:80
当请求http://example.com时,最终发送给example.com:80的HTTP请求头中将会添加User-Agent
,Foo
和Bar
三个字段。
自定义响应头¶
通过设置http.responseHeader
选项可以自定义响应头部信息,如果所设置的头部字段已存在则会被覆盖。
services:
- name: http
addr: :80
handler:
type: tcp
metadata:
sniffing: true
listener:
type: tcp
forwarder:
nodes:
- name: example-com
addr: example.com:80
filter:
host: example.com
http:
responseHeader:
foo: bar
bar: 123
当请求http://example.com时,最终来自example.com:80的HTTP响应头中将会添加Foo
和Bar
两个字段。
Basic Authentication¶
通过设置http.auth
选项为目标节点启用HTTP基本认证功能。
services:
- name: http
addr: :80
handler:
type: tcp
metadata:
sniffing: true
listener:
type: tcp
forwarder:
nodes:
- name: example-com
addr: example.com:80
filter:
host: example.com
http:
auth:
username: user
password: pass
当直接请求http://example.com时,会返回HTTP状态码401要求认证。
URL路径重写¶
通过设置http.rewrite
选项定义URL路径重写规则。
services:
- name: http
addr: :80
handler:
type: tcp
metadata:
sniffing: true
listener:
type: tcp
forwarder:
nodes:
- name: example-com
addr: example.com:80
filter:
host: example.com
http:
rewrite:
- match: /api/login
replacement: /user/login
- match: /api/(.*)
replacement: /$1
rewrite.match
(string)- 指定路径匹配模式(支持正则表达式)。
rewrite.replacement
(string)- 设置路径替换内容。
http://example.com/api/login
会被重写为http://example.com/user/login
。
http://example.com/api/logout
会被重写为http://example.com/logout
。
重写响应体¶
通过设置http.rewriteBody
选项定义响应体重写规则。
services:
- name: http
addr: :80
handler:
type: tcp
metadata:
sniffing: true
listener:
type: tcp
forwarder:
nodes:
- name: example-com
addr: example.com:80
filter:
host: example.com
http:
rewriteBody:
- match: foo
replacement: bar
type: text/html
rewriteBody.match
(string)- 指定内容匹配模式(支持正则表达式)。
rewriteBody.replacement
(string)- 设置替换内容。
rewriteBody.type
(string, default=text/html)- 设置响应的内容类型,与
Content-Type
匹配。可以是,
分割的多个类型或*
代表匹配所有类型。
TLS请求设置¶
如果转发的目标节点启用了TLS,可以通过设置forwarder.nodes.tls
来建立TLS连接。
services:
- name: http
addr: :80
handler:
type: tcp
metadata:
sniffing: true
listener:
type: tcp
forwarder:
nodes:
- name: example-com
addr: example.com:443
filter:
host: example.com
tls:
secure: true
serverName: example.com
options:
minVersion: VersionTLS12
maxVersion: VersionTLS13
cipherSuites:
- TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
tls.secure
(bool, default=false)- 是否开启服务器证书和域名校验。
tls.serverName
(string)- 若
secure
设置为true,则需要通过此参数指定服务器域名用于域名校验。 tls.options.minVersion
(string)- TLS最小版本,可选值
VersionTLS10
,VersionTLS11
,VersionTLS12
,VersionTLS13
。 tls.options.maxVersion
(string)- TLS最大版本,可选值
VersionTLS10
,VersionTLS11
,VersionTLS12
,VersionTLS13
。 tls.options.cipherSuites
(list)- 加密套件,可选值参考Cipher Suites。
转发通道¶
除了原始TCP数据通道可以用来作为端口转发,其他数据通道也可以作为端口转发服务。
TLS转发通道¶
HTTPS-to-HTTP反向代理。
TLS转发通道可以动态的给后端HTTP服务添加TLS支持。
services:
- name: https
addr: :443
handler:
type: forward
metadata:
sniffing: true
listener:
type: tls
forwarder:
nodes:
- name: example-com
addr: example.com:80
filter:
host: .example.com
- name: example-org
addr: example.org:80
filter:
host: .example.org
HTTP3转发通道¶
HTTP3-to-HTTP反向代理。
HTTP3转发通道可以动态的给后端HTTP服务添加HTTP/3支持。
services:
- name: http3
addr: :443
handler:
type: http3
listener:
type: http3
forwarder:
nodes:
- name: example-com
addr: example.com:80
filter:
host: .example.com
- name: example-org
addr: example.org:80
filter:
host: .example.org