-
Notifications
You must be signed in to change notification settings - Fork 145
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
The bucket server used by `gitops beta run` now serves over HTTP as well as HTTPS. HTTP is still necessary as Flux's Bucket resource doesn't have a field for providing a custom CA certificate. This is a backwards-incompatible change as the ports the server is listening on have to provided through flags. Also, providing TLS cert and key is mandatory.
- Loading branch information
Showing
5 changed files
with
282 additions
and
29 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,88 @@ | ||
package http | ||
|
||
import ( | ||
"context" | ||
"crypto/tls" | ||
"fmt" | ||
"log" | ||
"net" | ||
"net/http" | ||
"sync" | ||
) | ||
|
||
// MultiServer lets you create and run an HTTP server that serves over both, HTTP and HTTPS. It is a convenience wrapper around net/http and crypto/tls. | ||
type MultiServer struct { | ||
HTTPPort int | ||
HTTPSPort int | ||
CertFile string | ||
KeyFile string | ||
Logger *log.Logger | ||
} | ||
|
||
// Start creates listeners for HTTP and HTTPS and starts serving requests using the provided handler. The function blocks until both servers | ||
// are properly shut down. A shutdown can be initiated by cancelling the given context. | ||
func (srv MultiServer) Start(ctx context.Context, handler http.Handler) error { | ||
var wg sync.WaitGroup | ||
|
||
tlsListener, err := createTLSListener(srv.HTTPSPort, srv.CertFile, srv.KeyFile) | ||
if err != nil { | ||
return fmt.Errorf("failed to create TLS listener: %w", err) | ||
} | ||
|
||
wg.Add(1) | ||
|
||
go func() { | ||
defer wg.Done() | ||
startServer(ctx, handler, tlsListener, srv.Logger) | ||
}() | ||
|
||
listener, err := net.Listen("tcp", fmt.Sprintf(":%d", srv.HTTPPort)) | ||
if err != nil { | ||
return fmt.Errorf("failed to create TCP listener: %w", err) | ||
} | ||
|
||
wg.Add(1) | ||
|
||
go func() { | ||
defer wg.Done() | ||
startServer(ctx, handler, listener, srv.Logger) | ||
}() | ||
|
||
wg.Wait() | ||
|
||
return nil | ||
} | ||
|
||
func createTLSListener(port int, certFile, keyFile string) (net.Listener, error) { | ||
cert, err := tls.LoadX509KeyPair(certFile, keyFile) | ||
if err != nil { | ||
return nil, fmt.Errorf("unable to load TLS key pair: %w", err) | ||
} | ||
|
||
listener, err := tls.Listen("tcp", fmt.Sprintf(":%d", port), &tls.Config{Certificates: []tls.Certificate{cert}}) | ||
if err != nil { | ||
return nil, fmt.Errorf("unable to start TLS listener: %w", err) | ||
} | ||
|
||
return listener, nil | ||
} | ||
|
||
func startServer(ctx context.Context, hndlr http.Handler, listener net.Listener, logger *log.Logger) { | ||
srv := http.Server{ | ||
Addr: listener.Addr().String(), | ||
Handler: hndlr, | ||
} | ||
logger.Printf("https://" + srv.Addr) | ||
|
||
go func() { | ||
if err := srv.Serve(listener); err != http.ErrServerClosed { | ||
logger.Fatalf("server quit unexpectedly: %s", err) | ||
} | ||
}() | ||
<-ctx.Done() | ||
logger.Printf("shutting down %s", listener.Addr()) | ||
|
||
if err := srv.Shutdown(ctx); err != nil && err != context.Canceled { | ||
logger.Printf("error shutting down %s: %s", listener.Addr(), err) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,115 @@ | ||
package http_test | ||
|
||
import ( | ||
"context" | ||
"crypto/tls" | ||
"crypto/x509" | ||
"fmt" | ||
"io" | ||
"log" | ||
"math/rand" | ||
"net/http" | ||
"os" | ||
"testing" | ||
|
||
. "github.com/onsi/gomega" | ||
|
||
wegohttp "github.com/weaveworks/weave-gitops/pkg/http" | ||
) | ||
|
||
func TestMultiServerStartReturnsImmediatelyWithClosedContext(t *testing.T) { | ||
g := NewGomegaWithT(t) | ||
srv := wegohttp.MultiServer{ | ||
CertFile: "testdata/localhost.crt", | ||
KeyFile: "testdata/localhost.key", | ||
Logger: log.Default(), | ||
} | ||
ctx, cancel := context.WithCancel(context.Background()) | ||
cancel() | ||
g.Expect(srv.Start(ctx, nil)).To(Succeed()) | ||
} | ||
|
||
func TestMultiServerWithoutTLSConfigFailsToStart(t *testing.T) { | ||
g := NewGomegaWithT(t) | ||
srv := wegohttp.MultiServer{} | ||
ctx, cancel := context.WithCancel(context.Background()) | ||
cancel() | ||
|
||
err := srv.Start(ctx, nil) | ||
g.Expect(err).To(HaveOccurred()) | ||
g.Expect(err.Error()).To(HavePrefix("failed to create TLS listener")) | ||
} | ||
|
||
func TestMultiServerServesOverBothProtocols(t *testing.T) { | ||
g := NewGomegaWithT(t) | ||
|
||
httpPort := rand.Intn(49151-1024) + 1024 | ||
httpsPort := rand.Intn(49151-1024) + 1024 | ||
|
||
for httpPort == httpsPort { | ||
httpsPort = rand.Intn(49151-1024) + 1024 | ||
} | ||
|
||
srv := wegohttp.MultiServer{ | ||
HTTPPort: httpPort, | ||
HTTPSPort: httpsPort, | ||
CertFile: "testdata/localhost.crt", | ||
KeyFile: "testdata/localhost.key", | ||
Logger: log.Default(), | ||
} | ||
ctx, cancel := context.WithCancel(context.Background()) | ||
|
||
exitChan := make(chan struct{}) | ||
go func(exitChan chan<- struct{}) { | ||
hndlr := http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) { | ||
fmt.Fprintf(rw, "success") | ||
}) | ||
g.Expect(srv.Start(ctx, hndlr)).To(Succeed()) | ||
close(exitChan) | ||
}(exitChan) | ||
|
||
// test HTTP | ||
|
||
resp, err := http.Get(fmt.Sprintf("http://localhost:%d/", httpPort)) | ||
g.Expect(err).NotTo(HaveOccurred()) | ||
g.Expect(resp.StatusCode).To(Equal(http.StatusOK)) | ||
body, err := io.ReadAll(resp.Body) | ||
g.Expect(err).NotTo(HaveOccurred()) | ||
g.Expect(string(body)).To(Equal("success")) | ||
|
||
// test HTTPS | ||
|
||
certBytes, err := os.ReadFile("testdata/localhost.crt") | ||
g.Expect(err).NotTo(HaveOccurred()) | ||
|
||
rootCAs := x509.NewCertPool() | ||
rootCAs.AppendCertsFromPEM(certBytes) | ||
|
||
tr := &http.Transport{ | ||
TLSClientConfig: &tls.Config{ | ||
RootCAs: rootCAs, | ||
}, | ||
} | ||
c := http.Client{ | ||
Transport: tr, | ||
} | ||
resp, err = c.Get(fmt.Sprintf("https://localhost:%d/", httpsPort)) | ||
g.Expect(err).NotTo(HaveOccurred()) | ||
g.Expect(resp.StatusCode).To(Equal(http.StatusOK)) | ||
body, err = io.ReadAll(resp.Body) | ||
g.Expect(err).NotTo(HaveOccurred()) | ||
g.Expect(string(body)).To(Equal("success")) | ||
|
||
cancel() | ||
g.Eventually(exitChan, "3s").Should(BeClosed()) | ||
|
||
// ensure both ports are freed up | ||
|
||
_, err = c.Get(fmt.Sprintf("https://localhost:%d/", httpsPort)) | ||
g.Expect(err).To(HaveOccurred()) | ||
g.Expect(err.Error()).To(ContainSubstring("connection refused")) | ||
|
||
_, err = http.Get(fmt.Sprintf("http://localhost:%d/", httpPort)) | ||
g.Expect(err).To(HaveOccurred()) | ||
g.Expect(err.Error()).To(ContainSubstring("connection refused")) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
-----BEGIN CERTIFICATE----- | ||
MIIEBTCCAu2gAwIBAgIUX5xBltyah5x8qA6RrJ11nuTKNq8wDQYJKoZIhvcNAQEL | ||
BQAwRTELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoM | ||
GEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDAeFw0yMjEyMDExNjAxMjRaFw0zMjEx | ||
MjgxNjAxMjRaMEUxCzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEw | ||
HwYDVQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQwggEiMA0GCSqGSIb3DQEB | ||
AQUAA4IBDwAwggEKAoIBAQDBiUW6/gQwlU8bbQjt76pY59tgxANlGeuU8DyG7QwB | ||
RWSenrzQvvHhAy/+mexaAf4VheAU+efmYHtACgzzeL7c9sS4j5OiJVOgJ9DKg/AI | ||
6fz+mSFWJ6/ZT7YASG3LprGnoWHfTgGWMah+5rDwys+j/7M3f7RsUUB26hVuSgZJ | ||
d6KU70Fge80QMxJyu+twZpMKBrsm+FGM6f+JHj7fKNiHK/LeuTee9cCEJGRPtIHI | ||
T2NYEF9u+MR8b8MEzGL0v4HpEClhFVIb4WH0Gr5K6yFbVdi3CXYep4fJ7ggMwxJQ | ||
PxqU/mn15UpMapkPsfDtTEhH4kBbtaBimUayMKez9x25AgMBAAGjgewwgekwHQYD | ||
VR0OBBYEFEU59EKP2m+ZEayH7jmRfZlhJCAEMIGABgNVHSMEeTB3gBRFOfRCj9pv | ||
mRGsh+45kX2ZYSQgBKFJpEcwRTELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUt | ||
U3RhdGUxITAfBgNVBAoMGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZIIUX5xBltya | ||
h5x8qA6RrJ11nuTKNq8wDAYDVR0TBAUwAwEB/zALBgNVHQ8EBAMCAvwwFAYDVR0R | ||
BA0wC4IJbG9jYWxob3N0MBQGA1UdEgQNMAuCCWxvY2FsaG9zdDANBgkqhkiG9w0B | ||
AQsFAAOCAQEAhAvQfrfGr3cKoAEijjYtQ7hSAnTwtxmDUNXUP8O+sMaETEo/GMPI | ||
BGqR7oMTcvWJVbEYNifk68JrnXeNdggRSbM+wV2bCG1/Km+hhHxQp/z/U3uvn54U | ||
cF4INCBvoOk77UteMt77OGex+gasw2Wwnas+X+/m1ezveoxYGxJ9RnRpuFcU7csp | ||
N7cZizrRjGbpg8H+QIrq5Nf86Zo9kbBzyjPMV8Yw68eeiwJzNy3qbgAF1J1YjwXw | ||
Mp4mDIJCY8UB+We35y4V1BOZhFJDXuqD/R4HbKn9HZo3PmFeLo15bUkmzw9n9JaC | ||
Da8Nw7zO1EK8ifcViclb9Ubq3yyUR620zg== | ||
-----END CERTIFICATE----- |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
-----BEGIN RSA PRIVATE KEY----- | ||
MIIEpAIBAAKCAQEAwYlFuv4EMJVPG20I7e+qWOfbYMQDZRnrlPA8hu0MAUVknp68 | ||
0L7x4QMv/pnsWgH+FYXgFPnn5mB7QAoM83i+3PbEuI+ToiVToCfQyoPwCOn8/pkh | ||
Viev2U+2AEhty6axp6Fh304BljGofuaw8MrPo/+zN3+0bFFAduoVbkoGSXeilO9B | ||
YHvNEDMScrvrcGaTCga7JvhRjOn/iR4+3yjYhyvy3rk3nvXAhCRkT7SByE9jWBBf | ||
bvjEfG/DBMxi9L+B6RApYRVSG+Fh9Bq+SushW1XYtwl2HqeHye4IDMMSUD8alP5p | ||
9eVKTGqZD7Hw7UxIR+JAW7WgYplGsjCns/cduQIDAQABAoIBAQCljAFsmToGQMGB | ||
KTxZIwfosrNxy1lIEurz5KcxlvUM5UnTcN78BEkseyiDtTB6MXgg+voZl0bpRiBH | ||
QBGh9ef1ZNQTNyVGrn0g4s3zXPZm+ZfiRCRC6QG/djKtfUcFy5ntVNs+QyCSU/nY | ||
SwaRgjopA2FOmNtBSCNHVKZuR72m+sIasBC4lzZ6UJXiN8ccN6Wl2ey8Pq+Dt5Vw | ||
4j45naYACxdwnrTeAwaRGKCg6zSOb4SFM4/CSZ0/pzD2u79/StpJk43pSr9n8EON | ||
OZCH9GpIunsXEVLR/xR5k+Cr30ZAtvKY59rZQhKt3Q6LAE9yK4NkglV08yY314zG | ||
ELd/qLaNAoGBAPucQLxhQhwzSMRQiaq9cz6fyLPc1xqrD1piCTP36NOXyra76aIW | ||
/AbdVvSz8rfgGITFSRJ9XEaDvre4Po21JvXIXfk5oemp4Vmoo2lRS/oD56DbEzlP | ||
vDVid0Mk/PsK1m9ulxE7ta0B46fYCwTg+4zY+Mf6/nX4jw/voWfnuT2LAoGBAMTp | ||
pcugN5lbLWf7RaHH4DgU2Te8bPnFGiqCa6lkmrNKOR6ZtOdZE6pfAVdl9obBsc/l | ||
xGx8caLxCl1GVL/Nq79SdY4NuC/r0O9Eeosa+yf+b/fRrQtXWhjEEG9ITZ5PhLhI | ||
xkml1ma5kyNJF7X9qUkSRAWXCrFinG8mfRXPhQJLAoGAOsRNDnK86S9FQKz66okj | ||
QK47R18+Unk/tcGOGrg9hiY+751GPViW9td9ttvMxguuTlxx68Kh6cpdojWDTr/P | ||
4Loy0MIYQiYufy13NWMKltOQpy5j+A/airF734/lEpF+cjpnSFwk28rELHC2aiZO | ||
OqB2wuapxk4OxA8ZKNajmm8CgYEAqm1W9AB9XpvNltuhjr5B0AgrYNQStbLkTLqI | ||
mBnc0ySAf32lVz5/iMuli5FSZ5upXDiPYx3p9I8O22AN5dwKtBKYcBRrv/4n3Y61 | ||
SURW8GyFWEX/sXsvHZREbSx1EXndcup5xDBmeo5PTRDsFrWvGPFYMkZiGNkyb/kt | ||
9fygMDUCgYBiHmq2MBSgV8LTJrhvZqvW+ROj7jgPogsTix30PYIXoH1NnSH/YTVb | ||
D4Ede+YfVD/lDEz10WX8F2dgzupaPjk7KtMU+JRKX6Ran5pAZEgFxZExPuqK2g7h | ||
Hbpt2kO1W/nuz1c2DU7dxamLO3vH6OrqDVtr6PjfhKPwFgT7zigi2A== | ||
-----END RSA PRIVATE KEY----- |