host_secure_test.go
1 // Copyright 2026 Alibaba Group Holding Ltd. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package proxy 16 17 import ( 18 "context" 19 "fmt" 20 "net/http" 21 "net/http/httptest" 22 "strconv" 23 "testing" 24 "time" 25 26 "github.com/alibaba/opensandbox/ingress/pkg/sandbox" 27 "github.com/alibaba/opensandbox/ingress/pkg/signature" 28 "github.com/stretchr/testify/assert" 29 ) 30 31 // routeTestProvider stubs sandbox lookup for routing tests (non-empty access-token annotation). 32 type routeTestProvider struct{} 33 34 func (routeTestProvider) GetEndpoint(string) (*sandbox.EndpointInfo, error) { 35 return &sandbox.EndpointInfo{ 36 Endpoint: "127.0.0.1", 37 SecureAccessToken: "annot-secret", 38 }, nil 39 } 40 41 func (routeTestProvider) Start(context.Context) error { return nil } 42 43 func TestGetSandboxHostDefinition_HeaderSecureSig(t *testing.T) { 44 secret := []byte("ingress-test-secret") 45 sb := "gamma" 46 port := 7777 47 e := strconv.FormatUint(uint64(time.Now().Add(1*time.Hour).Unix()), 36) 48 sig := signature.ExpectedHex8(signature.Inner(secret, signature.CanonicalBytes(sb, port, e))) + "k" 49 50 p := &Proxy{ 51 mode: ModeHeader, 52 sandboxProvider: routeTestProvider{}, 53 secure: &signature.Verifier{Keys: map[string][]byte{"k": secret}}, 54 } 55 label := fmt.Sprintf("%s-%d-%s-%s", sb, port, e, sig) 56 r := httptest.NewRequest(http.MethodGet, "http://example/", nil) 57 r.Host = label + ".gw.example.com" 58 host, code, err := p.getSandboxHostDefinition(r) 59 assert.NoError(t, err) 60 assert.Equal(t, 0, code) 61 assert.Equal(t, sb, host.ingressKey) 62 assert.Equal(t, port, host.port) 63 } 64 65 func TestGetSandboxHostDefinition_HeaderSecureHeaderBypassSignedShape(t *testing.T) { 66 sb := "gamma" 67 port := 7777 68 // 9 characters: 8-hex + 1 key id (header bypasses HMAC, but must parse as OSEP-0011) 69 sig := "aabbccdd" + "1" 70 e := "0" 71 label := fmt.Sprintf("%s-%d-%s-%s", sb, port, e, sig) 72 73 p := &Proxy{mode: ModeHeader, sandboxProvider: routeTestProvider{}} 74 r := httptest.NewRequest(http.MethodGet, "http://example/", nil) 75 r.Host = label + ".gw.example.com" 76 r.Header.Set(signature.OpenSandboxSecureAccessCanonical, "annot-secret") 77 host, code, err := p.getSandboxHostDefinition(r) 78 assert.NoError(t, err) 79 assert.Equal(t, 0, code) 80 assert.Equal(t, sb, host.ingressKey) 81 assert.Equal(t, port, host.port) 82 } 83 84 func TestGetSandboxHostDefinition_HeaderSecureHeaderBypassLegacy(t *testing.T) { 85 p := &Proxy{mode: ModeHeader, sandboxProvider: routeTestProvider{}} 86 r := httptest.NewRequest(http.MethodGet, "http://example/", nil) 87 r.Host = "mysb-9090.example.com" 88 r.Header.Set(signature.OpenSandboxSecureAccessCanonical, "annot-secret") 89 host, code, err := p.getSandboxHostDefinition(r) 90 assert.NoError(t, err) 91 assert.Equal(t, 0, code) 92 assert.Equal(t, "mysb", host.ingressKey) 93 assert.Equal(t, 9090, host.port) 94 } 95 96 func TestGetSandboxHostDefinition_URISecureSig(t *testing.T) { 97 secret := []byte("uri-mode-secret") 98 sb := "d-e" 99 port := 3000 100 e := strconv.FormatUint(uint64(time.Now().Add(1*time.Hour).Unix()), 36) 101 sig := signature.ExpectedHex8(signature.Inner(secret, signature.CanonicalBytes(sb, port, e))) + "a" 102 103 p := &Proxy{ 104 mode: ModeURI, 105 sandboxProvider: routeTestProvider{}, 106 secure: &signature.Verifier{Keys: map[string][]byte{"a": secret}}, 107 } 108 path := fmt.Sprintf("/%s/%d/%s/%s/api/x", sb, port, e, sig) 109 r := httptest.NewRequest(http.MethodGet, "http://ingress.local"+path, nil) 110 host, code, err := p.getSandboxHostDefinition(r) 111 assert.NoError(t, err) 112 assert.Equal(t, 0, code) 113 assert.Equal(t, sb, host.ingressKey) 114 assert.Equal(t, port, host.port) 115 assert.Equal(t, "/api/x", host.requestURI) 116 } 117 118 func TestGetSandboxHostDefinition_URISecureHeaderBypass(t *testing.T) { 119 sb := "d-e" 120 port := 3000 121 e := "0" 122 sig := "cafebabe" + "0" 123 124 p := &Proxy{mode: ModeURI, sandboxProvider: routeTestProvider{}} 125 path := fmt.Sprintf("/%s/%d/%s/%s/api/x", sb, port, e, sig) 126 r := httptest.NewRequest(http.MethodGet, "http://ingress.local"+path, nil) 127 r.Header.Set(signature.OpenSandboxSecureAccessCanonical, "annot-secret") 128 host, code, err := p.getSandboxHostDefinition(r) 129 assert.NoError(t, err) 130 assert.Equal(t, 0, code) 131 assert.Equal(t, sb, host.ingressKey) 132 assert.Equal(t, port, host.port) 133 assert.Equal(t, "/api/x", host.requestURI) 134 }