Skip to content
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

Support Proxy protocol #12527

Merged
merged 30 commits into from
Aug 21, 2022
Merged
Show file tree
Hide file tree
Changes from 13 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
13 changes: 6 additions & 7 deletions cmd/web.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ func runHTTPRedirector() {
http.Redirect(w, r, target, http.StatusTemporaryRedirect)
})

var err = runHTTP("tcp", source, "HTTP Redirector", context2.ClearHandler(handler))
var err = runHTTP("tcp", source, "HTTP Redirector", context2.ClearHandler(handler), setting.RedirectorUseProxyProtocol)

if err != nil {
log.Fatal("Failed to start port redirection: %v", err)
Expand Down Expand Up @@ -200,7 +200,7 @@ func listen(m http.Handler, handleRedirector bool) error {
if handleRedirector {
NoHTTPRedirector()
}
err = runHTTP("tcp", listenAddr, "Web", context2.ClearHandler(m))
err = runHTTP("tcp", listenAddr, "Web", context2.ClearHandler(m), setting.UseProxyProtocol)
case setting.HTTPS:
if setting.EnableLetsEncrypt {
err = runLetsEncrypt(listenAddr, setting.Domain, setting.LetsEncryptDirectory, setting.LetsEncryptEmail, context2.ClearHandler(m))
Expand All @@ -213,26 +213,25 @@ func listen(m http.Handler, handleRedirector bool) error {
NoHTTPRedirector()
}
}
err = runHTTPS("tcp", listenAddr, "Web", setting.CertFile, setting.KeyFile, context2.ClearHandler(m))
err = runHTTPS("tcp", listenAddr, "Web", setting.CertFile, setting.KeyFile, context2.ClearHandler(m), setting.UseProxyProtocol, setting.ProxyProtocolTLSBridging)
case setting.FCGI:
if handleRedirector {
NoHTTPRedirector()
}
err = runFCGI("tcp", listenAddr, "FCGI Web", context2.ClearHandler(m))
err = runFCGI("tcp", listenAddr, "FCGI Web", context2.ClearHandler(m), setting.UseProxyProtocol)
case setting.UnixSocket:
if handleRedirector {
NoHTTPRedirector()
}
err = runHTTP("unix", listenAddr, "Web", context2.ClearHandler(m))
err = runHTTP("unix", listenAddr, "Web", context2.ClearHandler(m), setting.UseProxyProtocol)
case setting.FCGIUnix:
if handleRedirector {
NoHTTPRedirector()
}
err = runFCGI("unix", listenAddr, "Web", context2.ClearHandler(m))
err = runFCGI("unix", listenAddr, "Web", context2.ClearHandler(m), setting.UseProxyProtocol)
default:
log.Fatal("Invalid protocol: %s", setting.Protocol)
}

if err != nil {
log.Critical("Failed to start server: %v", err)
}
Expand Down
16 changes: 8 additions & 8 deletions cmd/web_graceful.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,16 +16,16 @@ 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, haProxy bool) error {
return graceful.HTTPListenAndServe(network, listenAddr, name, m, haProxy)
}

func runHTTPS(network, listenAddr, name, certFile, keyFile string, m http.Handler) error {
return graceful.HTTPListenAndServeTLS(network, listenAddr, name, certFile, keyFile, m)
func runHTTPS(network, listenAddr, name, certFile, keyFile string, m http.Handler, haProxy, haProxyTLSBridging bool) error {
return graceful.HTTPListenAndServeTLS(network, listenAddr, name, certFile, keyFile, m, haProxy, haProxyTLSBridging)
}

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, haProxy, haProxyTLSBridging bool) error {
return graceful.HTTPListenAndServeTLSConfig(network, listenAddr, name, tlsConfig, m, haProxy, haProxyTLSBridging)
}

// NoHTTPRedirector tells our cleanup routine that we will not be using a fallback http redirector
Expand All @@ -45,7 +45,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, haProxy bool) error {
// This needs to handle stdin as fcgi point
fcgiServer := graceful.NewServer(network, listenAddr, name)

Expand All @@ -56,7 +56,7 @@ func runFCGI(network, listenAddr, name string, m http.Handler) error {
}
m.ServeHTTP(resp, req)
}))
})
}, haProxy)
if err != nil {
log.Fatal("Failed to start FCGI main server: %v", err)
}
Expand Down
4 changes: 2 additions & 2 deletions cmd/web_letsencrypt.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,14 +59,14 @@ func runLetsEncrypt(listenAddr, domain, directory, email string, m http.Handler)
go func() {
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)
var err = runHTTP("tcp", setting.HTTPAddr+":"+setting.PortToRedirect, "Let's Encrypt HTTP Challenge", myACME.HTTPChallengeHandler(http.HandlerFunc(runLetsEncryptFallbackHandler)))
var 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, context2.ClearHandler(m))
return runHTTPSWithTLSConfig("tcp", listenAddr, "Web", tlsConfig, context2.ClearHandler(m), setting.UseProxyProtocol, setting.ProxyProtocolTLSBridging)
}

