v0.20.0 mtls hardening, fail-closed config, startup warnings, sink fixes
This commit is contained in:
1 parent
815ef345ea
commit
f192c26fa2
37 files changed
+1042
-152
No files matched your search
@@ -107,7 +107,7 @@ func NewHTTPSinkPlugin(
|
||||
Host: DefaultHTTPHost,
|
||||
WriteTimeoutMS: 0, // SSE indefinite streaming
|
||||
}
|
||||
if err := lconfig.ScanMap(configMap, opts); err != nil {
|
||||
if err := config.Scan(configMap, opts); err != nil {
|
||||
return nil, fmt.Errorf("failed to parse config: %w", err)
|
||||
}
|
||||
if err := lconfig.Port(opts.Port); err != nil {
|
||||
@@ -167,12 +167,8 @@ func NewHTTPSinkPlugin(
|
||||
"tls", tlsCfg != nil,
|
||||
"mtls", tlsCfg != nil && tlsCfg.ClientAuth == tls.RequireAndVerifyClientCert,
|
||||
"auth", authPolicy.Describe())
|
||||
if authPolicy.Unrestricted() {
|
||||
logger.Warn("msg", "Auth policy admits any identity the configured CA vouches for",
|
||||
"component", "http_sink",
|
||||
"instance_id", id,
|
||||
"hint", "set auth.allow or auth.allow_patterns to authorize named clients")
|
||||
}
|
||||
tlsx.LogWarnings(logger, "http_sink", id, opts.TLS, true)
|
||||
authPolicy.LogStartup(logger, "http_sink", id, false)
|
||||
return h, nil
|
||||
}
|
||||
|
||||
@@ -407,7 +403,10 @@ func (h *HTTPSink) handleStream(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
w.Header().Set("Content-Type", "text/event-stream")
|
||||
w.Header().Set("Cache-Control", "no-cache")
|
||||
w.Header().Set("Access-Control-Allow-Origin", "*")
|
||||
if !h.auth.Enabled() {
|
||||
// An authenticated stream is not offered to every web origin
|
||||
w.Header().Set("Access-Control-Allow-Origin", "*")
|
||||
}
|
||||
w.Header().Set("X-Accel-Buffering", "no")
|
||||
w.WriteHeader(http.StatusOK)
|
||||
|
||||
|
||||
@@ -2,13 +2,18 @@ package http
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"os"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"logwisp/internal/sink"
|
||||
"logwisp/internal/testutil"
|
||||
)
|
||||
|
||||
func TestStatusReportsQueueAndConnectionBounds(t *testing.T) {
|
||||
@@ -119,3 +124,48 @@ func TestHeadOnStreamPathIsRefused(t *testing.T) {
|
||||
t.Errorf("sessions after HEAD = %d, want 0", n)
|
||||
}
|
||||
}
|
||||
|
||||
// A stream gated by an auth policy is not offered to every web origin; an
|
||||
// open stream keeps the wildcard so browser dashboards still work.
|
||||
func TestWildcardCORSOnlyWithoutAuth(t *testing.T) {
|
||||
pki := testutil.NewPKI(t, "viewer-01")
|
||||
caPEM, err := os.ReadFile(pki.CA)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
roots := x509.NewCertPool()
|
||||
roots.AppendCertsFromPEM(caPEM)
|
||||
clientCert, err := tls.LoadX509KeyPair(pki.ClientCert, pki.ClientKey)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
open, _ := newTestHTTPSink(t, nil)
|
||||
gated, _ := newTestHTTPSink(t, map[string]any{
|
||||
"tls": map[string]any{"enabled": true, "cert_file": pki.ServerCert, "key_file": pki.ServerKey,
|
||||
"client_auth": true, "client_ca_file": pki.CA},
|
||||
"auth": map[string]any{"type": "mtls", "allow": []any{"viewer-01"}},
|
||||
})
|
||||
for _, tc := range []struct {
|
||||
name string
|
||||
sink *HTTPSink
|
||||
want string
|
||||
}{{"open", open, "*"}, {"gated", gated, ""}} {
|
||||
client, baseURL := serveTestHTTPSink(t, tc.sink)
|
||||
if tc.sink.tlsConfig != nil {
|
||||
client.Transport = &http.Transport{TLSClientConfig: &tls.Config{RootCAs: roots, Certificates: []tls.Certificate{clientCert}}}
|
||||
baseURL = "https" + strings.TrimPrefix(baseURL, "http")
|
||||
}
|
||||
resp, err := client.Get(baseURL + "/stream")
|
||||
if err != nil {
|
||||
t.Fatalf("%s: %v", tc.name, err)
|
||||
}
|
||||
resp.Body.Close()
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
t.Fatalf("%s: GET /stream = %d", tc.name, resp.StatusCode)
|
||||
}
|
||||
if got := resp.Header.Get("Access-Control-Allow-Origin"); got != tc.want {
|
||||
t.Errorf("%s: Access-Control-Allow-Origin = %q, want %q", tc.name, got, tc.want)
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user