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 24 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
Expand Up @@ -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)
}
Expand Down Expand Up @@ -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)
}
Expand Down
4 changes: 2 additions & 2 deletions cmd/web_acme.go
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
8 changes: 4 additions & 4 deletions cmd/web_graceful.go
Original file line number Diff line number Diff line change
Expand Up @@ -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, haProxy bool) error {
return graceful.HTTPListenAndServe(network, listenAddr, name, m, haProxy)
}

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

Expand All @@ -47,7 +47,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
10 changes: 5 additions & 5 deletions cmd/web_https.go
Original file line number Diff line number Diff line change
Expand Up @@ -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, haProxy, haProxyTLSBridging bool) error {
zeripath marked this conversation as resolved.
Show resolved Hide resolved
tlsConfig := &tls.Config{}
if tlsConfig.NextProtos == nil {
tlsConfig.NextProtos = []string{"h2", "http/1.1"}
Expand Down Expand Up @@ -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, 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)
}
22 changes: 21 additions & 1 deletion 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,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=
Expand All @@ -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
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 @@ -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.
Expand All @@ -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.
Expand Down Expand Up @@ -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.
Expand Down
48 changes: 41 additions & 7 deletions modules/graceful/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,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 @@ -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, 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 @@ -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, haProxy, haProxyTLSBridging 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 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
8 changes: 4 additions & 4 deletions modules/graceful/server_http.go
Original file line number Diff line number Diff line change
Expand Up @@ -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, haProxy bool) error {
server, lHandler := newHTTPServer(network, address, name, handler)
return server.ListenAndServe(lHandler)
return server.ListenAndServe(lHandler, haProxy)
}

// 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