feat(httpapi): harden control plane and speed up prefix refresh
Очередь job получила lease и fencing, телеметрия и прогресс deploy сохраняются, ревизия пишет manifest входов. Обновление префиксов не переписывает неизменённый снимок, большие наборы идут через COPY, а устаревшая ревизия не попадает в deploy. Бандл ноды кэшируется, prune и постановка job больше не держат горячий путь. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
1 parent
924f111e48
commit
f8e0520817
197 files changed
+18655
-1416
No files matched your search
@@ -0,0 +1,197 @@
|
||||
package pipeline
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"net/netip"
|
||||
"net/url"
|
||||
"strings"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestCDNDialPinsAndChecksEveryAnswer(t *testing.T) {
|
||||
t.Setenv("EVOBGP_CDN_ALLOW_PRIVATE", "0")
|
||||
answers := [][]net.IP{
|
||||
{net.ParseIP("203.0.113.10")},
|
||||
{net.ParseIP("127.0.0.1")},
|
||||
}
|
||||
lookups := 0
|
||||
var dialed []string
|
||||
lookup := func(context.Context, string) ([]net.IP, error) {
|
||||
answer := answers[lookups]
|
||||
lookups++
|
||||
return answer, nil
|
||||
}
|
||||
dial := func(_ context.Context, _, address string) (net.Conn, error) {
|
||||
dialed = append(dialed, address)
|
||||
return nil, nil
|
||||
}
|
||||
if _, err := cdnDial(context.Background(), "tcp", "cdn.example:443", lookup, dial); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if _, err := cdnDial(context.Background(), "tcp", "cdn.example:443", lookup, dial); err == nil {
|
||||
t.Fatal("changed DNS answer must be rejected before dial")
|
||||
}
|
||||
if lookups != 2 || len(dialed) != 1 || dialed[0] != "203.0.113.10:443" {
|
||||
t.Fatalf("lookups=%d dialed=%v", lookups, dialed)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCDNDialRejectsMixedAndMappedPrivateAnswers(t *testing.T) {
|
||||
t.Setenv("EVOBGP_CDN_ALLOW_PRIVATE", "0")
|
||||
tests := []struct {
|
||||
name string
|
||||
ips []net.IP
|
||||
}{
|
||||
{"mixed", []net.IP{net.ParseIP("203.0.113.10"), net.ParseIP("10.0.0.5")}},
|
||||
{"mapped loopback", []net.IP{net.ParseIP("::ffff:127.0.0.1")}},
|
||||
{"mapped private", []net.IP{net.ParseIP("::ffff:10.0.0.1")}},
|
||||
}
|
||||
for _, tc := range tests {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
called := false
|
||||
_, err := cdnDial(context.Background(), "tcp", "cdn.example:443", func(context.Context, string) ([]net.IP, error) {
|
||||
return tc.ips, nil
|
||||
}, func(context.Context, string, string) (net.Conn, error) {
|
||||
called = true
|
||||
return nil, nil
|
||||
})
|
||||
if err == nil || called {
|
||||
t.Fatalf("want rejection before dial, got err=%v called=%v", err, called)
|
||||
}
|
||||
})
|
||||
}
|
||||
if !isBlockedCDNIP(netip.MustParseAddr("::ffff:169.254.169.254")) {
|
||||
t.Fatal("mapped metadata address must be blocked")
|
||||
}
|
||||
}
|
||||
|
||||
func TestCDNDialFallsBackOnlyToCheckedIP(t *testing.T) {
|
||||
t.Setenv("EVOBGP_CDN_ALLOW_PRIVATE", "0")
|
||||
var dialed []string
|
||||
_, err := cdnDial(context.Background(), "tcp", "cdn.example:443", func(context.Context, string) ([]net.IP, error) {
|
||||
return []net.IP{net.ParseIP("2001:db8::1"), net.ParseIP("203.0.113.10")}, nil
|
||||
}, func(_ context.Context, _, address string) (net.Conn, error) {
|
||||
dialed = append(dialed, address)
|
||||
if len(dialed) == 1 {
|
||||
return nil, errors.New("unreachable")
|
||||
}
|
||||
return nil, nil
|
||||
})
|
||||
if err != nil || len(dialed) != 2 || dialed[0] != "[2001:db8::1]:443" || dialed[1] != "203.0.113.10:443" {
|
||||
t.Fatalf("err=%v dialed=%v", err, dialed)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCDNDialStopsOnCancellation(t *testing.T) {
|
||||
t.Setenv("EVOBGP_CDN_ALLOW_PRIVATE", "0")
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
called := 0
|
||||
_, err := cdnDial(ctx, "tcp", "cdn.example:443", func(context.Context, string) ([]net.IP, error) {
|
||||
return []net.IP{net.ParseIP("203.0.113.10"), net.ParseIP("203.0.113.11")}, nil
|
||||
}, func(context.Context, string, string) (net.Conn, error) {
|
||||
called++
|
||||
cancel()
|
||||
return nil, errors.New("unreachable")
|
||||
})
|
||||
if !errors.Is(err, context.Canceled) || called != 1 {
|
||||
t.Fatalf("err=%v dial attempts=%d", err, called)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCDNClientRejectsUnsafeRedirectsAndProxy(t *testing.T) {
|
||||
t.Setenv("EVOBGP_CDN_ALLOW_PRIVATE", "0")
|
||||
t.Setenv("HTTPS_PROXY", "http://127.0.0.1:8080")
|
||||
client := NewCDNHTTPClient()
|
||||
tr, ok := client.Transport.(*cdnRoundTripper)
|
||||
if !ok || tr.transport.Proxy != nil {
|
||||
t.Fatal("CDN transport must dial directly")
|
||||
}
|
||||
for _, raw := range []string{
|
||||
"https://127.0.0.1/secret",
|
||||
"https://[::ffff:127.0.0.1]/secret",
|
||||
"http://cdn.example/unsafe",
|
||||
"https://user:password@cdn.example/secret",
|
||||
} {
|
||||
t.Run(raw, func(t *testing.T) {
|
||||
u, err := url.Parse(raw)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := client.CheckRedirect(&http.Request{URL: u}, nil); err == nil {
|
||||
t.Fatal("unsafe redirect accepted")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
type cdnTestRoundTripper func(*http.Request) (*http.Response, error)
|
||||
|
||||
func (f cdnTestRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) { return f(req) }
|
||||
|
||||
func TestCDNClientRejectsOpaqueTransport(t *testing.T) {
|
||||
_, err := secureCDNClient(&http.Client{Transport: cdnTestRoundTripper(func(*http.Request) (*http.Response, error) {
|
||||
return nil, errors.New("must not be called")
|
||||
})})
|
||||
if err == nil {
|
||||
t.Fatal("opaque transport could bypass checked dial")
|
||||
}
|
||||
}
|
||||
|
||||
func TestCDNClientRejectsPrivateTLSRedirectBeforeDial(t *testing.T) {
|
||||
t.Setenv("EVOBGP_CDN_ALLOW_PRIVATE", "0")
|
||||
var hits atomic.Int32
|
||||
var secretHits atomic.Int32
|
||||
srv := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
hits.Add(1)
|
||||
if r.URL.Path == "/secret" {
|
||||
secretHits.Add(1)
|
||||
}
|
||||
http.Redirect(w, r, "https://private.example.com/secret", http.StatusFound)
|
||||
}))
|
||||
defer srv.Close()
|
||||
|
||||
client := NewCDNHTTPClient()
|
||||
tr := client.Transport.(*cdnRoundTripper).transport
|
||||
tr.TLSClientConfig = srv.Client().Transport.(*http.Transport).TLSClientConfig.Clone()
|
||||
actual := srv.Listener.Addr().String()
|
||||
var dialed []string
|
||||
var dialedMu sync.Mutex
|
||||
dialer := &net.Dialer{}
|
||||
tr.DialContext = func(ctx context.Context, network, address string) (net.Conn, error) {
|
||||
return cdnDial(ctx, network, address, func(_ context.Context, host string) ([]net.IP, error) {
|
||||
if host == "private.example.com" {
|
||||
return []net.IP{net.ParseIP("127.0.0.1")}, nil
|
||||
}
|
||||
return []net.IP{net.ParseIP("203.0.113.10")}, nil
|
||||
}, func(ctx context.Context, network, checkedAddress string) (net.Conn, error) {
|
||||
dialedMu.Lock()
|
||||
dialed = append(dialed, checkedAddress)
|
||||
dialedMu.Unlock()
|
||||
return dialer.DialContext(ctx, network, actual)
|
||||
})
|
||||
}
|
||||
_, port, err := net.SplitHostPort(actual)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
resp, err := client.Get("https://cdn.example.com:" + port + "/redirect")
|
||||
if resp != nil {
|
||||
_ = resp.Body.Close()
|
||||
}
|
||||
if err == nil || !strings.Contains(err.Error(), "blocked address") {
|
||||
t.Fatalf("private TLS redirect was not rejected: %v", err)
|
||||
}
|
||||
dialedMu.Lock()
|
||||
dialedSnapshot := append([]string(nil), dialed...)
|
||||
dialedMu.Unlock()
|
||||
if hits.Load() != 1 || secretHits.Load() != 0 || len(dialedSnapshot) != 1 || dialedSnapshot[0] != "203.0.113.10:"+port {
|
||||
t.Fatalf("hits=%d secretHits=%d dialed=%v", hits.Load(), secretHits.Load(), dialedSnapshot)
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user