mirror of
https://github.com/m13253/dns-over-https.git
synced 2026-03-29 22:50:01 +00:00
add client certificate authentication (#98)
* add client certificate authentication * fix #97 for ECS forward local addresses
This commit is contained in:
@@ -31,19 +31,21 @@ import (
|
||||
)
|
||||
|
||||
type config struct {
|
||||
Listen []string `toml:"listen"`
|
||||
LocalAddr string `toml:"local_addr"`
|
||||
Cert string `toml:"cert"`
|
||||
Key string `toml:"key"`
|
||||
Path string `toml:"path"`
|
||||
Upstream []string `toml:"upstream"`
|
||||
Timeout uint `toml:"timeout"`
|
||||
Tries uint `toml:"tries"`
|
||||
Verbose bool `toml:"verbose"`
|
||||
DebugHTTPHeaders []string `toml:"debug_http_headers"`
|
||||
LogGuessedIP bool `toml:"log_guessed_client_ip"`
|
||||
LocalIPFilter bool `toml:"ecs_allow_non_global_ip"`
|
||||
ECSFullSubnet bool `toml:"ecs_use_precise_ip"`
|
||||
Listen []string `toml:"listen"`
|
||||
LocalAddr string `toml:"local_addr"`
|
||||
Cert string `toml:"cert"`
|
||||
Key string `toml:"key"`
|
||||
Path string `toml:"path"`
|
||||
Upstream []string `toml:"upstream"`
|
||||
Timeout uint `toml:"timeout"`
|
||||
Tries uint `toml:"tries"`
|
||||
Verbose bool `toml:"verbose"`
|
||||
DebugHTTPHeaders []string `toml:"debug_http_headers"`
|
||||
LogGuessedIP bool `toml:"log_guessed_client_ip"`
|
||||
ECSAllowNonGlobalIP bool `toml:"ecs_allow_non_global_ip"`
|
||||
ECSUsePreciseIP bool `toml:"ecs_use_precise_ip"`
|
||||
TLSClientAuth bool `toml:"tls_client_auth"`
|
||||
TLSClientAuthCA string `toml:"tls_client_auth_ca"`
|
||||
}
|
||||
|
||||
func loadConfig(path string) (*config, error) {
|
||||
|
||||
@@ -57,9 +57,10 @@ log_guessed_client_ip = false
|
||||
# 1. the upstream server knowing your private LAN addresses;
|
||||
# 2. the upstream server unable to provide geographically near results,
|
||||
# or even fail to provide any result.
|
||||
# However, if you are deploying a split tunnel corporation network environment,
|
||||
# or for any other reason you want to inhibit this behavior, change the following
|
||||
# option to "true".
|
||||
# However, if you are deploying a split tunnel corporation network
|
||||
# environment, or for any other reason you want to inhibit this
|
||||
# behavior and allow local (eg RFC1918) address to be forwarded,
|
||||
# change the following option to "true".
|
||||
ecs_allow_non_global_ip = false
|
||||
|
||||
# If ECS is added to the request, let the full IP address or
|
||||
@@ -69,3 +70,9 @@ ecs_allow_non_global_ip = false
|
||||
# internet where IP address may be used to identify the user and
|
||||
# not only the approximate location.
|
||||
ecs_use_precise_ip = false
|
||||
|
||||
# If DOH is used for a controlled network, it is possible to enable
|
||||
# the client TLS certificate validation with a specific certificate
|
||||
# authority used to sign any client one. Disabled by default.
|
||||
# tls_client_auth = true
|
||||
# tls_client_auth_ca = "root-ca-public.crt"
|
||||
|
||||
@@ -134,7 +134,7 @@ func (s *Server) parseRequestIETF(ctx context.Context, w http.ResponseWriter, r
|
||||
if ipv4 := ednsClientAddress.To4(); ipv4 != nil {
|
||||
ednsClientFamily = 1
|
||||
ednsClientAddress = ipv4
|
||||
if s.conf.ECSFullSubnet {
|
||||
if s.conf.ECSUsePreciseIP {
|
||||
ednsClientNetmask = 32
|
||||
} else {
|
||||
ednsClientNetmask = 24
|
||||
@@ -142,7 +142,7 @@ func (s *Server) parseRequestIETF(ctx context.Context, w http.ResponseWriter, r
|
||||
}
|
||||
} else {
|
||||
ednsClientFamily = 2
|
||||
if s.conf.ECSFullSubnet {
|
||||
if s.conf.ECSUsePreciseIP {
|
||||
ednsClientNetmask = 128
|
||||
} else {
|
||||
ednsClientNetmask = 56
|
||||
|
||||
@@ -25,7 +25,10 @@ package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"math/rand"
|
||||
"net"
|
||||
@@ -107,12 +110,48 @@ func (s *Server) Start() error {
|
||||
if s.conf.Verbose {
|
||||
servemux = handlers.CombinedLoggingHandler(os.Stdout, servemux)
|
||||
}
|
||||
|
||||
var clientCAPool *x509.CertPool
|
||||
if s.conf.TLSClientAuth {
|
||||
if s.conf.TLSClientAuthCA != "" {
|
||||
clientCA, err := ioutil.ReadFile(s.conf.TLSClientAuthCA)
|
||||
if err != nil {
|
||||
log.Fatalf("Reading certificate for client authentication has failed: %v", err)
|
||||
}
|
||||
clientCAPool = x509.NewCertPool()
|
||||
clientCAPool.AppendCertsFromPEM(clientCA)
|
||||
log.Println("Certificate loaded for client TLS authentication")
|
||||
} else {
|
||||
log.Fatalln("TLS client authentication requires both tls_client_auth and tls_client_auth_ca, exiting.")
|
||||
}
|
||||
}
|
||||
|
||||
results := make(chan error, len(s.conf.Listen))
|
||||
for _, addr := range s.conf.Listen {
|
||||
go func(addr string) {
|
||||
var err error
|
||||
if s.conf.Cert != "" || s.conf.Key != "" {
|
||||
err = http.ListenAndServeTLS(addr, s.conf.Cert, s.conf.Key, servemux)
|
||||
if clientCAPool != nil {
|
||||
srvtls := &http.Server{
|
||||
Handler: servemux,
|
||||
Addr: addr,
|
||||
TLSConfig: &tls.Config{
|
||||
ClientCAs: clientCAPool,
|
||||
ClientAuth: tls.RequireAndVerifyClientCert,
|
||||
GetCertificate: func(info *tls.ClientHelloInfo) (certificate *tls.Certificate, e error) {
|
||||
c, err := tls.LoadX509KeyPair(s.conf.Cert, s.conf.Key)
|
||||
if err != nil {
|
||||
fmt.Printf("Error loading server certificate key pair: %v\n", err)
|
||||
return nil, err
|
||||
}
|
||||
return &c, nil
|
||||
},
|
||||
},
|
||||
}
|
||||
err = srvtls.ListenAndServeTLS("", "")
|
||||
} else {
|
||||
err = http.ListenAndServeTLS(addr, s.conf.Cert, s.conf.Key, servemux)
|
||||
}
|
||||
} else {
|
||||
err = http.ListenAndServe(addr, servemux)
|
||||
}
|
||||
@@ -265,7 +304,7 @@ func (s *Server) findClientIP(r *http.Request) net.IP {
|
||||
if XRealIP != "" {
|
||||
addr := strings.TrimSpace(XRealIP)
|
||||
ip := net.ParseIP(addr)
|
||||
if !s.conf.LocalIPFilter || jsondns.IsGlobalIP(ip) {
|
||||
if s.conf.ECSAllowNonGlobalIP || jsondns.IsGlobalIP(ip) {
|
||||
return ip
|
||||
}
|
||||
}
|
||||
@@ -274,13 +313,10 @@ func (s *Server) findClientIP(r *http.Request) net.IP {
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
if !s.conf.LocalIPFilter {
|
||||
return remoteAddr.IP
|
||||
}
|
||||
if ip := remoteAddr.IP; jsondns.IsGlobalIP(ip) {
|
||||
ip := remoteAddr.IP
|
||||
if s.conf.ECSAllowNonGlobalIP || jsondns.IsGlobalIP(ip) {
|
||||
return ip
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user