func runLetsEncryptFallbackHandler(w http.ResponseWriter, r *http.Request) {
Expand Down
21 changes: 21 additions & 0 deletions custom/conf/app.example.ini
Original file line number Diff line number Diff line change
Expand Up @@ -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
;;
Expand All @@ -51,6 +63,9 @@ 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)
;;
;; Timeout for any write to the connection. (Set to 0 to disable all timeouts.)
;PER_WRITE_TIMEOUT = 30s
;;
Expand All @@ -66,12 +81,18 @@ 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
;;
;; 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 =
;;
Expand Down
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
Expand Up @@ -228,6 +228,10 @@ The following configuration set `Content-Type: application/vnd.android.package-a
## Server (`server`)

- `PROTOCOL`: **http**: \[http, https, fcgi, 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.
Expand All @@ -252,12 +256,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 `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 0 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_DOMAIN`: **%(DOMAIN)s**: Domain name of this server, used for displayed clone URL.
- `SSH_PORT`: **22**: SSH port displayed in clone URL.
Expand Down Expand Up @@ -304,6 +311,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.
- `ENABLE_LETSENCRYPT`: **false**: If enabled you must set `DOMAIN` to valid internet facing domain (ensure DNS is set and port 80 is accessible by letsencrypt validation server).
By using Lets Encrypt **you must consent** to their [terms of service](https://letsencrypt.org/documents/LE-SA-v1.2-November-15-2017.pdf).
Expand Down
53 changes: 43 additions & 10 deletions modules/graceful/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import (
"syscall"
"time"

"code.gitea.io/gitea/modules/haproxy"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
)
Expand Down Expand Up @@ -80,16 +81,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, haProxy 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 HAProxy settings...
if haProxy {
listener = &haproxy.Listener{
Listener: listener,
ProxyHeaderTimeout: setting.ProxyProtocolHeaderTimeout,
AcceptUnknown: setting.ProxyProtocolAcceptUnknown,
}
}
srv.listener = listener

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

Expand All @@ -103,7 +115,7 @@ func (srv *Server) ListenAndServe(serve ServeFunction) error {
// 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 (srv *Server) ListenAndServeTLS(certFile, keyFile string, serve ServeFunction) error {
func (srv *Server) ListenAndServeTLS(certFile, keyFile string, serve ServeFunction, haProxy, haProxyTLSBridging bool) error {
config := &tls.Config{}
if config.NextProtos == nil {
config.NextProtos = []string{"http/1.1"}
Expand All @@ -129,25 +141,46 @@ func (srv *Server) ListenAndServeTLS(certFile, keyFile string, serve ServeFuncti
return err
}

return srv.ListenAndServeTLSConfig(config, serve)
return srv.ListenAndServeTLSConfig(config, serve, haProxy, haProxyTLSBridging)
}

// 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, haProxy, haProxyTLSBridging bool) error {
go srv.awaitShutdown()

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 HAProxy settings... If we're not bridging then we expect that the proxy will forward the connection to us
if haProxy && !haProxyTLSBridging {
listener = &haproxy.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 haProxy && haProxyTLSBridging {
listener = &haproxy.Listener{
Listener: listener,
ProxyHeaderTimeout: setting.ProxyProtocolHeaderTimeout,
AcceptUnknown: setting.ProxyProtocolAcceptUnknown,
}
}

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

return srv.Serve(serve)
Expand Down
12 changes: 6 additions & 6 deletions modules/graceful/server_http.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,21 +28,21 @@ 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, haProxy bool) error {
server, lHandler := newHTTPServer(network, address, name, handler)
return server.ListenAndServe(lHandler)
return server.ListenAndServe(lHandler, haProxy)
}

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

// 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, haProxy, haProxyTLSBridging bool) error {
server, lHandler := newHTTPServer(network, address, name, handler)
return server.ListenAndServeTLSConfig(tlsConfig, lHandler)
return server.ListenAndServeTLSConfig(tlsConfig, lHandler, haProxy, haProxyTLSBridging)
}
Loading