sniproxy

package
v2.0.0-...-e6665f0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Mar 3, 2026 License: BSD-2-Clause Imports: 21 Imported by: 0

Documentation

Overview

Package sniproxy is a simple SNI proxy server that allows you to serve multiple SSL-enabled websites from a single IP address.

Continuation of [byosh] and [SimpleSNIProxy] projects.

pre-requisites

To ensure that Sniproxy works correctly, it's important to have ports 80, 443, and 53 open. However, on Ubuntu, it's possible that port 53 may be in use by systemd-resolved. To disable systemd-resolved and free up the port, follow [these instructions].

If you prefer to keep systemd-resolved and just disable the built-in resolver, you can use the following command:

sed -i 's/#DNS=/DNS=9.9.9.9/; s/#DNSStubListener=yes/DNSStubListener=no/' /etc/systemd/resolved.conf
systemctl restart systemd-resolved

How to Install

The simplest way to install the software is by utilizing the pre-built binaries available on the releases page. Alternatively, there are other ways to install, which include:

Using "go install" command:

go install github.com/zamibd/sniproxy/v2@latest

Using Docker or Podman:

docker run -d --pull always -p 80:80 -p 443:443 -p 53:53/udp -v "$(pwd)/config.defaults.yaml:/tmp/config.yaml" ghcr.io/zamibd/sniproxy:latest --config /tmp/config.yaml

Using the installer script:

