/ components / ingress / pkg / proxy / host_secure_test.go
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  }