mirror of
https://github.com/status-im/consul.git
synced 2025-01-09 13:26:07 +00:00
5fb9df1640
* Adding explicit MPL license for sub-package This directory and its subdirectories (packages) contain files licensed with the MPLv2 `LICENSE` file in this directory and are intentionally licensed separately from the BSL `LICENSE` file at the root of this repository. * Adding explicit MPL license for sub-package This directory and its subdirectories (packages) contain files licensed with the MPLv2 `LICENSE` file in this directory and are intentionally licensed separately from the BSL `LICENSE` file at the root of this repository. * Updating the license from MPL to Business Source License Going forward, this project will be licensed under the Business Source License v1.1. Please see our blog post for more details at <Blog URL>, FAQ at www.hashicorp.com/licensing-faq, and details of the license at www.hashicorp.com/bsl. * add missing license headers * Update copyright file headers to BUSL-1.1 * Update copyright file headers to BUSL-1.1 * Update copyright file headers to BUSL-1.1 * Update copyright file headers to BUSL-1.1 * Update copyright file headers to BUSL-1.1 * Update copyright file headers to BUSL-1.1 * Update copyright file headers to BUSL-1.1 * Update copyright file headers to BUSL-1.1 * Update copyright file headers to BUSL-1.1 * Update copyright file headers to BUSL-1.1 * Update copyright file headers to BUSL-1.1 * Update copyright file headers to BUSL-1.1 * Update copyright file headers to BUSL-1.1 * Update copyright file headers to BUSL-1.1 * Update copyright file headers to BUSL-1.1 --------- Co-authored-by: hashicorp-copywrite[bot] <110428419+hashicorp-copywrite[bot]@users.noreply.github.com>
389 lines
9.8 KiB
Go
389 lines
9.8 KiB
Go
// Copyright (c) HashiCorp, Inc.
|
|
// SPDX-License-Identifier: BUSL-1.1
|
|
|
|
package agent
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/base64"
|
|
"fmt"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/hashicorp/consul/sdk/testutil"
|
|
"github.com/hashicorp/memberlist"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
func checkForKey(key string, keyring *memberlist.Keyring) error {
|
|
rk, err := base64.StdEncoding.DecodeString(key)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
pk := keyring.GetPrimaryKey()
|
|
if !bytes.Equal(rk, pk) {
|
|
return fmt.Errorf("got %q want %q", pk, rk)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func TestAgent_LoadKeyrings(t *testing.T) {
|
|
if testing.Short() {
|
|
t.Skip("too slow for testing.Short")
|
|
}
|
|
|
|
t.Parallel()
|
|
key := "tbLJg26ZJyJ9pK3qhc9jig=="
|
|
|
|
// Should be no configured keyring file by default
|
|
t.Run("no keys", func(t *testing.T) {
|
|
a1 := NewTestAgent(t, "")
|
|
defer a1.Shutdown()
|
|
|
|
c1 := a1.consulConfig()
|
|
if c1.SerfLANConfig.KeyringFile != "" {
|
|
t.Fatalf("bad: %#v", c1.SerfLANConfig.KeyringFile)
|
|
}
|
|
if c1.SerfLANConfig.MemberlistConfig.Keyring != nil {
|
|
t.Fatalf("keyring should not be loaded")
|
|
}
|
|
if c1.SerfWANConfig.KeyringFile != "" {
|
|
t.Fatalf("bad: %#v", c1.SerfLANConfig.KeyringFile)
|
|
}
|
|
if c1.SerfWANConfig.MemberlistConfig.Keyring != nil {
|
|
t.Fatalf("keyring should not be loaded")
|
|
}
|
|
})
|
|
|
|
// Server should auto-load LAN and WAN keyring files
|
|
t.Run("server with keys", func(t *testing.T) {
|
|
dataDir := testutil.TempDir(t, "keyfile")
|
|
writeKeyRings(t, key, dataDir)
|
|
|
|
a2 := StartTestAgent(t, TestAgent{DataDir: dataDir})
|
|
defer a2.Shutdown()
|
|
|
|
c2 := a2.consulConfig()
|
|
if c2.SerfLANConfig.KeyringFile == "" {
|
|
t.Fatalf("should have keyring file")
|
|
}
|
|
if c2.SerfLANConfig.MemberlistConfig.Keyring == nil {
|
|
t.Fatalf("keyring should be loaded")
|
|
}
|
|
if err := checkForKey(key, c2.SerfLANConfig.MemberlistConfig.Keyring); err != nil {
|
|
t.Fatalf("err: %v", err)
|
|
}
|
|
if c2.SerfWANConfig.KeyringFile == "" {
|
|
t.Fatalf("should have keyring file")
|
|
}
|
|
if c2.SerfWANConfig.MemberlistConfig.Keyring == nil {
|
|
t.Fatalf("keyring should be loaded")
|
|
}
|
|
if err := checkForKey(key, c2.SerfWANConfig.MemberlistConfig.Keyring); err != nil {
|
|
t.Fatalf("err: %v", err)
|
|
}
|
|
})
|
|
|
|
// Client should auto-load only the LAN keyring file
|
|
t.Run("client with keys", func(t *testing.T) {
|
|
dataDir := testutil.TempDir(t, "keyfile")
|
|
writeKeyRings(t, key, dataDir)
|
|
|
|
a3 := StartTestAgent(t, TestAgent{
|
|
HCL: `
|
|
server = false
|
|
bootstrap = false
|
|
`,
|
|
DataDir: dataDir,
|
|
})
|
|
defer a3.Shutdown()
|
|
|
|
c3 := a3.consulConfig()
|
|
if c3.SerfLANConfig.KeyringFile == "" {
|
|
t.Fatalf("should have keyring file")
|
|
}
|
|
if c3.SerfLANConfig.MemberlistConfig.Keyring == nil {
|
|
t.Fatalf("keyring should be loaded")
|
|
}
|
|
if err := checkForKey(key, c3.SerfLANConfig.MemberlistConfig.Keyring); err != nil {
|
|
t.Fatalf("err: %v", err)
|
|
}
|
|
if c3.SerfWANConfig.KeyringFile != "" {
|
|
t.Fatalf("bad: %#v", c3.SerfWANConfig.KeyringFile)
|
|
}
|
|
if c3.SerfWANConfig.MemberlistConfig.Keyring != nil {
|
|
t.Fatalf("keyring should not be loaded")
|
|
}
|
|
})
|
|
}
|
|
|
|
func writeKeyRings(t *testing.T, key string, dataDir string) {
|
|
t.Helper()
|
|
writeKey := func(key, filename string) {
|
|
path := filepath.Join(dataDir, filename)
|
|
require.NoError(t, initKeyring(path, key), "Error creating keyring %s", path)
|
|
}
|
|
writeKey(key, SerfLANKeyring)
|
|
writeKey(key, SerfWANKeyring)
|
|
}
|
|
|
|
func TestAgent_InmemKeyrings(t *testing.T) {
|
|
if testing.Short() {
|
|
t.Skip("too slow for testing.Short")
|
|
}
|
|
|
|
t.Parallel()
|
|
key := "tbLJg26ZJyJ9pK3qhc9jig=="
|
|
|
|
// Should be no configured keyring file by default
|
|
t.Run("no keys", func(t *testing.T) {
|
|
a1 := NewTestAgent(t, "")
|
|
defer a1.Shutdown()
|
|
|
|
c1 := a1.consulConfig()
|
|
if c1.SerfLANConfig.KeyringFile != "" {
|
|
t.Fatalf("bad: %#v", c1.SerfLANConfig.KeyringFile)
|
|
}
|
|
if c1.SerfLANConfig.MemberlistConfig.Keyring != nil {
|
|
t.Fatalf("keyring should not be loaded")
|
|
}
|
|
if c1.SerfWANConfig.KeyringFile != "" {
|
|
t.Fatalf("bad: %#v", c1.SerfLANConfig.KeyringFile)
|
|
}
|
|
if c1.SerfWANConfig.MemberlistConfig.Keyring != nil {
|
|
t.Fatalf("keyring should not be loaded")
|
|
}
|
|
})
|
|
|
|
// Server should auto-load LAN and WAN keyring
|
|
t.Run("server with keys", func(t *testing.T) {
|
|
a2 := NewTestAgent(t, `
|
|
encrypt = "`+key+`"
|
|
disable_keyring_file = true
|
|
`)
|
|
defer a2.Shutdown()
|
|
|
|
c2 := a2.consulConfig()
|
|
if c2.SerfLANConfig.KeyringFile != "" {
|
|
t.Fatalf("should not have keyring file")
|
|
}
|
|
if c2.SerfLANConfig.MemberlistConfig.Keyring == nil {
|
|
t.Fatalf("keyring should be loaded")
|
|
}
|
|
if err := checkForKey(key, c2.SerfLANConfig.MemberlistConfig.Keyring); err != nil {
|
|
t.Fatalf("err: %v", err)
|
|
}
|
|
if c2.SerfWANConfig.KeyringFile != "" {
|
|
t.Fatalf("should not have keyring file")
|
|
}
|
|
if c2.SerfWANConfig.MemberlistConfig.Keyring == nil {
|
|
t.Fatalf("keyring should be loaded")
|
|
}
|
|
if err := checkForKey(key, c2.SerfWANConfig.MemberlistConfig.Keyring); err != nil {
|
|
t.Fatalf("err: %v", err)
|
|
}
|
|
})
|
|
|
|
// Client should auto-load only the LAN keyring
|
|
t.Run("client with keys", func(t *testing.T) {
|
|
a3 := NewTestAgent(t, `
|
|
encrypt = "`+key+`"
|
|
server = false
|
|
bootstrap = false
|
|
disable_keyring_file = true
|
|
`)
|
|
defer a3.Shutdown()
|
|
|
|
c3 := a3.consulConfig()
|
|
if c3.SerfLANConfig.KeyringFile != "" {
|
|
t.Fatalf("should not have keyring file")
|
|
}
|
|
if c3.SerfLANConfig.MemberlistConfig.Keyring == nil {
|
|
t.Fatalf("keyring should be loaded")
|
|
}
|
|
if err := checkForKey(key, c3.SerfLANConfig.MemberlistConfig.Keyring); err != nil {
|
|
t.Fatalf("err: %v", err)
|
|
}
|
|
if c3.SerfWANConfig.KeyringFile != "" {
|
|
t.Fatalf("bad: %#v", c3.SerfWANConfig.KeyringFile)
|
|
}
|
|
if c3.SerfWANConfig.MemberlistConfig.Keyring != nil {
|
|
t.Fatalf("keyring should not be loaded")
|
|
}
|
|
})
|
|
|
|
// Any keyring files should be ignored
|
|
t.Run("ignore files", func(t *testing.T) {
|
|
dir := testutil.TempDir(t, "consul")
|
|
|
|
badKey := "unUzC2X3JgMKVJlZna5KVg=="
|
|
if err := initKeyring(filepath.Join(dir, SerfLANKeyring), badKey); err != nil {
|
|
t.Fatalf("err: %v", err)
|
|
}
|
|
if err := initKeyring(filepath.Join(dir, SerfWANKeyring), badKey); err != nil {
|
|
t.Fatalf("err: %v", err)
|
|
}
|
|
|
|
a4 := NewTestAgent(t, `
|
|
encrypt = "`+key+`"
|
|
disable_keyring_file = true
|
|
data_dir = "`+dir+`"
|
|
`)
|
|
defer a4.Shutdown()
|
|
|
|
c4 := a4.consulConfig()
|
|
if c4.SerfLANConfig.KeyringFile != "" {
|
|
t.Fatalf("should not have keyring file")
|
|
}
|
|
if c4.SerfLANConfig.MemberlistConfig.Keyring == nil {
|
|
t.Fatalf("keyring should be loaded")
|
|
}
|
|
if err := checkForKey(key, c4.SerfLANConfig.MemberlistConfig.Keyring); err != nil {
|
|
t.Fatalf("err: %v", err)
|
|
}
|
|
if c4.SerfWANConfig.KeyringFile != "" {
|
|
t.Fatalf("should not have keyring file")
|
|
}
|
|
if c4.SerfWANConfig.MemberlistConfig.Keyring == nil {
|
|
t.Fatalf("keyring should be loaded")
|
|
}
|
|
if err := checkForKey(key, c4.SerfWANConfig.MemberlistConfig.Keyring); err != nil {
|
|
t.Fatalf("err: %v", err)
|
|
}
|
|
})
|
|
}
|
|
|
|
func TestAgent_InitKeyring(t *testing.T) {
|
|
t.Parallel()
|
|
key1 := "tbLJg26ZJyJ9pK3qhc9jig=="
|
|
key2 := "4leC33rgtXKIVUr9Nr0snQ=="
|
|
expected := fmt.Sprintf(`["%s"]`, key1)
|
|
|
|
dir := testutil.TempDir(t, "consul")
|
|
file := filepath.Join(dir, "keyring")
|
|
|
|
// First initialize the keyring
|
|
if err := initKeyring(file, key1); err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
content, err := os.ReadFile(file)
|
|
if err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
if string(content) != expected {
|
|
t.Fatalf("bad: %s", content)
|
|
}
|
|
|
|
// Try initializing again with a different key
|
|
if err := initKeyring(file, key2); err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
// Content should still be the same
|
|
content, err = os.ReadFile(file)
|
|
if err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
if string(content) != expected {
|
|
t.Fatalf("bad: %s", content)
|
|
}
|
|
}
|
|
|
|
func TestAgentKeyring_ACL(t *testing.T) {
|
|
if testing.Short() {
|
|
t.Skip("too slow for testing.Short")
|
|
}
|
|
|
|
t.Parallel()
|
|
key1 := "tbLJg26ZJyJ9pK3qhc9jig=="
|
|
key2 := "4leC33rgtXKIVUr9Nr0snQ=="
|
|
|
|
dataDir := testutil.TempDir(t, "keyfile")
|
|
writeKeyRings(t, key1, dataDir)
|
|
|
|
a := StartTestAgent(t, TestAgent{HCL: `
|
|
primary_datacenter = "dc1"
|
|
|
|
acl {
|
|
enabled = true
|
|
default_policy = "deny"
|
|
|
|
tokens {
|
|
initial_management = "root"
|
|
}
|
|
}
|
|
`, DataDir: dataDir})
|
|
defer a.Shutdown()
|
|
|
|
// List keys without access fails
|
|
_, err := a.ListKeys("", false, 0)
|
|
if err == nil || !strings.Contains(err.Error(), "denied") {
|
|
t.Fatalf("expected denied error, got: %#v", err)
|
|
}
|
|
|
|
// List keys with access works
|
|
_, err = a.ListKeys("root", false, 0)
|
|
if err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
// Install without access fails
|
|
_, err = a.InstallKey(key2, "", 0)
|
|
if err == nil || !strings.Contains(err.Error(), "denied") {
|
|
t.Fatalf("expected denied error, got: %#v", err)
|
|
}
|
|
|
|
// Install with access works
|
|
_, err = a.InstallKey(key2, "root", 0)
|
|
if err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
// Use without access fails
|
|
_, err = a.UseKey(key2, "", 0)
|
|
if err == nil || !strings.Contains(err.Error(), "denied") {
|
|
t.Fatalf("expected denied error, got: %#v", err)
|
|
}
|
|
|
|
// Use with access works
|
|
_, err = a.UseKey(key2, "root", 0)
|
|
if err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
// Remove without access fails
|
|
_, err = a.RemoveKey(key1, "", 0)
|
|
if err == nil || !strings.Contains(err.Error(), "denied") {
|
|
t.Fatalf("expected denied error, got: %#v", err)
|
|
}
|
|
|
|
// Remove with access works
|
|
_, err = a.RemoveKey(key1, "root", 0)
|
|
if err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
}
|
|
|
|
func TestValidateLocalOnly(t *testing.T) {
|
|
require.NoError(t, ValidateLocalOnly(false, false))
|
|
require.NoError(t, ValidateLocalOnly(true, true))
|
|
|
|
require.Error(t, ValidateLocalOnly(true, false))
|
|
}
|
|
|
|
func TestAgent_KeyringIsMissingKey(t *testing.T) {
|
|
key1 := "tbLJg26ZJyJ9pK3qhc9jig=="
|
|
key2 := "4leC33rgtXKIVUr9Nr0snQ=="
|
|
decoded1, err := decodeStringKey(key1)
|
|
require.NoError(t, err)
|
|
keyring, err := memberlist.NewKeyring([][]byte{}, decoded1)
|
|
require.NoError(t, err)
|
|
|
|
require.True(t, keyringIsMissingKey(keyring, key2))
|
|
require.False(t, keyringIsMissingKey(keyring, key1))
|
|
}
|