Skip to content

Support Proxy protocol #12527

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 30 commits into from
Aug 21, 2022
Merged
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
dd337e1
Support HAProxy protocol
zeripath Aug 18, 2020
fc0065d
oops
zeripath Aug 18, 2020
2e488e8
Rename everything to UseProxyProtocol
zeripath Aug 18, 2020
b3be091
Merge remote-tracking branch 'origin/master' into fix-7508-support-pr…
zeripath Sep 28, 2020
e0f5e72
Merge remote-tracking branch 'origin/master' into fix-7508-support-pr…
zeripath Oct 24, 2020
d4d93a6
Merge branch 'master' into fix-7508-support-proxy-protocol
6543 Dec 3, 2020
e5ca3a2
Merge branch 'master' into fix-7508-support-proxy-protocol
6543 Jan 18, 2021
588bd55
Merge remote-tracking branch 'origin/master' into fix-7508-support-pr…
zeripath Feb 19, 2021
9d117e4
Merge branch 'master' into fix-7508-support-proxy-protocol
6543 Feb 19, 2021
e8194a5
lint fix
zeripath Feb 19, 2021
b34674d
Merge branch 'fix-7508-support-proxy-protocol' of github.com:zeripath…
zeripath Feb 19, 2021
6d82c15
Merge branch 'master' into fix-7508-support-proxy-protocol
6543 Jun 29, 2021
af2e349
fix
6543 Jun 29, 2021
ce969b7
Merge branch 'main' into fix-7508-support-proxy-protocol
zeripath Jul 3, 2021
6c7cf4b
Merge branch 'main' into fix-7508-support-proxy-protocol
zeripath Jul 4, 2021
bc96c0e
Merge remote-tracking branch 'origin/main' into fix-7508-support-prox…
zeripath Nov 19, 2021
8761f00
Merge remote-tracking branch 'origin/main' into fix-7508-support-prox…
zeripath Nov 20, 2021
6a0cc94
Merge branch 'main' into fix-7508-support-proxy-protocol
lunny Nov 20, 2021
1034070
Merge remote-tracking branch 'origin/main' into fix-7508-support-prox…
zeripath Jan 20, 2022
f31fa3c
Merge remote-tracking branch 'origin/main' into fix-7508-support-prox…
zeripath Jan 20, 2022
1f242a3
Merge remote-tracking branch 'origin/main' into fix-7508-support-prox…
zeripath Aug 16, 2022
d5bc6eb
Update custom/conf/app.example.ini
zeripath Aug 16, 2022
95e0f03
Update modules/setting/setting.go
zeripath Aug 16, 2022
e718469
placate the linter
zeripath Aug 17, 2022
9c2d512
as per silverwind
zeripath Aug 17, 2022
bd1302d
Merge remote-tracking branch 'origin/main' into fix-7508-support-prox…
zeripath Aug 17, 2022
cc2be6f
Merge branch 'main' into fix-7508-support-proxy-protocol
6543 Aug 19, 2022
c81db21
Merge branch 'main' into fix-7508-support-proxy-protocol
zeripath Aug 20, 2022
288a7fd
Merge branch 'main' into fix-7508-support-proxy-protocol
zeripath Aug 21, 2022
a07e09c
Merge branch 'main' into fix-7508-support-proxy-protocol
zeripath Aug 21, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 12 additions & 14 deletions cmd/web.go
Original file line number Diff line number Diff line change
@@ -76,7 +76,7 @@ func runHTTPRedirector() {
http.Redirect(w, r, target, http.StatusTemporaryRedirect)
})

