mirror of
https://github.com/tailscale/tailscale.git
synced 2024-12-01 22:15:51 +00:00
a97369f097
Signed-off-by: Shayne Sweeney <shayne@tailscale.com>
160 lines
5.7 KiB
Go
160 lines
5.7 KiB
Go
// Copyright (c) 2020 Tailscale Inc & AUTHORS All rights reserved.
|
|
// Use of this source code is governed by a BSD-style
|
|
// license that can be found in the LICENSE file.
|
|
|
|
package ipn
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"strconv"
|
|
)
|
|
|
|
// ErrStateNotExist is returned by StateStore.ReadState when the
|
|
// requested state ID doesn't exist.
|
|
var ErrStateNotExist = errors.New("no state with given ID")
|
|
|
|
const (
|
|
// MachineKeyStateKey is the key under which we store the machine key,
|
|
// in its key.NodePrivate.MarshalText representation.
|
|
MachineKeyStateKey = StateKey("_machinekey")
|
|
|
|
// LegacyGlobalDaemonStateKey is the ipn.StateKey that tailscaled
|
|
// loads on startup.
|
|
//
|
|
// We have to support multiple state keys for other OSes (Windows in
|
|
// particular), but right now Unix daemons run with a single
|
|
// node-global state. To keep open the option of having per-user state
|
|
// later, the global state key doesn't look like a username.
|
|
//
|
|
// As of 2022-10-21, it has been superseded by profiles and is no longer
|
|
// written to disk. It is only read at startup when there are no profiles,
|
|
// to migrate the state to the "default" profile.
|
|
// The existing state is left on disk in case the user downgrades to an
|
|
// older version of Tailscale that doesn't support profiles. We can
|
|
// remove this in a future release.
|
|
LegacyGlobalDaemonStateKey = StateKey("_daemon")
|
|
|
|
// ServerModeStartKey's value, if non-empty, is the value of a
|
|
// StateKey containing the prefs to start with which to start the
|
|
// server.
|
|
//
|
|
// For example, the value might be "user-1234", meaning the
|
|
// the server should start with the Prefs JSON loaded from
|
|
// StateKey "user-1234".
|
|
ServerModeStartKey = StateKey("server-mode-start-key")
|
|
|
|
// NLKeyStateKey is the key under which we store the node's
|
|
// network-lock node key, in its key.NLPrivate.MarshalText representation.
|
|
NLKeyStateKey = StateKey("_nl-node-key")
|
|
|
|
// KnownProfilesStateKey is the key under which we store the list of
|
|
// known profiles. The value is a JSON-encoded []LoginProfile.
|
|
KnownProfilesStateKey = StateKey("_profiles")
|
|
|
|
// CurrentProfileStateKey is the key under which we store the current
|
|
// profile.
|
|
CurrentProfileStateKey = StateKey("_current-profile")
|
|
)
|
|
|
|
// CurrentProfileID returns the StateKey that stores the
|
|
// current profile ID. The value is a JSON-encoded LoginProfile.
|
|
// If the userID is empty, the key returned is CurrentProfileStateKey,
|
|
// otherwise it is "_current/"+userID.
|
|
func CurrentProfileKey(userID string) StateKey {
|
|
if userID == "" {
|
|
return CurrentProfileStateKey
|
|
}
|
|
return StateKey("_current/" + userID)
|
|
}
|
|
|
|
// StateStore persists state, and produces it back on request.
|
|
type StateStore interface {
|
|
// ReadState returns the bytes associated with ID. Returns (nil,
|
|
// ErrStateNotExist) if the ID doesn't have associated state.
|
|
ReadState(id StateKey) ([]byte, error)
|
|
// WriteState saves bs as the state associated with ID.
|
|
WriteState(id StateKey, bs []byte) error
|
|
}
|
|
|
|
// ReadStoreInt reads an integer from a StateStore.
|
|
func ReadStoreInt(store StateStore, id StateKey) (int64, error) {
|
|
v, err := store.ReadState(id)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
return strconv.ParseInt(string(v), 10, 64)
|
|
}
|
|
|
|
// PutStoreInt puts an integer into a StateStore.
|
|
func PutStoreInt(store StateStore, id StateKey, val int64) error {
|
|
return store.WriteState(id, fmt.Appendf(nil, "%d", val))
|
|
}
|
|
|
|
// ServeConfigKey returns a StateKey that stores the
|
|
// JSON-encoded ServeConfig for a config profile.
|
|
func ServeConfigKey(profileID ProfileID) StateKey {
|
|
return StateKey("_serve/" + profileID)
|
|
}
|
|
|
|
// ServeConfig is the JSON type stored in the StateStore for
|
|
// StateKey "_serve/$PROFILE_ID" as returned by ServeConfigKey.
|
|
type ServeConfig struct {
|
|
// TCP are the list of TCP port numbers that tailscaled should handle for
|
|
// the Tailscale IP addresses. (not subnet routers, etc)
|
|
TCP map[uint16]*TCPPortHandler `json:",omitempty"`
|
|
|
|
// Web maps from "$SNI_NAME:$PORT" to a set of HTTP handlers
|
|
// keyed by mount point ("/", "/foo", etc)
|
|
Web map[HostPort]*WebServerConfig `json:",omitempty"`
|
|
|
|
// AllowFunnel is the set of SNI:port values for which funnel
|
|
// traffic is allowed, from trusted ingress peers.
|
|
AllowFunnel map[HostPort]bool `json:",omitempty"`
|
|
}
|
|
|
|
// HostPort is an SNI name and port number, joined by a colon.
|
|
// There is no implicit port 443. It must contain a colon.
|
|
type HostPort string
|
|
|
|
// WebServerConfig describes a web server's configuration.
|
|
type WebServerConfig struct {
|
|
Handlers map[string]*HTTPHandler // mountPoint => handler
|
|
}
|
|
|
|
// TCPPortHandler describes what to do when handling a TCP
|
|
// connection.
|
|
type TCPPortHandler struct {
|
|
// HTTPS, if true, means that tailscaled should handle this connection as an
|
|
// HTTPS request as configured by ServeConfig.Web.
|
|
//
|
|
// It is mutually exclusive with TCPForward.
|
|
HTTPS bool `json:",omitempty"`
|
|
|
|
// TCPForward is the IP:port to forward TCP connections to.
|
|
// Whether or not TLS is terminated by tailscaled depends on
|
|
// TerminateTLS.
|
|
//
|
|
// It is mutually exclusive with HTTPS.
|
|
TCPForward string `json:",omitempty"`
|
|
|
|
// TerminateTLS, if non-empty, means that tailscaled should terminate the
|
|
// TLS connections before forwarding them to TCPForward, permitting only the
|
|
// SNI name with this value. It is only used if TCPForward is non-empty.
|
|
// (the HTTPS mode uses ServeConfig.Web)
|
|
TerminateTLS string `json:",omitempty"`
|
|
}
|
|
|
|
// HTTPHandler is either a path or a proxy to serve.
|
|
type HTTPHandler struct {
|
|
// Exactly one of the following may be set.
|
|
|
|
Path string `json:",omitempty"` // absolute path to directory or file to serve
|
|
Proxy string `json:",omitempty"` // http://localhost:3000/, localhost:3030, 3030
|
|
|
|
Text string `json:",omitempty"` // plaintext to serve (primarily for testing)
|
|
|
|
// TODO(bradfitz): bool to not enumerate directories? TTL on mapping for
|
|
// temporary ones? Error codes? Redirects?
|
|
}
|