Skip to content

Commit 2409661

Browse files
committed
control/controlclient: delete old naclbox code, require ts2021 Noise
Updates tailscale#11585 Updates tailscale/corp#18882 Change-Id: I90e2e4a211c58d429e2b128604614dde18986442 Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
1 parent b961146 commit 2409661

File tree

3 files changed

+118
-206
lines changed

3 files changed

+118
-206
lines changed

control/controlclient/direct.go

Lines changed: 45 additions & 152 deletions
Original file line numberDiff line numberDiff line change
@@ -82,9 +82,9 @@ type Direct struct {
8282

8383
dialPlan ControlDialPlanner // can be nil
8484

85-
mu sync.Mutex // mutex guards the following fields
86-
serverKey key.MachinePublic // original ("legacy") nacl crypto_box-based public key
87-
serverNoiseKey key.MachinePublic
85+
mu sync.Mutex // mutex guards the following fields
86+
serverLegacyKey key.MachinePublic // original ("legacy") nacl crypto_box-based public key; only used for signRegisterRequest on Windows now
87+
serverNoiseKey key.MachinePublic
8888

8989
sfGroup singleflight.Group[struct{}, *NoiseClient] // protects noiseClient creation.
9090
noiseClient *NoiseClient
@@ -436,12 +436,6 @@ type loginOpt struct {
436436
OldNodeKeySignature tkatype.MarshaledSignature
437437
}
438438

439-
// httpClient provides a common interface for the noiseClient and
440-
// the NaCl box http.Client.
441-
type httpClient interface {
442-
Do(req *http.Request) (*http.Response, error)
443-
}
444-
445439
// hostInfoLocked returns a Clone of c.hostinfo and c.netinfo.
446440
// It must only be called with c.mu held.
447441
func (c *Direct) hostInfoLocked() *tailcfg.Hostinfo {
@@ -454,7 +448,7 @@ func (c *Direct) doLogin(ctx context.Context, opt loginOpt) (mustRegen bool, new
454448
c.mu.Lock()
455449
persist := c.persist.AsStruct()
456450
tryingNewKey := c.tryingNewKey
457-
serverKey := c.serverKey
451+
serverKey := c.serverLegacyKey
458452
serverNoiseKey := c.serverNoiseKey
459453
authKey, isWrapped, wrappedSig, wrappedKey := decodeWrappedAuthkey(c.authKey, c.logf)
460454
hi := c.hostInfoLocked()
@@ -494,20 +488,22 @@ func (c *Direct) doLogin(ctx context.Context, opt loginOpt) (mustRegen bool, new
494488
c.logf("control server key from %s: ts2021=%s, legacy=%v", c.serverURL, keys.PublicKey.ShortString(), keys.LegacyPublicKey.ShortString())
495489

496490
c.mu.Lock()
497-
c.serverKey = keys.LegacyPublicKey
491+
c.serverLegacyKey = keys.LegacyPublicKey
498492
c.serverNoiseKey = keys.PublicKey
499493
c.mu.Unlock()
500494
serverKey = keys.LegacyPublicKey
501495
serverNoiseKey = keys.PublicKey
502496

503-
// For servers supporting the Noise transport,
504-
// proactively shut down our TLS TCP connection.
497+
// Proactively shut down our TLS TCP connection.
505498
// We're not going to need it and it's nicer to the
506499
// server.
507-
if !serverNoiseKey.IsZero() {
508-
c.httpc.CloseIdleConnections()
509-
}
500+
c.httpc.CloseIdleConnections()
501+
}
502+
503+
if serverNoiseKey.IsZero() {
504+
return false, "", nil, errors.New("control server is too old; no noise key")
510505
}
506+
511507
var oldNodeKey key.NodePublic
512508
switch {
513509
case opt.Logout:
@@ -594,7 +590,7 @@ func (c *Direct) doLogin(ctx context.Context, opt loginOpt) (mustRegen bool, new
594590
request.Auth.Provider = persist.Provider
595591
request.Auth.LoginName = persist.UserProfile.LoginName
596592
request.Auth.AuthKey = authKey
597-
err = signRegisterRequest(&request, c.serverURL, c.serverKey, machinePrivKey.Public())
593+
err = signRegisterRequest(&request, c.serverURL, c.serverLegacyKey, machinePrivKey.Public())
598594
if err != nil {
599595
// If signing failed, clear all related fields
600596
request.SignatureType = tailcfg.SignatureNone
@@ -614,21 +610,16 @@ func (c *Direct) doLogin(ctx context.Context, opt loginOpt) (mustRegen bool, new
614610
}
615611

616612
// URL and httpc are protocol specific.
617-
var url string
618-
var httpc httpClient
619-
if serverNoiseKey.IsZero() {
620-
httpc = c.httpc
621-
url = fmt.Sprintf("%s/machine/%s", c.serverURL, machinePrivKey.Public().UntypedHexString())
622-
} else {
623-
request.Version = tailcfg.CurrentCapabilityVersion
624-
httpc, err = c.getNoiseClient()
625-
if err != nil {
626-
return regen, opt.URL, nil, fmt.Errorf("getNoiseClient: %w", err)
627-
}
628-
url = fmt.Sprintf("%s/machine/register", c.serverURL)
629-
url = strings.Replace(url, "http:", "https:", 1)
613+
614+
request.Version = tailcfg.CurrentCapabilityVersion
615+
httpc, err := c.getNoiseClient()
616+
if err != nil {
617+
return regen, opt.URL, nil, fmt.Errorf("getNoiseClient: %w", err)
630618
}
631-
bodyData, err := encode(request, serverKey, serverNoiseKey, machinePrivKey)
619+
url := fmt.Sprintf("%s/machine/register", c.serverURL)
620+
url = strings.Replace(url, "http:", "https:", 1)
621+
622+
bodyData, err := encode(request)
632623
if err != nil {
633624
return regen, opt.URL, nil, err
634625
}
@@ -650,7 +641,7 @@ func (c *Direct) doLogin(ctx context.Context, opt loginOpt) (mustRegen bool, new
650641
res.StatusCode, strings.TrimSpace(string(msg)))
651642
}
652643
resp := tailcfg.RegisterResponse{}
653-
if err := decode(res, &resp, serverKey, serverNoiseKey, machinePrivKey); err != nil {
644+
if err := decode(res, &resp); err != nil {
654645
c.logf("error decoding RegisterResponse with server key %s and machine key %s: %v", serverKey, machinePrivKey.Public(), err)
655646
return regen, opt.URL, nil, fmt.Errorf("register request: %v", err)
656647
}
@@ -844,7 +835,6 @@ func (c *Direct) sendMapRequest(ctx context.Context, isStreaming bool, nu Netmap
844835
c.mu.Lock()
845836
persist := c.persist
846837
serverURL := c.serverURL
847-
serverKey := c.serverKey
848838
serverNoiseKey := c.serverNoiseKey
849839
hi := c.hostInfoLocked()
850840
backendLogID := hi.BackendLogID
@@ -858,6 +848,10 @@ func (c *Direct) sendMapRequest(ctx context.Context, isStreaming bool, nu Netmap
858848
}
859849
c.mu.Unlock()
860850

851+
if serverNoiseKey.IsZero() {
852+
return errors.New("control server is too old; no noise key")
853+
}
854+
861855
machinePrivKey, err := c.getMachinePrivKey()
862856
if err != nil {
863857
return fmt.Errorf("getMachinePrivKey: %w", err)
@@ -914,7 +908,7 @@ func (c *Direct) sendMapRequest(ctx context.Context, isStreaming bool, nu Netmap
914908
}
915909
request.Compress = "zstd"
916910

917-
bodyData, err := encode(request, serverKey, serverNoiseKey, machinePrivKey)
911+
bodyData, err := encode(request)
918912
if err != nil {
919913
vlogf("netmap: encode: %v", err)
920914
return err
@@ -926,20 +920,12 @@ func (c *Direct) sendMapRequest(ctx context.Context, isStreaming bool, nu Netmap
926920
machinePubKey := machinePrivKey.Public()
927921
t0 := c.clock.Now()
928922

929-
// Url and httpc are protocol specific.
930-
var url string
931-
var httpc httpClient
932-
if serverNoiseKey.IsZero() {
933-
httpc = c.httpc
934-
url = fmt.Sprintf("%s/machine/%s/map", serverURL, machinePubKey.UntypedHexString())
935-
} else {
936-
httpc, err = c.getNoiseClient()
937-
if err != nil {
938-
return fmt.Errorf("getNoiseClient: %w", err)
939-
}
940-
url = fmt.Sprintf("%s/machine/map", serverURL)
941-
url = strings.Replace(url, "http:", "https:", 1)
923+
httpc, err := c.getNoiseClient()
924+
if err != nil {
925+
return fmt.Errorf("getNoiseClient: %w", err)
942926
}
927+
url := fmt.Sprintf("%s/machine/map", serverURL)
928+
url = strings.Replace(url, "http:", "https:", 1)
943929

944930
// Create a watchdog timer that breaks the connection if we don't receive a
945931
// MapResponse from the network at least once every two minutes. The
@@ -1047,7 +1033,7 @@ func (c *Direct) sendMapRequest(ctx context.Context, isStreaming bool, nu Netmap
10471033
vlogf("netmap: read body after %v", time.Since(t0).Round(time.Millisecond))
10481034

10491035
var resp tailcfg.MapResponse
1050-
if err := c.decodeMsg(msg, &resp, machinePrivKey); err != nil {
1036+
if err := c.decodeMsg(msg, &resp); err != nil {
10511037
vlogf("netmap: decode error: %v", err)
10521038
return err
10531039
}
@@ -1164,9 +1150,8 @@ func initDisplayNames(selfNode tailcfg.NodeView, resp *tailcfg.MapResponse) {
11641150
}
11651151
}
11661152

1167-
// decode JSON decodes the res.Body into v. If serverNoiseKey is not specified,
1168-
// it uses the serverKey and mkey to decode the message from the NaCl-crypto-box.
1169-
func decode(res *http.Response, v any, serverKey, serverNoiseKey key.MachinePublic, mkey key.MachinePrivate) error {
1153+
// decode JSON decodes the res.Body into v.
1154+
func decode(res *http.Response, v any) error {
11701155
defer res.Body.Close()
11711156
msg, err := io.ReadAll(io.LimitReader(res.Body, 1<<20))
11721157
if err != nil {
@@ -1175,10 +1160,7 @@ func decode(res *http.Response, v any, serverKey, serverNoiseKey key.MachinePubl
11751160
if res.StatusCode != 200 {
11761161
return fmt.Errorf("%d: %v", res.StatusCode, string(msg))
11771162
}
1178-
if !serverNoiseKey.IsZero() {
1179-
return json.Unmarshal(msg, v)
1180-
}
1181-
return decodeMsg(msg, v, serverKey, mkey)
1163+
return json.Unmarshal(msg, v)
11821164
}
11831165

11841166
var (
@@ -1189,25 +1171,8 @@ var (
11891171
var jsonEscapedZero = []byte(`\u0000`)
11901172

11911173
// decodeMsg is responsible for uncompressing msg and unmarshaling into v.
1192-
// If c.serverNoiseKey is not specified, it uses the c.serverKey and mkey
1193-
// to first the decrypt msg from the NaCl-crypto-box.
1194-
func (c *Direct) decodeMsg(msg []byte, v any, mkey key.MachinePrivate) error {
1195-
c.mu.Lock()
1196-
serverKey := c.serverKey
1197-
serverNoiseKey := c.serverNoiseKey
1198-
c.mu.Unlock()
1199-
1200-
var decrypted []byte
1201-
if serverNoiseKey.IsZero() {
1202-
var ok bool
1203-
decrypted, ok = mkey.OpenFrom(serverKey, msg)
1204-
if !ok {
1205-
return errors.New("cannot decrypt response")
1206-
}
1207-
} else {
1208-
decrypted = msg
1209-
}
1210-
b, err := zstdframe.AppendDecode(nil, decrypted)
1174+
func (c *Direct) decodeMsg(compressedMsg []byte, v any) error {
1175+
b, err := zstdframe.AppendDecode(nil, compressedMsg)
12111176
if err != nil {
12121177
return err
12131178
}
@@ -1224,26 +1189,11 @@ func (c *Direct) decodeMsg(msg []byte, v any, mkey key.MachinePrivate) error {
12241189
return fmt.Errorf("response: %v", err)
12251190
}
12261191
return nil
1227-
1228-
}
1229-
1230-
func decodeMsg(msg []byte, v any, serverKey key.MachinePublic, machinePrivKey key.MachinePrivate) error {
1231-
decrypted, ok := machinePrivKey.OpenFrom(serverKey, msg)
1232-
if !ok {
1233-
return errors.New("cannot decrypt response")
1234-
}
1235-
if bytes.Contains(decrypted, jsonEscapedZero) {
1236-
log.Printf("[unexpected] zero byte in controlclient decodeMsg into %T: %q", v, decrypted)
1237-
}
1238-
if err := json.Unmarshal(decrypted, v); err != nil {
1239-
return fmt.Errorf("response: %v", err)
1240-
}
1241-
return nil
12421192
}
12431193

1244-
// encode JSON encodes v. If serverNoiseKey is not specified, it uses the serverKey and mkey to
1245-
// seal the message into a NaCl-crypto-box.
1246-
func encode(v any, serverKey, serverNoiseKey key.MachinePublic, mkey key.MachinePrivate) ([]byte, error) {
1194+
// encode JSON encodes v as JSON, logging tailcfg.MapRequest values if
1195+
// debugMap is set.
1196+
func encode(v any) ([]byte, error) {
12471197
b, err := json.Marshal(v)
12481198
if err != nil {
12491199
return nil, err
@@ -1253,10 +1203,7 @@ func encode(v any, serverKey, serverNoiseKey key.MachinePublic, mkey key.Machine
12531203
log.Printf("MapRequest: %s", b)
12541204
}
12551205
}
1256-
if !serverNoiseKey.IsZero() {
1257-
return b, nil
1258-
}
1259-
return mkey.SealTo(serverKey, b), nil
1206+
return b, nil
12601207
}
12611208

12621209
func loadServerPubKeys(ctx context.Context, httpc *http.Client, serverURL string) (*tailcfg.OverTLSPublicKeyResponse, error) {
@@ -1353,7 +1300,7 @@ func (c *Direct) isUniquePingRequest(pr *tailcfg.PingRequest) bool {
13531300

13541301
func (c *Direct) answerPing(pr *tailcfg.PingRequest) {
13551302
httpc := c.httpc
1356-
useNoise := pr.URLIsNoise || pr.Types == "c2n" && c.noiseConfigured()
1303+
useNoise := pr.URLIsNoise || pr.Types == "c2n"
13571304
if useNoise {
13581305
nc, err := c.getNoiseClient()
13591306
if err != nil {
@@ -1554,14 +1501,6 @@ func (c *Direct) setDNSNoise(ctx context.Context, req *tailcfg.SetDNSRequest) er
15541501
return nil
15551502
}
15561503

1557-
// noiseConfigured reports whether the client can communicate with Control
1558-
// over Noise.
1559-
func (c *Direct) noiseConfigured() bool {
1560-
c.mu.Lock()
1561-
defer c.mu.Unlock()
1562-
return !c.serverNoiseKey.IsZero()
1563-
}
1564-
15651504
// SetDNS sends the SetDNSRequest request to the control plane server,
15661505
// requesting a DNS record be created or updated.
15671506
func (c *Direct) SetDNS(ctx context.Context, req *tailcfg.SetDNSRequest) (err error) {
@@ -1571,53 +1510,7 @@ func (c *Direct) SetDNS(ctx context.Context, req *tailcfg.SetDNSRequest) (err er
15711510
metricSetDNSError.Add(1)
15721511
}
15731512
}()
1574-
if c.noiseConfigured() {
1575-
return c.setDNSNoise(ctx, req)
1576-
}
1577-
c.mu.Lock()
1578-
serverKey := c.serverKey
1579-
c.mu.Unlock()
1580-
1581-
if serverKey.IsZero() {
1582-
return errors.New("zero serverKey")
1583-
}
1584-
machinePrivKey, err := c.getMachinePrivKey()
1585-
if err != nil {
1586-
return fmt.Errorf("getMachinePrivKey: %w", err)
1587-
}
1588-
if machinePrivKey.IsZero() {
1589-
return errors.New("getMachinePrivKey returned zero key")
1590-
}
1591-
1592-
// TODO(maisem): dedupe this codepath from SetDNSNoise.
1593-
var serverNoiseKey key.MachinePublic
1594-
bodyData, err := encode(req, serverKey, serverNoiseKey, machinePrivKey)
1595-
if err != nil {
1596-
return err
1597-
}
1598-
body := bytes.NewReader(bodyData)
1599-
1600-
u := fmt.Sprintf("%s/machine/%s/set-dns", c.serverURL, machinePrivKey.Public().UntypedHexString())
1601-
hreq, err := http.NewRequestWithContext(ctx, "POST", u, body)
1602-
if err != nil {
1603-
return err
1604-
}
1605-
res, err := c.httpc.Do(hreq)
1606-
if err != nil {
1607-
return err
1608-
}
1609-
defer res.Body.Close()
1610-
if res.StatusCode != 200 {
1611-
msg, _ := io.ReadAll(res.Body)
1612-
return fmt.Errorf("set-dns response: %v, %.200s", res.Status, strings.TrimSpace(string(msg)))
1613-
}
1614-
var setDNSRes tailcfg.SetDNSResponse
1615-
if err := decode(res, &setDNSRes, serverKey, serverNoiseKey, machinePrivKey); err != nil {
1616-
c.logf("error decoding SetDNSResponse with server key %s and machine key %s: %v", serverKey, machinePrivKey.Public(), err)
1617-
return fmt.Errorf("set-dns-response: %w", err)
1618-
}
1619-
1620-
return nil
1513+
return c.setDNSNoise(ctx, req)
16211514
}
16221515

16231516
func (c *Direct) DoNoiseRequest(req *http.Request) (*http.Response, error) {

tstest/integration/integration_test.go

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -562,16 +562,11 @@ func TestAddPingRequest(t *testing.T) {
562562
func TestC2NPingRequest(t *testing.T) {
563563
tstest.Shard(t)
564564
tstest.Parallel(t)
565-
env := newTestEnv(t)
566-
n1 := newTestNode(t, env)
567-
n1.StartDaemon()
568565

569-
n1.AwaitListening()
570-
n1.MustUp()
571-
n1.AwaitRunning()
566+
env := newTestEnv(t)
572567

573568
gotPing := make(chan bool, 1)
574-
waitPing := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
569+
env.Control.HandleC2N = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
575570
if r.Method != "POST" {
576571
t.Errorf("unexpected ping method %q", r.Method)
577572
}
@@ -584,8 +579,14 @@ func TestC2NPingRequest(t *testing.T) {
584579
t.Errorf("body error\n got: %q\nwant: %q", got, want)
585580
}
586581
gotPing <- true
587-
}))
588-
defer waitPing.Close()
582+
})
583+
584+
n1 := newTestNode(t, env)
585+
n1.StartDaemon()
586+
587+
n1.AwaitListening()
588+
n1.MustUp()
589+
n1.AwaitRunning()
589590

590591
nodes := env.Control.AllNodes()
591592
if len(nodes) != 1 {
@@ -604,7 +605,7 @@ func TestC2NPingRequest(t *testing.T) {
604605
cancel()
605606

606607
pr := &tailcfg.PingRequest{
607-
URL: fmt.Sprintf("%s/ping-%d", waitPing.URL, try),
608+
URL: fmt.Sprintf("https://unused/some-c2n-path/ping-%d", try),
608609
Log: true,
609610
Types: "c2n",
610611
Payload: []byte("POST /echo HTTP/1.0\r\nContent-Length: 3\r\n\r\nabc"),

0 commit comments

Comments
 (0)