store.go 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136
  1. // Copyright (c) Tailscale Inc & AUTHORS
  2. // SPDX-License-Identifier: BSD-3-Clause
  3. package ipn
  4. import (
  5. "bytes"
  6. "context"
  7. "errors"
  8. "fmt"
  9. "net"
  10. "strconv"
  11. "tailscale.com/health"
  12. )
  13. // ErrStateNotExist is returned by StateStore.ReadState when the
  14. // requested state ID doesn't exist.
  15. var ErrStateNotExist = errors.New("no state with given ID")
  16. const (
  17. // MachineKeyStateKey is the key under which we store the machine key,
  18. // in its key.NodePrivate.MarshalText representation.
  19. MachineKeyStateKey = StateKey("_machinekey")
  20. // LegacyGlobalDaemonStateKey is the ipn.StateKey that tailscaled
  21. // loads on startup.
  22. //
  23. // We have to support multiple state keys for other OSes (Windows in
  24. // particular), but right now Unix daemons run with a single
  25. // node-global state. To keep open the option of having per-user state
  26. // later, the global state key doesn't look like a username.
  27. //
  28. // As of 2022-10-21, it has been superseded by profiles and is no longer
  29. // written to disk. It is only read at startup when there are no profiles,
  30. // to migrate the state to the "default" profile.
  31. // The existing state is left on disk in case the user downgrades to an
  32. // older version of Tailscale that doesn't support profiles. We can
  33. // remove this in a future release.
  34. LegacyGlobalDaemonStateKey = StateKey("_daemon")
  35. // ServerModeStartKey's value, if non-empty, is the value of a
  36. // StateKey containing the prefs to start with which to start the
  37. // server.
  38. //
  39. // For example, the value might be "user-1234", meaning the
  40. // the server should start with the Prefs JSON loaded from
  41. // StateKey "user-1234".
  42. ServerModeStartKey = StateKey("server-mode-start-key")
  43. // KnownProfilesStateKey is the key under which we store the list of
  44. // known profiles. The value is a JSON-encoded []LoginProfile.
  45. KnownProfilesStateKey = StateKey("_profiles")
  46. // CurrentProfileStateKey is the key under which we store the current
  47. // profile.
  48. CurrentProfileStateKey = StateKey("_current-profile")
  49. // TaildropReceivedKey is the key to indicate whether any taildrop file
  50. // has ever been received (even if partially).
  51. // Any non-empty value indicates that at least one file has been received.
  52. TaildropReceivedKey = StateKey("_taildrop-received")
  53. )
  54. // StateStoreHealth is a Warnable set when store.New fails at startup. If
  55. // unhealthy, we block all login attempts and return a health message in status
  56. // responses.
  57. var StateStoreHealth = health.Register(&health.Warnable{
  58. Code: "state-store-health",
  59. Severity: health.SeverityHigh,
  60. Title: "Tailscale state store failed to initialize",
  61. Text: func(args health.Args) string {
  62. return fmt.Sprintf("State store failed to initialize, Tailscale will not work until this is resolved. See https://tailscale.com/s/state-store-init-error. Error: %s", args[health.ArgError])
  63. },
  64. ImpactsConnectivity: true,
  65. })
  66. // CurrentProfileID returns the StateKey that stores the
  67. // current profile ID. The value is a JSON-encoded LoginProfile.
  68. // If the userID is empty, the key returned is CurrentProfileStateKey,
  69. // otherwise it is "_current/"+userID.
  70. func CurrentProfileKey(userID string) StateKey {
  71. if userID == "" {
  72. return CurrentProfileStateKey
  73. }
  74. return StateKey("_current/" + userID)
  75. }
  76. // StateStore persists state, and produces it back on request.
  77. // Implementations of StateStore are expected to be safe for concurrent use.
  78. type StateStore interface {
  79. // ReadState returns the bytes associated with ID. Returns (nil,
  80. // ErrStateNotExist) if the ID doesn't have associated state.
  81. ReadState(id StateKey) ([]byte, error)
  82. // WriteState saves bs as the state associated with ID.
  83. //
  84. // Callers should generally use the ipn.WriteState wrapper func
  85. // instead, which only writes if the value is different from what's
  86. // already in the store.
  87. WriteState(id StateKey, bs []byte) error
  88. }
  89. // WriteState is a wrapper around store.WriteState that only writes if
  90. // the value is different from what's already in the store.
  91. func WriteState(store StateStore, id StateKey, v []byte) error {
  92. if was, err := store.ReadState(id); err == nil && bytes.Equal(was, v) {
  93. return nil
  94. }
  95. return store.WriteState(id, v)
  96. }
  97. // StateStoreDialerSetter is an optional interface that StateStores
  98. // can implement to allow the caller to set a custom dialer.
  99. type StateStoreDialerSetter interface {
  100. SetDialer(d func(ctx context.Context, network, address string) (net.Conn, error))
  101. }
  102. // ReadStoreInt reads an integer from a StateStore.
  103. func ReadStoreInt(store StateStore, id StateKey) (int64, error) {
  104. v, err := store.ReadState(id)
  105. if err != nil {
  106. return 0, err
  107. }
  108. return strconv.ParseInt(string(v), 10, 64)
  109. }
  110. // PutStoreInt puts an integer into a StateStore.
  111. func PutStoreInt(store StateStore, id StateKey, val int64) error {
  112. return WriteState(store, id, fmt.Appendf(nil, "%d", val))
  113. }
  114. // EncryptedStateStore is a marker interface implemented by StateStores that
  115. // encrypt data at rest.
  116. type EncryptedStateStore interface {
  117. stateStoreIsEncrypted()
  118. }