mirror of
https://github.com/tailscale/tailscale.git
synced 2025-02-22 21:08:38 +00:00

cmd/containerboot: load containerboot serve config that does not contain HTTPS endpoint in tailnets with HTTPS disabled Fixes an issue where, if a tailnet has HTTPS disabled, no serve config set via TS_SERVE_CONFIG was loaded, even if it does not contain an HTTPS endpoint. Now for tailnets with HTTPS disabled serve config provided to containerboot is considered invalid (and therefore not loaded) only if there is an HTTPS endpoint defined in the config. Fixes tailscale/tailscale#14495 Signed-off-by: Irbe Krumina <irbe@tailscale.com>
268 lines
5.3 KiB
Go
268 lines
5.3 KiB
Go
// Copyright (c) Tailscale Inc & AUTHORS
|
|
// SPDX-License-Identifier: BSD-3-Clause
|
|
|
|
//go:build linux
|
|
|
|
package main
|
|
|
|
import (
|
|
"context"
|
|
"os"
|
|
"path/filepath"
|
|
"testing"
|
|
|
|
"github.com/google/go-cmp/cmp"
|
|
"tailscale.com/client/tailscale"
|
|
"tailscale.com/ipn"
|
|
"tailscale.com/kube/kubetypes"
|
|
)
|
|
|
|
func TestUpdateServeConfig(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
sc *ipn.ServeConfig
|
|
certDomain string
|
|
wantCall bool
|
|
}{
|
|
{
|
|
name: "no_https_no_cert_domain",
|
|
sc: &ipn.ServeConfig{
|
|
TCP: map[uint16]*ipn.TCPPortHandler{
|
|
80: {HTTP: true},
|
|
},
|
|
},
|
|
certDomain: kubetypes.ValueNoHTTPS, // tailnet has HTTPS disabled
|
|
wantCall: true, // should set serve config as it doesn't have HTTPS endpoints
|
|
},
|
|
{
|
|
name: "https_with_cert_domain",
|
|
sc: &ipn.ServeConfig{
|
|
TCP: map[uint16]*ipn.TCPPortHandler{
|
|
443: {HTTPS: true},
|
|
},
|
|
Web: map[ipn.HostPort]*ipn.WebServerConfig{
|
|
"${TS_CERT_DOMAIN}:443": {
|
|
Handlers: map[string]*ipn.HTTPHandler{
|
|
"/": {Proxy: "http://10.0.1.100:8080"},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
certDomain: "test-node.tailnet.ts.net",
|
|
wantCall: true,
|
|
},
|
|
{
|
|
name: "https_without_cert_domain",
|
|
sc: &ipn.ServeConfig{
|
|
TCP: map[uint16]*ipn.TCPPortHandler{
|
|
443: {HTTPS: true},
|
|
},
|
|
},
|
|
certDomain: kubetypes.ValueNoHTTPS,
|
|
wantCall: false, // incorrect configuration- should not set serve config
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
fakeLC := &fakeLocalClient{}
|
|
err := updateServeConfig(context.Background(), tt.sc, tt.certDomain, fakeLC)
|
|
if err != nil {
|
|
t.Errorf("updateServeConfig() error = %v", err)
|
|
}
|
|
if fakeLC.setServeCalled != tt.wantCall {
|
|
t.Errorf("SetServeConfig() called = %v, want %v", fakeLC.setServeCalled, tt.wantCall)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestReadServeConfig(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
gotSC string
|
|
certDomain string
|
|
wantSC *ipn.ServeConfig
|
|
wantErr bool
|
|
}{
|
|
{
|
|
name: "empty_file",
|
|
},
|
|
{
|
|
name: "valid_config_with_cert_domain_placeholder",
|
|
gotSC: `{
|
|
"TCP": {
|
|
"443": {
|
|
"HTTPS": true
|
|
}
|
|
},
|
|
"Web": {
|
|
"${TS_CERT_DOMAIN}:443": {
|
|
"Handlers": {
|
|
"/api": {
|
|
"Proxy": "https://10.2.3.4/api"
|
|
}}}}}`,
|
|
certDomain: "example.com",
|
|
wantSC: &ipn.ServeConfig{
|
|
TCP: map[uint16]*ipn.TCPPortHandler{
|
|
443: {
|
|
HTTPS: true,
|
|
},
|
|
},
|
|
Web: map[ipn.HostPort]*ipn.WebServerConfig{
|
|
ipn.HostPort("example.com:443"): {
|
|
Handlers: map[string]*ipn.HTTPHandler{
|
|
"/api": {
|
|
Proxy: "https://10.2.3.4/api",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "valid_config_for_http_proxy",
|
|
gotSC: `{
|
|
"TCP": {
|
|
"80": {
|
|
"HTTP": true
|
|
}
|
|
}}`,
|
|
wantSC: &ipn.ServeConfig{
|
|
TCP: map[uint16]*ipn.TCPPortHandler{
|
|
80: {
|
|
HTTP: true,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "config_without_cert_domain",
|
|
gotSC: `{
|
|
"TCP": {
|
|
"443": {
|
|
"HTTPS": true
|
|
}
|
|
},
|
|
"Web": {
|
|
"localhost:443": {
|
|
"Handlers": {
|
|
"/api": {
|
|
"Proxy": "https://10.2.3.4/api"
|
|
}}}}}`,
|
|
certDomain: "",
|
|
wantErr: false,
|
|
wantSC: &ipn.ServeConfig{
|
|
TCP: map[uint16]*ipn.TCPPortHandler{
|
|
443: {
|
|
HTTPS: true,
|
|
},
|
|
},
|
|
Web: map[ipn.HostPort]*ipn.WebServerConfig{
|
|
ipn.HostPort("localhost:443"): {
|
|
Handlers: map[string]*ipn.HTTPHandler{
|
|
"/api": {
|
|
Proxy: "https://10.2.3.4/api",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "invalid_json",
|
|
gotSC: "invalid json",
|
|
wantErr: true,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
dir := t.TempDir()
|
|
path := filepath.Join(dir, "serve-config.json")
|
|
if err := os.WriteFile(path, []byte(tt.gotSC), 0644); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
got, err := readServeConfig(path, tt.certDomain)
|
|
if (err != nil) != tt.wantErr {
|
|
t.Errorf("readServeConfig() error = %v, wantErr %v", err, tt.wantErr)
|
|
return
|
|
}
|
|
if !cmp.Equal(got, tt.wantSC) {
|
|
t.Errorf("readServeConfig() diff (-got +want):\n%s", cmp.Diff(got, tt.wantSC))
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
type fakeLocalClient struct {
|
|
*tailscale.LocalClient
|
|
setServeCalled bool
|
|
}
|
|
|
|
func (m *fakeLocalClient) SetServeConfig(ctx context.Context, cfg *ipn.ServeConfig) error {
|
|
m.setServeCalled = true
|
|
return nil
|
|
}
|
|
|
|
func TestHasHTTPSEndpoint(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
cfg *ipn.ServeConfig
|
|
want bool
|
|
}{
|
|
{
|
|
name: "nil_config",
|
|
cfg: nil,
|
|
want: false,
|
|
},
|
|
{
|
|
name: "empty_config",
|
|
cfg: &ipn.ServeConfig{},
|
|
want: false,
|
|
},
|
|
{
|
|
name: "no_https_endpoints",
|
|
cfg: &ipn.ServeConfig{
|
|
TCP: map[uint16]*ipn.TCPPortHandler{
|
|
80: {
|
|
HTTPS: false,
|
|
},
|
|
},
|
|
},
|
|
want: false,
|
|
},
|
|
{
|
|
name: "has_https_endpoint",
|
|
cfg: &ipn.ServeConfig{
|
|
TCP: map[uint16]*ipn.TCPPortHandler{
|
|
443: {
|
|
HTTPS: true,
|
|
},
|
|
},
|
|
},
|
|
want: true,
|
|
},
|
|
{
|
|
name: "mixed_endpoints",
|
|
cfg: &ipn.ServeConfig{
|
|
TCP: map[uint16]*ipn.TCPPortHandler{
|
|
80: {HTTPS: false},
|
|
443: {HTTPS: true},
|
|
},
|
|
},
|
|
want: true,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
got := hasHTTPSEndpoint(tt.cfg)
|
|
if got != tt.want {
|
|
t.Errorf("hasHTTPSEndpoint() = %v, want %v", got, tt.want)
|
|
}
|
|
})
|
|
}
|
|
}
|