Files
go-away/lib/settings/backend.go
2025-05-02 02:23:48 +02:00

163 lines
4.8 KiB
Go

package settings
import (
"git.gammaspectra.live/git/go-away/lib/challenge"
"git.gammaspectra.live/git/go-away/utils"
"net/http"
"net/http/httputil"
"time"
)
type Backend struct {
// URL Target server backend path. Supports http/https/unix protocols.
URL string `yaml:"url"`
// Host Override the Host header and TLS SNI with this value if specified
Host string `yaml:"host"`
//ProxyProtocol uint8 `yaml:"proxy-protocol"`
// HTTP2Enabled Enable HTTP2 to backend
HTTP2Enabled bool `yaml:"http2-enabled"`
// TLSSkipVerify Disable TLS certificate verification, if any
TLSSkipVerify bool `yaml:"tls-skip-verify"`
// IpHeader HTTP header to set containing the IP header. Set - to forcefully ignore global defaults.
IpHeader string `yaml:"ip-header"`
// GoDNS Resolve URL using the Go DNS server
// Only relevant when running with CGO enabled
GoDNS bool `yaml:"go-dns"`
// Transparent Do not add extra headers onto this backend
// This prevents GoAway headers from being set, or other state
Transparent bool `yaml:"transparent"`
// DialTimeout is the maximum amount of time a dial will wait for
// a connect to complete.
//
// The default is no timeout.
//
// When using TCP and dialing a host name with multiple IP
// addresses, the timeout may be divided between them.
//
// With or without a timeout, the operating system may impose
// its own earlier timeout. For instance, TCP timeouts are
// often around 3 minutes.
DialTimeout time.Duration `yaml:"dial-timeout"`
// TLSHandshakeTimeout specifies the maximum amount of time to
// wait for a TLS handshake. Zero means no timeout.
TLSHandshakeTimeout time.Duration `yaml:"tls-handshake-timeout"`
// IdleConnTimeout is the maximum amount of time an idle
// (keep-alive) connection will remain idle before closing
// itself.
// Zero means no limit.
IdleConnTimeout time.Duration `yaml:"idle-conn-timeout"`
// ResponseHeaderTimeout, if non-zero, specifies the amount of
// time to wait for a server's response headers after fully
// writing the request (including its body, if any). This
// time does not include the time to read the response body.
ResponseHeaderTimeout time.Duration `yaml:"response-header-timeout"`
// ExpectContinueTimeout, if non-zero, specifies the amount of
// time to wait for a server's first response headers after fully
// writing the request headers if the request has an
// "Expect: 100-continue" header. Zero means no timeout and
// causes the body to be sent immediately, without
// waiting for the server to approve.
// This time does not include the time to send the request header.
ExpectContinueTimeout time.Duration `yaml:"expect-continue-timeout"`
}
func (b Backend) Create() (*httputil.ReverseProxy, error) {
if b.IpHeader == "-" {
b.IpHeader = ""
}
proxy, err := utils.MakeReverseProxy(b.URL, b.GoDNS, b.DialTimeout)
if err != nil {
return nil, err
}
transport := proxy.Transport.(*http.Transport)
// set transport timeouts
transport.TLSHandshakeTimeout = b.TLSHandshakeTimeout
transport.IdleConnTimeout = b.IdleConnTimeout
transport.ResponseHeaderTimeout = b.ResponseHeaderTimeout
transport.ExpectContinueTimeout = b.ExpectContinueTimeout
if b.HTTP2Enabled {
transport.ForceAttemptHTTP2 = true
}
if b.TLSSkipVerify {
transport.TLSClientConfig.InsecureSkipVerify = true
}
if b.Host != "" {
transport.TLSClientConfig.ServerName = b.Host
}
if b.IpHeader != "" || b.Host != "" || !b.Transparent {
director := proxy.Director
proxy.Director = func(req *http.Request) {
if b.IpHeader != "" && !b.Transparent {
if ip := utils.GetRemoteAddress(req.Context()); ip != nil {
req.Header.Set(b.IpHeader, ip.Addr().Unmap().String())
}
}
if b.Host != "" {
req.Host = b.Host
}
if !b.Transparent {
if data := challenge.RequestDataFromContext(req.Context()); data != nil {
data.RequestHeaders(req.Header)
}
}
director(req)
}
}
/*if b.ProxyProtocol > 0 {
dialContext := transport.DialContext
if dialContext == nil {
dialContext = (&net.Dialer{}).DialContext
}
transport.DialContext = func(ctx context.Context, network, addr string) (net.Conn, error) {
conn, err := dialContext(ctx, network, addr)
if err != nil {
return nil, err
}
addrPort := utils.GetRemoteAddress(ctx)
if addrPort == nil {
// pass as is
hdr := proxyproto.HeaderProxyFromAddrs(b.ProxyProtocol, conn.LocalAddr(), conn.RemoteAddr())
_, err = hdr.WriteTo(conn)
if err != nil {
conn.Close()
return nil, err
}
} else {
// set proper headers!
hdr := proxyproto.HeaderProxyFromAddrs(b.ProxyProtocol, net.TCPAddrFromAddrPort(*addrPort), conn.RemoteAddr())
_, err = hdr.WriteTo(conn)
if err != nil {
conn.Close()
return nil, err
}
}
return conn, nil
}
}*/
proxy.Transport = transport
return proxy, nil
}