Skip to content

Commit

Permalink
updated command-line interface and readme
Browse files Browse the repository at this point in the history
  • Loading branch information
ameshkov committed Jun 15, 2024
1 parent 63082ca commit 1b10156
Show file tree
Hide file tree
Showing 5 changed files with 121 additions and 27 deletions.
57 changes: 48 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,10 @@ Simple SNI relay server written in Go.

What it does:

1. Listens for incoming HTTP or HTTPS connections.
2. Parses the hostname from the HTTP request or TLS ClientHello.
3. Proxies the traffic further to that hostname.
1. Provides a DNS server that can re-route domains to the SNI relay server.
2. Listens for incoming HTTP or HTTPS connections.
3. Parses the hostname from the HTTP request or TLS ClientHello.
4. Proxies the traffic further to that hostname.

Why would you need it? For instance, if you operate a DNS server, and you want
to relay some domains to an intermediate server (effectively, change your IP
Expand All @@ -20,26 +21,64 @@ make

### How to run it locally

See the [`config.yaml.dist`][configyaml] for more information on what can be
configured. In normal environment you want to change ports there.

```shell
./snirelay -l 127.0.0.1 -p 80:443
./snirelay -c config.yaml

```

Alternatively, you can supply a list of custom domain<->IP mappings:
[configyaml]: ./config.yaml.dist

### How to test

Note that instructions here use [dnslookup][dnslookup] and [gocurl][gocurl].

#### DNS queries

Plain DNS:

```shell
./snirelay -l 127.0.0.1 -p 80:443 --sni-mappings-path=sni_mapping.csv
# IPv4 will be redirected to 127.0.0.1.
dnslookup www.google.com 127.0.0.1:5353

# IPv6 will be redirected to ::.
RRTYPE=AAAA dnslookup www.google.com 127.0.0.1:5353

# HTTPS will be suppressed.
RRTYPE=HTTPS dnslookup www.google.com 127.0.0.1:5353
```

### How to test
Encrypted DNS:

```shell
# DNS-over-TLS.
VERIFY=0 dnslookup www.google.com tls://127.0.0.1:8853

# DNS-over-QUIC.
VERIFY=0 dnslookup www.google.com quic://127.0.0.1:8853

# DNS-over-HTTPS.
VERIFY=0 dnslookup www.google.com https://127.0.0.1:8443/dns-query

```

#### SNI relay

```shell
# Simple connect via relay:
gocurl --connect-to="example.org:443:127.0.0.1:80" -I https://example.org/
# Relay for plain HTTP:
gocurl --connect-to="example.org:443:127.0.0.1:9080" -I http://example.org/

# Relay for HTTPS:
gocurl --connect-to="example.org:443:127.0.0.1:9443" -I https://example.org/

```

[dnslookup]: https://github.com/ameshkov/dnslookup

[gocurl]: https://github.com/ameshkov/gocurl

## Docker

The docker image [is available][dockerregistry]. `snirelay` listens to the
Expand Down
39 changes: 23 additions & 16 deletions config.yaml.dist
Original file line number Diff line number Diff line change
Expand Up @@ -3,36 +3,36 @@
dns:
# listen-addr is the address where the DNS server will listen to incoming
# requests. Must be specified.
listen-addr: 127.0.0.1
listen-addr: "127.0.0.1"

# redirect-addr-v4 is the IPv4 address where the DNS server will re-route
# type=A queries for domains listed in domain-rules. Must be specified.
redirect-addr-v4: 127.0.0.1
redirect-addr-v4: "127.0.0.1"

# redirect-addr-v6 is the IPv4 address where the DNS server will re-route
# type=AAAA queries for domains listed in domain-rules. If not specified,
# the DNS server will respond with empty NOERROR to AAAA queries.
redirect-addr-v6: ::
redirect-addr-v6: "::"

# plain-port is the port for plain DNS server. Optional, if not specified,
# the plain DNS server will not be started.
plain-port: 53
plain-port: 5353

# tls-port is the port for DNS-over-TLS server. Optional, if not specified,
# the plain DNS-over-TLS server will not be started.
tls-port: 853
tls-port: 8853

# https-port is the port for DNS-over-HTTPS server. Optional, if not
# specified, the plain DNS-over-HTTPS server will not be started.
https-port: 443
https-port: 8443

# quic-port is the port for DNS-over-QUIC server. Optional, if not
# specified, the plain DNS-over-QUIC server will not be started.
quic-port: 853
quic-port: 8853

# upstream-addr is the address of the upstream DNS server. This server will
# be used for queries that shouldn't be re-routed. Must be specified.
upstream-addr: 8.8.8.8:53
upstream-addr: "8.8.8.8:53"

# RateLimit is the maximum number of requests per second for a plain DNS
# server. If 0 or not specified, there will be no rate limit.
Expand All @@ -45,27 +45,27 @@ dns:
# tls-cert-path is the path to the TLS certificate. It is only required if
# one of the following properties are specified: TLSPort, HTTPSPort,
# QUICPort.
tls-cert-path: example.crt
tls-cert-path: "./example.crt"

# tls-key-path is the path to the TLS private key. It is only required if
# one of the following properties are specified: TLSPort, HTTPSPort,
# QUICPort.
tls-key-path: example.key
tls-key-path: "./example.key"

# Relay is the SNI relay server section of the configuration file. Must be
# specified.
relay:
# listen-addr is the address where the Relay server will listen to incoming
# connections.
listen-addr: 127.0.0.1
listen-addr: "127.0.0.1"

# http-port is the port where relay will expect to receive plain HTTP
# connections.
http-port: 80
http-port: 9080

# https-port is the port where relay will expect to receive HTTPS
# connections.
https-port: 443
https-port: 9443

# proxy-url is the optional port for upstream connections by the relay.
# Format of the URL: [protocol://username:password@]host[:port]
Expand All @@ -84,6 +84,13 @@ relay:
# queries and re-route traffic to the relay server. HTTPS queries will be
# suppressed in this case.
domain-rules:
# Google domains.
"google.*": "relay"
"*.google.*": "relay"
# Re-route all domains.
"*": "relay"

# prometheus is a section for prometheus configuration.
prometheus:
# addr is the address where prometheus metrics are exposed.
addr: "127.0.0.1"

# port where prometheus metrics are exposed.
port: 8123
36 changes: 36 additions & 0 deletions internal/cmd/cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,22 @@ package cmd

import (
"fmt"
"io"
"net/http"
"os"
"runtime"
"time"

"github.com/AdguardTeam/golibs/errors"
"github.com/AdguardTeam/golibs/log"
"github.com/AdguardTeam/golibs/netutil"
"github.com/ameshkov/snirelay/internal/config"
"github.com/ameshkov/snirelay/internal/dnssrv"
"github.com/ameshkov/snirelay/internal/metrics"
"github.com/ameshkov/snirelay/internal/relay"
"github.com/ameshkov/snirelay/internal/version"
goFlags "github.com/jessevdk/go-flags"
"github.com/prometheus/client_golang/prometheus/promhttp"
)

// Main is the entry point of the program.
Expand Down Expand Up @@ -59,6 +66,12 @@ func Main() {
check("start dns server", err)
}

metrics.SetUpGauge(version.Version(), "", "", runtime.Version())

if cfg.Prometheus != nil {
go serveMetrics(cfg.Prometheus.Addr, cfg.Prometheus.Port)
}

sigHandler := newSignalHandler(relaySrv)
os.Exit(sigHandler.handle())
}
Expand All @@ -71,3 +84,26 @@ func check(operationName string, err error) {
os.Exit(1)
}
}

