2023-01-27 21:37:20 +00:00
|
|
|
// Copyright (c) Tailscale Inc & AUTHORS
|
|
|
|
// SPDX-License-Identifier: BSD-3-Clause
|
2021-10-01 05:13:38 +01:00
|
|
|
|
|
|
|
package tlsdial
|
|
|
|
|
|
|
|
import (
|
|
|
|
"crypto/x509"
|
|
|
|
"io"
|
|
|
|
"net"
|
|
|
|
"net/http"
|
|
|
|
"os"
|
|
|
|
"os/exec"
|
|
|
|
"path/filepath"
|
|
|
|
"reflect"
|
|
|
|
"runtime"
|
|
|
|
"sync/atomic"
|
|
|
|
"testing"
|
tsd, ipnlocal, etc: add tsd.System.HealthTracker, start some plumbing
This adds a health.Tracker to tsd.System, accessible via
a new tsd.System.HealthTracker method.
In the future, that new method will return a tsd.System-specific
HealthTracker, so multiple tsnet.Servers in the same process are
isolated. For now, though, it just always returns the temporary
health.Global value. That permits incremental plumbing over a number
of changes. When the second to last health.Global reference is gone,
then the tsd.System.HealthTracker implementation can return a private
Tracker.
The primary plumbing this does is adding it to LocalBackend and its
dozen and change health calls. A few misc other callers are also
plumbed. Subsequent changes will flesh out other parts of the tree
(magicsock, controlclient, etc).
Updates #11874
Updates #4136
Change-Id: Id51e73cfc8a39110425b6dc19d18b3975eac75ce
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
2024-04-26 04:29:20 +01:00
|
|
|
|
|
|
|
"tailscale.com/health"
|
2021-10-01 05:13:38 +01:00
|
|
|
)
|
|
|
|
|
|
|
|
func resetOnce() {
|
|
|
|
rv := reflect.ValueOf(&bakedInRootsOnce).Elem()
|
|
|
|
rv.Set(reflect.Zero(rv.Type()))
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestBakedInRoots(t *testing.T) {
|
|
|
|
resetOnce()
|
|
|
|
p := bakedInRoots()
|
|
|
|
got := p.Subjects()
|
|
|
|
if len(got) != 1 {
|
|
|
|
t.Errorf("subjects = %v; want 1", len(got))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestFallbackRootWorks(t *testing.T) {
|
|
|
|
defer resetOnce()
|
|
|
|
|
|
|
|
const debug = false
|
|
|
|
if runtime.GOOS != "linux" {
|
|
|
|
t.Skip("test assumes Linux")
|
|
|
|
}
|
|
|
|
d := t.TempDir()
|
|
|
|
crtFile := filepath.Join(d, "tlsdial.test.crt")
|
|
|
|
keyFile := filepath.Join(d, "tlsdial.test.key")
|
|
|
|
caFile := filepath.Join(d, "rootCA.pem")
|
2022-12-09 22:05:21 +00:00
|
|
|
cmd := exec.Command("go",
|
2021-10-01 05:13:38 +01:00
|
|
|
"run", "filippo.io/mkcert",
|
|
|
|
"--cert-file="+crtFile,
|
|
|
|
"--key-file="+keyFile,
|
|
|
|
"tlsdial.test")
|
|
|
|
cmd.Env = append(os.Environ(), "CAROOT="+d)
|
|
|
|
out, err := cmd.CombinedOutput()
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("mkcert: %v, %s", err, out)
|
|
|
|
}
|
|
|
|
if debug {
|
|
|
|
t.Logf("Ran: %s", out)
|
|
|
|
dents, err := os.ReadDir(d)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
for _, de := range dents {
|
|
|
|
t.Logf(" - %v", de)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
caPEM, err := os.ReadFile(caFile)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
resetOnce()
|
|
|
|
bakedInRootsOnce.Do(func() {
|
|
|
|
p := x509.NewCertPool()
|
|
|
|
if !p.AppendCertsFromPEM(caPEM) {
|
|
|
|
t.Fatal("failed to add")
|
|
|
|
}
|
|
|
|
bakedInRootsOnce.p = p
|
|
|
|
})
|
|
|
|
|
|
|
|
ln, err := net.Listen("tcp", "127.0.0.1:0")
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
defer ln.Close()
|
|
|
|
if debug {
|
|
|
|
t.Logf("listener running at %v", ln.Addr())
|
|
|
|
}
|
|
|
|
done := make(chan struct{})
|
|
|
|
defer close(done)
|
|
|
|
|
|
|
|
errc := make(chan error, 1)
|
|
|
|
go func() {
|
|
|
|
err := http.ServeTLS(ln, http.HandlerFunc(sayHi), crtFile, keyFile)
|
|
|
|
select {
|
|
|
|
case <-done:
|
|
|
|
return
|
|
|
|
default:
|
|
|
|
t.Logf("ServeTLS: %v", err)
|
|
|
|
errc <- err
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
|
|
|
|
tr := &http.Transport{
|
|
|
|
Dial: func(network, addr string) (net.Conn, error) {
|
|
|
|
return net.Dial("tcp", ln.Addr().String())
|
|
|
|
},
|
|
|
|
DisableKeepAlives: true, // for test cleanup ease
|
|
|
|
}
|
tsd, ipnlocal, etc: add tsd.System.HealthTracker, start some plumbing
This adds a health.Tracker to tsd.System, accessible via
a new tsd.System.HealthTracker method.
In the future, that new method will return a tsd.System-specific
HealthTracker, so multiple tsnet.Servers in the same process are
isolated. For now, though, it just always returns the temporary
health.Global value. That permits incremental plumbing over a number
of changes. When the second to last health.Global reference is gone,
then the tsd.System.HealthTracker implementation can return a private
Tracker.
The primary plumbing this does is adding it to LocalBackend and its
dozen and change health calls. A few misc other callers are also
plumbed. Subsequent changes will flesh out other parts of the tree
(magicsock, controlclient, etc).
Updates #11874
Updates #4136
Change-Id: Id51e73cfc8a39110425b6dc19d18b3975eac75ce
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
2024-04-26 04:29:20 +01:00
|
|
|
ht := new(health.Tracker)
|
|
|
|
tr.TLSClientConfig = Config("tlsdial.test", ht, tr.TLSClientConfig)
|
2021-10-01 05:13:38 +01:00
|
|
|
c := &http.Client{Transport: tr}
|
|
|
|
|
|
|
|
ctr0 := atomic.LoadInt32(&counterFallbackOK)
|
|
|
|
|
|
|
|
res, err := c.Get("https://tlsdial.test/")
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
defer res.Body.Close()
|
|
|
|
if res.StatusCode != 200 {
|
|
|
|
t.Fatal(res.Status)
|
|
|
|
}
|
|
|
|
|
|
|
|
ctrDelta := atomic.LoadInt32(&counterFallbackOK) - ctr0
|
|
|
|
if ctrDelta != 1 {
|
|
|
|
t.Errorf("fallback root success count = %d; want 1", ctrDelta)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func sayHi(w http.ResponseWriter, r *http.Request) {
|
|
|
|
io.WriteString(w, "hi")
|
|
|
|
}
|