f5eac39ea ("feature/acme, ipn/ipnlocal: start moving ACME/cert state
into an extension") started to move the cert code into feature/acme
but was meant as a baby step.
This goes further, moving almost everything, leaving only some hooks
in ipnlocal.
When we later move "serve" support out to feature/serve, this will
look a bit different in that the hooks currently in ipnlocal will move
to feature/serve (cert support already depends on serve).
As part of this, cert-related tests move to feaure/acme too, which
means some test infra from ipnlocal now moves to shared ipnlocaltest.
(it's not big at the moment, but I imagine it growing)
Updates #12614
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
Change-Id: I9ea89aa9754f12d54b81751b6bd830f2664241ff
153 lines
5.0 KiB
Go
153 lines
5.0 KiB
Go
// Copyright (c) Tailscale Inc & contributors
|
|
// SPDX-License-Identifier: BSD-3-Clause
|
|
|
|
package ipnlocal
|
|
|
|
import (
|
|
"crypto/tls"
|
|
"net/http"
|
|
|
|
"tailscale.com/control/controlclient"
|
|
"tailscale.com/ipn"
|
|
"tailscale.com/ipn/ipnauth"
|
|
"tailscale.com/tailcfg"
|
|
"tailscale.com/tstime"
|
|
"tailscale.com/types/key"
|
|
"tailscale.com/types/netmap"
|
|
"tailscale.com/util/testenv"
|
|
"tailscale.com/wgengine/filter"
|
|
)
|
|
|
|
// forTest is an unexported type to hide all the test-only
|
|
// methods on [LocalBackend] from godoc.
|
|
type forTest struct{ b *LocalBackend }
|
|
|
|
// ForTest returns a handle to test-only methods on b.
|
|
// The resulting type is unexported to make it very obvious
|
|
// in godoc that this is not stable API. This method panics
|
|
// if called outside of tests, which also centralizes all
|
|
// must-be-in-tests validation.
|
|
func (b *LocalBackend) ForTest() forTest {
|
|
testenv.AssertInTest()
|
|
return forTest{b}
|
|
}
|
|
|
|
// HandleC2N calls [LocalBackend.handleC2N], for use by feature/ packages that
|
|
// register C2N handlers and want to test them.
|
|
func (f forTest) HandleC2N(w http.ResponseWriter, r *http.Request) {
|
|
f.b.handleC2N(w, r)
|
|
}
|
|
|
|
// SetIPServiceMappings overwrites the LocalBackend's IP-to-service mappings
|
|
// and propagates them to the netstack subsystem if registered.
|
|
func (f forTest) SetIPServiceMappings(m netmap.IPServiceMappings) {
|
|
b := f.b
|
|
b.mu.Lock()
|
|
defer b.mu.Unlock()
|
|
b.ipVIPServiceMap = m
|
|
if ns, ok := b.sys.Netstack.GetOK(); ok {
|
|
ns.UpdateIPServiceMappings(m)
|
|
}
|
|
}
|
|
|
|
// GetFilter returns the current packet filter.
|
|
func (f forTest) GetFilter() *filter.Filter {
|
|
b := f.b
|
|
// Take b.mu so the read serializes with [LocalBackend.setControlClientStatusLocked],
|
|
// which installs the netmap and the filter at separate sub-steps. Without
|
|
// this, a test thread that observes the new netmap (via [LocalBackend.NetMapWithPeers])
|
|
// can race ahead of the filter store and read the previous filter.
|
|
b.mu.Lock()
|
|
defer b.mu.Unlock()
|
|
return b.currentNode().filterAtomic.Load()
|
|
}
|
|
|
|
// SetControlClientGetter sets the func that creates a control plane
|
|
// client. It can be called at most once, before Start.
|
|
func (f forTest) SetControlClientGetter(newControlClient func(controlclient.Options) (controlclient.Client, error)) {
|
|
b := f.b
|
|
b.mu.Lock()
|
|
defer b.mu.Unlock()
|
|
if b.ccGen != nil {
|
|
panic("invalid use of forTest.SetControlClientGetter after Start")
|
|
}
|
|
b.ccGen = newControlClient
|
|
}
|
|
|
|
// Peers returns all the current peers, sorted by Node.ID, for integration
|
|
// tests in another repo.
|
|
func (f forTest) Peers() []tailcfg.NodeView {
|
|
return f.b.currentNode().PeersForTest()
|
|
}
|
|
|
|
// AwaitNodeKey returns a channel that is closed once a peer with the given
|
|
// node key first appears in the current netmap. If the peer is already
|
|
// present, the returned channel is already closed. See
|
|
// [nodeBackend.AwaitNodeKeyForTest].
|
|
func (f forTest) AwaitNodeKey(k key.NodePublic) <-chan struct{} {
|
|
return f.b.currentNode().AwaitNodeKeyForTest(k)
|
|
}
|
|
|
|
// CurrentUser returns the current user and the associated WindowsUserID.
|
|
// It will be removed along with the rest of the "current user" functionality
|
|
// as we progress on the multi-user improvements (tailscale/corp#18342).
|
|
func (f forTest) CurrentUser() (ipn.WindowsUserID, ipnauth.Actor) {
|
|
b := f.b
|
|
b.mu.Lock()
|
|
defer b.mu.Unlock()
|
|
return b.pm.CurrentUserID(), b.currentUser
|
|
}
|
|
|
|
// ConfigureCerts sets a certificate retrieval function to be used by this
|
|
// local backend, skipping the usual ACME certificate registration.
|
|
func (f forTest) ConfigureCerts(getCert func(hostname string) (*TLSCertKeyPair, error)) {
|
|
hook, ok := HookConfigureCertsForTest.GetOk()
|
|
if !ok {
|
|
panic("forTest.ConfigureCerts called without cert extension registered")
|
|
}
|
|
hook(f.b, getCert)
|
|
}
|
|
|
|
// GetACMETLSALPNCert returns the short-lived ACME tls-alpn-01 challenge
|
|
// certificate for hi, if any.
|
|
func (f forTest) GetACMETLSALPNCert(hi *tls.ClientHelloInfo) (*tls.Certificate, bool) {
|
|
return f.b.getACMETLSALPNCert(hi)
|
|
}
|
|
|
|
// SetServeConfig installs sc as the backend's current
|
|
// [ipn.ServeConfig] without going through the validation in
|
|
// [LocalBackend.SetServeConfig]. It is intended for tests that need a
|
|
// specific serve config without first standing up the prerequisites
|
|
// (netmap, prefs, etc.).
|
|
func (f forTest) SetServeConfig(sc ipn.ServeConfigView) {
|
|
b := f.b
|
|
b.mu.Lock()
|
|
defer b.mu.Unlock()
|
|
b.serveConfig = sc
|
|
}
|
|
|
|
// SetNetMap installs nm as the backend's current netmap without going
|
|
// through control-plane plumbing. It is intended for tests that need a
|
|
// specific netmap (e.g. CertDomains, capabilities).
|
|
func (f forTest) SetNetMap(nm *netmap.NetworkMap) {
|
|
b := f.b
|
|
b.mu.Lock()
|
|
defer b.mu.Unlock()
|
|
b.currentNode().SetNetMap(nm)
|
|
}
|
|
|
|
// SetClock replaces b's clock with c, for tests that need
|
|
// time-dependent behavior to be deterministic.
|
|
func (f forTest) SetClock(c tstime.Clock) { f.b.clock = c }
|
|
|
|
// SetPrefs replaces the current prefs with newp.
|
|
func (f forTest) SetPrefs(newp *ipn.Prefs) {
|
|
if newp == nil {
|
|
panic("forTest.SetPrefs got nil prefs")
|
|
}
|
|
b := f.b
|
|
b.mu.Lock()
|
|
defer b.mu.Unlock()
|
|
b.setPrefsLocked(newp)
|
|
}
|