// serveMetrics starts
func serveMetrics(listenAddr string, port uint16) {
metricsAddr := netutil.JoinHostPort(listenAddr, port)
log.Info("Starting metrics at %s", metricsAddr)

mux := &http.ServeMux{}
mux.Handle("/metrics", promhttp.Handler())
mux.HandleFunc("/health-check", func(w http.ResponseWriter, _ *http.Request) {
_, _ = io.WriteString(w, "OK")
})

srv := &http.Server{
Addr: metricsAddr,
Handler: mux,
ReadTimeout: time.Minute,
WriteTimeout: time.Minute,
}

if err := srv.ListenAndServe(); err != nil {
log.Fatalf("Metrics failed to listen to %s: %v", metricsAddr, err)
}
}
14 changes: 13 additions & 1 deletion internal/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,15 @@ const actionRelay = "relay"
type File struct {
// DNS is the DNS server section of the configuration file. If not
// specified, the DNS server will not be started.
DNS *DNS `yaml:"dnssrv"`
DNS *DNS `yaml:"dns"`

// Relay is the SNI relay server section of the configuration file. Must be
// specified.
Relay *Relay `yaml:"relay"`

// Prometheus
Prometheus *Prometheus `yaml:"prometheus"`

// DomainRules is the map that controls what the snirelay does with the
// domains. The key of this map is a wildcard and the value is the action.
// Must be specified.
Expand All @@ -36,6 +39,15 @@ type File struct {
DomainRules map[string]string `yaml:"domain-rules"`
}

// Prometheus represents the prometheus configuration.
type Prometheus struct {
// Addr is the address where prometheus metrics are exposed.
Addr string `yaml:"addr"`

// Port is the port where prometheus metrics will be exposed.
Port uint16 `yaml:"port"`
}

// Load loads and validates configuration from the specified file.
func Load(path string) (cfg *File, err error) {
// Ignore G304 here as it's trusted context.
Expand Down
2 changes: 1 addition & 1 deletion internal/config/dns.go
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ func (f *File) ToDNSConfig() (dnsCfg *dnssrv.Config, err error) {
}

listenIP := net.ParseIP(f.DNS.ListenAddr)
if listenIP != nil {
if listenIP == nil {
return nil, fmt.Errorf("failed to parse %s", f.DNS.ListenAddr)
}

Expand Down

0 comments on commit 1b10156

Please sign in to comment.