carlos / platform Public

Clone
git clone https://amadan.net/carlos/platform

Plain git — no account needed to clone.

Download

Download this file

1package console
2
3import (
4 "context"
5 "encoding/hex"
6 "net/http"
7 "net/http/httptest"
8 "path/filepath"
9 "testing"
10 "time"
11
12 "github.com/carlosframework/platform/internal/edgeproof"
13)
14
15// TestClientIPLoopbackNeedsTheEdgeProof: a process on the console's box can
16// reach 127.0.0.1:8090 as easily as the edge can, so once the edge signs its
17// X-Forwarded-For, a loopback chain without that signature is a string the
18// caller typed and counts against the peer (discussion 214, item 5). Before
19// the first signed request the old trust stands, so a console rolled ahead
20// of its edge does not put every client in one rate-limit bucket; after it,
21// the latch holds across a restart.
22func TestClientIPLoopbackNeedsTheEdgeProof(t *testing.T) {
23 fx := newConsoleFixture(t)
24 key := []byte("0123456789abcdef0123456789abcdef")
25 if _, err := fx.rel.Put(context.Background(), edgeproof.MeshKeyObject, []byte(hex.EncodeToString(key)), ""); err != nil {
26 t.Fatal(err)
27 }
28 req := func(xff, proof string) *http.Request {
29 r := httptest.NewRequest(http.MethodPost, "/api/cli/auth/start", nil)
30 r.RemoteAddr = "127.0.0.1:50000"
31 r.Header.Set("X-Forwarded-For", xff)
32 if proof != "" {
33 r.Header.Set(edgeproof.Header, proof)
34 }
35 return r
36 }
37
38 // Before any proof: an unsigned chain is still believed.
39 if got := fx.srv.clientIP(req("9.9.9.9", "")); got != "9.9.9.9" {
40 t.Fatalf("unsigned chain before any proof = %q, want the old trust (9.9.9.9)", got)
41 }
42 // A forged proof is not a proof, and does not close the latch.
43 if got := fx.srv.clientIP(req("9.9.9.9", edgeproof.Sign([]byte("not the mesh key"), "example.com", "9.9.9.9", time.Now()))); got != "9.9.9.9" {
44 t.Fatalf("forged proof before any real one = %q, want the old trust", got)
45 }
46 if seen, _ := fx.st.EdgeProofSeen(); seen {
47 t.Fatal("a forged proof closed the latch")
48 }
49
50 // The edge's signed chain is believed, and closes the latch.
51 if got := fx.srv.clientIP(req("1.2.3.4", edgeproof.Sign(key, "example.com", "1.2.3.4", time.Now()))); got != "1.2.3.4" {
52 t.Fatalf("signed chain = %q, want 1.2.3.4", got)
53 }
54 // From now on: no proof, a forged proof, a proof for another chain, or a
55 // stale one all count against the peer.
56 for what, r := range map[string]*http.Request{
57 "no proof": req("5.6.7.8", ""),
58 "forged": req("5.6.7.8", edgeproof.Sign([]byte("not the mesh key"), "example.com", "5.6.7.8", time.Now())),
59 "other chain": req("5.6.7.8", edgeproof.Sign(key, "example.com", "1.2.3.4", time.Now())),
60 "stale": req("5.6.7.8", edgeproof.Sign(key, "example.com", "5.6.7.8", time.Now().Add(-2*edgeproof.Skew))),
61 "extra xff line": func() *http.Request {
62 r := req("1.2.3.4", edgeproof.Sign(key, "example.com", "1.2.3.4", time.Now()))
63 r.Header.Add("X-Forwarded-For", "5.6.7.8")
64 return r
65 }(),
66 } {
67 if got := fx.srv.clientIP(r); got != "127.0.0.1" {
68 t.Errorf("%s after the latch = %q, want the peer 127.0.0.1", what, got)
69 }
70 }
71 // A proof is bound to the host it was minted for: a tenant app on this
72 // box sees valid proofs on its own traffic, and must not be able to
73 // replay one here as the visitor it names.
74 if got := fx.srv.clientIP(req("5.6.7.8", edgeproof.Sign(key, "tenant.example.net", "5.6.7.8", time.Now()))); got != "127.0.0.1" {
75 t.Errorf("proof minted for another host = %q, want the peer 127.0.0.1", got)
76 }
77 // The host's port and case are not part of what is bound.
78 spelled := req("5.6.7.8", edgeproof.Sign(key, "Example.COM", "5.6.7.8", time.Now()))
79 spelled.Host = "example.com:443"
80 if got := fx.srv.clientIP(spelled); got != "5.6.7.8" {
81 t.Errorf("proof with the host in another spelling = %q, want 5.6.7.8", got)
82 }
83 // A signed multi-element chain still yields its last element.
84 if got := fx.srv.clientIP(req("9.9.9.9, 1.2.3.4", edgeproof.Sign(key, "example.com", "9.9.9.9, 1.2.3.4", time.Now()))); got != "1.2.3.4" {
85 t.Fatalf("signed chain of two = %q, want 1.2.3.4", got)
86 }
87
88 // The latch is stored: a fresh process on the same database starts closed.
89 again := &Server{cfg: fx.srv.cfg, store: fx.st}
90 if got := again.clientIP(req("5.6.7.8", "")); got != "127.0.0.1" {
91 t.Fatalf("unsigned chain after a restart = %q, want the peer", got)
92 }
93 // And a configured edge address is still believed without a proof: it is
94 // not a peer a tenant on this box can be.
95 r := req("5.6.7.8", "")
96 r.RemoteAddr = "198.51.100.9:9"
97 edged := &Server{cfg: Config{EdgeIPs: []string{"198.51.100.9"}, Releases: fx.rel}, store: fx.st}
98 if got := edged.clientIP(r); got != "5.6.7.8" {
99 t.Fatalf("configured edge peer = %q, want 5.6.7.8", got)
100 }
101}
102
103// TestEdgeProofSeenMark: the stored latch is one row and keeps its first
104// moment.
105func TestEdgeProofSeenMark(t *testing.T) {
106 st, err := Open(filepath.Join(t.TempDir(), "console.db"))
107 if err != nil {
108 t.Fatal(err)
109 }
110 defer st.Close()
111 if seen, err := st.EdgeProofSeen(); err != nil || seen {
112 t.Fatalf("fresh store seen = %v, %v; want false, nil", seen, err)
113 }
114 for i := 0; i < 2; i++ {
115 if err := st.MarkEdgeProofSeen(time.Now()); err != nil {
116 t.Fatal(err)
117 }
118 }
119 if seen, err := st.EdgeProofSeen(); err != nil || !seen {
120 t.Fatalf("marked store seen = %v, %v; want true, nil", seen, err)
121 }
122}
123