err := runHTTP("tcp", source, "HTTP Redirector", handler)
err := runHTTP("tcp", source, "HTTP Redirector", handler, setting.RedirectorUseProxyProtocol)
if err != nil {
log.Fatal("Failed to start port redirection: %v", err)
}
@@ -231,40 +231,38 @@ func listen(m http.Handler, handleRedirector bool) error {
if handleRedirector {
NoHTTPRedirector()
}
err = runHTTP("tcp", listenAddr, "Web", m)
err = runHTTP("tcp", listenAddr, "Web", m, setting.UseProxyProtocol)
case setting.HTTPS:
if setting.EnableAcme {
err = runACME(listenAddr, m)
break
} else {
if handleRedirector {
if setting.RedirectOtherPort {
go runHTTPRedirector()
} else {
NoHTTPRedirector()
}
}
if handleRedirector {
if setting.RedirectOtherPort {
go runHTTPRedirector()
} else {
NoHTTPRedirector()
}
err = runHTTPS("tcp", listenAddr, "Web", setting.CertFile, setting.KeyFile, m)
}
err = runHTTPS("tcp", listenAddr, "Web", setting.CertFile, setting.KeyFile, m, setting.UseProxyProtocol, setting.ProxyProtocolTLSBridging)
case setting.FCGI:
if handleRedirector {
NoHTTPRedirector()
}
err = runFCGI("tcp", listenAddr, "FCGI Web", m)
err = runFCGI("tcp", listenAddr, "FCGI Web", m, setting.UseProxyProtocol)
case setting.HTTPUnix:
if handleRedirector {
NoHTTPRedirector()
}
err = runHTTP("unix", listenAddr, "Web", m)
err = runHTTP("unix", listenAddr, "Web", m, setting.UseProxyProtocol)
case setting.FCGIUnix:
if handleRedirector {
NoHTTPRedirector()
}
err = runFCGI("unix", listenAddr, "Web", m)
err = runFCGI("unix", listenAddr, "Web", m, setting.UseProxyProtocol)
default:
log.Fatal("Invalid protocol: %s", setting.Protocol)
}

if err != nil {
log.Critical("Failed to start server: %v", err)
}
4 changes: 2 additions & 2 deletions cmd/web_acme.go
Original file line number Diff line number Diff line change
@@ -113,14 +113,14 @@ func runACME(listenAddr string, m http.Handler) error {

log.Info("Running Let's Encrypt handler on %s", setting.HTTPAddr+":"+setting.PortToRedirect)
// all traffic coming into HTTP will be redirect to HTTPS automatically (LE HTTP-01 validation happens here)
err := runHTTP("tcp", setting.HTTPAddr+":"+setting.PortToRedirect, "Let's Encrypt HTTP Challenge", myACME.HTTPChallengeHandler(http.HandlerFunc(runLetsEncryptFallbackHandler)))
err := runHTTP("tcp", setting.HTTPAddr+":"+setting.PortToRedirect, "Let's Encrypt HTTP Challenge", myACME.HTTPChallengeHandler(http.HandlerFunc(runLetsEncryptFallbackHandler)), setting.RedirectorUseProxyProtocol)
if err != nil {
log.Fatal("Failed to start the Let's Encrypt handler on port %s: %v", setting.PortToRedirect, err)
}
}()
}

return runHTTPSWithTLSConfig("tcp", listenAddr, "Web", tlsConfig, m)
return runHTTPSWithTLSConfig("tcp", listenAddr, "Web", tlsConfig, m, setting.UseProxyProtocol, setting.ProxyProtocolTLSBridging)
}

func runLetsEncryptFallbackHandler(w http.ResponseWriter, r *http.Request) {
8 changes: 4 additions & 4 deletions cmd/web_graceful.go
Original file line number Diff line number Diff line change
@@ -15,8 +15,8 @@ import (
"code.gitea.io/gitea/modules/setting"
)

func runHTTP(network, listenAddr, name string, m http.Handler) error {
return graceful.HTTPListenAndServe(network, listenAddr, name, m)
func runHTTP(network, listenAddr, name string, m http.Handler, useProxyProtocol bool) error {
return graceful.HTTPListenAndServe(network, listenAddr, name, m, useProxyProtocol)
}

// NoHTTPRedirector tells our cleanup routine that we will not be using a fallback http redirector
@@ -36,7 +36,7 @@ func NoInstallListener() {
graceful.GetManager().InformCleanup()
}

func runFCGI(network, listenAddr, name string, m http.Handler) error {
func runFCGI(network, listenAddr, name string, m http.Handler, useProxyProtocol bool) error {
// This needs to handle stdin as fcgi point
fcgiServer := graceful.NewServer(network, listenAddr, name)

@@ -47,7 +47,7 @@ func runFCGI(network, listenAddr, name string, m http.Handler) error {
}
m.ServeHTTP(resp, req)
}))
})
}, useProxyProtocol)
if err != nil {
log.Fatal("Failed to start FCGI main server: %v", err)
}
10 changes: 5 additions & 5 deletions cmd/web_https.go
Original file line number Diff line number Diff line change
@@ -129,14 +129,14 @@ var (
defaultCiphersChaChaFirst = append(defaultCiphersChaCha, defaultCiphersAES...)
)

