Reverse Proxy¶
Reverse Proxy is a type of proxy service. According to the client's request, the server obtains resources from one or more groups of backend servers (such as web servers) related to it, and then returns these resources to the client. The client only knows the IP address of the reverse proxy, without knowing the existence of server clusters behind proxy servers.
The port forwarding service in GOST can also be regarded as a reverse proxy with limited functions, because it can only forward to a fixed one or a set of backend services.
Reverse proxy is an extension of the port forwarding service, which relies on the port forwarding function, and obtains the target host information in a specific protocol (currently supports HTTP/HTTPS) by sniffing the forwarded data.
Local Port Forwarding¶
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(`/`)
Use the sniffing option to enable traffic sniffing, and set routing conditions or rules through the filter or matcher.rule options in forwarder.nodes.
When traffic sniffing is enabled, the forwarding service will apply the matching conditions (filter) or matching rules (matcher.rule) set in the node of the forwarder to the client's request information to filter out the final forwarding target node.
 
 
filter.host also supports wildcards, *.example.com or .example.com matches example.com and its subdomains: abc.example.com, def.abc.example.com, etc.
At this time, the corresponding domain name can be resolved to the local and then accessed through the reverse proxy:
Remote Port Forwarding¶
Remote port forwarding services can also sniff traffic.
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
 
 
At this time, the corresponding domain name can be resolved to the server address to access the internal service through the reverse proxy:
If the accessed target host does not match the hostname set by the node in the forwarder, when there are nodes without a hostname set, one of these nodes will be selected for use.
Since srv-2.local does not match the node, it will be forwarded to the fallback node (192.168.2.1:443).
Request Routing¶
There are two modes for routing requests to target nodes: conditional filtering and rule matching. When choosing between the two modes, rule matching takes precedence.
Conditional Filtering¶
The filter condition is set on the node through the filter option. When the request meets this filter condition, this node is a qualified node and will participate in the next step of target node selection.
Hostname Filtering¶
Set hostname filtering for a node via the filter.host option.
filter.host also supports wildcards, *.example.com or .example.com matches example.com and its subdomains abc.example.com, def.abc.example.com, etc.
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
Protocol Filtering¶
The protocol type filter is set through the filter.protocol option. When the corresponding type of traffic is sniffed, it will be forwarded to this node.
Currently supported application protocols are:
- http- HTTP traffic.
- tls- TLS traffic.
- ssh- SSH traffic.
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 Path Filtering¶
Set the path prefix filtering for the node through the filter.path option. When sniffing HTTP traffic, the URL path prefix matching pattern is used to select the node.
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
Rule Matching¶
In addition to simple conditional filtering, request routing also integrates the more flexible rule-based routing function in Traefik.
The matching rule of the node can be set by the matcher.rule option. When the rule is set, filter will be ignored.
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`)
Rule¶
Currently supported rules are
| Rule | Description | Example | 
|---|---|---|
| Host(`domain`) | The host for HTTP requests or SNI for TLS requests match domain, equivalent tofilter.host. | Host(`example.com`),Host(`*.example.org`) | 
| HostRegexp(`regexp`) | The host for HTTP requests or SNI for TLS requests match the regular expression regexp. | HostRegexp(`^.+\.example\.com$`) | 
| Method(`method`) | The method for HTTP requests match method. | Method(`POST`) | 
| Path(`path`) | The path for HTTP requests match path. | Path(`/products/1234`) | 
| PathPrefix(`prefix`) | The path for HTTP requests match the prefix prefix, equivalent tofilter.path. | PathPrefix(`/products`) | 
| PathRegexp(`regexp`) | The path for HTTP requests match the regular expression regexp. | PathRegexp(`\.(jpeg|jpg|png)$`) | 
| Query(`key`) | The query parameters for HTTP requests contain key | Query(`foo``) | 
| Query(`key`, `value`) | The query parameters for HTTP requests contain key, and the corresponding value matchvalue. | Query(`foo`, `bar`) | 
| QueryRegexp(`key`, `regexp`) | The query parameters for HTTP requests contain key, and the corresponding value match the regular expressionregexp. | QueryRegexp(`foo`, `^.*$`) | 
| Header(`key`) | The header for HTTP requests contain key. | Header(`Content-Type`) | 
| Header(`key`, `value`) | The header for HTTP requests contain key, and the corresponding value matchvalue. | Header(`Content-Type`, `application/json`) | 
| HeaderRegexp(`key`, `regexp`) | The header for HTTP requests contain key, and the corresponding value match the regular expressionregexp. | HeaderRegexp(`Content-Type`, `^application/(json|yaml)$`) | 
| ClientIP(`ip`) | The requests client IP match ip. The format ofipis IPv4, IPv6 or CIDR. | ClientIP(`192.168.0.1`),ClientIP(`::1`),ClientIP(`192.168.1.0/24`),ClientIP(`fe80::/10`) | 
| Proto(`proto`) | Match the protocol,equivalent to filter.protocol. | Proto(`http`) | 
| Admission(`admission-name`) | 3.2.4 Match the admission name, apply admission filtering to client IP. | Admission(`admission-0`) | 
| Bypass(`bypass-name`) | 3.2.4 Match bypass name and apply bypass filtering to the target host name. | Bypass(`bypass-0`) | 
Regexp Syntax
Matchers that accept a regexp as their value use a Go flavored syntax.
Expressing Complex Rules Using Operators and Parenthesis
The usual AND (&&) and OR (||) logical operators can be used, with the expected precedence rules, as well as parentheses.
One can invert a matcher by using the NOT (!) operator.
The following rule matches requests where:
- either host is example.comOR,
- host is example.orgAND path is NOT/path
Priority¶
To avoid path overlap, routes are sorted, by default, in descending order using rules length. The priority is directly equal to the length of the rule, and so the longest length has the highest priority.
The matcher.priority option can be used to set the priority of the node, thereby changing the priority of node selection.
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
When the requested Host is www.example.com, the target-0 node will be selected first.
HTTP Request Settings¶
When sniffing HTTP traffic, you can set the HTTP request information on the target node through the forwarder.nodes.http option, including Host header rewriting, custom header information, basic auth, URL path rewriting.
Rewrite Host Header¶
The Host in the original request header can be overridden by setting the http.host option.
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
      matcher:
        rule: Host(`example.com`)
      http:
        host: test.example.com
    - name: example-org
      addr: example.org:80
      # filter:
      #   host: example.org
      matcher:
        rule: Host(`example.org`)
      http:
        host: test.example.org:80
