mirror of
https://github.com/tailscale/tailscale.git
synced 2025-01-07 08:07:42 +00:00
control/controlclient: start fetching the server noise key
Updates #3488 Signed-off-by: Maisem Ali <maisem@tailscale.com>
This commit is contained in:
parent
d5f8f38ac6
commit
249758df90
@ -47,6 +47,7 @@
|
|||||||
"tailscale.com/types/opt"
|
"tailscale.com/types/opt"
|
||||||
"tailscale.com/types/persist"
|
"tailscale.com/types/persist"
|
||||||
"tailscale.com/util/clientmetric"
|
"tailscale.com/util/clientmetric"
|
||||||
|
"tailscale.com/util/multierr"
|
||||||
"tailscale.com/util/systemd"
|
"tailscale.com/util/systemd"
|
||||||
"tailscale.com/wgengine/monitor"
|
"tailscale.com/wgengine/monitor"
|
||||||
)
|
)
|
||||||
@ -68,8 +69,10 @@ type Direct struct {
|
|||||||
skipIPForwardingCheck bool
|
skipIPForwardingCheck bool
|
||||||
pinger Pinger
|
pinger Pinger
|
||||||
|
|
||||||
mu sync.Mutex // mutex guards the following fields
|
mu sync.Mutex // mutex guards the following fields
|
||||||
serverKey key.MachinePublic
|
serverKey key.MachinePublic // original ("legacy") nacl crypto_box-based public key
|
||||||
|
serverNoiseKey key.MachinePublic
|
||||||
|
|
||||||
persist persist.Persist
|
persist persist.Persist
|
||||||
authKey string
|
authKey string
|
||||||
tryingNewKey key.NodePrivate
|
tryingNewKey key.NodePrivate
|
||||||
@ -326,16 +329,17 @@ func (c *Direct) doLogin(ctx context.Context, opt loginOpt) (mustRegen bool, new
|
|||||||
|
|
||||||
c.logf("doLogin(regen=%v, hasUrl=%v)", regen, opt.URL != "")
|
c.logf("doLogin(regen=%v, hasUrl=%v)", regen, opt.URL != "")
|
||||||
if serverKey.IsZero() {
|
if serverKey.IsZero() {
|
||||||
var err error
|
keys, err := loadServerPubKeys(ctx, c.httpc, c.serverURL)
|
||||||
serverKey, err = loadServerKey(ctx, c.httpc, c.serverURL)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return regen, opt.URL, err
|
return regen, opt.URL, err
|
||||||
}
|
}
|
||||||
c.logf("control server key %s from %s", serverKey.ShortString(), c.serverURL)
|
c.logf("control server key %s from %s", serverKey.ShortString(), c.serverURL)
|
||||||
|
|
||||||
c.mu.Lock()
|
c.mu.Lock()
|
||||||
c.serverKey = serverKey
|
c.serverKey = keys.LegacyPublicKey
|
||||||
|
c.serverNoiseKey = keys.PublicKey
|
||||||
c.mu.Unlock()
|
c.mu.Unlock()
|
||||||
|
serverKey = keys.LegacyPublicKey
|
||||||
}
|
}
|
||||||
|
|
||||||
var oldNodeKey key.NodePublic
|
var oldNodeKey key.NodePublic
|
||||||
@ -950,29 +954,39 @@ func encode(v interface{}, serverKey key.MachinePublic, mkey key.MachinePrivate)
|
|||||||
return mkey.SealTo(serverKey, b), nil
|
return mkey.SealTo(serverKey, b), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func loadServerKey(ctx context.Context, httpc *http.Client, serverURL string) (key.MachinePublic, error) {
|
func loadServerPubKeys(ctx context.Context, httpc *http.Client, serverURL string) (*tailcfg.OverTLSPublicKeyResponse, error) {
|
||||||
req, err := http.NewRequest("GET", serverURL+"/key", nil)
|
keyURL := fmt.Sprintf("%v/key?v=%d", serverURL, tailcfg.CurrentCapabilityVersion)
|
||||||
|
req, err := http.NewRequestWithContext(ctx, "GET", keyURL, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return key.MachinePublic{}, fmt.Errorf("create control key request: %v", err)
|
return nil, fmt.Errorf("create control key request: %v", err)
|
||||||
}
|
}
|
||||||
req = req.WithContext(ctx)
|
|
||||||
res, err := httpc.Do(req)
|
res, err := httpc.Do(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return key.MachinePublic{}, fmt.Errorf("fetch control key: %v", err)
|
return nil, fmt.Errorf("fetch control key: %v", err)
|
||||||
}
|
}
|
||||||
defer res.Body.Close()
|
defer res.Body.Close()
|
||||||
b, err := ioutil.ReadAll(io.LimitReader(res.Body, 1<<16))
|
b, err := ioutil.ReadAll(io.LimitReader(res.Body, 64<<10))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return key.MachinePublic{}, fmt.Errorf("fetch control key response: %v", err)
|
return nil, fmt.Errorf("fetch control key response: %v", err)
|
||||||
}
|
}
|
||||||
if res.StatusCode != 200 {
|
if res.StatusCode != 200 {
|
||||||
return key.MachinePublic{}, fmt.Errorf("fetch control key: %d: %s", res.StatusCode, string(b))
|
return nil, fmt.Errorf("fetch control key: %d", res.StatusCode)
|
||||||
}
|
}
|
||||||
|
var out tailcfg.OverTLSPublicKeyResponse
|
||||||
|
jsonErr := json.Unmarshal(b, &out)
|
||||||
|
if jsonErr == nil {
|
||||||
|
return &out, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Some old control servers might not be updated to send the new format.
|
||||||
|
// Accept the old pre-JSON format too.
|
||||||
|
out = tailcfg.OverTLSPublicKeyResponse{}
|
||||||
k, err := key.ParseMachinePublicUntyped(mem.B(b))
|
k, err := key.ParseMachinePublicUntyped(mem.B(b))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return key.MachinePublic{}, fmt.Errorf("fetch control key: %v", err)
|
return nil, multierr.New(jsonErr, err)
|
||||||
}
|
}
|
||||||
return k, nil
|
out.LegacyPublicKey = k
|
||||||
|
return &out, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Debug contains temporary internal-only debug knobs.
|
// Debug contains temporary internal-only debug knobs.
|
||||||
|
@ -53,11 +53,15 @@ type Server struct {
|
|||||||
initMuxOnce sync.Once
|
initMuxOnce sync.Once
|
||||||
mux *http.ServeMux
|
mux *http.ServeMux
|
||||||
|
|
||||||
mu sync.Mutex
|
mu sync.Mutex
|
||||||
inServeMap int
|
inServeMap int
|
||||||
cond *sync.Cond // lazily initialized by condLocked
|
cond *sync.Cond // lazily initialized by condLocked
|
||||||
pubKey key.MachinePublic
|
pubKey key.MachinePublic
|
||||||
privKey key.ControlPrivate // not strictly needed vs. MachinePrivate, but handy to test type interactions.
|
privKey key.ControlPrivate // not strictly needed vs. MachinePrivate, but handy to test type interactions.
|
||||||
|
|
||||||
|
noisePubKey key.MachinePublic
|
||||||
|
noisePrivKey key.ControlPrivate // not strictly needed vs. MachinePrivate, but handy to test type interactions.
|
||||||
|
|
||||||
nodes map[key.NodePublic]*tailcfg.Node
|
nodes map[key.NodePublic]*tailcfg.Node
|
||||||
users map[key.NodePublic]*tailcfg.User
|
users map[key.NodePublic]*tailcfg.User
|
||||||
logins map[key.NodePublic]*tailcfg.Login
|
logins map[key.NodePublic]*tailcfg.Login
|
||||||
@ -211,30 +215,42 @@ func (s *Server) serveUnhandled(w http.ResponseWriter, r *http.Request) {
|
|||||||
go panic(fmt.Sprintf("testcontrol.Server received unhandled request: %s", got.Bytes()))
|
go panic(fmt.Sprintf("testcontrol.Server received unhandled request: %s", got.Bytes()))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) publicKey() key.MachinePublic {
|
func (s *Server) publicKeys() (noiseKey, pubKey key.MachinePublic) {
|
||||||
pub, _ := s.keyPair()
|
s.mu.Lock()
|
||||||
return pub
|
defer s.mu.Unlock()
|
||||||
|
s.ensureKeyPairLocked()
|
||||||
|
return s.noisePubKey, s.pubKey
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) privateKey() key.ControlPrivate {
|
func (s *Server) privateKey() key.ControlPrivate {
|
||||||
_, priv := s.keyPair()
|
|
||||||
return priv
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Server) keyPair() (pub key.MachinePublic, priv key.ControlPrivate) {
|
|
||||||
s.mu.Lock()
|
s.mu.Lock()
|
||||||
defer s.mu.Unlock()
|
defer s.mu.Unlock()
|
||||||
if s.pubKey.IsZero() {
|
s.ensureKeyPairLocked()
|
||||||
s.privKey = key.NewControl()
|
return s.privKey
|
||||||
s.pubKey = s.privKey.Public()
|
}
|
||||||
|
|
||||||
|
func (s *Server) ensureKeyPairLocked() {
|
||||||
|
if !s.pubKey.IsZero() {
|
||||||
|
return
|
||||||
}
|
}
|
||||||
return s.pubKey, s.privKey
|
s.noisePrivKey = key.NewControl()
|
||||||
|
s.noisePubKey = s.noisePrivKey.Public()
|
||||||
|
s.privKey = key.NewControl()
|
||||||
|
s.pubKey = s.privKey.Public()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) serveKey(w http.ResponseWriter, r *http.Request) {
|
func (s *Server) serveKey(w http.ResponseWriter, r *http.Request) {
|
||||||
w.Header().Set("Content-Type", "text/plain")
|
noiseKey, legacyKey := s.publicKeys()
|
||||||
w.WriteHeader(200)
|
if r.FormValue("v") == "" {
|
||||||
io.WriteString(w, s.publicKey().UntypedHexString())
|
w.Header().Set("Content-Type", "text/plain")
|
||||||
|
io.WriteString(w, legacyKey.UntypedHexString())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
w.Header().Set("Content-Type", "application/json")
|
||||||
|
json.NewEncoder(w).Encode(&tailcfg.OverTLSPublicKeyResponse{
|
||||||
|
LegacyPublicKey: legacyKey,
|
||||||
|
PublicKey: noiseKey,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) serveMachine(w http.ResponseWriter, r *http.Request) {
|
func (s *Server) serveMachine(w http.ResponseWriter, r *http.Request) {
|
||||||
@ -245,6 +261,7 @@ func (s *Server) serveMachine(w http.ResponseWriter, r *http.Request) {
|
|||||||
mkeyStr = mkeyStr[:i]
|
mkeyStr = mkeyStr[:i]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO(maisem/bradfitz): support noise protocol here.
|
||||||
mkey, err := key.ParseMachinePublicUntyped(mem.S(mkeyStr))
|
mkey, err := key.ParseMachinePublicUntyped(mem.S(mkeyStr))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
http.Error(w, "bad machine key hex", 400)
|
http.Error(w, "bad machine key hex", 400)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user