// runHTTPs listens on the provided network address and then calls
// runHTTPS listens on the provided network address and then calls
// Serve to handle requests on incoming TLS connections.
//
// Filenames containing a certificate and matching private key for the server must
// be provided. If the certificate is signed by a certificate authority, the
// certFile should be the concatenation of the server's certificate followed by the
// CA's certificate.
func runHTTPS(network, listenAddr, name, certFile, keyFile string, m http.Handler) error {
func runHTTPS(network, listenAddr, name, certFile, keyFile string, m http.Handler, useProxyProtocol, proxyProtocolTLSBridging bool) error {
tlsConfig := &tls.Config{}
if tlsConfig.NextProtos == nil {
tlsConfig.NextProtos = []string{"h2", "http/1.1"}
@@ -184,9 +184,9 @@ func runHTTPS(network, listenAddr, name, certFile, keyFile string, m http.Handle
return err
}

return graceful.HTTPListenAndServeTLSConfig(network, listenAddr, name, tlsConfig, m)
return graceful.HTTPListenAndServeTLSConfig(network, listenAddr, name, tlsConfig, m, useProxyProtocol, proxyProtocolTLSBridging)
}

func runHTTPSWithTLSConfig(network, listenAddr, name string, tlsConfig *tls.Config, m http.Handler) error {
return graceful.HTTPListenAndServeTLSConfig(network, listenAddr, name, tlsConfig, m)
func runHTTPSWithTLSConfig(network, listenAddr, name string, tlsConfig *tls.Config, m http.Handler, useProxyProtocol, proxyProtocolTLSBridging bool) error {
return graceful.HTTPListenAndServeTLSConfig(network, listenAddr, name, tlsConfig, m, useProxyProtocol, proxyProtocolTLSBridging)
}
22 changes: 21 additions & 1 deletion custom/conf/app.example.ini
Original file line number Diff line number Diff line change
@@ -29,6 +29,18 @@ RUN_MODE = ; prod
;; The protocol the server listens on. One of 'http', 'https', 'unix' or 'fcgi'. Defaults to 'http'
;PROTOCOL = http
;;
;; Expect PROXY protocol headers on connections
;USE_PROXY_PROTOCOL = false
;;
;; Use PROXY protocol in TLS Bridging mode
;PROXY_PROTOCOL_TLS_BRIDGING = false
;;
; Timeout to wait for PROXY protocol header (set to 0 to have no timeout)
;PROXY_PROTOCOL_HEADER_TIMEOUT=5s
;;
; Accept PROXY protocol headers with UNKNOWN type
;PROXY_PROTOCOL_ACCEPT_UNKNOWN=false
;;
;; Set the domain for the server
;DOMAIN = localhost
;;
@@ -51,6 +63,8 @@ RUN_MODE = ; prod
;REDIRECT_OTHER_PORT = false
;PORT_TO_REDIRECT = 80
;;
;; expect PROXY protocol header on connections to https redirector.
;REDIRECTOR_USE_PROXY_PROTOCOL = %(USE_PROXY_PROTOCOL)
;; Minimum and maximum supported TLS versions
;SSL_MIN_VERSION=TLSv1.2
;SSL_MAX_VERSION=
@@ -76,13 +90,19 @@ RUN_MODE = ; prod
;; Do not set this variable if PROTOCOL is set to 'unix'.
;LOCAL_ROOT_URL = %(PROTOCOL)s://%(HTTP_ADDR)s:%(HTTP_PORT)s/
;;
;; When making local connections pass the PROXY protocol header.
;LOCAL_USE_PROXY_PROTOCOL = %(USE_PROXY_PROTOCOL)
;;
;; Disable SSH feature when not available
;DISABLE_SSH = false
;;
;; Whether to use the builtin SSH server or not.
;START_SSH_SERVER = false
;;
;; Username to use for the builtin SSH server.
;; Expect PROXY protocol header on connections to the built-in SSH server
;SSH_SERVER_USE_PROXY_PROTOCOL = false
;;
;; Username to use for the builtin SSH server. If blank, then it is the value of RUN_USER.
;BUILTIN_SSH_SERVER_USER = %(RUN_USER)s
;;
;; Domain name to be exposed in clone URL
8 changes: 8 additions & 0 deletions docs/content/doc/advanced/config-cheat-sheet.en-us.md
Original file line number Diff line number Diff line change
@@ -238,6 +238,10 @@ The following configuration set `Content-Type: application/vnd.android.package-a
## Server (`server`)

- `PROTOCOL`: **http**: \[http, https, fcgi, http+unix, fcgi+unix\]
- `USE_PROXY_PROTOCOL`: **false**: Expect PROXY protocol headers on connections
- `PROXY_PROTOCOL_TLS_BRIDGING`: **false**: When protocol is https, expect PROXY protocol headers after TLS negotiation.
- `PROXY_PROTOCOL_HEADER_TIMEOUT`: **5s**: Timeout to wait for PROXY protocol header (set to 0 to have no timeout)
- `PROXY_PROTOCOL_ACCEPT_UNKNOWN`: **false**: Accept PROXY protocol headers with Unknown type.
- `DOMAIN`: **localhost**: Domain name of this server.
- `ROOT_URL`: **%(PROTOCOL)s://%(DOMAIN)s:%(HTTP\_PORT)s/**:
Overwrite the automatically generated public URL.
@@ -262,12 +266,15 @@ The following configuration set `Content-Type: application/vnd.android.package-a
most cases you do not need to change the default value. Alter it only if
your SSH server node is not the same as HTTP node. Do not set this variable
if `PROTOCOL` is set to `http+unix`.
- `LOCAL_USE_PROXY_PROTOCOL`: **%(USE_PROXY_PROTOCOL)**: When making local connections pass the PROXY protocol header.
This should be set to false if the local connection will go through the proxy.
- `PER_WRITE_TIMEOUT`: **30s**: Timeout for any write to the connection. (Set to -1 to
disable all timeouts.)
- `PER_WRITE_PER_KB_TIMEOUT`: **10s**: Timeout per Kb written to connections.

- `DISABLE_SSH`: **false**: Disable SSH feature when it's not available.
- `START_SSH_SERVER`: **false**: When enabled, use the built-in SSH server.
- `SSH_SERVER_USE_PROXY_PROTOCOL`: **false**: Expect PROXY protocol header on connections to the built-in SSH Server.
- `BUILTIN_SSH_SERVER_USER`: **%(RUN_USER)s**: Username to use for the built-in SSH Server.
- `SSH_USER`: **%(BUILTIN_SSH_SERVER_USER)**: SSH username displayed in clone URLs. This is only for people who configure the SSH server themselves; in most cases, you want to leave this blank and modify the `BUILTIN_SSH_SERVER_USER`.
- `SSH_DOMAIN`: **%(DOMAIN)s**: Domain name of this server, used for displayed clone URL.
@@ -313,6 +320,7 @@ The following configuration set `Content-Type: application/vnd.android.package-a
- `LFS_LOCKS_PAGING_NUM`: **50**: Maximum number of LFS Locks returned per page.

- `REDIRECT_OTHER_PORT`: **false**: If true and `PROTOCOL` is https, allows redirecting http requests on `PORT_TO_REDIRECT` to the https port Gitea listens on.
- `REDIRECTOR_USE_PROXY_PROTOCOL`: **%(USE_PROXY_PROTOCOL)**: expect PROXY protocol header on connections to https redirector.
- `PORT_TO_REDIRECT`: **80**: Port for the http redirection service to listen on. Used when `REDIRECT_OTHER_PORT` is true.
- `SSL_MIN_VERSION`: **TLSv1.2**: Set the minimum version of ssl support.
- `SSL_MAX_VERSION`: **\<empty\>**: Set the maximum version of ssl support.
48 changes: 41 additions & 7 deletions modules/graceful/server.go
Original file line number Diff line number Diff line change
@@ -16,6 +16,7 @@ import (
"time"

"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/proxyprotocol"
"code.gitea.io/gitea/modules/setting"
)

@@ -79,16 +80,27 @@ func NewServer(network, address, name string) *Server {

// ListenAndServe listens on the provided network address and then calls Serve
// to handle requests on incoming connections.
func (srv *Server) ListenAndServe(serve ServeFunction) error {
func (srv *Server) ListenAndServe(serve ServeFunction, useProxyProtocol bool) error {
go srv.awaitShutdown()

l, err := GetListener(srv.network, srv.address)
listener, err := GetListener(srv.network, srv.address)
if err != nil {
log.Error("Unable to GetListener: %v", err)
return err
}

srv.listener = newWrappedListener(l, srv)
// we need to wrap the listener to take account of our lifecycle
listener = newWrappedListener(listener, srv)

// Now we need to take account of ProxyProtocol settings...
if useProxyProtocol {
listener = &proxyprotocol.Listener{
Listener: listener,
ProxyHeaderTimeout: setting.ProxyProtocolHeaderTimeout,
AcceptUnknown: setting.ProxyProtocolAcceptUnknown,
}
}
srv.listener = listener

srv.BeforeBegin(srv.network, srv.address)

@@ -97,22 +109,44 @@ func (srv *Server) ListenAndServe(serve ServeFunction) error {

// ListenAndServeTLSConfig listens on the provided network address and then calls
// Serve to handle requests on incoming TLS connections.
func (srv *Server) ListenAndServeTLSConfig(tlsConfig *tls.Config, serve ServeFunction) error {
func (srv *Server) ListenAndServeTLSConfig(tlsConfig *tls.Config, serve ServeFunction, useProxyProtocol, proxyProtocolTLSBridging bool) error {
go srv.awaitShutdown()

if tlsConfig.MinVersion == 0 {
tlsConfig.MinVersion = tls.VersionTLS12
}

l, err := GetListener(srv.network, srv.address)
listener, err := GetListener(srv.network, srv.address)
if err != nil {
log.Error("Unable to get Listener: %v", err)
return err
}

wl := newWrappedListener(l, srv)
srv.listener = tls.NewListener(wl, tlsConfig)
// we need to wrap the listener to take account of our lifecycle
listener = newWrappedListener(listener, srv)

// Now we need to take account of ProxyProtocol settings... If we're not bridging then we expect that the proxy will forward the connection to us
if useProxyProtocol && !proxyProtocolTLSBridging {
listener = &proxyprotocol.Listener{
Listener: listener,
ProxyHeaderTimeout: setting.ProxyProtocolHeaderTimeout,
AcceptUnknown: setting.ProxyProtocolAcceptUnknown,
}
}

// Now handle the tls protocol
listener = tls.NewListener(listener, tlsConfig)

// Now if we're bridging then we need the proxy to tell us who we're bridging for...
if useProxyProtocol && proxyProtocolTLSBridging {
listener = &proxyprotocol.Listener{
Listener: listener,
ProxyHeaderTimeout: setting.ProxyProtocolHeaderTimeout,
AcceptUnknown: setting.ProxyProtocolAcceptUnknown,
}
}

srv.listener = listener
srv.BeforeBegin(srv.network, srv.address)

return srv.Serve(serve)
8 changes: 4 additions & 4 deletions modules/graceful/server_http.go
Original file line number Diff line number Diff line change
@@ -28,14 +28,14 @@ func newHTTPServer(network, address, name string, handler http.Handler) (*Server

// HTTPListenAndServe listens on the provided network address and then calls Serve
// to handle requests on incoming connections.
func HTTPListenAndServe(network, address, name string, handler http.Handler) error {
func HTTPListenAndServe(network, address, name string, handler http.Handler, useProxyProtocol bool) error {
server, lHandler := newHTTPServer(network, address, name, handler)
return server.ListenAndServe(lHandler)
return server.ListenAndServe(lHandler, useProxyProtocol)
}

// HTTPListenAndServeTLSConfig listens on the provided network address and then calls Serve
// to handle requests on incoming connections.
func HTTPListenAndServeTLSConfig(network, address, name string, tlsConfig *tls.Config, handler http.Handler) error {
func HTTPListenAndServeTLSConfig(network, address, name string, tlsConfig *tls.Config, handler http.Handler, useProxyProtocol, proxyProtocolTLSBridging bool) error {
server, lHandler := newHTTPServer(network, address, name, handler)
return server.ListenAndServeTLSConfig(tlsConfig, lHandler)
return server.ListenAndServeTLSConfig(tlsConfig, lHandler, useProxyProtocol, proxyProtocolTLSBridging)
}
28 changes: 27 additions & 1 deletion modules/private/internal.go
Original file line number Diff line number Diff line change
@@ -14,6 +14,7 @@ import (
"code.gitea.io/gitea/modules/httplib"
"code.gitea.io/gitea/modules/json"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/proxyprotocol"
"code.gitea.io/gitea/modules/setting"
)

@@ -50,7 +51,32 @@ func newInternalRequest(ctx context.Context, url, method string) *httplib.Reques
req.SetTransport(&http.Transport{
DialContext: func(ctx context.Context, _, _ string) (net.Conn, error) {
var d net.Dialer
return d.DialContext(ctx, "unix", setting.HTTPAddr)
conn, err := d.DialContext(ctx, "unix", setting.HTTPAddr)
if err != nil {
return conn, err
}
if setting.LocalUseProxyProtocol {
if err = proxyprotocol.WriteLocalHeader(conn); err != nil {
_ = conn.Close()
return nil, err
}
}
return conn, err
},
})
} else if setting.LocalUseProxyProtocol {
req.SetTransport(&http.Transport{
DialContext: func(ctx context.Context, network, address string) (net.Conn, error) {
var d net.Dialer
conn, err := d.DialContext(ctx, network, address)
if err != nil {
return conn, err
}
if err = proxyprotocol.WriteLocalHeader(conn); err != nil {
_ = conn.Close()
return nil, err
}
return conn, err
},
})
}
506 changes: 506 additions & 0 deletions modules/proxyprotocol/conn.go

Large diffs are not rendered by default.

45 changes: 45 additions & 0 deletions modules/proxyprotocol/errors.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
// Copyright 2020 The Gitea Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.

package proxyprotocol

import "fmt"

// ErrBadHeader is an error demonstrating a bad proxy header
type ErrBadHeader struct {
Header []byte
}

func (e *ErrBadHeader) Error() string {
return fmt.Sprintf("Unexpected proxy header: %v", e.Header)
}

// ErrBadAddressType is an error demonstrating a bad proxy header with bad Address type
type ErrBadAddressType struct {
Address string
}

func (e *ErrBadAddressType) Error() string {
return fmt.Sprintf("Unexpected proxy header address type: %s", e.Address)
}

// ErrBadRemote is an error demonstrating a bad proxy header with bad Remote
type ErrBadRemote struct {
IP string
Port string
}

func (e *ErrBadRemote) Error() string {
return fmt.Sprintf("Unexpected proxy header remote IP and port: %s %s", e.IP, e.Port)
}

// ErrBadLocal is an error demonstrating a bad proxy header with bad Local
type ErrBadLocal struct {
IP string
Port string
}

func (e *ErrBadLocal) Error() string {
return fmt.Sprintf("Unexpected proxy header local IP and port: %s %s", e.IP, e.Port)
}
47 changes: 47 additions & 0 deletions modules/proxyprotocol/listener.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
// Copyright 2020 The Gitea Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.

package proxyprotocol

import (
"net"
"time"
)

// Listener is used to wrap an underlying listener,
// whose connections may be using the HAProxy Proxy Protocol (version 1 or 2).
// If the connection is using the protocol, the RemoteAddr() will return
// the correct client address.
//
// Optionally define ProxyHeaderTimeout to set a maximum time to
// receive the Proxy Protocol Header. Zero means no timeout.
type Listener struct {
Listener net.Listener
ProxyHeaderTimeout time.Duration
AcceptUnknown bool // allow PROXY UNKNOWN
}

// Accept implements the Accept method in the Listener interface
// it waits for the next call and returns a wrapped Conn.
func (p *Listener) Accept() (net.Conn, error) {
// Get the underlying connection
conn, err := p.Listener.Accept()
if err != nil {
return nil, err
}

newConn := NewConn(conn, p.ProxyHeaderTimeout)
newConn.acceptUnknown = p.AcceptUnknown
return newConn, nil
}

// Close closes the underlying listener.
func (p *Listener) Close() error {
return p.Listener.Close()
}

// Addr returns the underlying listener's network address.
func (p *Listener) Addr() net.Addr {
return p.Listener.Addr()
}
15 changes: 15 additions & 0 deletions modules/proxyprotocol/util.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// Copyright 2020 The Gitea Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.

package proxyprotocol

import "io"

var localHeader = append(v2Prefix, '\x20', '\x00', '\x00', '\x00', '\x00')

// WriteLocalHeader will write the ProxyProtocol Header for a local connection to the provided writer
func WriteLocalHeader(w io.Writer) error {
_, err := w.Write(localHeader)
return err
}
82 changes: 48 additions & 34 deletions modules/setting/setting.go
Original file line number Diff line number Diff line change
@@ -94,45 +94,52 @@ var (
LocalURL string

// Server settings
Protocol Scheme
Domain string
HTTPAddr string
HTTPPort string
RedirectOtherPort bool
PortToRedirect string
OfflineMode bool
CertFile string
KeyFile string
StaticRootPath string
StaticCacheTime time.Duration
EnableGzip bool
LandingPageURL LandingPage
LandingPageCustom string
UnixSocketPermission uint32
EnablePprof bool
PprofDataPath string
EnableAcme bool
AcmeTOS bool
AcmeLiveDirectory string
AcmeEmail string
AcmeURL string
AcmeCARoot string
SSLMinimumVersion string
SSLMaximumVersion string
SSLCurvePreferences []string
SSLCipherSuites []string
GracefulRestartable bool
GracefulHammerTime time.Duration
StartupTimeout time.Duration
PerWriteTimeout = 30 * time.Second
PerWritePerKbTimeout = 10 * time.Second
StaticURLPrefix string
AbsoluteAssetURL string
Protocol Scheme
UseProxyProtocol bool // `ini:"USE_PROXY_PROTOCOL"`
ProxyProtocolTLSBridging bool //`ini:"PROXY_PROTOCOL_TLS_BRIDGING"`
ProxyProtocolHeaderTimeout time.Duration
ProxyProtocolAcceptUnknown bool
Domain string
HTTPAddr string
HTTPPort string
LocalUseProxyProtocol bool
RedirectOtherPort bool
RedirectorUseProxyProtocol bool
PortToRedirect string
OfflineMode bool
CertFile string
KeyFile string
StaticRootPath string
StaticCacheTime time.Duration
EnableGzip bool
LandingPageURL LandingPage
LandingPageCustom string
UnixSocketPermission uint32
EnablePprof bool
PprofDataPath string
EnableAcme bool
AcmeTOS bool
AcmeLiveDirectory string
AcmeEmail string
AcmeURL string
AcmeCARoot string
SSLMinimumVersion string
SSLMaximumVersion string
SSLCurvePreferences []string
SSLCipherSuites []string
GracefulRestartable bool
GracefulHammerTime time.Duration
StartupTimeout time.Duration
PerWriteTimeout = 30 * time.Second
PerWritePerKbTimeout = 10 * time.Second
StaticURLPrefix string
AbsoluteAssetURL string

SSH = struct {
Disabled bool `ini:"DISABLE_SSH"`
StartBuiltinServer bool `ini:"START_SSH_SERVER"`
BuiltinServerUser string `ini:"BUILTIN_SSH_SERVER_USER"`
UseProxyProtocol bool `ini:"SSH_SERVER_USE_PROXY_PROTOCOL"`
Domain string `ini:"SSH_DOMAIN"`
Port int `ini:"SSH_PORT"`
User string `ini:"SSH_USER"`
@@ -717,6 +724,10 @@ func loadFromConf(allowEmpty bool, extraConfig string) {
HTTPAddr = filepath.Join(AppWorkPath, HTTPAddr)
}
}
UseProxyProtocol = sec.Key("USE_PROXY_PROTOCOL").MustBool(false)
ProxyProtocolTLSBridging = sec.Key("PROXY_PROTOCOL_TLS_BRIDGING").MustBool(false)
ProxyProtocolHeaderTimeout = sec.Key("PROXY_PROTOCOL_HEADER_TIMEOUT").MustDuration(5 * time.Second)
ProxyProtocolAcceptUnknown = sec.Key("PROXY_PROTOCOL_ACCEPT_UNKNOWN").MustBool(false)
GracefulRestartable = sec.Key("ALLOW_GRACEFUL_RESTARTS").MustBool(true)
GracefulHammerTime = sec.Key("GRACEFUL_HAMMER_TIME").MustDuration(60 * time.Second)
StartupTimeout = sec.Key("STARTUP_TIMEOUT").MustDuration(0 * time.Second)
@@ -770,8 +781,10 @@ func loadFromConf(allowEmpty bool, extraConfig string) {
}
LocalURL = sec.Key("LOCAL_ROOT_URL").MustString(defaultLocalURL)
LocalURL = strings.TrimRight(LocalURL, "/") + "/"
LocalUseProxyProtocol = sec.Key("LOCAL_USE_PROXY_PROTOCOL").MustBool(UseProxyProtocol)
RedirectOtherPort = sec.Key("REDIRECT_OTHER_PORT").MustBool(false)
PortToRedirect = sec.Key("PORT_TO_REDIRECT").MustString("80")
RedirectorUseProxyProtocol = sec.Key("REDIRECTOR_USE_PROXY_PROTOCOL").MustBool(UseProxyProtocol)
OfflineMode = sec.Key("OFFLINE_MODE").MustBool()
DisableRouterLog = sec.Key("DISABLE_ROUTER_LOG").MustBool()
if len(StaticRootPath) == 0 {
@@ -836,6 +849,7 @@ func loadFromConf(allowEmpty bool, extraConfig string) {
SSH.KeygenPath = sec.Key("SSH_KEYGEN_PATH").MustString("ssh-keygen")
SSH.Port = sec.Key("SSH_PORT").MustInt(22)
SSH.ListenPort = sec.Key("SSH_LISTEN_PORT").MustInt(SSH.Port)
SSH.UseProxyProtocol = sec.Key("SSH_SERVER_USE_PROXY_PROTOCOL").MustBool(false)

// When disable SSH, start builtin server value is ignored.
if SSH.Disabled {
2 changes: 1 addition & 1 deletion modules/ssh/ssh_graceful.go
Original file line number Diff line number Diff line change
@@ -17,7 +17,7 @@ func listen(server *ssh.Server) {
gracefulServer.PerWriteTimeout = setting.SSH.PerWriteTimeout
gracefulServer.PerWritePerKbTimeout = setting.SSH.PerWritePerKbTimeout

err := gracefulServer.ListenAndServe(server.Serve)
err := gracefulServer.ListenAndServe(server.Serve, setting.SSH.UseProxyProtocol)
if err != nil {
select {
case <-graceful.GetManager().IsShutdown():