When requesting http://example.com, the Host in the HTTP request header sent to example.com:80 is test.example.com.
Custom Request Header¶
The request header can be customized by setting the http.requestHeader option, if the header field already exists, it will be overwritten.
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
      matcher:
        rule: Host(`example.com`)
      http:
        requestHeader:
          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
      matcher:
        rule: Host(`example.org`)
      http:
        requestHeader:
          User-Agent: curl/7.81.0
          foo: bar
          bar: baz
        # host: test.example.org:80
When requesting http://example.com, three fields User-Agent, Foo and Bar will be added to the HTTP request header sent to example.com:80.
Custom Response Header¶
The response header can be customized by setting the http.responseHeader option, if the header field already exists, it will be overwritten.
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
      matcher:
        rule: Host(`example.com`)
      http:
        responseHeader:
          foo: bar
          bar: 123
When requesting http://example.com, Foo and Bar fields will be added to the HTTP response header received from example.com:80.
HTTP Basic Authentication¶
You can enable HTTP Basic Authentication for target node by setting the http.auth option.
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
      matcher:
        rule: Host(`example.com`)
      http:
        auth:
          username: user
          password: pass
When requesting http://example.com directly, HTTP status code 401 will be returned to require authentication.
Rewrite URL Path¶
Define URL path rewriting rules by setting the http.rewriteURL option. 
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
      matcher:
        rule: Host(`example.com`)
      http:
        rewriteURL:
        - match: /api/login
          replacement: /user/login
        - match: /api/(.*)
          replacement: /$1
- rewriteURL.match(string)
- specify path matching pattern (supports regular expression).
- rewriteURL.replacement(string)
- set the path replacement content.
http://example.com/api/login will be rewritten to http://example.com/user/login.
http://example.com/api/logout will be rewritten to http://example.com/logout.
Rewrite Response Body¶
Define the response body rewriting rules by setting the http.rewriteBody option.
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
      matcher:
        rule: Host(`example.com`)
      http:
        rewriteBody:
        - match: foo
          replacement: bar
          type: text/html
- rewriteBody.match(string)
- Specify content matching pattern (regular expressions are supported).
- rewriteBody.replacement(string)
- Set the replacement content.
- rewriteBody.type(string, default=text/html)
- Set the content type of the response, matching the Content-Typeheader. It can be multiple types separated by,or*to match all types.
TLS Settings¶
If the forwarding target node has TLS enabled, you can establish a TLS connection by setting forwarder.nodes.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
      matcher:
        rule: 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)
- Whether to enable server certificate and domain name verification.
- tls.serverName(string)
- If secureis set to true, you need to specify the server domain name for domain name verification through this parameter.
- tls.options.minVersion(string)
- Minimum TLS Version, VersionTLS10,VersionTLS11,VersionTLS12orVersionTLS13.
- tls.options.maxVersion(string)
- Maximum TLS Version, VersionTLS10,VersionTLS11,VersionTLS12orVersionTLS13.
- tls.options.cipherSuites(list)
- Cipher Suites, See Cipher Suites for more information.
Empty Node¶
3.2.3
When the address of a node is empty, this node is called an empty node. In the reverse proxy mode, empty node has some special behaviors.
services:
- name: http
  addr: :80
  handler:
    type: tcp
    metadata:
      sniffing: true
  listener:
    type: tcp
  forwarder:
    nodes:
    - name: sni
      # addr is empty
      matcher:
        rule: Host(`example.com`)
If the selected node in the forwarder is an empty node, the address of the node will be set to the sniffed hostname. At this time, the reverse proxy is equivalent to an SNI proxy and will dynamically connect to the corresponding target address based on the request information.
Forwarding Tunnel¶
In addition to the original TCP data tunnel can be used as port forwarding, other tunnels can also be used as port forwarding services.
TLS¶
HTTPS-to-HTTP
The TLS forwarding tunnel can dynamically add TLS support to the backend HTTP service.
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
      matcher:
        rule: Host(`.example.com`)
    - name: example-org
      addr: example.org:80
      # filter:
      #   host: .example.org
      matcher:
        rule: Host(`.example.org`)
HTTP3¶
HTTP3-to-HTTP.
The HTTP3 forwarding tunnel can dynamically add HTTP/3 support to the backend HTTP service.
services:
- name: http3
  addr: :443
  handler:
    type: http3
  listener:
    type: http3
  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
      matcher:
        rule: Host(`.example.org`)