cmd/tailscaled, util/winutil: log our registry keys during tailscaled startup
In order to improve our ability to understand the state of policies and registry settings when troubleshooting, we enumerate all values in all subkeys. x/sys/windows does not already offer this, so we need to call RegEnumValue directly. For now we're just logging this during startup, however in a future PR I plan to also trigger this code during a bugreport. I also want to log more than just registry. Fixes #8141 Signed-off-by: Aaron Klotz <aaron@tailscale.com>
This commit is contained in:
@ -4,7 +4,13 @@
|
||||
package winutil
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"golang.org/x/exp/maps"
|
||||
"golang.org/x/sys/windows/registry"
|
||||
)
|
||||
|
||||
const (
|
||||
@ -28,3 +34,117 @@ func TestLookupPseudoUser(t *testing.T) {
|
||||
t.Errorf("LookupPseudoUser(%q) unexpectedly succeeded", networkSID)
|
||||
}
|
||||
}
|
||||
|
||||
func makeLongBinaryValue() []byte {
|
||||
buf := make([]byte, maxBinaryValueLen*2)
|
||||
for i, _ := range buf {
|
||||
buf[i] = byte(i % 0xFF)
|
||||
}
|
||||
return buf
|
||||
}
|
||||
|
||||
var testData = map[string]any{
|
||||
"": "I am the default",
|
||||
"StringEmpty": "",
|
||||
"StringShort": "Hello",
|
||||
"StringLong": strings.Repeat("7", initialValueBufLen+1),
|
||||
"MultiStringEmpty": []string{},
|
||||
"MultiStringSingle": []string{"Foo"},
|
||||
"MultiStringSingleEmpty": []string{""},
|
||||
"MultiString": []string{"Foo", "Bar", "Baz"},
|
||||
"MultiStringWithEmptyBeginning": []string{"", "Foo", "Bar"},
|
||||
"MultiStringWithEmptyMiddle": []string{"Foo", "", "Bar"},
|
||||
"MultiStringWithEmptyEnd": []string{"Foo", "Bar", ""},
|
||||
"DWord": uint32(0x12345678),
|
||||
"QWord": uint64(0x123456789abcdef0),
|
||||
"BinaryEmpty": []byte{},
|
||||
"BinaryShort": []byte{0x01, 0x02, 0x03, 0x04},
|
||||
"BinaryLong": makeLongBinaryValue(),
|
||||
}
|
||||
|
||||
const (
|
||||
keyNameTest = `SOFTWARE\Tailscale Test`
|
||||
subKeyNameTest = "SubKey"
|
||||
)
|
||||
|
||||
func setValues(t *testing.T, k registry.Key) {
|
||||
for vk, v := range testData {
|
||||
var err error
|
||||
switch tv := v.(type) {
|
||||
case string:
|
||||
err = k.SetStringValue(vk, tv)
|
||||
case []string:
|
||||
err = k.SetStringsValue(vk, tv)
|
||||
case uint32:
|
||||
err = k.SetDWordValue(vk, tv)
|
||||
case uint64:
|
||||
err = k.SetQWordValue(vk, tv)
|
||||
case []byte:
|
||||
err = k.SetBinaryValue(vk, tv)
|
||||
default:
|
||||
t.Fatalf("Unknown type")
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
t.Fatalf("Error setting %q: %v", vk, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestRegistrySupportInfo(t *testing.T) {
|
||||
// Make sure the key doesn't exist yet
|
||||
k, err := registry.OpenKey(registry.CURRENT_USER, keyNameTest, registry.READ)
|
||||
switch {
|
||||
case err == nil:
|
||||
k.Close()
|
||||
t.Fatalf("Test key already exists")
|
||||
case !errors.Is(err, registry.ErrNotExist):
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
func() {
|
||||
k, _, err := registry.CreateKey(registry.CURRENT_USER, keyNameTest, registry.WRITE)
|
||||
if err != nil {
|
||||
t.Fatalf("Error creating test key: %v", err)
|
||||
}
|
||||
defer k.Close()
|
||||
|
||||
setValues(t, k)
|
||||
|
||||
sk, _, err := registry.CreateKey(k, subKeyNameTest, registry.WRITE)
|
||||
if err != nil {
|
||||
t.Fatalf("Error creating test subkey: %v", err)
|
||||
}
|
||||
defer sk.Close()
|
||||
|
||||
setValues(t, sk)
|
||||
}()
|
||||
|
||||
t.Cleanup(func() {
|
||||
registry.DeleteKey(registry.CURRENT_USER, keyNameTest+"\\"+subKeyNameTest)
|
||||
registry.DeleteKey(registry.CURRENT_USER, keyNameTest)
|
||||
})
|
||||
|
||||
wantValuesData := maps.Clone(testData)
|
||||
wantValuesData["BinaryLong"] = (wantValuesData["BinaryLong"].([]byte))[:maxBinaryValueLen]
|
||||
|
||||
wantKeyData := make(map[string]any)
|
||||
maps.Copy(wantKeyData, wantValuesData)
|
||||
wantSubKeyData := make(map[string]any)
|
||||
maps.Copy(wantSubKeyData, wantValuesData)
|
||||
wantKeyData[subKeyNameTest] = wantSubKeyData
|
||||
|
||||
wantData := map[string]any{
|
||||
"HKCU\\" + keyNameTest: wantKeyData,
|
||||
}
|
||||
|
||||
gotData, err := getRegistrySupportInfo(registry.CURRENT_USER, []string{keyNameTest})
|
||||
if err != nil {
|
||||
t.Errorf("getRegistrySupportInfo error: %v", err)
|
||||
}
|
||||
|
||||
want, got := fmt.Sprintf("%#v", wantData), fmt.Sprintf("%#v", gotData)
|
||||
if want != got {
|
||||
t.Errorf("Compare error: want\n%s,\ngot %s", want, got)
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user