mirror of
https://github.com/tailscale/tailscale.git
synced 2026-09-13 14:29:49 -04:00
State keys written by dev-set-state-store are otherwise gated by their own handlers, for example serve-config requires a local admin before storing a _serve/<profile-id> key. Writing such a key directly through dev-set-state-store skipped that check. Require`IsLocalAdmin` in the handler, the same check serve-config performs. Credit to @johnnymiranda for reporting this issue. Fixes tailscale/corp#47886 Change-Id: Ie82f961b016f78895793d801929a4fa11ebf7fd8 Signed-off-by: Mike Jensen <mikej@tailscale.com>
76 lines
1.7 KiB
Go
76 lines
1.7 KiB
Go
// Copyright (c) Tailscale Inc & contributors
|
|
// SPDX-License-Identifier: BSD-3-Clause
|
|
|
|
//go:build !ts_omit_debug
|
|
|
|
package localapi
|
|
|
|
import (
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"net/url"
|
|
"testing"
|
|
|
|
"tailscale.com/ipn"
|
|
"tailscale.com/ipn/ipnauth"
|
|
"tailscale.com/tstest"
|
|
)
|
|
|
|
// TestServeDevSetStateStore verifies writing state keys requires a local admin,
|
|
// not just PermitWrite; guards against bypassing serve-config's authz check.
|
|
func TestServeDevSetStateStore(t *testing.T) {
|
|
tstest.Replace(t, &validLocalHostForTesting, true)
|
|
|
|
profileID := ipn.ProfileID("test-profile")
|
|
tests := []struct {
|
|
desc string
|
|
permitWrite bool
|
|
localAdmin bool
|
|
wantStatus int
|
|
}{
|
|
{
|
|
desc: "no-permission",
|
|
permitWrite: false,
|
|
localAdmin: false,
|
|
wantStatus: http.StatusForbidden,
|
|
},
|
|
{
|
|
desc: "write-not-admin",
|
|
permitWrite: true,
|
|
localAdmin: false,
|
|
wantStatus: http.StatusUnauthorized,
|
|
},
|
|
{
|
|
desc: "write-admin",
|
|
permitWrite: true,
|
|
localAdmin: true,
|
|
wantStatus: http.StatusOK,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.desc, func(t *testing.T) {
|
|
h := handlerForTest(t, &Handler{
|
|
PermitWrite: tt.permitWrite,
|
|
Actor: &ipnauth.TestActor{LocalAdmin: tt.localAdmin},
|
|
b: newTestLocalBackend(t),
|
|
})
|
|
s := httptest.NewServer(h)
|
|
t.Cleanup(s.Close)
|
|
|
|
form := url.Values{
|
|
"key": {string(ipn.ServeConfigKey(profileID))},
|
|
"value": {"{}"},
|
|
}
|
|
res, err := s.Client().PostForm(s.URL+"/localapi/v0/dev-set-state-store", form)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer res.Body.Close()
|
|
if res.StatusCode != tt.wantStatus {
|
|
t.Errorf("res.StatusCode = %d, want %d", res.StatusCode, tt.wantStatus)
|
|
}
|
|
})
|
|
}
|
|
}
|