mirror of
https://github.com/tailscale/tailscale.git
synced 2025-03-28 03:52:35 +00:00
cmd/tailscaled: add -state=mem:
to support creation of an ephemeral node.
RELNOTE=`tailscaled --state=mem:` registers as an ephemeral node and does not store state to disk. Signed-off-by: Maisem Ali <maisem@tailscale.com>
This commit is contained in:
parent
823d970d60
commit
f9a50779e2
@ -28,6 +28,7 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"inet.af/netaddr"
|
"inet.af/netaddr"
|
||||||
|
"tailscale.com/control/controlclient"
|
||||||
"tailscale.com/envknob"
|
"tailscale.com/envknob"
|
||||||
"tailscale.com/ipn"
|
"tailscale.com/ipn"
|
||||||
"tailscale.com/ipn/ipnserver"
|
"tailscale.com/ipn/ipnserver"
|
||||||
@ -123,7 +124,7 @@ func main() {
|
|||||||
flag.StringVar(&args.httpProxyAddr, "outbound-http-proxy-listen", "", `optional [ip]:port to run an outbound HTTP proxy (e.g. "localhost:8080")`)
|
flag.StringVar(&args.httpProxyAddr, "outbound-http-proxy-listen", "", `optional [ip]:port to run an outbound HTTP proxy (e.g. "localhost:8080")`)
|
||||||
flag.StringVar(&args.tunname, "tun", defaultTunName(), `tunnel interface name; use "userspace-networking" (beta) to not use TUN`)
|
flag.StringVar(&args.tunname, "tun", defaultTunName(), `tunnel interface name; use "userspace-networking" (beta) to not use TUN`)
|
||||||
flag.Var(flagtype.PortValue(&args.port, 0), "port", "UDP port to listen on for WireGuard and peer-to-peer traffic; 0 means automatically select")
|
flag.Var(flagtype.PortValue(&args.port, 0), "port", "UDP port to listen on for WireGuard and peer-to-peer traffic; 0 means automatically select")
|
||||||
flag.StringVar(&args.statepath, "state", paths.DefaultTailscaledStateFile(), "absolute path of state file; use 'kube:<secret-name>' to use Kubernetes secrets or 'arn:aws:ssm:...' to store in AWS SSM. If empty and --statedir is provided, the default is <statedir>/tailscaled.state")
|
flag.StringVar(&args.statepath, "state", paths.DefaultTailscaledStateFile(), "absolute path of state file; use 'kube:<secret-name>' to use Kubernetes secrets or 'arn:aws:ssm:...' to store in AWS SSM; use 'mem:' to not store state and register as an emphemeral node. If empty and --statedir is provided, the default is <statedir>/tailscaled.state")
|
||||||
flag.StringVar(&args.statedir, "statedir", "", "path to directory for storage of config state, TLS certs, temporary incoming Taildrop files, etc. If empty, it's derived from --state when possible.")
|
flag.StringVar(&args.statedir, "statedir", "", "path to directory for storage of config state, TLS certs, temporary incoming Taildrop files, etc. If empty, it's derived from --state when possible.")
|
||||||
flag.StringVar(&args.socketpath, "socket", paths.DefaultTailscaledSocket(), "path of the service unix socket")
|
flag.StringVar(&args.socketpath, "socket", paths.DefaultTailscaledSocket(), "path of the service unix socket")
|
||||||
flag.StringVar(&args.birdSocketPath, "bird-socket", "", "path of the bird unix socket")
|
flag.StringVar(&args.birdSocketPath, "bird-socket", "", "path of the bird unix socket")
|
||||||
@ -238,8 +239,19 @@ func ipnServerOpts() (o ipnserver.Options) {
|
|||||||
o.VarRoot = dir
|
o.VarRoot = dir
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if strings.HasPrefix(statePathOrDefault(), "mem:") {
|
||||||
|
// Register as an ephemeral node.
|
||||||
|
o.LoginFlags = controlclient.LoginEphemeral
|
||||||
|
}
|
||||||
|
|
||||||
switch goos {
|
switch goos {
|
||||||
|
case "js":
|
||||||
|
// The js/wasm client has no state storage so for now
|
||||||
|
// treat all interactive logins as ephemeral.
|
||||||
|
// TODO(bradfitz): if we start using browser LocalStorage
|
||||||
|
// or something, then rethink this.
|
||||||
|
o.LoginFlags = controlclient.LoginEphemeral
|
||||||
|
fallthrough
|
||||||
default:
|
default:
|
||||||
o.SurviveDisconnects = true
|
o.SurviveDisconnects = true
|
||||||
o.AutostartStateKey = ipn.GlobalDaemonStateKey
|
o.AutostartStateKey = ipn.GlobalDaemonStateKey
|
||||||
|
@ -136,6 +136,7 @@ type LocalBackend struct {
|
|||||||
prevIfState *interfaces.State
|
prevIfState *interfaces.State
|
||||||
peerAPIServer *peerAPIServer // or nil
|
peerAPIServer *peerAPIServer // or nil
|
||||||
peerAPIListeners []*peerAPIListener
|
peerAPIListeners []*peerAPIListener
|
||||||
|
loginFlags controlclient.LoginFlags
|
||||||
incomingFiles map[*incomingFile]bool
|
incomingFiles map[*incomingFile]bool
|
||||||
// directFileRoot, if non-empty, means to write received files
|
// directFileRoot, if non-empty, means to write received files
|
||||||
// directly to this directory, without staging them in an
|
// directly to this directory, without staging them in an
|
||||||
@ -166,7 +167,7 @@ type clientGen func(controlclient.Options) (controlclient.Client, error)
|
|||||||
// but is not actually running.
|
// but is not actually running.
|
||||||
//
|
//
|
||||||
// If dialer is nil, a new one is made.
|
// If dialer is nil, a new one is made.
|
||||||
func NewLocalBackend(logf logger.Logf, logid string, store ipn.StateStore, dialer *tsdial.Dialer, e wgengine.Engine) (*LocalBackend, error) {
|
func NewLocalBackend(logf logger.Logf, logid string, store ipn.StateStore, dialer *tsdial.Dialer, e wgengine.Engine, loginFlags controlclient.LoginFlags) (*LocalBackend, error) {
|
||||||
if e == nil {
|
if e == nil {
|
||||||
panic("ipn.NewLocalBackend: engine must not be nil")
|
panic("ipn.NewLocalBackend: engine must not be nil")
|
||||||
}
|
}
|
||||||
@ -199,6 +200,7 @@ func NewLocalBackend(logf logger.Logf, logid string, store ipn.StateStore, diale
|
|||||||
state: ipn.NoState,
|
state: ipn.NoState,
|
||||||
portpoll: portpoll,
|
portpoll: portpoll,
|
||||||
gotPortPollRes: make(chan struct{}),
|
gotPortPollRes: make(chan struct{}),
|
||||||
|
loginFlags: loginFlags,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Default filter blocks everything and logs nothing, until Start() is called.
|
// Default filter blocks everything and logs nothing, until Start() is called.
|
||||||
@ -1569,13 +1571,14 @@ func (b *LocalBackend) InServerMode() bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Login implements Backend.
|
// Login implements Backend.
|
||||||
|
// As of 2022-02-17, this is only exists for tests.
|
||||||
func (b *LocalBackend) Login(token *tailcfg.Oauth2Token) {
|
func (b *LocalBackend) Login(token *tailcfg.Oauth2Token) {
|
||||||
b.mu.Lock()
|
b.mu.Lock()
|
||||||
b.assertClientLocked()
|
b.assertClientLocked()
|
||||||
cc := b.cc
|
cc := b.cc
|
||||||
b.mu.Unlock()
|
b.mu.Unlock()
|
||||||
|
|
||||||
cc.Login(token, controlclient.LoginInteractive)
|
cc.Login(token, b.loginFlags|controlclient.LoginInteractive)
|
||||||
}
|
}
|
||||||
|
|
||||||
// StartLoginInteractive implements Backend. It requests a new
|
// StartLoginInteractive implements Backend. It requests a new
|
||||||
@ -1594,15 +1597,7 @@ func (b *LocalBackend) StartLoginInteractive() {
|
|||||||
if url != "" {
|
if url != "" {
|
||||||
b.popBrowserAuthNow()
|
b.popBrowserAuthNow()
|
||||||
} else {
|
} else {
|
||||||
flags := controlclient.LoginInteractive
|
cc.Login(nil, b.loginFlags|controlclient.LoginInteractive)
|
||||||
if runtime.GOOS == "js" {
|
|
||||||
// The js/wasm client has no state storage so for now
|
|
||||||
// treat all interactive logins as ephemeral.
|
|
||||||
// TODO(bradfitz): if we start using browser LocalStorage
|
|
||||||
// or something, then rethink this.
|
|
||||||
flags |= controlclient.LoginEphemeral
|
|
||||||
}
|
|
||||||
cc.Login(nil, flags)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -463,7 +463,7 @@ func TestLazyMachineKeyGeneration(t *testing.T) {
|
|||||||
t.Fatalf("NewFakeUserspaceEngine: %v", err)
|
t.Fatalf("NewFakeUserspaceEngine: %v", err)
|
||||||
}
|
}
|
||||||
t.Cleanup(eng.Close)
|
t.Cleanup(eng.Close)
|
||||||
lb, err := NewLocalBackend(logf, "logid", store, nil, eng)
|
lb, err := NewLocalBackend(logf, "logid", store, nil, eng, 0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("NewLocalBackend: %v", err)
|
t.Fatalf("NewLocalBackend: %v", err)
|
||||||
}
|
}
|
||||||
|
@ -54,7 +54,7 @@ func TestLocalLogLines(t *testing.T) {
|
|||||||
}
|
}
|
||||||
t.Cleanup(e.Close)
|
t.Cleanup(e.Close)
|
||||||
|
|
||||||
lb, err := NewLocalBackend(logf, idA.String(), store, nil, e)
|
lb, err := NewLocalBackend(logf, idA.String(), store, nil, e, 0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
@ -293,7 +293,7 @@ func TestStateMachine(t *testing.T) {
|
|||||||
|
|
||||||
cc := newMockControl(t)
|
cc := newMockControl(t)
|
||||||
t.Cleanup(func() { cc.preventLog.Set(true) }) // hacky way to pacify issue 3020
|
t.Cleanup(func() { cc.preventLog.Set(true) }) // hacky way to pacify issue 3020
|
||||||
b, err := NewLocalBackend(logf, "logid", store, nil, e)
|
b, err := NewLocalBackend(logf, "logid", store, nil, e, 0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("NewLocalBackend: %v", err)
|
t.Fatalf("NewLocalBackend: %v", err)
|
||||||
}
|
}
|
||||||
@ -954,7 +954,7 @@ func TestWGEngineStatusRace(t *testing.T) {
|
|||||||
eng, err := wgengine.NewFakeUserspaceEngine(logf, 0)
|
eng, err := wgengine.NewFakeUserspaceEngine(logf, 0)
|
||||||
c.Assert(err, qt.IsNil)
|
c.Assert(err, qt.IsNil)
|
||||||
t.Cleanup(eng.Close)
|
t.Cleanup(eng.Close)
|
||||||
b, err := NewLocalBackend(logf, "logid", new(ipn.MemoryStore), nil, eng)
|
b, err := NewLocalBackend(logf, "logid", new(ipn.MemoryStore), nil, eng, 0)
|
||||||
c.Assert(err, qt.IsNil)
|
c.Assert(err, qt.IsNil)
|
||||||
|
|
||||||
cc := newMockControl(t)
|
cc := newMockControl(t)
|
||||||
|
@ -85,6 +85,9 @@ type Options struct {
|
|||||||
// the actual definition of "disconnect" is when the
|
// the actual definition of "disconnect" is when the
|
||||||
// connection count transitions from 1 to 0.
|
// connection count transitions from 1 to 0.
|
||||||
SurviveDisconnects bool
|
SurviveDisconnects bool
|
||||||
|
|
||||||
|
// LoginFlags specifies the LoginFlags to pass to the client.
|
||||||
|
LoginFlags controlclient.LoginFlags
|
||||||
}
|
}
|
||||||
|
|
||||||
// Server is an IPN backend and its set of 0 or more active localhost
|
// Server is an IPN backend and its set of 0 or more active localhost
|
||||||
@ -660,6 +663,8 @@ func tryWindowsAppDataMigration(logf logger.Logf, path string) string {
|
|||||||
// Special cases:
|
// Special cases:
|
||||||
//
|
//
|
||||||
// * empty string means to use an in-memory store
|
// * empty string means to use an in-memory store
|
||||||
|
// * if the string begins with "mem:", the suffix
|
||||||
|
// is ignored and an in-memory store is used.
|
||||||
// * if the string begins with "kube:", the suffix
|
// * if the string begins with "kube:", the suffix
|
||||||
// is a Kubernetes secret name
|
// is a Kubernetes secret name
|
||||||
// * if the string begins with "arn:", the value is
|
// * if the string begins with "arn:", the value is
|
||||||
@ -668,9 +673,12 @@ func StateStore(path string, logf logger.Logf) (ipn.StateStore, error) {
|
|||||||
if path == "" {
|
if path == "" {
|
||||||
return &ipn.MemoryStore{}, nil
|
return &ipn.MemoryStore{}, nil
|
||||||
}
|
}
|
||||||
|
const memPrefix = "mem:"
|
||||||
const kubePrefix = "kube:"
|
const kubePrefix = "kube:"
|
||||||
const arnPrefix = "arn:"
|
const arnPrefix = "arn:"
|
||||||
switch {
|
switch {
|
||||||
|
case strings.HasPrefix(path, memPrefix):
|
||||||
|
return &ipn.MemoryStore{}, nil
|
||||||
case strings.HasPrefix(path, kubePrefix):
|
case strings.HasPrefix(path, kubePrefix):
|
||||||
secretName := strings.TrimPrefix(path, kubePrefix)
|
secretName := strings.TrimPrefix(path, kubePrefix)
|
||||||
store, err := ipn.NewKubeStore(secretName)
|
store, err := ipn.NewKubeStore(secretName)
|
||||||
@ -797,7 +805,7 @@ func Run(ctx context.Context, logf logger.Logf, ln net.Listener, store ipn.State
|
|||||||
//
|
//
|
||||||
// To start it, use the Server.Run method.
|
// To start it, use the Server.Run method.
|
||||||
func New(logf logger.Logf, logid string, store ipn.StateStore, eng wgengine.Engine, dialer *tsdial.Dialer, serverModeUser *user.User, opts Options) (*Server, error) {
|
func New(logf logger.Logf, logid string, store ipn.StateStore, eng wgengine.Engine, dialer *tsdial.Dialer, serverModeUser *user.User, opts Options) (*Server, error) {
|
||||||
b, err := ipnlocal.NewLocalBackend(logf, logid, store, dialer, eng)
|
b, err := ipnlocal.NewLocalBackend(logf, logid, store, dialer, eng, opts.LoginFlags)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("NewLocalBackend: %v", err)
|
return nil, fmt.Errorf("NewLocalBackend: %v", err)
|
||||||
}
|
}
|
||||||
|
@ -54,6 +54,10 @@ type Server struct {
|
|||||||
// log.Printf is used.
|
// log.Printf is used.
|
||||||
Logf logger.Logf
|
Logf logger.Logf
|
||||||
|
|
||||||
|
// Ephemeral, if true, specifies that the instance should register
|
||||||
|
// as an Ephemeral node (https://tailscale.com/kb/1111/ephemeral-nodes/).
|
||||||
|
Emphemeral bool
|
||||||
|
|
||||||
initOnce sync.Once
|
initOnce sync.Once
|
||||||
initErr error
|
initErr error
|
||||||
lb *ipnlocal.LocalBackend
|
lb *ipnlocal.LocalBackend
|
||||||
@ -173,7 +177,11 @@ func (s *Server) start() error {
|
|||||||
}
|
}
|
||||||
logid := "tslib-TODO"
|
logid := "tslib-TODO"
|
||||||
|
|
||||||
lb, err := ipnlocal.NewLocalBackend(logf, logid, store, s.dialer, eng)
|
loginFlags := controlclient.LoginDefault
|
||||||
|
if s.Emphemeral {
|
||||||
|
loginFlags = controlclient.LoginEphemeral
|
||||||
|
}
|
||||||
|
lb, err := ipnlocal.NewLocalBackend(logf, logid, store, s.dialer, eng, loginFlags)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("NewLocalBackend: %v", err)
|
return fmt.Errorf("NewLocalBackend: %v", err)
|
||||||
}
|
}
|
||||||
|
@ -13,6 +13,7 @@ import (
|
|||||||
// process and can cache a prior success when a dependency changes.
|
// process and can cache a prior success when a dependency changes.
|
||||||
_ "inet.af/netaddr"
|
_ "inet.af/netaddr"
|
||||||
_ "tailscale.com/chirp"
|
_ "tailscale.com/chirp"
|
||||||
|
_ "tailscale.com/control/controlclient"
|
||||||
_ "tailscale.com/derp/derphttp"
|
_ "tailscale.com/derp/derphttp"
|
||||||
_ "tailscale.com/envknob"
|
_ "tailscale.com/envknob"
|
||||||
_ "tailscale.com/ipn"
|
_ "tailscale.com/ipn"
|
||||||
|
@ -13,6 +13,7 @@ import (
|
|||||||
// process and can cache a prior success when a dependency changes.
|
// process and can cache a prior success when a dependency changes.
|
||||||
_ "inet.af/netaddr"
|
_ "inet.af/netaddr"
|
||||||
_ "tailscale.com/chirp"
|
_ "tailscale.com/chirp"
|
||||||
|
_ "tailscale.com/control/controlclient"
|
||||||
_ "tailscale.com/derp/derphttp"
|
_ "tailscale.com/derp/derphttp"
|
||||||
_ "tailscale.com/envknob"
|
_ "tailscale.com/envknob"
|
||||||
_ "tailscale.com/ipn"
|
_ "tailscale.com/ipn"
|
||||||
|
@ -13,6 +13,7 @@ import (
|
|||||||
// process and can cache a prior success when a dependency changes.
|
// process and can cache a prior success when a dependency changes.
|
||||||
_ "inet.af/netaddr"
|
_ "inet.af/netaddr"
|
||||||
_ "tailscale.com/chirp"
|
_ "tailscale.com/chirp"
|
||||||
|
_ "tailscale.com/control/controlclient"
|
||||||
_ "tailscale.com/derp/derphttp"
|
_ "tailscale.com/derp/derphttp"
|
||||||
_ "tailscale.com/envknob"
|
_ "tailscale.com/envknob"
|
||||||
_ "tailscale.com/ipn"
|
_ "tailscale.com/ipn"
|
||||||
|
@ -13,6 +13,7 @@ import (
|
|||||||
// process and can cache a prior success when a dependency changes.
|
// process and can cache a prior success when a dependency changes.
|
||||||
_ "inet.af/netaddr"
|
_ "inet.af/netaddr"
|
||||||
_ "tailscale.com/chirp"
|
_ "tailscale.com/chirp"
|
||||||
|
_ "tailscale.com/control/controlclient"
|
||||||
_ "tailscale.com/derp/derphttp"
|
_ "tailscale.com/derp/derphttp"
|
||||||
_ "tailscale.com/envknob"
|
_ "tailscale.com/envknob"
|
||||||
_ "tailscale.com/ipn"
|
_ "tailscale.com/ipn"
|
||||||
|
@ -16,6 +16,7 @@ import (
|
|||||||
_ "golang.org/x/sys/windows/svc/mgr"
|
_ "golang.org/x/sys/windows/svc/mgr"
|
||||||
_ "golang.zx2c4.com/wireguard/windows/tunnel/winipcfg"
|
_ "golang.zx2c4.com/wireguard/windows/tunnel/winipcfg"
|
||||||
_ "inet.af/netaddr"
|
_ "inet.af/netaddr"
|
||||||
|
_ "tailscale.com/control/controlclient"
|
||||||
_ "tailscale.com/derp/derphttp"
|
_ "tailscale.com/derp/derphttp"
|
||||||
_ "tailscale.com/envknob"
|
_ "tailscale.com/envknob"
|
||||||
_ "tailscale.com/ipn"
|
_ "tailscale.com/ipn"
|
||||||
|
Loading…
x
Reference in New Issue
Block a user