bash <(curl -L https://raw.githubusercontent.com/zamibd/sniproxy/master/install.sh)

How to Run

sniproxy can be configured using a configuration file or environment variables The configuration file is a YAML file, and an example configuration file can be found under [Sample config file]. you can find the instructions for the environment variables there as well.

sniproxy [flags]

Flags:

	-c, --config string   path to YAML configuration file
		--defaultconfig   write the default config yaml file to stdout
	-h, --help            help for sniproxy
	-v, --version         show version info and exit

Setting Up an SNI Proxy Using Vultr

In this tutorial, we will go over the steps to set up an SNI proxy using Vultr as a service provider. This will allow you to serve multiple SSL-enabled websites from a single IP address.

Prerequisites

- A Vultr account. If you don't have one, you can sign up for free using my [Vultr referal link]

## Step 1: Create a Vultr Server

First, log in to your Vultr account and click on the "Instances" tab in the top menu. Then, click the "+" button to deploy a new server.

On the "Deploy New Instance" page, select the following options:

- Choose Server: Choose "Cloud Compute" - CPU & Storage Technology: Any of the choices should work perfectly fine - Server Location: Choose the location of the server. This will affect the latency of your website, so it's a good idea to choose a location that is close to your target audience. - Server Image: Any OS listed there is supported. If you're not sure what to choose, Ubuntu is a good option - Server Size: Choose a server size that is suitable for your needs. A small or medium-sized server should be sufficient for most SNI proxy setups. Pay attention to the monthly bandwidth usage as well - "Add Auto Backups": not strictly needed for sniproxy. - "SSH Keys": choose a SSH key to facilitate logging in later on. you can always use Vultr's builtin console as well. - Server Hostname: Choose a hostname for your server. This can be any name you like. After you have selected the appropriate options, click the "Deploy Now" button to create your server.

## Step 2: Install the SNI Proxy

Once your server has been created, log in to the server using SSH or console. The root password is available under the "Overview" tab in instances list.

Ensure the firewall (firewalld, ufw or iptables) is allowing connectivity to ports 80/TCP, 443/TCP and 53/UDP. For `ufw`, allow these ports with:

sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw allow 53/udp
sudo ufw reload

once you have a shell in front of you, run the following (assuming you're on Ubuntu 22.04)

bash <(curl -L https://raw.githubusercontent.com/zamibd/sniproxy/master/install.sh)

above script is an interactive installer, it will ask you a few questions and then install sniproxy for you. it also installs sniproxy as a systemd servers, and enables it to start on boot.

step 3: customize your configuration

above wizard will set up execution arguments for sniproxy. you can edit them by running

sudo vim /opt/sniproxy/sniproxy.yaml

and edit parameters as you see fit. for example, you can add more domains to the list of domains to proxy, or change the port numbers.

[byosh]: https://github.com/zamibd/byosh [SimpleSNIProxy]: https://github.com/ziozzang/SimpleSNIProxy [these instructions]: https://gist.github.com/zoilomora/f7d264cefbb589f3f1b1fc2cea2c844c [Vultr referal link]: https://www.vultr.com/?ref=8578601 [Sample config file]: ./config.sample.yaml

Index

Constants

View Source
const (
	// DNSTimeout is the default timeout for DNS queries
	DNSTimeout = 10 * time.Second
	// HTTPReadTimeout is the default timeout for HTTP requests
	HTTPReadTimeout = 10 * time.Second
	// HTTPWriteTimeout is the default timeout for HTTP responses
	HTTPWriteTimeout = 10 * time.Second
	// DNSUDPSize is the EDNS0 UDP size for DNS queries
	DNSUDPSize = 1232
	// DNSClientUDPSize is the UDP size for DNS client options
	DNSClientUDPSize = 1300
	// SOCKS5TCPTimeout is the timeout for SOCKS5 TCP connections
	SOCKS5TCPTimeout = 60
	// SOCKS5UDPTimeout is the timeout for SOCKS5 UDP connections
	SOCKS5UDPTimeout = 60
)
View Source
const (
	// TLSClientHelloBufferSize is the buffer size for reading TLS Client Hello
	// 2048 should be enough for a TLS Client Hello packet. But it could become
	// problematic if tcp connection is fragmented or too big
	TLSClientHelloBufferSize = 2048
)

Variables

This section is empty.

Functions

func GetHostname

func GetHostname(data []byte) (string, error)

GetHostname extracts the Server Name Indication (SNI) from a TLS Client Hello packet. This function takes raw TLS handshake data and returns the hostname requested by the client. It returns an error if the data doesn't contain a valid TLS Client Hello or SNI extension.

func GetPublicIPv4

func GetPublicIPv4() (string, error)

GetPublicIPv4 tries to determine the IPv4 address of the host method 1: establish a udp connection to a known DNS server and see if we can get lucky by having a non-RFC1918 address on the interface method 2: use a public HTTP service to get the public IP note that neither of these methods are bulletproof, so there is always a chance that you need to enter the public IP manually

func GetPublicIPv6

func GetPublicIPv6() (string, error)

GetPublicIPv6 tries to determine the IPv6 address of the host method 1: establish a udp connection to a known DNS server and see if we can get lucky by having a non-RFC1918 address on the interface method 2: use a public HTTP service to get the public IP method 3: send a DNS query to OpenDNS to get the public IP. DISABLED note that neither of these methods are bulletproof, so there is always a chance that you need to enter the public IP manually

func RunDNS

func RunDNS(c *Config, l zerolog.Logger)

RunDNS starts DNS servers based on the provided configuration.

func RunHTTP

func RunHTTP(c *Config, bind string, l zerolog.Logger)

RunHTTP starts the HTTP proxy server on the specified bind address. The bind address should be in the format "0.0.0.0:80" or similar. This function blocks and should typically be run in a goroutine.

func RunHTTPS

func RunHTTPS(c *Config, bind string, l zerolog.Logger)

RunHTTPS starts the HTTPS/TLS proxy server on the specified bind address. The bind address should be in the format "0.0.0.0:443" or similar (ip:port). This function blocks and should typically be run in a goroutine.

Types

type CacheResolver

type CacheResolver struct {
	// contains filtered or unexported fields
}

func (*CacheResolver) Resolve

func (r *CacheResolver) Resolve(q *dns.Msg, ci rdns.ClientInfo) (*dns.Msg, error)

func (*CacheResolver) String

func (r *CacheResolver) String() string

type Config

type Config struct {
	PublicIPv4            string   `yaml:"public_ipv4"`
	PublicIPv6            string   `yaml:"public_ipv6"`
	UpstreamDNS           string   `yaml:"upstream_dns"`
	UpstreamDNSOverSocks5 bool     `yaml:"upstream_dns_over_socks5"`
	UpstreamSOCKS5        string   `yaml:"upstream_socks5"`
	BindDNSOverUDP        string   `yaml:"bind_dns_over_udp"`
	BindDNSOverTCP        string   `yaml:"bind_dns_over_tcp"`
	BindDNSOverTLS        string   `yaml:"bind_dns_over_tls"`
	BindDNSOverQuic       string   `yaml:"bind_dns_over_quic"`
	TLSCert               string   `yaml:"tls_cert"`
	TLSKey                string   `yaml:"tls_key"`
	BindHTTP              string   `yaml:"bind_http"`
	BindHTTPAdditional    []string `yaml:"bind_http_additional"`
	BindHTTPListeners     []string `yaml:"-"` // compiled list of bind_http and bind_http_additional listen addresses
	BindHTTPS             string   `yaml:"bind_https"`
	BindHTTPSAdditional   []string `yaml:"bind_https_additional"`
	BindHTTPSListeners    []string `yaml:"-"` // compiled list of bind_https and bind_https_additional listen addresses
	Interface             string   `yaml:"interface"`
	BindPrometheus        string   `yaml:"bind_prometheus"`
	AllowConnToLocal      bool     `yaml:"allow_conn_to_local"`

	ACL []acl.ACL `yaml:"-"`

	DNSClient DNSClient    `yaml:"-"`
	Dialer    proxy.Dialer `yaml:"-"`
	// list of interface source IPs; used to rotate source IPs when initializing connections
	SourceAddr       []netip.Addr `yaml:"-"`
	PreferredVersion string       `yaml:"preferred_version"` // ipv4 (or 4), ipv6 (or 6), ipv4only, ipv6only, any. empty (or 0) means any.

	// DNS Cache configuration
	DNSCacheEnabled        bool `yaml:"dns_cache_enabled"`
	DNSCacheSize           int  `yaml:"dns_cache_size"`            // in bytes
	DNSCacheMinTTL         int  `yaml:"dns_cache_min_ttl"`         // in seconds
	DNSCacheMaxTTL         int  `yaml:"dns_cache_max_ttl"`         // in seconds
	DNSCacheRefreshTrigger int  `yaml:"dns_cache_refresh_trigger"` // in seconds

	// metrics
	ReceivedHTTP        metrics.Counter `yaml:"-"`
	ProxiedHTTP         metrics.Counter `yaml:"-"`
	ReceivedHTTPS       metrics.Counter `yaml:"-"`
	ProxiedHTTPS        metrics.Counter `yaml:"-"`
	ReceivedDNS         metrics.Counter `yaml:"-"`
	ProxiedDNS          metrics.Counter `yaml:"-"`
	DNSCacheHit         metrics.Counter `yaml:"-"`
	DNSCacheMiss        metrics.Counter `yaml:"-"`
	DNSCacheCurrentSize metrics.Gauge   `yaml:"-"`
}

Config is the main runtime configuration for the proxy

func (*Config) SetBindHTTPListeners

func (c *Config) SetBindHTTPListeners(_ zerolog.Logger) error

SetBindHTTPListeners sets up a list of bind addresses for HTTP it gets the bind address from bind_http as 0.0.0.0:80 format and the additional bind addresses from bind_http_additional as a list of ports or port ranges such as 8080, 8081-8083, 8085 when this function is called, it will compile the list of bind addresses and store it in BindHTTPListeners

func (*Config) SetBindHTTPSListeners

func (c *Config) SetBindHTTPSListeners(_ zerolog.Logger) error

SetBindHTTPSListeners sets up a list of bind addresses for HTTPS

func (*Config) SetDNSClient

func (c *Config) SetDNSClient(logger zerolog.Logger) error

SetDNSClient sets up a DNS client based on the proxy settings provided an error in this function means the application cannot continue

func (*Config) SetDialer

func (c *Config) SetDialer(logger zerolog.Logger) error

SetDialer sets up a TCP/UDP Dialer based on the proxy settings provided an error in this function means the application cannot continue

func (*Config) Validate

func (c *Config) Validate() error

Validate checks if the configuration is valid and returns an error if it's not. It ensures that at least one DNS binding is configured and other critical settings are valid.

type DNSClient

type DNSClient struct {
	rdns.Resolver
	C *Config
}

DNSClient is a wrapper around the DNS client

func NewDNSClient

func NewDNSClient(C *Config, uri string, skipVerify bool, proxy string, l zerolog.Logger) (*DNSClient, error)

NewDNSClient creates a DNS Client by parsing a URI and returning the appropriate client for it.

Supported URI schemes and formats:

  • udp://1.1.1.1:53 - Plain DNS over UDP (IPv4)
  • udp6://[2606:4700:4700::1111]:53 - Plain DNS over UDP (IPv6)
  • tcp://9.9.9.9:5353 - Plain DNS over TCP (IPv4)
  • tcp6://[2606:4700:4700::1111]:53 - Plain DNS over TCP (IPv6)
  • tcp-tls://dns.adguard.com:853 - DNS over TLS (DoT)
  • tcp-tls6://[2606:4700:4700::1111]:853 - DNS over TLS IPv6
  • https://dns.adguard.com/dns-query - DNS over HTTPS (DoH)
  • quic://dns.adguard.com:8853 - DNS over QUIC (DoQ)

Parameters:

  • C: Configuration object containing network settings
  • uri: The DNS server URI to connect to
  • skipVerify: Skip TLS certificate verification (not recommended for production)
  • proxy: Optional SOCKS5 proxy URL for DNS queries

Returns a configured DNSClient or an error if the URI is invalid or connection fails.

func (*DNSClient) PerformExternalAQuery

func (dnsc *DNSClient) PerformExternalAQuery(fqdn string, QType uint16) ([]dns.RR, error)

PerformExternalAQuery performs an external DNS query for the given domain name.

type IPVersion

type IPVersion int

IPVersion represents the preferred IP version for connections

const (
	// IPVersionAny allows both IPv4 and IPv6 with no preference
	IPVersionAny IPVersion = iota
	// IPVersionIPv4Preferred prefers IPv4 but falls back to IPv6
	IPVersionIPv4Preferred
	// IPVersionIPv6Preferred prefers IPv6 but falls back to IPv4
	IPVersionIPv6Preferred
	// IPVersionIPv4Only only allows IPv4 connections
	IPVersionIPv4Only
	// IPVersionIPv6Only only allows IPv6 connections
	IPVersionIPv6Only
)

func ParseIPVersion

func ParseIPVersion(s string) IPVersion

ParseIPVersion converts a string to IPVersion type

func (IPVersion) String

func (v IPVersion) String() string

String returns the string representation of IPVersion

Directories

Path Synopsis
Package acl contains the logic for Access Control Lists.
Package acl contains the logic for Access Control Lists.
Package doh contains the logic for DNS over HTTPS.
Package doh contains the logic for DNS over HTTPS.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL