Merge branch 'docs/admin-partitions-rc-updates' of github.com:hashicorp/consul into docs/admin-partitions-rc-updates

applying feedback on admin partitions rc docs
This commit is contained in:
trujillo-adam 2021-12-14 11:44:15 -08:00
commit 4c073e0d9f
221 changed files with 4829 additions and 2450 deletions

3
.changelog/10895.txt Normal file
View File

@ -0,0 +1,3 @@
```release-note:improvement
grpc, xds: improved reliability of grpc and xds servers by adding recovery-middleware to return and log error in case of panic.
```

3
.changelog/11645.txt Normal file
View File

@ -0,0 +1,3 @@
```release-note:improvement
types: add TLSVersion and TLSCipherSuite
```

3
.changelog/11668.txt Normal file
View File

@ -0,0 +1,3 @@
```release-note:feature
ui: Add documentation link to Partition empty state
```

3
.changelog/11670.txt Normal file
View File

@ -0,0 +1,3 @@
```release-note:bug
ui: Fix visual issue with slight table header overflow
```

3
.changelog/11679.txt Normal file
View File

@ -0,0 +1,3 @@
```release-note:feature
ui: Adds support for partitions to the Routing visualization.
```

3
.changelog/11680.txt Normal file
View File

@ -0,0 +1,3 @@
```release-note:improvement
server: block enterprise-specific partition-exports config entry from being used in OSS Consul.
```

3
.changelog/11696.txt Normal file
View File

@ -0,0 +1,3 @@
```release-note:feature
ui: Adds support for partitions to Service and Node Identity template visuals.
```

3
.changelog/11702.txt Normal file
View File

@ -0,0 +1,3 @@
```release-note:feature
ui: Adds basic support for showing Services exported from another partition.
```

11
.changelog/11720.txt Normal file
View File

@ -0,0 +1,11 @@
```release-note:improvement
raft: Use bbolt instead of the legacy boltdb implementation
```
```release-note:improvement
raft: Emit boltdb related performance metrics
```
```release-note:improvement
raft: Added a configuration to disable boltdb freelist syncing
```

3
.changelog/11737.txt Normal file
View File

@ -0,0 +1,3 @@
```release-note:improvement
partitions: **(Enterprise only)** rename APIs, commands, and public types to use "partition" rather than "admin partition".
```

3
.changelog/11738.txt Normal file
View File

@ -0,0 +1,3 @@
```release-note:improvement
connect: **(Enterprise only)** add support for cross-partition transparent proxying.
```

3
.changelog/11739.txt Normal file
View File

@ -0,0 +1,3 @@
```release-note:improvement
api: **(Enterprise Only)** rename partition-exports config entry to exported-services.
```

3
.changelog/11744.txt Normal file
View File

@ -0,0 +1,3 @@
```release-note:note
Renamed the `agent_master` field to `agent_recovery` in the `acl-tokens.json` file in which tokens are persisted on-disk (when `acl.enable_token_persistence` is enabled)
```

3
.changelog/11748.txt Normal file
View File

@ -0,0 +1,3 @@
```release-note:bug
areas: **(Enterprise only)** make the gRPC server tracker network area aware
```

3
.changelog/11757.txt Normal file
View File

@ -0,0 +1,3 @@
```release-note:improvement
connect: **(Enterprise only)** add support for targeting partitions in discovery chain routes, splits, and redirects.
```

3
.changelog/_1391.txt Normal file
View File

@ -0,0 +1,3 @@
```release-note:feature
partitions: **(Enterprise only)** Ensure partitions and serf-based WAN federation are mutually exclusive.
```

View File

@ -3,30 +3,55 @@ updates:
- package-ecosystem: gomod
open-pull-requests-limit: 5
directory: "/"
labels:
- "go"
- "dependencies"
- "pr/no-changelog"
schedule:
interval: daily
- package-ecosystem: gomod
open-pull-requests-limit: 5
directory: "/api"
labels:
- "go"
- "dependencies"
- "pr/no-changelog"
schedule:
interval: daily
- package-ecosystem: gomod
open-pull-requests-limit: 5
directory: "/sdk"
labels:
- "go"
- "dependencies"
- "pr/no-changelog"
schedule:
interval: daily
- package-ecosystem: npm
open-pull-requests-limit: 5
directory: "/ui"
labels:
- "javascript"
- "dependencies"
- "pr/no-changelog"
schedule:
interval: daily
- package-ecosystem: npm
open-pull-requests-limit: 5
directory: "/website"
labels:
- "javascript"
- "dependencies"
- "type/docs-cherrypick"
- "pr/no-changelog"
schedule:
interval: daily
- package-ecosystem: github-actions
open-pull-requests-limit: 5
directory: /
labels:
- "github_actions"
- "dependencies"
- "pr/no-changelog"
schedule:
interval: daily
interval: daily

View File

@ -1,4 +1,118 @@
## UNRELEASED
## 1.11.0-rc (December 08, 2021)
BREAKING CHANGES:
* cli: `consul acl set-agent-token master` has been replaced with `consul acl set-agent-token recovery` [[GH-11669](https://github.com/hashicorp/consul/issues/11669)]
FEATURES:
* partitions: **(Enterprise only)** Ensure partitions and serf-based WAN federation are mutually exclusive.
* ui: Add documentation link to Partition empty state [[GH-11668](https://github.com/hashicorp/consul/issues/11668)]
* ui: Adds basic support for showing Services exported from another partition. [[GH-11702](https://github.com/hashicorp/consul/issues/11702)]
* ui: Adds support for partitions to Service and Node Identity template visuals. [[GH-11696](https://github.com/hashicorp/consul/issues/11696)]
* ui: Adds support for partitions to the Routing visualization. [[GH-11679](https://github.com/hashicorp/consul/issues/11679)]
* ui: Don't offer a 'Valid Datacenters' option when editing policies for non-default partitions [[GH-11656](https://github.com/hashicorp/consul/issues/11656)]
* ui: Include `Service.Partition` into available variables for `dashboard_url_templates` [[GH-11654](https://github.com/hashicorp/consul/issues/11654)]
* ui: Upgrade Lock Sessions to use partitions [[GH-11666](https://github.com/hashicorp/consul/issues/11666)]
IMPROVEMENTS:
* agent: **(Enterprise only)** purge service/check registration files for incorrect partitions on reload [[GH-11607](https://github.com/hashicorp/consul/issues/11607)]
* agent: add variation of force-leave that exclusively works on the WAN [[GH-11722](https://github.com/hashicorp/consul/issues/11722)]
* api: **(Enterprise Only)** rename partition-exports config entry to exported-services. [[GH-11739](https://github.com/hashicorp/consul/issues/11739)]
* auto-config: ensure the feature works properly with partitions [[GH-11699](https://github.com/hashicorp/consul/issues/11699)]
* connect: **(Enterprise only)** add support for cross-partition transparent proxying. [[GH-11738](https://github.com/hashicorp/consul/issues/11738)]
* connect: **(Enterprise only)** add support for targeting partitions in discovery chain routes, splits, and redirects. [[GH-11757](https://github.com/hashicorp/consul/issues/11757)]
* connect: Consul will now generate a unique virtual IP for each connect-enabled service (this will also differ across namespace/partition in Enterprise). [[GH-11724](https://github.com/hashicorp/consul/issues/11724)]
* connect: Support Vault auth methods for the Connect CA Vault provider. Currently, we support any non-deprecated auth methods
the latest version of Vault supports (v1.8.5), which include AppRole, AliCloud, AWS, Azure, Cloud Foundry, GitHub, Google Cloud,
JWT/OIDC, Kerberos, Kubernetes, LDAP, Oracle Cloud Infrastructure, Okta, Radius, TLS Certificates, and Username & Password. [[GH-11573](https://github.com/hashicorp/consul/issues/11573)]
* dns: Added a `virtual` endpoint for querying the assigned virtual IP for a service. [[GH-11725](https://github.com/hashicorp/consul/issues/11725)]
* partitions: **(Enterprise only)** rename APIs, commands, and public types to use "partition" rather than "admin partition". [[GH-11737](https://github.com/hashicorp/consul/issues/11737)]
* raft: Added a configuration to disable boltdb freelist syncing [[GH-11720](https://github.com/hashicorp/consul/issues/11720)]
* raft: Emit boltdb related performance metrics [[GH-11720](https://github.com/hashicorp/consul/issues/11720)]
* raft: Use bbolt instead of the legacy boltdb implementation [[GH-11720](https://github.com/hashicorp/consul/issues/11720)]
* sentinel: **(Enterprise Only)** Sentinel now uses SHA256 to generate policy ids
* server: block enterprise-specific partition-exports config entry from being used in OSS Consul. [[GH-11680](https://github.com/hashicorp/consul/issues/11680)]
* types: add TLSVersion and TLSCipherSuite [[GH-11645](https://github.com/hashicorp/consul/issues/11645)]
* ui: Add partition support for SSO [[GH-11604](https://github.com/hashicorp/consul/issues/11604)]
* ui: Update global notification styling [[GH-11577](https://github.com/hashicorp/consul/issues/11577)]
DEPRECATIONS:
* api: `/v1/agent/token/agent_master` is deprecated and will be removed in a future major release - use `/v1/agent/token/agent_recovery` instead [[GH-11669](https://github.com/hashicorp/consul/issues/11669)]
* config: `acl.tokens.master` has been renamed to `acl.tokens.initial_management`, and `acl.tokens.agent_master` has been renamed to `acl.tokens.agent_recovery` - the old field names are now deprecated and will be removed in a future major release [[GH-11665](https://github.com/hashicorp/consul/issues/11665)]
BUG FIXES:
* areas: **(Enterprise Only)** Fixes a bug when using Yamux pool ( for servers version 1.7.3 and later), the entire pool was locked while connecting to a remote location, which could potentially take a long time. [[GH-1368](https://github.com/hashicorp/consul/issues/1368)]
* areas: **(Enterprise only)** make the gRPC server tracker network area aware [[GH-11748](https://github.com/hashicorp/consul/issues/11748)]
* ca: fixes a bug that caused non blocking leaf cert queries to return the same cached response regardless of ca rotation or leaf cert expiry [[GH-11693](https://github.com/hashicorp/consul/issues/11693)]
* ca: fixes a bug that caused the SigningKeyID to be wrong in the primary DC, when the Vault provider is used, after a CA config creates a new root. [[GH-11672](https://github.com/hashicorp/consul/issues/11672)]
* ca: fixes a bug that caused the intermediate cert used to sign leaf certs to be missing from the /connect/ca/roots API response when the Vault provider was used. [[GH-11671](https://github.com/hashicorp/consul/issues/11671)]
* ui: Fix inline-code brand styling [[GH-11578](https://github.com/hashicorp/consul/issues/11578)]
* ui: Fix visual issue with slight table header overflow [[GH-11670](https://github.com/hashicorp/consul/issues/11670)]
* ui: Fixes an issue where under some circumstances after logging we present the
data loaded previous to you logging in. [[GH-11681](https://github.com/hashicorp/consul/issues/11681)]
* ui: Include `Service.Namespace` into available variables for `dashboard_url_templates` [[GH-11640](https://github.com/hashicorp/consul/issues/11640)]
## 1.11.0-beta3 (November 17, 2021)
SECURITY:
* agent: Use SHA256 instead of MD5 to generate persistence file names. [[GH-11491](https://github.com/hashicorp/consul/issues/11491)]
* namespaces: **(Enterprise only)** Creating or editing namespaces that include default ACL policies or ACL roles now requires `acl:write` permission in the default namespace. This change fixes CVE-2021-41805.
FEATURES:
* ca: Add a configurable TTL for Connect CA root certificates. The configuration is supported by the Vault and Consul providers. [[GH-11428](https://github.com/hashicorp/consul/issues/11428)]
* ca: Add a configurable TTL to the AWS ACM Private CA provider root certificate. [[GH-11449](https://github.com/hashicorp/consul/issues/11449)]
* health-checks: add support for h2c in http2 ping health checks [[GH-10690](https://github.com/hashicorp/consul/issues/10690)]
* partitions: **(Enterprise only)** segment serf LAN gossip between nodes in different partitions
* ui: Adding support of Consul API Gateway as an external source. [[GH-11371](https://github.com/hashicorp/consul/issues/11371)]
* ui: Topology - New views for scenarios where no dependencies exist or ACLs are disabled [[GH-11280](https://github.com/hashicorp/consul/issues/11280)]
IMPROVEMENTS:
* ci: Artifact builds will now only run on merges to the release branches or to `main` [[GH-11417](https://github.com/hashicorp/consul/issues/11417)]
* ci: The Linux packages are now available for all supported Linux architectures including arm, arm64, 386, and amd64 [[GH-11417](https://github.com/hashicorp/consul/issues/11417)]
* ci: The Linux packaging service configs and pre/post install scripts are now available under [.release/linux] [[GH-11417](https://github.com/hashicorp/consul/issues/11417)]
* config: warn the user if client_addr is empty because client services won't be listening [[GH-11461](https://github.com/hashicorp/consul/issues/11461)]
* connect/ca: Return an error when querying roots from uninitialized CA. [[GH-11514](https://github.com/hashicorp/consul/issues/11514)]
* connect: **(Enterprise only)** Allow ingress gateways to target services in another partition [[GH-11566](https://github.com/hashicorp/consul/issues/11566)]
* connect: add Namespace configuration setting for Vault CA provider [[GH-11477](https://github.com/hashicorp/consul/issues/11477)]
* namespaces: **(Enterprise only)** policy and role defaults can reference policies in any namespace in the same partition by ID
* partitions: Prevent writing partition-exports entries to secondary DCs. [[GH-11541](https://github.com/hashicorp/consul/issues/11541)]
* sdk: Add support for iptable rules that allow DNS lookup redirection to Consul DNS. [[GH-11480](https://github.com/hashicorp/consul/issues/11480)]
* segments: **(Enterprise only)** ensure that the serf_lan_allowed_cidrs applies to network segments [[GH-11495](https://github.com/hashicorp/consul/issues/11495)]
* ui: Add upstream icons for upstreams and upstream instances [[GH-11556](https://github.com/hashicorp/consul/issues/11556)]
* ui: Update UI browser support to 'roughly ~2 years back' [[GH-11505](https://github.com/hashicorp/consul/issues/11505)]
* ui: When switching partitions reset the namespace back to the tokens default namespace or default [[GH-11479](https://github.com/hashicorp/consul/issues/11479)]
* ui: added copy to clipboard button in code editor toolbars [[GH-11474](https://github.com/hashicorp/consul/issues/11474)]
BUG FIXES:
* acl: **(Enterprise only)** fix namespace and namespace_prefix policy evaluation when both govern an authz request
* api: ensure new partition fields are omit empty for compatibility with older versions of consul [[GH-11585](https://github.com/hashicorp/consul/issues/11585)]
* connect/ca: Allow secondary initialization to resume after being deferred due to unreachable or incompatible primary DC servers. [[GH-11514](https://github.com/hashicorp/consul/issues/11514)]
* connect: fix issue with attempting to generate an invalid upstream cluster from UpstreamConfig.Defaults. [[GH-11245](https://github.com/hashicorp/consul/issues/11245)]
* macos: fixes building with a non-Apple LLVM (such as installed via Homebrew) [[GH-11586](https://github.com/hashicorp/consul/issues/11586)]
* namespaces: **(Enterprise only)** ensure the namespace replicator doesn't replicate deleted namespaces
* partitions: **(Enterprise only)** fix panic when forwarding delete operations to the leader
* snapshot: **(Enterprise only)** fixed a bug where the snapshot agent would ignore the `license_path` setting in config files
* snapshot: **(Enterprise only)** snapshot agent no longer attempts to refresh its license from the server when a local license is provided (i.e. via config or an environment variable)
* state: **(Enterprise Only)** ensure partition delete triggers namespace deletes
* ui: **(Enterprise only)** When no namespace is selected, make sure to default to the tokens default namespace when requesting permissions [[GH-11472](https://github.com/hashicorp/consul/issues/11472)]
* ui: Ensure the UI stores the default partition for the users token [[GH-11591](https://github.com/hashicorp/consul/issues/11591)]
* ui: Ensure we check intention permissions for specific services when deciding
whether to show action buttons for per service intention actions [[GH-11409](https://github.com/hashicorp/consul/issues/11409)]
* ui: Filter the global intentions list by the currently selected parition rather
than a wildcard [[GH-11475](https://github.com/hashicorp/consul/issues/11475)]
* ui: Revert to depending on the backend, 'post-user-action', to report
permissions errors rather than using UI capabilities 'pre-user-action' [[GH-11520](https://github.com/hashicorp/consul/issues/11520)]
* ui: code editor styling (layout consistency + wide screen support) [[GH-11474](https://github.com/hashicorp/consul/issues/11474)]
* windows: fixes arm and arm64 builds [[GH-11586](https://github.com/hashicorp/consul/issues/11586)]
* xds: fixes a bug where replacing a mesh gateway node used for WAN federation (with another that has a different IP) could leave gateways in the other DC unable to re-establish the connection [[GH-11522](https://github.com/hashicorp/consul/issues/11522)]
## 1.11.0-beta2 (November 02, 2021)

View File

@ -16,10 +16,10 @@ type Config struct {
type ExportFetcher interface {
// ExportsForPartition returns the config entry defining exports for a partition
ExportsForPartition(partition string) PartitionExports
ExportsForPartition(partition string) ExportedServices
}
type PartitionExports struct {
type ExportedServices struct {
Data map[string]map[string][]string
}

View File

@ -91,9 +91,14 @@ func TestACL_Bootstrap(t *testing.T) {
}
t.Parallel()
a := NewTestAgent(t, TestACLConfig()+`
acl_master_token = ""
`)
a := NewTestAgent(t, `
primary_datacenter = "dc1"
acl {
enabled = true
default_policy = "deny"
}
`)
defer a.Shutdown()
tests := []struct {
@ -881,7 +886,7 @@ func TestACL_HTTP(t *testing.T) {
require.True(t, ok)
require.Len(t, tokens, 1)
token := tokens[0]
require.Equal(t, "Master Token", token.Description)
require.Equal(t, "Initial Management Token", token.Description)
require.Len(t, token.Policies, 1)
require.Equal(t, structs.ACLPolicyGlobalManagementID, token.Policies[0].ID)
})
@ -1689,7 +1694,7 @@ func TestACLEndpoint_LoginLogout_jwt(t *testing.T) {
for name, tc := range cases {
tc := tc
t.Run(name, func(t *testing.T) {
method, err := upsertTestCustomizedAuthMethod(a.RPC, TestDefaultMasterToken, "dc1", func(method *structs.ACLAuthMethod) {
method, err := upsertTestCustomizedAuthMethod(a.RPC, TestDefaultInitialManagementToken, "dc1", func(method *structs.ACLAuthMethod) {
method.Type = "jwt"
method.Config = map[string]interface{}{
"JWTSupportedAlgs": []string{"ES256"},
@ -1758,7 +1763,7 @@ func TestACLEndpoint_LoginLogout_jwt(t *testing.T) {
testutil.RequireErrorContains(t, err, "Permission denied")
})
_, err = upsertTestCustomizedBindingRule(a.RPC, TestDefaultMasterToken, "dc1", func(rule *structs.ACLBindingRule) {
_, err = upsertTestCustomizedBindingRule(a.RPC, TestDefaultInitialManagementToken, "dc1", func(rule *structs.ACLBindingRule) {
rule.AuthMethod = method.Name
rule.BindType = structs.BindingRuleBindTypeService
rule.BindName = "test--${value.name}--${value.primary_org}"
@ -1798,7 +1803,7 @@ func TestACLEndpoint_LoginLogout_jwt(t *testing.T) {
// verify the token was deleted
req, _ = http.NewRequest("GET", "/v1/acl/token/"+token.AccessorID, nil)
req.Header.Add("X-Consul-Token", TestDefaultMasterToken)
req.Header.Add("X-Consul-Token", TestDefaultInitialManagementToken)
resp = httptest.NewRecorder()
// make the request
@ -1819,7 +1824,7 @@ func TestACL_Authorize(t *testing.T) {
a1 := NewTestAgent(t, TestACLConfigWithParams(nil))
defer a1.Shutdown()
testrpc.WaitForTestAgent(t, a1.RPC, "dc1", testrpc.WithToken(TestDefaultMasterToken))
testrpc.WaitForTestAgent(t, a1.RPC, "dc1", testrpc.WithToken(TestDefaultInitialManagementToken))
policyReq := structs.ACLPolicySetRequest{
Policy: structs.ACLPolicy{
@ -1827,7 +1832,7 @@ func TestACL_Authorize(t *testing.T) {
Rules: `acl = "read" operator = "write" service_prefix "" { policy = "read"} node_prefix "" { policy= "write" } key_prefix "/foo" { policy = "write" } `,
},
Datacenter: "dc1",
WriteRequest: structs.WriteRequest{Token: TestDefaultMasterToken},
WriteRequest: structs.WriteRequest{Token: TestDefaultInitialManagementToken},
}
var policy structs.ACLPolicy
require.NoError(t, a1.RPC("ACL.PolicySet", &policyReq, &policy))
@ -1841,15 +1846,15 @@ func TestACL_Authorize(t *testing.T) {
},
},
Datacenter: "dc1",
WriteRequest: structs.WriteRequest{Token: TestDefaultMasterToken},
WriteRequest: structs.WriteRequest{Token: TestDefaultInitialManagementToken},
}
var token structs.ACLToken
require.NoError(t, a1.RPC("ACL.TokenSet", &tokenReq, &token))
// secondary also needs to setup a replication token to pull tokens and policies
secondaryParams := DefaulTestACLConfigParams()
secondaryParams.ReplicationToken = secondaryParams.MasterToken
secondaryParams := DefaultTestACLConfigParams()
secondaryParams.ReplicationToken = secondaryParams.InitialManagementToken
secondaryParams.EnableTokenReplication = true
a2 := NewTestAgent(t, `datacenter = "dc2" `+TestACLConfigWithParams(secondaryParams))
@ -1859,7 +1864,7 @@ func TestACL_Authorize(t *testing.T) {
_, err := a2.JoinWAN([]string{addr})
require.NoError(t, err)
testrpc.WaitForTestAgent(t, a2.RPC, "dc2", testrpc.WithToken(TestDefaultMasterToken))
testrpc.WaitForTestAgent(t, a2.RPC, "dc2", testrpc.WithToken(TestDefaultInitialManagementToken))
// this actually ensures a few things. First the dcs got connect okay, secondly that the policy we
// are about ready to use in our local token creation exists in the secondary DC
testrpc.WaitForACLReplication(t, a2.RPC, "dc2", structs.ACLReplicateTokens, policy.CreateIndex, 1, 0)
@ -1874,7 +1879,7 @@ func TestACL_Authorize(t *testing.T) {
Local: true,
},
Datacenter: "dc2",
WriteRequest: structs.WriteRequest{Token: TestDefaultMasterToken},
WriteRequest: structs.WriteRequest{Token: TestDefaultInitialManagementToken},
}
var localToken structs.ACLToken
@ -2004,7 +2009,7 @@ func TestACL_Authorize(t *testing.T) {
for _, dc := range []string{"dc1", "dc2"} {
t.Run(dc, func(t *testing.T) {
req, _ := http.NewRequest("POST", "/v1/internal/acl/authorize?dc="+dc, jsonBody(request))
req.Header.Add("X-Consul-Token", TestDefaultMasterToken)
req.Header.Add("X-Consul-Token", TestDefaultInitialManagementToken)
recorder := httptest.NewRecorder()
raw, err := a1.srv.ACLAuthorize(recorder, req)
require.NoError(t, err)

View File

@ -1153,8 +1153,8 @@ func newConsulConfig(runtimeCfg *config.RuntimeConfig, logger hclog.Logger) (*co
if runtimeCfg.RaftTrailingLogs != 0 {
cfg.RaftConfig.TrailingLogs = uint64(runtimeCfg.RaftTrailingLogs)
}
if runtimeCfg.ACLMasterToken != "" {
cfg.ACLMasterToken = runtimeCfg.ACLMasterToken
if runtimeCfg.ACLInitialManagementToken != "" {
cfg.ACLInitialManagementToken = runtimeCfg.ACLInitialManagementToken
}
cfg.ACLTokenReplication = runtimeCfg.ACLTokenReplication
cfg.ACLsEnabled = runtimeCfg.ACLsEnabled
@ -1263,6 +1263,7 @@ func newConsulConfig(runtimeCfg *config.RuntimeConfig, logger hclog.Logger) (*co
}
cfg.ConfigEntryBootstrap = runtimeCfg.ConfigEntryBootstrap
cfg.RaftBoltDBConfig = runtimeCfg.RaftBoltDBConfig
// Duplicate our own serf config once to make sure that the duplication
// function does not drift.

View File

@ -1005,7 +1005,7 @@ func (s *HTTPHandlers) AgentHealthServiceByID(resp http.ResponseWriter, req *htt
}
notFoundReason := fmt.Sprintf("ServiceId %s not found", sid.String())
if returnTextPlain(req) {
return notFoundReason, CodeWithPayloadError{StatusCode: http.StatusNotFound, Reason: notFoundReason, ContentType: "application/json"}
return notFoundReason, CodeWithPayloadError{StatusCode: http.StatusNotFound, Reason: notFoundReason, ContentType: "text/plain"}
}
return &api.AgentServiceChecksInfo{
AggregatedStatus: api.HealthCritical,
@ -1510,7 +1510,7 @@ func (s *HTTPHandlers) AgentToken(resp http.ResponseWriter, req *http.Request) (
}
case "acl_agent_master_token", "agent_master", "agent_recovery":
s.agent.tokens.UpdateAgentMasterToken(args.Token, token_store.TokenSourceAPI)
s.agent.tokens.UpdateAgentRecoveryToken(args.Token, token_store.TokenSourceAPI)
case "acl_replication_token", "replication":
s.agent.tokens.UpdateReplicationToken(args.Token, token_store.TokenSourceAPI)

File diff suppressed because it is too large Load Diff

View File

@ -214,10 +214,14 @@ func TestAgent_TokenStore(t *testing.T) {
t.Parallel()
a := NewTestAgent(t, `
acl_token = "user"
acl_agent_token = "agent"
acl_agent_master_token = "master"`,
)
acl {
tokens {
default = "user"
agent = "agent"
agent_recovery = "recovery"
}
}
`)
defer a.Shutdown()
if got, want := a.tokens.UserToken(), "user"; got != want {
@ -226,7 +230,7 @@ func TestAgent_TokenStore(t *testing.T) {
if got, want := a.tokens.AgentToken(), "agent"; got != want {
t.Fatalf("got %q want %q", got, want)
}
if got, want := a.tokens.IsAgentMasterToken("master"), true; got != want {
if got, want := a.tokens.IsAgentRecoveryToken("recovery"), true; got != want {
t.Fatalf("got %v want %v", got, want)
}
}
@ -5037,7 +5041,7 @@ func TestAutoConfig_Integration(t *testing.T) {
srv := StartTestAgent(t, TestAgent{Name: "TestAgent-Server", HCL: hclConfig})
defer srv.Shutdown()
testrpc.WaitForTestAgent(t, srv.RPC, "dc1", testrpc.WithToken(TestDefaultMasterToken))
testrpc.WaitForTestAgent(t, srv.RPC, "dc1", testrpc.WithToken(TestDefaultInitialManagementToken))
// sign a JWT token
now := time.Now()
@ -5084,7 +5088,7 @@ func TestAutoConfig_Integration(t *testing.T) {
// when this is successful we managed to get the gossip key and serf addresses to bind to
// and then connect. Additionally we would have to have certificates or else the
// verify_incoming config on the server would not let it work.
testrpc.WaitForTestAgent(t, client.RPC, "dc1", testrpc.WithToken(TestDefaultMasterToken))
testrpc.WaitForTestAgent(t, client.RPC, "dc1", testrpc.WithToken(TestDefaultInitialManagementToken))
// spot check that we now have an ACL token
require.NotEmpty(t, client.tokens.AgentToken())
@ -5098,7 +5102,7 @@ func TestAutoConfig_Integration(t *testing.T) {
ca := connect.TestCA(t, nil)
req := &structs.CARequest{
Datacenter: "dc1",
WriteRequest: structs.WriteRequest{Token: TestDefaultMasterToken},
WriteRequest: structs.WriteRequest{Token: TestDefaultInitialManagementToken},
Config: &structs.CAConfiguration{
Provider: "consul",
Config: map[string]interface{}{
@ -5170,7 +5174,7 @@ func TestAgent_AutoEncrypt(t *testing.T) {
srv := StartTestAgent(t, TestAgent{Name: "test-server", HCL: hclConfig})
defer srv.Shutdown()
testrpc.WaitForTestAgent(t, srv.RPC, "dc1", testrpc.WithToken(TestDefaultMasterToken))
testrpc.WaitForTestAgent(t, srv.RPC, "dc1", testrpc.WithToken(TestDefaultInitialManagementToken))
client := StartTestAgent(t, TestAgent{Name: "test-client", HCL: TestACLConfigWithParams(nil) + `
bootstrap = false
@ -5193,7 +5197,7 @@ func TestAgent_AutoEncrypt(t *testing.T) {
// when this is successful we managed to get a TLS certificate and are using it for
// encrypted RPC connections.
testrpc.WaitForTestAgent(t, client.RPC, "dc1", testrpc.WithToken(TestDefaultMasterToken))
testrpc.WaitForTestAgent(t, client.RPC, "dc1", testrpc.WithToken(TestDefaultInitialManagementToken))
// now we need to validate that our certificate has the correct CN
aeCert := client.tlsConfigurator.Cert()

View File

@ -65,14 +65,12 @@ func translateConfig(c *pbconfig.Config) config.Config {
}
result.ACL.Tokens = config.Tokens{
InitialManagement: stringPtrOrNil(t.InitialManagement),
AgentRecovery: stringPtrOrNil(t.AgentRecovery),
Replication: stringPtrOrNil(t.Replication),
Default: stringPtrOrNil(t.Default),
Agent: stringPtrOrNil(t.Agent),
ManagedServiceProvider: tokens,
DeprecatedTokens: config.DeprecatedTokens{
Master: stringPtrOrNil(t.Master),
AgentMaster: stringPtrOrNil(t.AgentMaster),
},
}
}
}

View File

@ -69,11 +69,11 @@ func TestTranslateConfig(t *testing.T) {
EnableTokenPersistence: true,
MSPDisableBootstrap: false,
Tokens: &pbconfig.ACLTokens{
Master: "99e7e490-6baf-43fc-9010-78b6aa9a6813",
Replication: "51308d40-465c-4ac6-a636-7c0747edec89",
AgentMaster: "e012e1ea-78a2-41cc-bc8b-231a44196f39",
Default: "8781a3f5-de46-4b45-83e1-c92f4cfd0332",
Agent: "ddb8f1b0-8a99-4032-b601-87926bce244e",
InitialManagement: "99e7e490-6baf-43fc-9010-78b6aa9a6813",
Replication: "51308d40-465c-4ac6-a636-7c0747edec89",
AgentRecovery: "e012e1ea-78a2-41cc-bc8b-231a44196f39",
Default: "8781a3f5-de46-4b45-83e1-c92f4cfd0332",
Agent: "ddb8f1b0-8a99-4032-b601-87926bce244e",
ManagedServiceProvider: []*pbconfig.ACLServiceProviderToken{
{
AccessorID: "23f37987-7b9e-4e5b-acae-dbc9bc137bae",
@ -129,19 +129,17 @@ func TestTranslateConfig(t *testing.T) {
EnableKeyListPolicy: boolPointer(true),
EnableTokenPersistence: boolPointer(true),
Tokens: config.Tokens{
Replication: stringPointer("51308d40-465c-4ac6-a636-7c0747edec89"),
Default: stringPointer("8781a3f5-de46-4b45-83e1-c92f4cfd0332"),
Agent: stringPointer("ddb8f1b0-8a99-4032-b601-87926bce244e"),
InitialManagement: stringPointer("99e7e490-6baf-43fc-9010-78b6aa9a6813"),
AgentRecovery: stringPointer("e012e1ea-78a2-41cc-bc8b-231a44196f39"),
Replication: stringPointer("51308d40-465c-4ac6-a636-7c0747edec89"),
Default: stringPointer("8781a3f5-de46-4b45-83e1-c92f4cfd0332"),
Agent: stringPointer("ddb8f1b0-8a99-4032-b601-87926bce244e"),
ManagedServiceProvider: []config.ServiceProviderToken{
{
AccessorID: stringPointer("23f37987-7b9e-4e5b-acae-dbc9bc137bae"),
SecretID: stringPointer("e28b820a-438e-4e2b-ad24-fe59e6a4914f"),
},
},
DeprecatedTokens: config.DeprecatedTokens{
Master: stringPointer("99e7e490-6baf-43fc-9010-78b6aa9a6813"),
AgentMaster: stringPointer("e012e1ea-78a2-41cc-bc8b-231a44196f39"),
},
},
},
AutoEncrypt: config.AutoEncrypt{

View File

@ -860,18 +860,18 @@ func (b *builder) build() (rt RuntimeConfig, err error) {
ACLDefaultPolicy: stringVal(c.ACL.DefaultPolicy),
},
ACLEnableKeyListPolicy: boolVal(c.ACL.EnableKeyListPolicy),
ACLMasterToken: stringVal(c.ACL.Tokens.InitialManagement),
ACLEnableKeyListPolicy: boolVal(c.ACL.EnableKeyListPolicy),
ACLInitialManagementToken: stringVal(c.ACL.Tokens.InitialManagement),
ACLTokenReplication: boolVal(c.ACL.TokenReplication),
ACLTokens: token.Config{
DataDir: dataDir,
EnablePersistence: boolValWithDefault(c.ACL.EnableTokenPersistence, false),
ACLDefaultToken: stringVal(c.ACL.Tokens.Default),
ACLAgentToken: stringVal(c.ACL.Tokens.Agent),
ACLAgentMasterToken: stringVal(c.ACL.Tokens.AgentRecovery),
ACLReplicationToken: stringVal(c.ACL.Tokens.Replication),
DataDir: dataDir,
EnablePersistence: boolValWithDefault(c.ACL.EnableTokenPersistence, false),
ACLDefaultToken: stringVal(c.ACL.Tokens.Default),
ACLAgentToken: stringVal(c.ACL.Tokens.Agent),
ACLAgentRecoveryToken: stringVal(c.ACL.Tokens.AgentRecovery),
ACLReplicationToken: stringVal(c.ACL.Tokens.Replication),
},
// Autopilot
@ -1094,6 +1094,10 @@ func (b *builder) build() (rt RuntimeConfig, err error) {
rt.UseStreamingBackend = boolValWithDefault(c.UseStreamingBackend, true)
if c.RaftBoltDBConfig != nil {
rt.RaftBoltDBConfig = *c.RaftBoltDBConfig
}
if rt.Cache.EntryFetchMaxBurst <= 0 {
return RuntimeConfig{}, fmt.Errorf("cache.entry_fetch_max_burst must be strictly positive, was: %v", rt.Cache.EntryFetchMaxBurst)
}

View File

@ -4,6 +4,8 @@ import (
"encoding/json"
"fmt"
"github.com/hashicorp/consul/agent/consul"
"github.com/hashicorp/hcl"
"github.com/mitchellh/mapstructure"
@ -256,6 +258,8 @@ type Config struct {
RPC RPC `mapstructure:"rpc"`
RaftBoltDBConfig *consul.RaftBoltDBConfig `mapstructure:"raft_boltdb"`
// UseStreamingBackend instead of blocking queries for service health and
// any other endpoints which support streaming.
UseStreamingBackend *bool `mapstructure:"use_streaming_backend"`

View File

@ -110,8 +110,8 @@ func TestLoad_DeprecatedConfig_ACLMasterTokens(t *testing.T) {
require.ElementsMatch(expectWarns, result.Warnings)
rt := result.RuntimeConfig
require.Equal("token1", rt.ACLMasterToken)
require.Equal("token2", rt.ACLTokens.ACLAgentMasterToken)
require.Equal("token1", rt.ACLInitialManagementToken)
require.Equal("token2", rt.ACLTokens.ACLAgentRecoveryToken)
})
t.Run("embedded in tokens struct", func(t *testing.T) {
@ -141,8 +141,8 @@ func TestLoad_DeprecatedConfig_ACLMasterTokens(t *testing.T) {
require.ElementsMatch(expectWarns, result.Warnings)
rt := result.RuntimeConfig
require.Equal("token1", rt.ACLMasterToken)
require.Equal("token2", rt.ACLTokens.ACLAgentMasterToken)
require.Equal("token1", rt.ACLInitialManagementToken)
require.Equal("token2", rt.ACLTokens.ACLAgentRecoveryToken)
})
t.Run("both", func(t *testing.T) {
@ -169,7 +169,7 @@ func TestLoad_DeprecatedConfig_ACLMasterTokens(t *testing.T) {
require.NoError(err)
rt := result.RuntimeConfig
require.Equal("token3", rt.ACLMasterToken)
require.Equal("token4", rt.ACLTokens.ACLAgentMasterToken)
require.Equal("token3", rt.ACLInitialManagementToken)
require.Equal("token4", rt.ACLTokens.ACLAgentRecoveryToken)
})
}

View File

@ -73,12 +73,12 @@ type RuntimeConfig struct {
// hcl: acl.enable_key_list_policy = (true|false)
ACLEnableKeyListPolicy bool
// ACLMasterToken is used to bootstrap the ACL system. It should be specified
// ACLInitialManagementToken is used to bootstrap the ACL system. It should be specified
// on the servers in the PrimaryDatacenter. When the leader comes online, it ensures
// that the Master token is available. This provides the initial token.
// that the initial management token is available. This provides the initial token.
//
// hcl: acl.tokens.initial_management = string
ACLMasterToken string
ACLInitialManagementToken string
// ACLtokenReplication is used to indicate that both tokens and policies
// should be replicated instead of just policies
@ -943,6 +943,8 @@ type RuntimeConfig struct {
// hcl: raft_trailing_logs = int
RaftTrailingLogs int
RaftBoltDBConfig consul.RaftBoltDBConfig
// ReconnectTimeoutLAN specifies the amount of time to wait to reconnect with
// another agent before deciding it's permanently gone. This can be used to
// control the time it takes to reap failed nodes from the cluster.

View File

@ -23,6 +23,7 @@ import (
"github.com/google/go-cmp/cmp/cmpopts"
"github.com/stretchr/testify/require"
"github.com/hashicorp/consul/acl"
"github.com/hashicorp/consul/agent/cache"
"github.com/hashicorp/consul/agent/checks"
"github.com/hashicorp/consul/agent/consul"
@ -4085,6 +4086,7 @@ func TestLoad_IntegrationWithFlags(t *testing.T) {
Service: "carrot",
ServiceSubset: "kale",
Namespace: "leek",
Partition: acl.DefaultPartitionName,
PrefixRewrite: "/alternate",
RequestTimeout: 99 * time.Second,
NumRetries: 12345,
@ -5339,12 +5341,12 @@ func TestLoad_FullConfig(t *testing.T) {
// user configurable values
ACLTokens: token.Config{
EnablePersistence: true,
DataDir: dataDir,
ACLDefaultToken: "418fdff1",
ACLAgentToken: "bed2377c",
ACLAgentMasterToken: "1dba6aba",
ACLReplicationToken: "5795983a",
EnablePersistence: true,
DataDir: dataDir,
ACLDefaultToken: "418fdff1",
ACLAgentToken: "bed2377c",
ACLAgentRecoveryToken: "1dba6aba",
ACLReplicationToken: "5795983a",
},
ACLsEnabled: true,
@ -5361,7 +5363,7 @@ func TestLoad_FullConfig(t *testing.T) {
ACLRoleTTL: 9876 * time.Second,
},
ACLEnableKeyListPolicy: true,
ACLMasterToken: "3820e09a",
ACLInitialManagementToken: "3820e09a",
ACLTokenReplication: true,
AdvertiseAddrLAN: ipAddr("17.99.29.16"),
AdvertiseAddrWAN: ipAddr("78.63.37.19"),
@ -6015,6 +6017,7 @@ func TestLoad_FullConfig(t *testing.T) {
"args": []interface{}{"dltjDJ2a", "flEa7C2d"},
},
},
RaftBoltDBConfig: consul.RaftBoltDBConfig{NoFreelistSync: true},
}
entFullRuntimeConfig(expected)

View File

@ -1,6 +1,6 @@
{
"ACLEnableKeyListPolicy": false,
"ACLMasterToken": "hidden",
"ACLInitialManagementToken": "hidden",
"ACLResolverSettings": {
"ACLDefaultPolicy": "",
"ACLDownPolicy": "",
@ -14,7 +14,7 @@
},
"ACLTokenReplication": false,
"ACLTokens": {
"ACLAgentMasterToken": "hidden",
"ACLAgentRecoveryToken": "hidden",
"ACLAgentToken": "hidden",
"ACLDefaultToken": "hidden",
"ACLReplicationToken": "hidden",
@ -252,6 +252,9 @@
"RPCMaxConnsPerClient": 0,
"RPCProtocol": 0,
"RPCRateLimit": 0,
"RaftBoltDBConfig": {
"NoFreelistSync": false
},
"RaftProtocol": 3,
"RaftSnapshotInterval": "0s",
"RaftSnapshotThreshold": 0,
@ -421,4 +424,4 @@
"Version": "",
"VersionPrerelease": "",
"Watches": []
}
}

View File

@ -328,6 +328,9 @@ raft_protocol = 3
raft_snapshot_threshold = 16384
raft_snapshot_interval = "30s"
raft_trailing_logs = 83749
raft_boltdb {
NoFreelistSync = true
}
read_replica = true
reconnect_timeout = "23739s"
reconnect_timeout_wan = "26694s"

View File

@ -326,6 +326,9 @@
"raft_snapshot_threshold": 16384,
"raft_snapshot_interval": "30s",
"raft_trailing_logs": 83749,
"raft_boltdb": {
"NoFreelistSync": true
},
"read_replica": true,
"reconnect_timeout": "23739s",
"reconnect_timeout_wan": "26694s",

View File

@ -1053,7 +1053,7 @@ func (r *ACLResolver) resolveLocallyManagedToken(token string) (structs.ACLIdent
return nil, nil, false
}
if r.tokens.IsAgentMasterToken(token) {
if r.tokens.IsAgentRecoveryToken(token) {
return structs.NewAgentMasterTokenIdentity(r.config.NodeName, token), r.agentMasterAuthz, true
}
@ -1465,11 +1465,13 @@ func (f *aclFilter) filterIntentions(ixns *structs.Intentions) bool {
}
// filterNodeDump is used to filter through all parts of a node dump and
// remove elements the provided ACL token cannot access.
func (f *aclFilter) filterNodeDump(dump *structs.NodeDump) {
// remove elements the provided ACL token cannot access. Returns true if
// any elements were removed.
func (f *aclFilter) filterNodeDump(dump *structs.NodeDump) bool {
nd := *dump
var authzContext acl.AuthorizerContext
var removed bool
for i := 0; i < len(nd); i++ {
info := nd[i]
@ -1477,6 +1479,7 @@ func (f *aclFilter) filterNodeDump(dump *structs.NodeDump) {
info.FillAuthzContext(&authzContext)
if node := info.Node; !f.allowNode(node, &authzContext) {
f.logger.Debug("dropping node from result due to ACLs", "node", structs.NodeNameString(node, info.GetEnterpriseMeta()))
removed = true
nd = append(nd[:i], nd[i+1:]...)
i--
continue
@ -1490,6 +1493,7 @@ func (f *aclFilter) filterNodeDump(dump *structs.NodeDump) {
continue
}
f.logger.Debug("dropping service from result due to ACLs", "service", svc)
removed = true
info.Services = append(info.Services[:j], info.Services[j+1:]...)
j--
}
@ -1502,17 +1506,21 @@ func (f *aclFilter) filterNodeDump(dump *structs.NodeDump) {
continue
}
f.logger.Debug("dropping check from result due to ACLs", "check", chk.CheckID)
removed = true
info.Checks = append(info.Checks[:j], info.Checks[j+1:]...)
j--
}
}
*dump = nd
return removed
}
// filterServiceDump is used to filter nodes based on ACL rules.
func (f *aclFilter) filterServiceDump(services *structs.ServiceDump) {
// filterServiceDump is used to filter nodes based on ACL rules. Returns true
// if any elements were removed.
func (f *aclFilter) filterServiceDump(services *structs.ServiceDump) bool {
svcs := *services
var authzContext acl.AuthorizerContext
var removed bool
for i := 0; i < len(svcs); i++ {
service := svcs[i]
@ -1530,10 +1538,12 @@ func (f *aclFilter) filterServiceDump(services *structs.ServiceDump) {
}
f.logger.Debug("dropping service from result due to ACLs", "service", service.GatewayService.Service)
removed = true
svcs = append(svcs[:i], svcs[i+1:]...)
i--
}
*services = svcs
return removed
}
// filterNodes is used to filter through all parts of a node list and remove
@ -1592,8 +1602,10 @@ func (f *aclFilter) redactPreparedQueryTokens(query **structs.PreparedQuery) {
// filterPreparedQueries is used to filter prepared queries based on ACL rules.
// We prune entries the user doesn't have access to, and we redact any tokens
// if the user doesn't have a management token.
func (f *aclFilter) filterPreparedQueries(queries *structs.PreparedQueries) {
// if the user doesn't have a management token. Returns true if any (named)
// queries were removed - un-named queries are meant to be ephemeral and can
// only be enumerated by a management token
func (f *aclFilter) filterPreparedQueries(queries *structs.PreparedQueries) bool {
var authzContext acl.AuthorizerContext
structs.DefaultEnterpriseMetaInDefaultPartition().FillAuthzContext(&authzContext)
// Management tokens can see everything with no filtering.
@ -1601,17 +1613,22 @@ func (f *aclFilter) filterPreparedQueries(queries *structs.PreparedQueries) {
// the 1.4 ACL rewrite. The global-management token will provide unrestricted query privileges
// so asking for ACLWrite should be unnecessary.
if f.authorizer.ACLWrite(&authzContext) == acl.Allow {
return
return false
}
// Otherwise, we need to see what the token has access to.
var namedQueriesRemoved bool
ret := make(structs.PreparedQueries, 0, len(*queries))
for _, query := range *queries {
// If no prefix ACL applies to this query then filter it, since
// we know at this point the user doesn't have a management
// token, otherwise see what the policy says.
prefix, ok := query.GetACLPrefix()
if !ok || f.authorizer.PreparedQueryRead(prefix, &authzContext) != acl.Allow {
prefix, hasName := query.GetACLPrefix()
switch {
case hasName && f.authorizer.PreparedQueryRead(prefix, &authzContext) != acl.Allow:
namedQueriesRemoved = true
fallthrough
case !hasName:
f.logger.Debug("dropping prepared query from result due to ACLs", "query", query.ID)
continue
}
@ -1623,6 +1640,7 @@ func (f *aclFilter) filterPreparedQueries(queries *structs.PreparedQueries) {
ret = append(ret, final)
}
*queries = ret
return namedQueriesRemoved
}
func (f *aclFilter) filterToken(token **structs.ACLToken) {
@ -1815,8 +1833,8 @@ func (f *aclFilter) filterServiceList(services *structs.ServiceList) bool {
// filterGatewayServices is used to filter gateway to service mappings based on ACL rules.
// Returns true if any elements were removed.
func (f *aclFilter) filterGatewayServices(mappings *structs.GatewayServices) bool {
var removed bool
ret := make(structs.GatewayServices, 0, len(*mappings))
var removed bool
for _, s := range *mappings {
// This filter only checks ServiceRead on the linked service.
// ServiceRead on the gateway is checked in the GatewayServices endpoint before filtering.
@ -1847,6 +1865,9 @@ func filterACLWithAuthorizer(logger hclog.Logger, authorizer acl.Authorizer, sub
case *structs.IndexedCheckServiceNodes:
v.QueryMeta.ResultsFilteredByACLs = filt.filterCheckServiceNodes(&v.Nodes)
case *structs.PreparedQueryExecuteResponse:
v.QueryMeta.ResultsFilteredByACLs = filt.filterCheckServiceNodes(&v.Nodes)
case *structs.IndexedServiceTopology:
filtered := filt.filterServiceTopology(v.ServiceTopology)
if filtered {
@ -1867,10 +1888,10 @@ func filterACLWithAuthorizer(logger hclog.Logger, authorizer acl.Authorizer, sub
v.QueryMeta.ResultsFilteredByACLs = filt.filterIntentions(&v.Intentions)
case *structs.IndexedNodeDump:
filt.filterNodeDump(&v.Dump)
v.QueryMeta.ResultsFilteredByACLs = filt.filterNodeDump(&v.Dump)
case *structs.IndexedServiceDump:
filt.filterServiceDump(&v.Dump)
v.QueryMeta.ResultsFilteredByACLs = filt.filterServiceDump(&v.Dump)
case *structs.IndexedNodes:
v.QueryMeta.ResultsFilteredByACLs = filt.filterNodes(&v.Nodes)
@ -1891,7 +1912,7 @@ func filterACLWithAuthorizer(logger hclog.Logger, authorizer acl.Authorizer, sub
v.QueryMeta.ResultsFilteredByACLs = filt.filterSessions(&v.Sessions)
case *structs.IndexedPreparedQueries:
filt.filterPreparedQueries(&v.Queries)
v.QueryMeta.ResultsFilteredByACLs = filt.filterPreparedQueries(&v.Queries)
case **structs.PreparedQuery:
filt.redactPreparedQueryTokens(v)
@ -1959,6 +1980,6 @@ func filterACL(r *ACLResolver, token string, subj interface{}) error {
type partitionInfoNoop struct{}
func (p *partitionInfoNoop) ExportsForPartition(partition string) acl.PartitionExports {
return acl.PartitionExports{}
func (p *partitionInfoNoop) ExportsForPartition(partition string) acl.ExportedServices {
return acl.ExportedServices{}
}

View File

@ -32,7 +32,7 @@ func TestACLEndpoint_BootstrapTokens(t *testing.T) {
t.Parallel()
dir, srv, codec := testACLServerWithConfig(t, func(c *Config) {
// remove this as we are bootstrapping
c.ACLMasterToken = ""
c.ACLInitialManagementToken = ""
}, false)
waitForLeaderEstablishment(t, srv)

View File

@ -301,7 +301,7 @@ func TestACLReplication_Tokens(t *testing.T) {
dir1, s1 := testServerWithConfig(t, func(c *Config) {
c.PrimaryDatacenter = "dc1"
c.ACLsEnabled = true
c.ACLMasterToken = "root"
c.ACLInitialManagementToken = "root"
})
defer os.RemoveAll(dir1)
defer s1.Shutdown()
@ -513,7 +513,7 @@ func TestACLReplication_Policies(t *testing.T) {
dir1, s1 := testServerWithConfig(t, func(c *Config) {
c.PrimaryDatacenter = "dc1"
c.ACLsEnabled = true
c.ACLMasterToken = "root"
c.ACLInitialManagementToken = "root"
})
defer os.RemoveAll(dir1)
defer s1.Shutdown()
@ -633,7 +633,7 @@ func TestACLReplication_TokensRedacted(t *testing.T) {
dir1, s1 := testServerWithConfig(t, func(c *Config) {
c.PrimaryDatacenter = "dc1"
c.ACLsEnabled = true
c.ACLMasterToken = "root"
c.ACLInitialManagementToken = "root"
})
defer os.RemoveAll(dir1)
defer s1.Shutdown()
@ -783,7 +783,7 @@ func TestACLReplication_AllTypes(t *testing.T) {
dir1, s1 := testServerWithConfig(t, func(c *Config) {
c.PrimaryDatacenter = "dc1"
c.ACLsEnabled = true
c.ACLMasterToken = "root"
c.ACLInitialManagementToken = "root"
})
defer os.RemoveAll(dir1)
defer s1.Shutdown()

View File

@ -2752,6 +2752,108 @@ func TestACL_filterCheckServiceNodes(t *testing.T) {
})
}
func TestACL_filterPreparedQueryExecuteResponse(t *testing.T) {
t.Parallel()
logger := hclog.NewNullLogger()
makeList := func() *structs.PreparedQueryExecuteResponse {
return &structs.PreparedQueryExecuteResponse{
Nodes: structs.CheckServiceNodes{
{
Node: &structs.Node{
Node: "node1",
},
Service: &structs.NodeService{
ID: "foo",
Service: "foo",
},
Checks: structs.HealthChecks{
{
Node: "node1",
CheckID: "check1",
ServiceName: "foo",
},
},
},
},
}
}
t.Run("allowed", func(t *testing.T) {
require := require.New(t)
policy, err := acl.NewPolicyFromSource(`
service "foo" {
policy = "read"
}
node "node1" {
policy = "read"
}
`, acl.SyntaxLegacy, nil, nil)
require.NoError(err)
authz, err := acl.NewPolicyAuthorizerWithDefaults(acl.DenyAll(), []*acl.Policy{policy}, nil)
require.NoError(err)
list := makeList()
filterACLWithAuthorizer(logger, authz, list)
require.Len(list.Nodes, 1)
require.False(list.QueryMeta.ResultsFilteredByACLs, "ResultsFilteredByACLs should be false")
})
t.Run("allowed to read the service, but not the node", func(t *testing.T) {
require := require.New(t)
policy, err := acl.NewPolicyFromSource(`
service "foo" {
policy = "read"
}
`, acl.SyntaxLegacy, nil, nil)
require.NoError(err)
authz, err := acl.NewPolicyAuthorizerWithDefaults(acl.DenyAll(), []*acl.Policy{policy}, nil)
require.NoError(err)
list := makeList()
filterACLWithAuthorizer(logger, authz, list)
require.Empty(list.Nodes)
require.True(list.QueryMeta.ResultsFilteredByACLs, "ResultsFilteredByACLs should be true")
})
t.Run("allowed to read the node, but not the service", func(t *testing.T) {
require := require.New(t)
policy, err := acl.NewPolicyFromSource(`
node "node1" {
policy = "read"
}
`, acl.SyntaxLegacy, nil, nil)
require.NoError(err)
authz, err := acl.NewPolicyAuthorizerWithDefaults(acl.DenyAll(), []*acl.Policy{policy}, nil)
require.NoError(err)
list := makeList()
filterACLWithAuthorizer(logger, authz, list)
require.Empty(list.Nodes)
require.True(list.QueryMeta.ResultsFilteredByACLs, "ResultsFilteredByACLs should be true")
})
t.Run("denied", func(t *testing.T) {
require := require.New(t)
list := makeList()
filterACLWithAuthorizer(logger, acl.DenyAll(), list)
require.Empty(list.Nodes)
require.True(list.QueryMeta.ResultsFilteredByACLs, "ResultsFilteredByACLs should be true")
})
}
func TestACL_filterServiceTopology(t *testing.T) {
t.Parallel()
// Create some nodes.
@ -3024,107 +3126,105 @@ func TestACL_filterSessions(t *testing.T) {
func TestACL_filterNodeDump(t *testing.T) {
t.Parallel()
// Create a node dump.
fill := func() structs.NodeDump {
return structs.NodeDump{
&structs.NodeInfo{
Node: "node1",
Services: []*structs.NodeService{
{
ID: "foo",
Service: "foo",
logger := hclog.NewNullLogger()
makeList := func() *structs.IndexedNodeDump {
return &structs.IndexedNodeDump{
Dump: structs.NodeDump{
{
Node: "node1",
Services: []*structs.NodeService{
{
ID: "foo",
Service: "foo",
},
},
},
Checks: []*structs.HealthCheck{
{
Node: "node1",
CheckID: "check1",
ServiceName: "foo",
Checks: []*structs.HealthCheck{
{
Node: "node1",
CheckID: "check1",
ServiceName: "foo",
},
},
},
},
}
}
// Try permissive filtering.
{
dump := fill()
filt := newACLFilter(acl.AllowAll(), nil)
filt.filterNodeDump(&dump)
if len(dump) != 1 {
t.Fatalf("bad: %#v", dump)
}
if len(dump[0].Services) != 1 {
t.Fatalf("bad: %#v", dump[0].Services)
}
if len(dump[0].Checks) != 1 {
t.Fatalf("bad: %#v", dump[0].Checks)
}
}
t.Run("allowed", func(t *testing.T) {
require := require.New(t)
// Try restrictive filtering.
{
dump := fill()
filt := newACLFilter(acl.DenyAll(), nil)
filt.filterNodeDump(&dump)
if len(dump) != 0 {
t.Fatalf("bad: %#v", dump)
}
}
policy, err := acl.NewPolicyFromSource(`
service "foo" {
policy = "read"
}
node "node1" {
policy = "read"
}
`, acl.SyntaxLegacy, nil, nil)
require.NoError(err)
// Allowed to see the service but not the node.
policy, err := acl.NewPolicyFromSource(`
service "foo" {
policy = "read"
}
`, acl.SyntaxLegacy, nil, nil)
if err != nil {
t.Fatalf("err %v", err)
}
perms, err := acl.NewPolicyAuthorizerWithDefaults(acl.DenyAll(), []*acl.Policy{policy}, nil)
if err != nil {
t.Fatalf("err: %v", err)
}
authz, err := acl.NewPolicyAuthorizerWithDefaults(acl.DenyAll(), []*acl.Policy{policy}, nil)
require.NoError(err)
// But the node will block it.
{
dump := fill()
filt := newACLFilter(perms, nil)
filt.filterNodeDump(&dump)
if len(dump) != 0 {
t.Fatalf("bad: %#v", dump)
}
}
list := makeList()
filterACLWithAuthorizer(logger, authz, list)
// Chain on access to the node.
policy, err = acl.NewPolicyFromSource(`
node "node1" {
policy = "read"
}
`, acl.SyntaxLegacy, nil, nil)
if err != nil {
t.Fatalf("err %v", err)
}
perms, err = acl.NewPolicyAuthorizerWithDefaults(perms, []*acl.Policy{policy}, nil)
if err != nil {
t.Fatalf("err: %v", err)
}
require.Len(list.Dump, 1)
require.False(list.QueryMeta.ResultsFilteredByACLs, "ResultsFilteredByACLs should be false")
})
// Now it should go through.
{
dump := fill()
filt := newACLFilter(perms, nil)
filt.filterNodeDump(&dump)
if len(dump) != 1 {
t.Fatalf("bad: %#v", dump)
}
if len(dump[0].Services) != 1 {
t.Fatalf("bad: %#v", dump[0].Services)
}
if len(dump[0].Checks) != 1 {
t.Fatalf("bad: %#v", dump[0].Checks)
}
}
t.Run("allowed to read the service, but not the node", func(t *testing.T) {
require := require.New(t)
policy, err := acl.NewPolicyFromSource(`
service "foo" {
policy = "read"
}
`, acl.SyntaxLegacy, nil, nil)
require.NoError(err)
authz, err := acl.NewPolicyAuthorizerWithDefaults(acl.DenyAll(), []*acl.Policy{policy}, nil)
require.NoError(err)
list := makeList()
filterACLWithAuthorizer(logger, authz, list)
require.Empty(list.Dump)
require.True(list.QueryMeta.ResultsFilteredByACLs, "ResultsFilteredByACLs should be true")
})
t.Run("allowed to read the node, but not the service", func(t *testing.T) {
require := require.New(t)
policy, err := acl.NewPolicyFromSource(`
node "node1" {
policy = "read"
}
`, acl.SyntaxLegacy, nil, nil)
require.NoError(err)
authz, err := acl.NewPolicyAuthorizerWithDefaults(acl.DenyAll(), []*acl.Policy{policy}, nil)
require.NoError(err)
list := makeList()
filterACLWithAuthorizer(logger, authz, list)
require.Len(list.Dump, 1)
require.Empty(list.Dump[0].Services)
require.True(list.QueryMeta.ResultsFilteredByACLs, "ResultsFilteredByACLs should be true")
})
t.Run("denied", func(t *testing.T) {
require := require.New(t)
list := makeList()
filterACLWithAuthorizer(logger, acl.DenyAll(), list)
require.Empty(list.Dump)
require.True(list.QueryMeta.ResultsFilteredByACLs, "ResultsFilteredByACLs should be true")
})
}
func TestACL_filterNodes(t *testing.T) {
@ -3155,6 +3255,277 @@ func TestACL_filterNodes(t *testing.T) {
require.Len(nodes, 0)
}
func TestACL_filterIndexedNodesWithGateways(t *testing.T) {
t.Parallel()
logger := hclog.NewNullLogger()
makeList := func() *structs.IndexedNodesWithGateways {
return &structs.IndexedNodesWithGateways{
Nodes: structs.CheckServiceNodes{
{
Node: &structs.Node{
Node: "node1",
},
Service: &structs.NodeService{
ID: "foo",
Service: "foo",
},
Checks: structs.HealthChecks{
{
Node: "node1",
CheckID: "check1",
ServiceName: "foo",
},
},
},
},
Gateways: structs.GatewayServices{
{Service: structs.ServiceNameFromString("foo")},
{Service: structs.ServiceNameFromString("bar")},
},
}
}
t.Run("allowed", func(t *testing.T) {
require := require.New(t)
policy, err := acl.NewPolicyFromSource(`
service "foo" {
policy = "read"
}
service "bar" {
policy = "read"
}
node "node1" {
policy = "read"
}
`, acl.SyntaxLegacy, nil, nil)
require.NoError(err)
authz, err := acl.NewPolicyAuthorizerWithDefaults(acl.DenyAll(), []*acl.Policy{policy}, nil)
require.NoError(err)
list := makeList()
filterACLWithAuthorizer(logger, authz, list)
require.Len(list.Nodes, 1)
require.Len(list.Gateways, 2)
require.False(list.QueryMeta.ResultsFilteredByACLs, "ResultsFilteredByACLs should be false")
})
t.Run("not allowed to read the node", func(t *testing.T) {
require := require.New(t)
policy, err := acl.NewPolicyFromSource(`
service "foo" {
policy = "read"
}
service "bar" {
policy = "read"
}
`, acl.SyntaxLegacy, nil, nil)
require.NoError(err)
authz, err := acl.NewPolicyAuthorizerWithDefaults(acl.DenyAll(), []*acl.Policy{policy}, nil)
require.NoError(err)
list := makeList()
filterACLWithAuthorizer(logger, authz, list)
require.Empty(list.Nodes)
require.Len(list.Gateways, 2)
require.True(list.QueryMeta.ResultsFilteredByACLs, "ResultsFilteredByACLs should be true")
})
t.Run("allowed to read the node, but not the service", func(t *testing.T) {
require := require.New(t)
policy, err := acl.NewPolicyFromSource(`
node "node1" {
policy = "read"
}
service "bar" {
policy = "read"
}
`, acl.SyntaxLegacy, nil, nil)
require.NoError(err)
authz, err := acl.NewPolicyAuthorizerWithDefaults(acl.DenyAll(), []*acl.Policy{policy}, nil)
require.NoError(err)
list := makeList()
filterACLWithAuthorizer(logger, authz, list)
require.Empty(list.Nodes)
require.Len(list.Gateways, 1)
require.True(list.QueryMeta.ResultsFilteredByACLs, "ResultsFilteredByACLs should be true")
})
t.Run("not allowed to read the other gatway service", func(t *testing.T) {
require := require.New(t)
policy, err := acl.NewPolicyFromSource(`
service "foo" {
policy = "read"
}
node "node1" {
policy = "read"
}
`, acl.SyntaxLegacy, nil, nil)
require.NoError(err)
authz, err := acl.NewPolicyAuthorizerWithDefaults(acl.DenyAll(), []*acl.Policy{policy}, nil)
require.NoError(err)
list := makeList()
filterACLWithAuthorizer(logger, authz, list)
require.Len(list.Nodes, 1)
require.Len(list.Gateways, 1)
require.True(list.QueryMeta.ResultsFilteredByACLs, "ResultsFilteredByACLs should be true")
})
t.Run("denied", func(t *testing.T) {
require := require.New(t)
list := makeList()
filterACLWithAuthorizer(logger, acl.DenyAll(), list)
require.Empty(list.Nodes)
require.Empty(list.Gateways)
require.True(list.QueryMeta.ResultsFilteredByACLs, "ResultsFilteredByACLs should be true")
})
}
func TestACL_filterIndexedServiceDump(t *testing.T) {
t.Parallel()
logger := hclog.NewNullLogger()
makeList := func() *structs.IndexedServiceDump {
return &structs.IndexedServiceDump{
Dump: structs.ServiceDump{
{
Node: &structs.Node{
Node: "node1",
},
Service: &structs.NodeService{
Service: "foo",
},
GatewayService: &structs.GatewayService{
Service: structs.ServiceNameFromString("foo"),
Gateway: structs.ServiceNameFromString("foo-gateway"),
},
},
// No node information.
{
GatewayService: &structs.GatewayService{
Service: structs.ServiceNameFromString("bar"),
Gateway: structs.ServiceNameFromString("bar-gateway"),
},
},
},
}
}
t.Run("allowed", func(t *testing.T) {
require := require.New(t)
policy, err := acl.NewPolicyFromSource(`
node "node1" {
policy = "read"
}
service_prefix "foo" {
policy = "read"
}
service_prefix "bar" {
policy = "read"
}
`, acl.SyntaxCurrent, nil, nil)
require.NoError(err)
authz, err := acl.NewPolicyAuthorizerWithDefaults(acl.DenyAll(), []*acl.Policy{policy}, nil)
require.NoError(err)
list := makeList()
filterACLWithAuthorizer(logger, authz, list)
require.Len(list.Dump, 2)
require.False(list.QueryMeta.ResultsFilteredByACLs, "ResultsFilteredByACLs should be false")
})
t.Run("not allowed to access node", func(t *testing.T) {
require := require.New(t)
policy, err := acl.NewPolicyFromSource(`
service_prefix "foo" {
policy = "read"
}
service_prefix "bar" {
policy = "read"
}
`, acl.SyntaxCurrent, nil, nil)
require.NoError(err)
authz, err := acl.NewPolicyAuthorizerWithDefaults(acl.DenyAll(), []*acl.Policy{policy}, nil)
require.NoError(err)
list := makeList()
filterACLWithAuthorizer(logger, authz, list)
require.Len(list.Dump, 1)
require.Equal("bar", list.Dump[0].GatewayService.Service.Name)
require.True(list.QueryMeta.ResultsFilteredByACLs, "ResultsFilteredByACLs should be true")
})
t.Run("not allowed to access service", func(t *testing.T) {
require := require.New(t)
policy, err := acl.NewPolicyFromSource(`
node "node1" {
policy = "read"
}
service "foo-gateway" {
policy = "read"
}
`, acl.SyntaxCurrent, nil, nil)
require.NoError(err)
authz, err := acl.NewPolicyAuthorizerWithDefaults(acl.DenyAll(), []*acl.Policy{policy}, nil)
require.NoError(err)
list := makeList()
filterACLWithAuthorizer(logger, authz, list)
require.Empty(list.Dump)
require.True(list.QueryMeta.ResultsFilteredByACLs, "ResultsFilteredByACLs should be true")
})
t.Run("not allowed to access gateway", func(t *testing.T) {
require := require.New(t)
policy, err := acl.NewPolicyFromSource(`
node "node1" {
policy = "read"
}
service "foo" {
policy = "read"
}
`, acl.SyntaxCurrent, nil, nil)
require.NoError(err)
authz, err := acl.NewPolicyAuthorizerWithDefaults(acl.DenyAll(), []*acl.Policy{policy}, nil)
require.NoError(err)
list := makeList()
filterACLWithAuthorizer(logger, authz, list)
require.Empty(list.Dump)
require.True(list.QueryMeta.ResultsFilteredByACLs, "ResultsFilteredByACLs should be true")
})
}
func TestACL_filterDatacenterCheckServiceNodes(t *testing.T) {
t.Parallel()
@ -3353,70 +3724,97 @@ func TestFilterACL_redactTokenSecrets(t *testing.T) {
func TestACL_filterPreparedQueries(t *testing.T) {
t.Parallel()
queries := structs.PreparedQueries{
&structs.PreparedQuery{
ID: "f004177f-2c28-83b7-4229-eacc25fe55d1",
},
&structs.PreparedQuery{
ID: "f004177f-2c28-83b7-4229-eacc25fe55d2",
Name: "query-with-no-token",
},
&structs.PreparedQuery{
ID: "f004177f-2c28-83b7-4229-eacc25fe55d3",
Name: "query-with-a-token",
Token: "root",
},
logger := hclog.NewNullLogger()
makeList := func() *structs.IndexedPreparedQueries {
return &structs.IndexedPreparedQueries{
Queries: structs.PreparedQueries{
{ID: "f004177f-2c28-83b7-4229-eacc25fe55d1"},
{
ID: "f004177f-2c28-83b7-4229-eacc25fe55d2",
Name: "query-with-no-token",
},
{
ID: "f004177f-2c28-83b7-4229-eacc25fe55d3",
Name: "query-with-a-token",
Token: "root",
},
},
}
}
expected := structs.PreparedQueries{
&structs.PreparedQuery{
ID: "f004177f-2c28-83b7-4229-eacc25fe55d1",
},
&structs.PreparedQuery{
ID: "f004177f-2c28-83b7-4229-eacc25fe55d2",
Name: "query-with-no-token",
},
&structs.PreparedQuery{
ID: "f004177f-2c28-83b7-4229-eacc25fe55d3",
Name: "query-with-a-token",
Token: "root",
},
}
t.Run("management token", func(t *testing.T) {
require := require.New(t)
// Try permissive filtering with a management token. This will allow the
// embedded token to be seen.
filt := newACLFilter(acl.ManageAll(), nil)
filt.filterPreparedQueries(&queries)
if !reflect.DeepEqual(queries, expected) {
t.Fatalf("bad: %#v", queries)
}
list := makeList()
filterACLWithAuthorizer(logger, acl.ManageAll(), list)
// Hang on to the entry with a token, which needs to survive the next
// operation.
original := queries[2]
// Check we get the un-named query.
require.Len(list.Queries, 3)
// Now try permissive filtering with a client token, which should cause
// the embedded token to get redacted, and the query with no name to get
// filtered out.
filt = newACLFilter(acl.AllowAll(), nil)
filt.filterPreparedQueries(&queries)
expected[2].Token = redactedToken
expected = append(structs.PreparedQueries{}, expected[1], expected[2])
if !reflect.DeepEqual(queries, expected) {
t.Fatalf("bad: %#v", queries)
}
// Check we get the un-redacted token.
require.Equal("root", list.Queries[2].Token)
// Make sure that the original object didn't lose its token.
if original.Token != "root" {
t.Fatalf("bad token: %s", original.Token)
}
require.False(list.QueryMeta.ResultsFilteredByACLs, "ResultsFilteredByACLs should be false")
})
// Now try restrictive filtering.
filt = newACLFilter(acl.DenyAll(), nil)
filt.filterPreparedQueries(&queries)
if len(queries) != 0 {
t.Fatalf("bad: %#v", queries)
}
t.Run("permissive filtering", func(t *testing.T) {
require := require.New(t)
list := makeList()
queryWithToken := list.Queries[2]
filterACLWithAuthorizer(logger, acl.AllowAll(), list)
// Check the un-named query is filtered out.
require.Len(list.Queries, 2)
// Check the token is redacted.
require.Equal(redactedToken, list.Queries[1].Token)
// Check the original object is unmodified.
require.Equal("root", queryWithToken.Token)
// ResultsFilteredByACLs should not include un-named queries, which are only
// readable by a management token.
require.False(list.QueryMeta.ResultsFilteredByACLs, "ResultsFilteredByACLs should be false")
})
t.Run("limited access", func(t *testing.T) {
require := require.New(t)
policy, err := acl.NewPolicyFromSource(`
query "query-with-a-token" {
policy = "read"
}
`, acl.SyntaxLegacy, nil, nil)
require.NoError(err)
authz, err := acl.NewPolicyAuthorizerWithDefaults(acl.DenyAll(), []*acl.Policy{policy}, nil)
require.NoError(err)
list := makeList()
filterACLWithAuthorizer(logger, authz, list)
// Check we only get the query we have access to.
require.Len(list.Queries, 1)
// Check the token is redacted.
require.Equal(redactedToken, list.Queries[0].Token)
require.True(list.QueryMeta.ResultsFilteredByACLs, "ResultsFilteredByACLs should be true")
})
t.Run("restrictive filtering", func(t *testing.T) {
require := require.New(t)
list := makeList()
filterACLWithAuthorizer(logger, acl.DenyAll(), list)
require.Empty(list.Queries)
require.True(list.QueryMeta.ResultsFilteredByACLs, "ResultsFilteredByACLs should be true")
})
}
func TestACL_filterServiceList(t *testing.T) {
@ -3622,7 +4020,7 @@ func TestACLResolver_AgentMaster(t *testing.T) {
cfg.DisableDuration = 0
})
tokens.UpdateAgentMasterToken("9a184a11-5599-459e-b71a-550e5f9a5a23", token.TokenSourceConfig)
tokens.UpdateAgentRecoveryToken("9a184a11-5599-459e-b71a-550e5f9a5a23", token.TokenSourceConfig)
ident, authz, err := r.ResolveTokenToIdentityAndAuthorizer("9a184a11-5599-459e-b71a-550e5f9a5a23")
require.NoError(t, err)

View File

@ -44,7 +44,7 @@ func testACLTokenReap_Primary(t *testing.T, local, global bool) {
dir1, s1 := testServerWithConfig(t, func(c *Config) {
c.PrimaryDatacenter = "dc1"
c.ACLsEnabled = true
c.ACLMasterToken = "root"
c.ACLInitialManagementToken = "root"
c.ACLTokenMinExpirationTTL = 10 * time.Millisecond
c.ACLTokenMaxExpirationTTL = 8 * time.Second
})

View File

@ -71,76 +71,8 @@ type Catalog struct {
logger hclog.Logger
}
// nodePreApply does the verification of a node before it is applied to Raft.
func nodePreApply(nodeName, nodeID string) error {
if nodeName == "" {
return fmt.Errorf("Must provide node")
}
if nodeID != "" {
if _, err := uuid.ParseUUID(nodeID); err != nil {
return fmt.Errorf("Bad node ID: %v", err)
}
}
return nil
}
func servicePreApply(service *structs.NodeService, authz acl.Authorizer, authzCtxFill func(*acl.AuthorizerContext)) error {
// Validate the service. This is in addition to the below since
// the above just hasn't been moved over yet. We should move it over
// in time.
if err := service.Validate(); err != nil {
return err
}
// If no service id, but service name, use default
if service.ID == "" && service.Service != "" {
service.ID = service.Service
}
// Verify ServiceName provided if ID.
if service.ID != "" && service.Service == "" {
return fmt.Errorf("Must provide service name with ID")
}
// Check the service address here and in the agent endpoint
// since service registration isn't synchronous.
if ipaddr.IsAny(service.Address) {
return fmt.Errorf("Invalid service address")
}
var authzContext acl.AuthorizerContext
authzCtxFill(&authzContext)
// Apply the ACL policy if any. The 'consul' service is excluded
// since it is managed automatically internally (that behavior
// is going away after version 0.8). We check this same policy
// later if version 0.8 is enabled, so we can eventually just
// delete this and do all the ACL checks down there.
if service.Service != structs.ConsulServiceName {
if authz.ServiceWrite(service.Service, &authzContext) != acl.Allow {
return acl.ErrPermissionDenied
}
}
// Proxies must have write permission on their destination
if service.Kind == structs.ServiceKindConnectProxy {
if authz.ServiceWrite(service.Proxy.DestinationServiceName, &authzContext) != acl.Allow {
return acl.ErrPermissionDenied
}
}
return nil
}
// checkPreApply does the verification of a check before it is applied to Raft.
func checkPreApply(check *structs.HealthCheck) {
if check.CheckID == "" && check.Name != "" {
check.CheckID = types.CheckID(check.Name)
}
}
// Register is used register that a node is providing a given service.
// Register a service and/or check(s) in a node, creating the node if it doesn't exist.
// It is valid to pass no service or checks to simply create the node itself.
func (c *Catalog) Register(args *structs.RegisterRequest, reply *struct{}) error {
if done, err := c.srv.ForwardRPC("Catalog.Register", args, reply); done {
return err
@ -212,6 +144,75 @@ func (c *Catalog) Register(args *structs.RegisterRequest, reply *struct{}) error
return err
}
// nodePreApply does the verification of a node before it is applied to Raft.
func nodePreApply(nodeName, nodeID string) error {
if nodeName == "" {
return fmt.Errorf("Must provide node")
}
if nodeID != "" {
if _, err := uuid.ParseUUID(nodeID); err != nil {
return fmt.Errorf("Bad node ID: %v", err)
}
}
return nil
}
func servicePreApply(service *structs.NodeService, authz acl.Authorizer, authzCtxFill func(*acl.AuthorizerContext)) error {
// Validate the service. This is in addition to the below since
// the above just hasn't been moved over yet. We should move it over
// in time.
if err := service.Validate(); err != nil {
return err
}
// If no service id, but service name, use default
if service.ID == "" && service.Service != "" {
service.ID = service.Service
}
// Verify ServiceName provided if ID.
if service.ID != "" && service.Service == "" {
return fmt.Errorf("Must provide service name with ID")
}
// Check the service address here and in the agent endpoint
// since service registration isn't synchronous.
if ipaddr.IsAny(service.Address) {
return fmt.Errorf("Invalid service address")
}
var authzContext acl.AuthorizerContext
authzCtxFill(&authzContext)
// Apply the ACL policy if any. The 'consul' service is excluded
// since it is managed automatically internally (that behavior
// is going away after version 0.8). We check this same policy
// later if version 0.8 is enabled, so we can eventually just
// delete this and do all the ACL checks down there.
if service.Service != structs.ConsulServiceName {
if authz.ServiceWrite(service.Service, &authzContext) != acl.Allow {
return acl.ErrPermissionDenied
}
}
// Proxies must have write permission on their destination
if service.Kind == structs.ServiceKindConnectProxy {
if authz.ServiceWrite(service.Proxy.DestinationServiceName, &authzContext) != acl.Allow {
return acl.ErrPermissionDenied
}
}
return nil
}
// checkPreApply does the verification of a check before it is applied to Raft.
func checkPreApply(check *structs.HealthCheck) {
if check.CheckID == "" && check.Name != "" {
check.CheckID = types.CheckID(check.Name)
}
}
// vetRegisterWithACL applies the given ACL's policy to the catalog update and
// determines if it is allowed. Since the catalog register request is so
// dynamic, this is a pretty complex algorithm and was worth breaking out of the
@ -330,7 +331,13 @@ func vetRegisterWithACL(
return nil
}
// Deregister is used to remove a service registration for a given node.
// Deregister a service or check in a node, or the entire node itself.
//
// If a ServiceID is provided in the request, any associated Checks
// with that service are also deregistered.
//
// If a ServiceID or CheckID is not provided in the request, the entire
// node is deregistered.
func (c *Catalog) Deregister(args *structs.DeregisterRequest, reply *struct{}) error {
if done, err := c.srv.ForwardRPC("Catalog.Deregister", args, reply); done {
return err
@ -458,7 +465,7 @@ func (c *Catalog) ListDatacenters(args *structs.DatacentersRequest, reply *[]str
return nil
}
// ListNodes is used to query the nodes in a DC
// ListNodes is used to query the nodes in a DC.
func (c *Catalog) ListNodes(args *structs.DCSpecificRequest, reply *structs.IndexedNodes) error {
if done, err := c.srv.ForwardRPC("Catalog.ListNodes", args, reply); done {
return err
@ -509,7 +516,8 @@ func isUnmodified(opts structs.QueryOptions, index uint64) bool {
return opts.AllowNotModifiedResponse && opts.MinQueryIndex > 0 && opts.MinQueryIndex == index
}
// ListServices is used to query the services in a DC
// ListServices is used to query the services in a DC.
// Returns services as a map of service names to available tags.
func (c *Catalog) ListServices(args *structs.DCSpecificRequest, reply *structs.IndexedServices) error {
if done, err := c.srv.ForwardRPC("Catalog.ListServices", args, reply); done {
return err
@ -552,6 +560,8 @@ func (c *Catalog) ListServices(args *structs.DCSpecificRequest, reply *structs.I
})
}
// ServiceList is used to query the services in a DC.
// Returns services as a list of ServiceNames.
func (c *Catalog) ServiceList(args *structs.DCSpecificRequest, reply *structs.IndexedServiceList) error {
if done, err := c.srv.ForwardRPC("Catalog.ServiceList", args, reply); done {
return err
@ -570,7 +580,7 @@ func (c *Catalog) ServiceList(args *structs.DCSpecificRequest, reply *structs.In
&args.QueryOptions,
&reply.QueryMeta,
func(ws memdb.WatchSet, state *state.Store) error {
index, services, err := state.ServiceList(ws, nil, &args.EnterpriseMeta)
index, services, err := state.ServiceList(ws, &args.EnterpriseMeta)
if err != nil {
return err
}
@ -581,7 +591,7 @@ func (c *Catalog) ServiceList(args *structs.DCSpecificRequest, reply *structs.In
})
}
// ServiceNodes returns all the nodes registered as part of a service
// ServiceNodes returns all the nodes registered as part of a service.
func (c *Catalog) ServiceNodes(args *structs.ServiceSpecificRequest, reply *structs.IndexedServiceNodes) error {
if done, err := c.srv.ForwardRPC("Catalog.ServiceNodes", args, reply); done {
return err
@ -721,7 +731,8 @@ func (c *Catalog) ServiceNodes(args *structs.ServiceSpecificRequest, reply *stru
return err
}
// NodeServices returns all the services registered as part of a node
// NodeServices returns all the services registered as part of a node.
// Returns NodeServices as a map of service IDs to services.
func (c *Catalog) NodeServices(args *structs.NodeSpecificRequest, reply *structs.IndexedNodeServices) error {
if done, err := c.srv.ForwardRPC("Catalog.NodeServices", args, reply); done {
return err
@ -776,6 +787,8 @@ func (c *Catalog) NodeServices(args *structs.NodeSpecificRequest, reply *structs
})
}
// NodeServiceList returns all the services registered as part of a node.
// Returns NodeServices as a list of services.
func (c *Catalog) NodeServiceList(args *structs.NodeSpecificRequest, reply *structs.IndexedNodeServiceList) error {
if done, err := c.srv.ForwardRPC("Catalog.NodeServiceList", args, reply); done {
return err

View File

@ -184,7 +184,7 @@ func TestCatalog_Register_ACLDeny(t *testing.T) {
dir1, s1 := testServerWithConfig(t, func(c *Config) {
c.PrimaryDatacenter = "dc1"
c.ACLsEnabled = true
c.ACLMasterToken = "root"
c.ACLInitialManagementToken = "root"
c.ACLResolverSettings.ACLDefaultPolicy = "deny"
})
defer os.RemoveAll(dir1)
@ -452,7 +452,7 @@ func TestCatalog_Register_ConnectProxy_ACLDestinationServiceName(t *testing.T) {
dir1, s1 := testServerWithConfig(t, func(c *Config) {
c.PrimaryDatacenter = "dc1"
c.ACLsEnabled = true
c.ACLMasterToken = "root"
c.ACLInitialManagementToken = "root"
c.ACLResolverSettings.ACLDefaultPolicy = "deny"
})
defer os.RemoveAll(dir1)
@ -570,7 +570,7 @@ func TestCatalog_Deregister_ACLDeny(t *testing.T) {
dir1, s1 := testServerWithConfig(t, func(c *Config) {
c.PrimaryDatacenter = "dc1"
c.ACLsEnabled = true
c.ACLMasterToken = "root"
c.ACLInitialManagementToken = "root"
c.ACLResolverSettings.ACLDefaultPolicy = "deny"
})
defer os.RemoveAll(dir1)
@ -1297,7 +1297,7 @@ func TestCatalog_ListNodes_ACLFilter(t *testing.T) {
dir1, s1 := testServerWithConfig(t, func(c *Config) {
c.PrimaryDatacenter = "dc1"
c.ACLsEnabled = true
c.ACLMasterToken = "root"
c.ACLInitialManagementToken = "root"
c.ACLResolverSettings.ACLDefaultPolicy = "deny"
})
defer os.RemoveAll(dir1)
@ -2409,7 +2409,7 @@ func TestCatalog_ListServiceNodes_ConnectProxy_ACL(t *testing.T) {
dir1, s1 := testServerWithConfig(t, func(c *Config) {
c.PrimaryDatacenter = "dc1"
c.ACLsEnabled = true
c.ACLMasterToken = "root"
c.ACLInitialManagementToken = "root"
c.ACLResolverSettings.ACLDefaultPolicy = "deny"
})
defer os.RemoveAll(dir1)
@ -2699,7 +2699,7 @@ func testACLFilterServer(t *testing.T) (dir, token string, srv *Server, codec rp
dir, srv = testServerWithConfig(t, func(c *Config) {
c.PrimaryDatacenter = "dc1"
c.ACLsEnabled = true
c.ACLMasterToken = "root"
c.ACLInitialManagementToken = "root"
c.ACLResolverSettings.ACLDefaultPolicy = "deny"
})
@ -2855,7 +2855,7 @@ func TestCatalog_NodeServices_ACL(t *testing.T) {
dir1, s1 := testServerWithConfig(t, func(c *Config) {
c.PrimaryDatacenter = "dc1"
c.ACLsEnabled = true
c.ACLMasterToken = "root"
c.ACLInitialManagementToken = "root"
c.ACLResolverSettings.ACLDefaultPolicy = "deny"
})
defer os.RemoveAll(dir1)
@ -3258,7 +3258,7 @@ func TestCatalog_GatewayServices_ACLFiltering(t *testing.T) {
dir1, s1 := testServerWithConfig(t, func(c *Config) {
c.PrimaryDatacenter = "dc1"
c.ACLsEnabled = true
c.ACLMasterToken = "root"
c.ACLInitialManagementToken = "root"
c.ACLResolverSettings.ACLDefaultPolicy = "deny"
})
defer os.RemoveAll(dir1)
@ -3970,7 +3970,7 @@ func TestCatalog_VirtualIPForService_ACLDeny(t *testing.T) {
dir1, s1 := testServerWithConfig(t, func(c *Config) {
c.PrimaryDatacenter = "dc1"
c.ACLsEnabled = true
c.ACLMasterToken = "root"
c.ACLInitialManagementToken = "root"
c.ACLResolverSettings.ACLDefaultPolicy = "deny"
c.Build = "1.11.0"
})

View File

@ -180,10 +180,10 @@ type Config struct {
// ACLEnabled is used to enable ACLs
ACLsEnabled bool
// ACLMasterToken is used to bootstrap the ACL system. It should be specified
// ACLInitialManagementToken is used to bootstrap the ACL system. It should be specified
// on the servers in the PrimaryDatacenter. When the leader comes online, it ensures
// that the Master token is available. This provides the initial token.
ACLMasterToken string
// that the initial management token is available. This provides the initial token.
ACLInitialManagementToken string
// ACLTokenReplication is used to enabled token replication.
//
@ -391,6 +391,8 @@ type Config struct {
RPCConfig RPCConfig
RaftBoltDBConfig RaftBoltDBConfig
// Embedded Consul Enterprise specific configuration
*EnterpriseConfig
}
@ -603,3 +605,7 @@ type ReloadableConfig struct {
RaftSnapshotInterval time.Duration
RaftTrailingLogs int
}
type RaftBoltDBConfig struct {
NoFreelistSync bool
}

View File

@ -594,10 +594,10 @@ func (c *ConfigEntry) ResolveServiceConfig(args *structs.ServiceConfigRequest, r
}
func gateWriteToSecondary(targetDC, localDC, primaryDC, kind string) error {
// Partition exports are gated from interactions from secondary DCs
// ExportedServices entries are gated from interactions from secondary DCs
// because non-default partitions cannot be created in secondaries
// and services cannot be exported to another datacenter.
if kind != structs.PartitionExports {
if kind != structs.ExportedServices {
return nil
}
if localDC == "" {
@ -611,10 +611,10 @@ func gateWriteToSecondary(targetDC, localDC, primaryDC, kind string) error {
switch {
case targetDC == "" && localDC != primaryDC:
return fmt.Errorf("partition-exports writes in secondary datacenters must target the primary datacenter explicitly.")
return fmt.Errorf("exported-services writes in secondary datacenters must target the primary datacenter explicitly.")
case targetDC != "" && targetDC != primaryDC:
return fmt.Errorf("partition-exports writes must not target secondary datacenters.")
return fmt.Errorf("exported-services writes must not target secondary datacenters.")
}
return nil

View File

@ -155,7 +155,7 @@ func TestConfigEntry_Apply_ACLDeny(t *testing.T) {
dir1, s1 := testServerWithConfig(t, func(c *Config) {
c.PrimaryDatacenter = "dc1"
c.ACLsEnabled = true
c.ACLMasterToken = "root"
c.ACLInitialManagementToken = "root"
c.ACLResolverSettings.ACLDefaultPolicy = "deny"
})
defer os.RemoveAll(dir1)
@ -271,7 +271,7 @@ func TestConfigEntry_Get_ACLDeny(t *testing.T) {
dir1, s1 := testServerWithConfig(t, func(c *Config) {
c.PrimaryDatacenter = "dc1"
c.ACLsEnabled = true
c.ACLMasterToken = "root"
c.ACLInitialManagementToken = "root"
c.ACLResolverSettings.ACLDefaultPolicy = "deny"
})
defer os.RemoveAll(dir1)
@ -471,7 +471,7 @@ func TestConfigEntry_List_ACLDeny(t *testing.T) {
dir1, s1 := testServerWithConfig(t, func(c *Config) {
c.PrimaryDatacenter = "dc1"
c.ACLsEnabled = true
c.ACLMasterToken = "root"
c.ACLInitialManagementToken = "root"
c.ACLResolverSettings.ACLDefaultPolicy = "deny"
})
defer os.RemoveAll(dir1)
@ -545,7 +545,7 @@ func TestConfigEntry_ListAll_ACLDeny(t *testing.T) {
dir1, s1 := testServerWithConfig(t, func(c *Config) {
c.PrimaryDatacenter = "dc1"
c.ACLsEnabled = true
c.ACLMasterToken = "root"
c.ACLInitialManagementToken = "root"
c.ACLResolverSettings.ACLDefaultPolicy = "deny"
})
defer os.RemoveAll(dir1)
@ -750,7 +750,7 @@ func TestConfigEntry_Delete_ACLDeny(t *testing.T) {
dir1, s1 := testServerWithConfig(t, func(c *Config) {
c.PrimaryDatacenter = "dc1"
c.ACLsEnabled = true
c.ACLMasterToken = "root"
c.ACLInitialManagementToken = "root"
c.ACLResolverSettings.ACLDefaultPolicy = "deny"
})
defer os.RemoveAll(dir1)
@ -1959,7 +1959,7 @@ func TestConfigEntry_ResolveServiceConfig_ACLDeny(t *testing.T) {
dir1, s1 := testServerWithConfig(t, func(c *Config) {
c.PrimaryDatacenter = "dc1"
c.ACLsEnabled = true
c.ACLMasterToken = "root"
c.ACLInitialManagementToken = "root"
c.ACLResolverSettings.ACLDefaultPolicy = "deny"
})
defer os.RemoveAll(dir1)
@ -2093,7 +2093,7 @@ func Test_gateWriteToSecondary(t *testing.T) {
targetDC: "",
localDC: "dc1",
primaryDC: "",
kind: structs.PartitionExports,
kind: structs.ExportedServices,
},
},
{
@ -2102,7 +2102,7 @@ func Test_gateWriteToSecondary(t *testing.T) {
targetDC: "",
localDC: "dc1",
primaryDC: "dc1",
kind: structs.PartitionExports,
kind: structs.ExportedServices,
},
},
{
@ -2111,7 +2111,7 @@ func Test_gateWriteToSecondary(t *testing.T) {
targetDC: "dc1",
localDC: "dc1",
primaryDC: "dc1",
kind: structs.PartitionExports,
kind: structs.ExportedServices,
},
},
{
@ -2120,7 +2120,7 @@ func Test_gateWriteToSecondary(t *testing.T) {
targetDC: "dc2",
localDC: "dc1",
primaryDC: "",
kind: structs.PartitionExports,
kind: structs.ExportedServices,
},
wantErr: "writes must not target secondary datacenters",
},
@ -2130,7 +2130,7 @@ func Test_gateWriteToSecondary(t *testing.T) {
targetDC: "dc2",
localDC: "dc1",
primaryDC: "dc1",
kind: structs.PartitionExports,
kind: structs.ExportedServices,
},
wantErr: "writes must not target secondary datacenters",
},
@ -2140,7 +2140,7 @@ func Test_gateWriteToSecondary(t *testing.T) {
targetDC: "dc2",
localDC: "dc2",
primaryDC: "dc1",
kind: structs.PartitionExports,
kind: structs.ExportedServices,
},
wantErr: "writes must not target secondary datacenters",
},
@ -2150,7 +2150,7 @@ func Test_gateWriteToSecondary(t *testing.T) {
targetDC: "",
localDC: "dc2",
primaryDC: "dc1",
kind: structs.PartitionExports,
kind: structs.ExportedServices,
},
wantErr: "must target the primary datacenter explicitly",
},
@ -2158,7 +2158,7 @@ func Test_gateWriteToSecondary(t *testing.T) {
name: "empty local DC",
args: args{
localDC: "",
kind: structs.PartitionExports,
kind: structs.ExportedServices,
},
wantErr: "unknown local datacenter",
},
@ -2179,7 +2179,7 @@ func Test_gateWriteToSecondary_AllowedKinds(t *testing.T) {
}
for _, kind := range structs.AllConfigEntryKinds {
if kind == structs.PartitionExports {
if kind == structs.ExportedServices {
continue
}

View File

@ -92,8 +92,8 @@ func (s *Server) reconcileLocalConfig(ctx context.Context, configs []structs.Con
defer ticker.Stop()
for i, entry := range configs {
// Partition exports only apply to the primary datacenter.
if entry.GetKind() == structs.PartitionExports {
// Exported services only apply to the primary datacenter.
if entry.GetKind() == structs.ExportedServices {
continue
}
req := structs.ConfigEntryRequest{

View File

@ -92,107 +92,6 @@ func TestReplication_ConfigSort(t *testing.T) {
}
}
func TestReplication_DisallowedConfigEntries(t *testing.T) {
if testing.Short() {
t.Skip("too slow for testing.Short")
}
dir1, s1 := testServerWithConfig(t, func(c *Config) {
c.PrimaryDatacenter = "dc1"
})
defer os.RemoveAll(dir1)
defer s1.Shutdown()
testrpc.WaitForLeader(t, s1.RPC, "dc1")
client := rpcClient(t, s1)
defer client.Close()
dir2, s2 := testServerWithConfig(t, func(c *Config) {
c.Datacenter = "dc2"
c.PrimaryDatacenter = "dc1"
c.ConfigReplicationRate = 100
c.ConfigReplicationBurst = 100
c.ConfigReplicationApplyLimit = 1000000
})
testrpc.WaitForLeader(t, s2.RPC, "dc2")
defer os.RemoveAll(dir2)
defer s2.Shutdown()
// Try to join.
joinWAN(t, s2, s1)
testrpc.WaitForLeader(t, s1.RPC, "dc1")
testrpc.WaitForLeader(t, s1.RPC, "dc2")
args := []structs.ConfigEntryRequest{
{
Datacenter: "dc1",
Op: structs.ConfigEntryUpsert,
Entry: &structs.ServiceConfigEntry{
Kind: structs.ServiceDefaults,
Name: "foo",
Protocol: "http2",
},
},
{
Datacenter: "dc1",
Op: structs.ConfigEntryUpsert,
Entry: &structs.PartitionExportsConfigEntry{
Name: "default",
Services: []structs.ExportedService{
{
Name: structs.WildcardSpecifier,
Consumers: []structs.ServiceConsumer{
{
Partition: "non-default",
},
},
},
},
},
},
{
Datacenter: "dc1",
Op: structs.ConfigEntryUpsert,
Entry: &structs.ProxyConfigEntry{
Kind: structs.ProxyDefaults,
Name: "global",
Config: map[string]interface{}{
"Protocol": "http",
},
},
},
{
Datacenter: "dc1",
Op: structs.ConfigEntryUpsert,
Entry: &structs.MeshConfigEntry{
TransparentProxy: structs.TransparentProxyMeshConfig{
MeshDestinationsOnly: true,
},
},
},
}
for _, arg := range args {
out := false
require.NoError(t, s1.RPC("ConfigEntry.Apply", &arg, &out))
}
retry.Run(t, func(r *retry.R) {
_, local, err := s2.fsm.State().ConfigEntries(nil, structs.ReplicationEnterpriseMeta())
require.NoError(r, err)
require.Len(r, local, 3)
localKinds := make([]string, 0)
for _, entry := range local {
localKinds = append(localKinds, entry.GetKind())
}
// Should have all inserted kinds except for partition-exports.
expectKinds := []string{
structs.ProxyDefaults, structs.ServiceDefaults, structs.MeshConfig,
}
require.ElementsMatch(r, expectKinds, localKinds)
})
}
func TestReplication_ConfigEntries(t *testing.T) {
if testing.Short() {
t.Skip("too slow for testing.Short")

View File

@ -163,7 +163,7 @@ func TestConnectCAConfig_GetSet_ACLDeny(t *testing.T) {
dir1, s1 := testServerWithConfig(t, func(c *Config) {
c.PrimaryDatacenter = "dc1"
c.ACLsEnabled = true
c.ACLMasterToken = TestDefaultMasterToken
c.ACLInitialManagementToken = TestDefaultMasterToken
c.ACLResolverSettings.ACLDefaultPolicy = "deny"
})
defer os.RemoveAll(dir1)
@ -1108,7 +1108,7 @@ func TestConnectCASignValidation(t *testing.T) {
dir1, s1 := testServerWithConfig(t, func(c *Config) {
c.PrimaryDatacenter = "dc1"
c.ACLsEnabled = true
c.ACLMasterToken = "root"
c.ACLInitialManagementToken = "root"
c.ACLResolverSettings.ACLDefaultPolicy = "deny"
})
defer os.RemoveAll(dir1)

View File

@ -191,7 +191,7 @@ func TestCoordinate_Update_ACLDeny(t *testing.T) {
dir1, s1 := testServerWithConfig(t, func(c *Config) {
c.PrimaryDatacenter = "dc1"
c.ACLsEnabled = true
c.ACLMasterToken = "root"
c.ACLInitialManagementToken = "root"
c.ACLResolverSettings.ACLDefaultPolicy = "deny"
})
defer os.RemoveAll(dir1)
@ -349,7 +349,7 @@ func TestCoordinate_ListNodes_ACLFilter(t *testing.T) {
dir1, s1 := testServerWithConfig(t, func(c *Config) {
c.PrimaryDatacenter = "dc1"
c.ACLsEnabled = true
c.ACLMasterToken = "root"
c.ACLInitialManagementToken = "root"
c.ACLResolverSettings.ACLDefaultPolicy = "deny"
})
defer os.RemoveAll(dir1)
@ -524,7 +524,7 @@ func TestCoordinate_Node_ACLDeny(t *testing.T) {
dir1, s1 := testServerWithConfig(t, func(c *Config) {
c.PrimaryDatacenter = "dc1"
c.ACLsEnabled = true
c.ACLMasterToken = "root"
c.ACLInitialManagementToken = "root"
c.ACLResolverSettings.ACLDefaultPolicy = "deny"
})
defer os.RemoveAll(dir1)

View File

@ -26,7 +26,7 @@ func TestDiscoveryChainEndpoint_Get(t *testing.T) {
c.PrimaryDatacenter = "dc1"
c.PrimaryDatacenter = "dc1"
c.ACLsEnabled = true
c.ACLMasterToken = "root"
c.ACLInitialManagementToken = "root"
c.ACLResolverSettings.ACLDefaultPolicy = "deny"
})
defer os.RemoveAll(dir1)

View File

@ -185,7 +185,7 @@ type customizationMarkers struct {
// the String() method on the type itself. It is this way to be more
// consistent with other string ids within the discovery chain.
func serviceIDString(sid structs.ServiceID) string {
return fmt.Sprintf("%s.%s", sid.ID, sid.NamespaceOrDefault())
return fmt.Sprintf("%s.%s.%s", sid.ID, sid.NamespaceOrDefault(), sid.PartitionOrDefault())
}
func (m *customizationMarkers) IsZero() bool {
@ -213,10 +213,10 @@ func (c *compiler) recordServiceProtocol(sid structs.ServiceID) error {
if serviceDefault := c.entries.GetService(sid); serviceDefault != nil {
return c.recordProtocol(sid, serviceDefault.Protocol)
}
if c.entries.GlobalProxy != nil {
if proxyDefault := c.entries.GetProxyDefaults(sid.PartitionOrDefault()); proxyDefault != nil {
var cfg proxyConfig
// Ignore errors and fallback on defaults if it does happen.
_ = mapstructure.WeakDecode(c.entries.GlobalProxy.Config, &cfg)
_ = mapstructure.WeakDecode(proxyDefault.Config, &cfg)
if cfg.Protocol != "" {
return c.recordProtocol(sid, cfg.Protocol)
}
@ -567,11 +567,12 @@ func (c *compiler) assembleChain() error {
dest = &structs.ServiceRouteDestination{
Service: c.serviceName,
Namespace: router.NamespaceOrDefault(),
Partition: router.PartitionOrDefault(),
}
}
svc := defaultIfEmpty(dest.Service, c.serviceName)
destNamespace := defaultIfEmpty(dest.Namespace, router.NamespaceOrDefault())
destPartition := router.PartitionOrDefault()
destPartition := defaultIfEmpty(dest.Partition, router.PartitionOrDefault())
// Check to see if the destination is eligible for splitting.
var (
@ -602,7 +603,7 @@ func (c *compiler) assembleChain() error {
}
defaultRoute := &structs.DiscoveryRoute{
Definition: newDefaultServiceRoute(router.Name, router.NamespaceOrDefault()),
Definition: newDefaultServiceRoute(router.Name, router.NamespaceOrDefault(), router.PartitionOrDefault()),
NextNode: defaultDestinationNode.MapKey(),
}
routeNode.Routes = append(routeNode.Routes, defaultRoute)
@ -613,7 +614,7 @@ func (c *compiler) assembleChain() error {
return nil
}
func newDefaultServiceRoute(serviceName string, namespace string) *structs.ServiceRoute {
func newDefaultServiceRoute(serviceName, namespace, partition string) *structs.ServiceRoute {
return &structs.ServiceRoute{
Match: &structs.ServiceRouteMatch{
HTTP: &structs.ServiceRouteHTTPMatch{
@ -623,6 +624,7 @@ func newDefaultServiceRoute(serviceName string, namespace string) *structs.Servi
Destination: &structs.ServiceRouteDestination{
Service: serviceName,
Namespace: namespace,
Partition: partition,
},
}
}
@ -836,7 +838,7 @@ RESOLVE_AGAIN:
target,
redirect.Service,
redirect.ServiceSubset,
target.Partition,
redirect.Partition,
redirect.Namespace,
redirect.Datacenter,
)
@ -940,9 +942,9 @@ RESOLVE_AGAIN:
if serviceDefault := c.entries.GetService(targetID); serviceDefault != nil {
target.MeshGateway = serviceDefault.MeshGateway
}
if c.entries.GlobalProxy != nil && target.MeshGateway.Mode == structs.MeshGatewayModeDefault {
target.MeshGateway.Mode = c.entries.GlobalProxy.MeshGateway.Mode
proxyDefault := c.entries.GetProxyDefaults(targetID.PartitionOrDefault())
if proxyDefault != nil && target.MeshGateway.Mode == structs.MeshGatewayModeDefault {
target.MeshGateway.Mode = proxyDefault.MeshGateway.Mode
}
if c.overrideMeshGateway.Mode != structs.MeshGatewayModeDefault {

View File

@ -158,14 +158,14 @@ func testcase_JustRouterWithDefaults() compileTestCase {
expect := &structs.CompiledDiscoveryChain{
Protocol: "http",
StartNode: "router:main.default",
StartNode: "router:main.default.default",
Nodes: map[string]*structs.DiscoveryGraphNode{
"router:main.default": {
"router:main.default.default": {
Type: structs.DiscoveryGraphNodeTypeRouter,
Name: "main.default",
Name: "main.default.default",
Routes: []*structs.DiscoveryRoute{
{
Definition: newDefaultServiceRoute("main", "default"),
Definition: newDefaultServiceRoute("main", "default", "default"),
NextNode: "resolver:main.default.default.dc1",
},
},
@ -210,11 +210,11 @@ func testcase_JustRouterWithNoDestination() compileTestCase {
expect := &structs.CompiledDiscoveryChain{
Protocol: "http",
StartNode: "router:main.default",
StartNode: "router:main.default.default",
Nodes: map[string]*structs.DiscoveryGraphNode{
"router:main.default": {
"router:main.default.default": {
Type: structs.DiscoveryGraphNodeTypeRouter,
Name: "main.default",
Name: "main.default.default",
Routes: []*structs.DiscoveryRoute{
{
Definition: &structs.ServiceRoute{
@ -227,7 +227,7 @@ func testcase_JustRouterWithNoDestination() compileTestCase {
NextNode: "resolver:main.default.default.dc1",
},
{
Definition: newDefaultServiceRoute("main", "default"),
Definition: newDefaultServiceRoute("main", "default", "default"),
NextNode: "resolver:main.default.default.dc1",
},
},
@ -270,14 +270,14 @@ func testcase_RouterWithDefaults_NoSplit_WithResolver() compileTestCase {
expect := &structs.CompiledDiscoveryChain{
Protocol: "http",
StartNode: "router:main.default",
StartNode: "router:main.default.default",
Nodes: map[string]*structs.DiscoveryGraphNode{
"router:main.default": {
"router:main.default.default": {
Type: structs.DiscoveryGraphNodeTypeRouter,
Name: "main.default",
Name: "main.default.default",
Routes: []*structs.DiscoveryRoute{
{
Definition: newDefaultServiceRoute("main", "default"),
Definition: newDefaultServiceRoute("main", "default", "default"),
NextNode: "resolver:main.default.default.dc1",
},
},
@ -321,21 +321,21 @@ func testcase_RouterWithDefaults_WithNoopSplit_DefaultResolver() compileTestCase
expect := &structs.CompiledDiscoveryChain{
Protocol: "http",
StartNode: "router:main.default",
StartNode: "router:main.default.default",
Nodes: map[string]*structs.DiscoveryGraphNode{
"router:main.default": {
"router:main.default.default": {
Type: structs.DiscoveryGraphNodeTypeRouter,
Name: "main.default",
Name: "main.default.default",
Routes: []*structs.DiscoveryRoute{
{
Definition: newDefaultServiceRoute("main", "default"),
NextNode: "splitter:main.default",
Definition: newDefaultServiceRoute("main", "default", "default"),
NextNode: "splitter:main.default.default",
},
},
},
"splitter:main.default": {
"splitter:main.default.default": {
Type: structs.DiscoveryGraphNodeTypeSplitter,
Name: "main.default",
Name: "main.default.default",
Splits: []*structs.DiscoverySplit{
{
Definition: &structs.ServiceSplit{
@ -386,21 +386,21 @@ func testcase_NoopSplit_DefaultResolver_ProtocolFromProxyDefaults() compileTestC
expect := &structs.CompiledDiscoveryChain{
Protocol: "http",
StartNode: "router:main.default",
StartNode: "router:main.default.default",
Nodes: map[string]*structs.DiscoveryGraphNode{
"router:main.default": {
"router:main.default.default": {
Type: structs.DiscoveryGraphNodeTypeRouter,
Name: "main.default",
Name: "main.default.default",
Routes: []*structs.DiscoveryRoute{
{
Definition: newDefaultServiceRoute("main", "default"),
NextNode: "splitter:main.default",
Definition: newDefaultServiceRoute("main", "default", "default"),
NextNode: "splitter:main.default.default",
},
},
},
"splitter:main.default": {
"splitter:main.default.default": {
Type: structs.DiscoveryGraphNodeTypeSplitter,
Name: "main.default",
Name: "main.default.default",
Splits: []*structs.DiscoverySplit{
{
Definition: &structs.ServiceSplit{
@ -458,21 +458,21 @@ func testcase_RouterWithDefaults_WithNoopSplit_WithResolver() compileTestCase {
expect := &structs.CompiledDiscoveryChain{
Protocol: "http",
StartNode: "router:main.default",
StartNode: "router:main.default.default",
Nodes: map[string]*structs.DiscoveryGraphNode{
"router:main.default": {
"router:main.default.default": {
Type: structs.DiscoveryGraphNodeTypeRouter,
Name: "main.default",
Name: "main.default.default",
Routes: []*structs.DiscoveryRoute{
{
Definition: newDefaultServiceRoute("main", "default"),
NextNode: "splitter:main.default",
Definition: newDefaultServiceRoute("main", "default", "default"),
NextNode: "splitter:main.default.default",
},
},
},
"splitter:main.default": {
"splitter:main.default.default": {
Type: structs.DiscoveryGraphNodeTypeSplitter,
Name: "main.default",
Name: "main.default.default",
Splits: []*structs.DiscoverySplit{
{
Definition: &structs.ServiceSplit{
@ -542,18 +542,18 @@ func testcase_RouteBypassesSplit() compileTestCase {
expect := &structs.CompiledDiscoveryChain{
Protocol: "http",
StartNode: "router:main.default",
StartNode: "router:main.default.default",
Nodes: map[string]*structs.DiscoveryGraphNode{
"router:main.default": {
"router:main.default.default": {
Type: structs.DiscoveryGraphNodeTypeRouter,
Name: "main.default",
Name: "main.default.default",
Routes: []*structs.DiscoveryRoute{
{
Definition: &router.Routes[0],
NextNode: "resolver:bypass.other.default.default.dc1",
},
{
Definition: newDefaultServiceRoute("main", "default"),
Definition: newDefaultServiceRoute("main", "default", "default"),
NextNode: "resolver:main.default.default.dc1",
},
},
@ -605,11 +605,11 @@ func testcase_NoopSplit_DefaultResolver() compileTestCase {
expect := &structs.CompiledDiscoveryChain{
Protocol: "http",
StartNode: "splitter:main.default",
StartNode: "splitter:main.default.default",
Nodes: map[string]*structs.DiscoveryGraphNode{
"splitter:main.default": {
"splitter:main.default.default": {
Type: structs.DiscoveryGraphNodeTypeSplitter,
Name: "main.default",
Name: "main.default.default",
Splits: []*structs.DiscoverySplit{
{
Definition: &structs.ServiceSplit{
@ -661,11 +661,11 @@ func testcase_NoopSplit_WithResolver() compileTestCase {
expect := &structs.CompiledDiscoveryChain{
Protocol: "http",
StartNode: "splitter:main.default",
StartNode: "splitter:main.default.default",
Nodes: map[string]*structs.DiscoveryGraphNode{
"splitter:main.default": {
"splitter:main.default.default": {
Type: structs.DiscoveryGraphNodeTypeSplitter,
Name: "main.default",
Name: "main.default.default",
Splits: []*structs.DiscoverySplit{
{
Definition: &structs.ServiceSplit{
@ -724,11 +724,11 @@ func testcase_SubsetSplit() compileTestCase {
expect := &structs.CompiledDiscoveryChain{
Protocol: "http",
StartNode: "splitter:main.default",
StartNode: "splitter:main.default.default",
Nodes: map[string]*structs.DiscoveryGraphNode{
"splitter:main.default": {
"splitter:main.default.default": {
Type: structs.DiscoveryGraphNodeTypeSplitter,
Name: "main.default",
Name: "main.default.default",
Splits: []*structs.DiscoverySplit{
{
Definition: &structs.ServiceSplit{
@ -801,11 +801,11 @@ func testcase_ServiceSplit() compileTestCase {
expect := &structs.CompiledDiscoveryChain{
Protocol: "http",
StartNode: "splitter:main.default",
StartNode: "splitter:main.default.default",
Nodes: map[string]*structs.DiscoveryGraphNode{
"splitter:main.default": {
"splitter:main.default.default": {
Type: structs.DiscoveryGraphNodeTypeSplitter,
Name: "main.default",
Name: "main.default.default",
Splits: []*structs.DiscoverySplit{
{
Definition: &structs.ServiceSplit{
@ -898,11 +898,11 @@ func testcase_SplitBypassesSplit() compileTestCase {
expect := &structs.CompiledDiscoveryChain{
Protocol: "http",
StartNode: "splitter:main.default",
StartNode: "splitter:main.default.default",
Nodes: map[string]*structs.DiscoveryGraphNode{
"splitter:main.default": {
"splitter:main.default.default": {
Type: structs.DiscoveryGraphNodeTypeSplitter,
Name: "main.default",
Name: "main.default.default",
Splits: []*structs.DiscoverySplit{
{
Definition: &structs.ServiceSplit{
@ -1053,13 +1053,14 @@ func testcase_DatacenterRedirect() compileTestCase {
func testcase_DatacenterRedirect_WithMeshGateways() compileTestCase {
entries := newEntries()
entries.GlobalProxy = &structs.ProxyConfigEntry{
entries.AddProxyDefaults(&structs.ProxyConfigEntry{
Kind: structs.ProxyDefaults,
Name: structs.ProxyConfigGlobal,
MeshGateway: structs.MeshGatewayConfig{
Mode: structs.MeshGatewayModeRemote,
},
}
})
entries.AddResolvers(
&structs.ServiceResolverConfigEntry{
Kind: "service-resolver",
@ -1300,13 +1301,15 @@ func testcase_DatacenterFailover() compileTestCase {
func testcase_DatacenterFailover_WithMeshGateways() compileTestCase {
entries := newEntries()
entries.GlobalProxy = &structs.ProxyConfigEntry{
entries.AddProxyDefaults(&structs.ProxyConfigEntry{
Kind: structs.ProxyDefaults,
Name: structs.ProxyConfigGlobal,
MeshGateway: structs.MeshGatewayConfig{
Mode: structs.MeshGatewayModeRemote,
},
}
})
entries.AddResolvers(
&structs.ServiceResolverConfigEntry{
Kind: "service-resolver",
@ -1384,11 +1387,11 @@ func testcase_NoopSplit_WithDefaultSubset() compileTestCase {
expect := &structs.CompiledDiscoveryChain{
Protocol: "http",
StartNode: "splitter:main.default",
StartNode: "splitter:main.default.default",
Nodes: map[string]*structs.DiscoveryGraphNode{
"splitter:main.default": {
"splitter:main.default.default": {
Type: structs.DiscoveryGraphNodeTypeSplitter,
Name: "main.default",
Name: "main.default.default",
Splits: []*structs.DiscoverySplit{
{
Definition: &structs.ServiceSplit{
@ -1446,7 +1449,8 @@ func testcase_DefaultResolver() compileTestCase {
func testcase_DefaultResolver_WithProxyDefaults() compileTestCase {
entries := newEntries()
entries.GlobalProxy = &structs.ProxyConfigEntry{
entries.AddProxyDefaults(&structs.ProxyConfigEntry{
Kind: structs.ProxyDefaults,
Name: structs.ProxyConfigGlobal,
Config: map[string]interface{}{
@ -1455,7 +1459,7 @@ func testcase_DefaultResolver_WithProxyDefaults() compileTestCase {
MeshGateway: structs.MeshGatewayConfig{
Mode: structs.MeshGatewayModeRemote,
},
}
})
expect := &structs.CompiledDiscoveryChain{
Protocol: "grpc",
@ -1699,11 +1703,11 @@ func testcase_MultiDatacenterCanary() compileTestCase {
expect := &structs.CompiledDiscoveryChain{
Protocol: "http",
StartNode: "splitter:main.default",
StartNode: "splitter:main.default.default",
Nodes: map[string]*structs.DiscoveryGraphNode{
"splitter:main.default": {
"splitter:main.default.default": {
Type: structs.DiscoveryGraphNodeTypeSplitter,
Name: "main.default",
Name: "main.default.default",
Splits: []*structs.DiscoverySplit{
{
Definition: &structs.ServiceSplit{
@ -1880,11 +1884,11 @@ func testcase_AllBellsAndWhistles() compileTestCase {
expect := &structs.CompiledDiscoveryChain{
Protocol: "http",
StartNode: "router:main.default",
StartNode: "router:main.default.default",
Nodes: map[string]*structs.DiscoveryGraphNode{
"router:main.default": {
"router:main.default.default": {
Type: structs.DiscoveryGraphNodeTypeRouter,
Name: "main.default",
Name: "main.default.default",
Routes: []*structs.DiscoveryRoute{
{
Definition: &router.Routes[0],
@ -1892,17 +1896,17 @@ func testcase_AllBellsAndWhistles() compileTestCase {
},
{
Definition: &router.Routes[1],
NextNode: "splitter:svc-split.default",
NextNode: "splitter:svc-split.default.default",
},
{
Definition: newDefaultServiceRoute("main", "default"),
Definition: newDefaultServiceRoute("main", "default", "default"),
NextNode: "resolver:default-subset.main.default.default.dc1",
},
},
},
"splitter:svc-split.default": {
"splitter:svc-split.default.default": {
Type: structs.DiscoveryGraphNodeTypeSplitter,
Name: "svc-split.default",
Name: "svc-split.default.default",
Splits: []*structs.DiscoverySplit{
{
Definition: &structs.ServiceSplit{
@ -2455,11 +2459,11 @@ func testcase_LBSplitterAndResolver() compileTestCase {
expect := &structs.CompiledDiscoveryChain{
Protocol: "http",
StartNode: "splitter:main.default",
StartNode: "splitter:main.default.default",
Nodes: map[string]*structs.DiscoveryGraphNode{
"splitter:main.default": {
"splitter:main.default.default": {
Type: structs.DiscoveryGraphNodeTypeSplitter,
Name: "main.default",
Name: "main.default.default",
Splits: []*structs.DiscoverySplit{
{
Definition: &structs.ServiceSplit{
@ -2642,13 +2646,13 @@ func newSimpleRoute(name string, muts ...func(*structs.ServiceRoute)) structs.Se
}
func setGlobalProxyProtocol(entries *structs.DiscoveryChainConfigEntries, protocol string) {
entries.GlobalProxy = &structs.ProxyConfigEntry{
entries.AddProxyDefaults(&structs.ProxyConfigEntry{
Kind: structs.ProxyDefaults,
Name: structs.ProxyConfigGlobal,
Config: map[string]interface{}{
"protocol": protocol,
},
}
})
}
func setServiceProtocol(entries *structs.DiscoveryChainConfigEntries, name, protocol string) {

View File

@ -88,7 +88,7 @@ func (s *Server) validateEnterpriseIntentionNamespace(ns string, _ bool) error {
func (s *Server) setupSerfLAN(config *Config) error {
var err error
// Initialize the LAN Serf for the default network segment.
s.serfLAN, err = s.setupSerf(setupSerfOptions{
s.serfLAN, _, err = s.setupSerf(setupSerfOptions{
Config: config.SerfLANConfig,
EventCh: s.eventChLAN,
SnapshotPath: serfLANSnapshot,

View File

@ -116,7 +116,7 @@ func TestFederationState_Apply_Upsert_ACLDeny(t *testing.T) {
c.DisableFederationStateAntiEntropy = true
c.PrimaryDatacenter = "dc1"
c.ACLsEnabled = true
c.ACLMasterToken = "root"
c.ACLInitialManagementToken = "root"
c.ACLResolverSettings.ACLDefaultPolicy = "deny"
})
defer os.RemoveAll(dir1)
@ -237,7 +237,7 @@ func TestFederationState_Get_ACLDeny(t *testing.T) {
c.DisableFederationStateAntiEntropy = true
c.PrimaryDatacenter = "dc1"
c.ACLsEnabled = true
c.ACLMasterToken = "root"
c.ACLInitialManagementToken = "root"
c.ACLResolverSettings.ACLDefaultPolicy = "deny"
})
defer os.RemoveAll(dir1)
@ -409,7 +409,7 @@ func TestFederationState_List_ACLDeny(t *testing.T) {
c.PrimaryDatacenter = "dc1"
c.PrimaryDatacenter = "dc1"
c.ACLsEnabled = true
c.ACLMasterToken = "root"
c.ACLInitialManagementToken = "root"
c.ACLResolverSettings.ACLDefaultPolicy = "deny"
})
defer os.RemoveAll(dir1)
@ -425,7 +425,7 @@ func TestFederationState_List_ACLDeny(t *testing.T) {
c.PrimaryDatacenter = "dc1"
c.PrimaryDatacenter = "dc1"
c.ACLsEnabled = true
c.ACLMasterToken = "root"
c.ACLInitialManagementToken = "root"
c.ACLResolverSettings.ACLDefaultPolicy = "deny"
})
defer os.RemoveAll(dir2)
@ -695,7 +695,7 @@ func TestFederationState_Apply_Delete_ACLDeny(t *testing.T) {
c.DisableFederationStateAntiEntropy = true
c.PrimaryDatacenter = "dc1"
c.ACLsEnabled = true
c.ACLMasterToken = "root"
c.ACLInitialManagementToken = "root"
c.ACLResolverSettings.ACLDefaultPolicy = "deny"
})
defer os.RemoveAll(dir1)

View File

@ -464,6 +464,14 @@ func TestFSM_SnapshotRestore_OSS(t *testing.T) {
require.NoError(t, err)
require.Equal(t, vip, "240.0.0.2")
_, serviceNames, err := fsm.state.ServiceNamesOfKind(nil, structs.ServiceKindTypical)
require.NoError(t, err)
expect := []string{"backend", "db", "frontend", "web"}
for i, sn := range serviceNames {
require.Equal(t, expect[i], sn.Service.Name)
}
// Snapshot
snap, err := fsm.Snapshot()
require.NoError(t, err)
@ -690,10 +698,10 @@ func TestFSM_SnapshotRestore_OSS(t *testing.T) {
require.Len(t, roots, 2)
// Verify provider state is restored.
_, state, err := fsm2.state.CAProviderState("asdf")
_, provider, err := fsm2.state.CAProviderState("asdf")
require.NoError(t, err)
require.Equal(t, "foo", state.PrivateKey)
require.Equal(t, "bar", state.RootCert)
require.Equal(t, "foo", provider.PrivateKey)
require.Equal(t, "bar", provider.RootCert)
// Verify CA configuration is restored.
_, caConf, err := fsm2.state.CAConfig(nil)
@ -751,6 +759,14 @@ func TestFSM_SnapshotRestore_OSS(t *testing.T) {
require.NoError(t, err)
require.Equal(t, meshConfig, meshConfigEntry)
_, restoredServiceNames, err := fsm2.state.ServiceNamesOfKind(nil, structs.ServiceKindTypical)
require.NoError(t, err)
expect = []string{"backend", "db", "frontend", "web"}
for i, sn := range restoredServiceNames {
require.Equal(t, expect[i], sn.Service.Name)
}
// Snapshot
snap, err = fsm2.Snapshot()
require.NoError(t, err)

View File

@ -983,7 +983,7 @@ func TestHealth_ServiceNodes_ConnectProxy_ACL(t *testing.T) {
dir1, s1 := testServerWithConfig(t, func(c *Config) {
c.PrimaryDatacenter = "dc1"
c.ACLsEnabled = true
c.ACLMasterToken = "root"
c.ACLInitialManagementToken = "root"
c.ACLResolverSettings.ACLDefaultPolicy = "deny"
})
defer os.RemoveAll(dir1)
@ -1289,7 +1289,7 @@ func TestHealth_ServiceNodes_Ingress_ACL(t *testing.T) {
dir1, s1 := testServerWithConfig(t, func(c *Config) {
c.PrimaryDatacenter = "dc1"
c.ACLsEnabled = true
c.ACLMasterToken = "root"
c.ACLInitialManagementToken = "root"
c.ACLResolverSettings.ACLDefaultPolicy = "deny"
})
defer os.RemoveAll(dir1)

View File

@ -867,7 +867,7 @@ func TestIntentionApply_aclDeny(t *testing.T) {
dir1, s1 := testServerWithConfig(t, func(c *Config) {
c.PrimaryDatacenter = "dc1"
c.ACLsEnabled = true
c.ACLMasterToken = "root"
c.ACLInitialManagementToken = "root"
c.ACLResolverSettings.ACLDefaultPolicy = "deny"
})
defer os.RemoveAll(dir1)
@ -1257,7 +1257,7 @@ func TestIntentionApply_aclDelete(t *testing.T) {
dir1, s1 := testServerWithConfig(t, func(c *Config) {
c.PrimaryDatacenter = "dc1"
c.ACLsEnabled = true
c.ACLMasterToken = "root"
c.ACLInitialManagementToken = "root"
c.ACLResolverSettings.ACLDefaultPolicy = "deny"
})
defer os.RemoveAll(dir1)
@ -1323,7 +1323,7 @@ func TestIntentionApply_aclUpdate(t *testing.T) {
dir1, s1 := testServerWithConfig(t, func(c *Config) {
c.PrimaryDatacenter = "dc1"
c.ACLsEnabled = true
c.ACLMasterToken = "root"
c.ACLInitialManagementToken = "root"
c.ACLResolverSettings.ACLDefaultPolicy = "deny"
})
defer os.RemoveAll(dir1)
@ -1377,7 +1377,7 @@ func TestIntentionApply_aclManagement(t *testing.T) {
dir1, s1 := testServerWithConfig(t, func(c *Config) {
c.PrimaryDatacenter = "dc1"
c.ACLsEnabled = true
c.ACLMasterToken = "root"
c.ACLInitialManagementToken = "root"
c.ACLResolverSettings.ACLDefaultPolicy = "deny"
})
defer os.RemoveAll(dir1)
@ -1422,7 +1422,7 @@ func TestIntentionApply_aclUpdateChange(t *testing.T) {
dir1, s1 := testServerWithConfig(t, func(c *Config) {
c.PrimaryDatacenter = "dc1"
c.ACLsEnabled = true
c.ACLMasterToken = "root"
c.ACLInitialManagementToken = "root"
c.ACLResolverSettings.ACLDefaultPolicy = "deny"
})
defer os.RemoveAll(dir1)
@ -1472,7 +1472,7 @@ func TestIntentionGet_acl(t *testing.T) {
dir1, s1 := testServerWithConfig(t, func(c *Config) {
c.PrimaryDatacenter = "dc1"
c.ACLsEnabled = true
c.ACLMasterToken = "root"
c.ACLInitialManagementToken = "root"
c.ACLResolverSettings.ACLDefaultPolicy = "deny"
})
defer os.RemoveAll(dir1)
@ -1879,7 +1879,7 @@ func TestIntentionCheck_defaultACLDeny(t *testing.T) {
dir1, s1 := testServerWithConfig(t, func(c *Config) {
c.PrimaryDatacenter = "dc1"
c.ACLsEnabled = true
c.ACLMasterToken = "root"
c.ACLInitialManagementToken = "root"
c.ACLResolverSettings.ACLDefaultPolicy = "deny"
})
defer os.RemoveAll(dir1)
@ -1915,7 +1915,7 @@ func TestIntentionCheck_defaultACLAllow(t *testing.T) {
dir1, s1 := testServerWithConfig(t, func(c *Config) {
c.PrimaryDatacenter = "dc1"
c.ACLsEnabled = true
c.ACLMasterToken = "root"
c.ACLInitialManagementToken = "root"
c.ACLResolverSettings.ACLDefaultPolicy = "allow"
})
defer os.RemoveAll(dir1)
@ -1951,7 +1951,7 @@ func TestIntentionCheck_aclDeny(t *testing.T) {
dir1, s1 := testServerWithConfig(t, func(c *Config) {
c.PrimaryDatacenter = "dc1"
c.ACLsEnabled = true
c.ACLMasterToken = "root"
c.ACLInitialManagementToken = "root"
c.ACLResolverSettings.ACLDefaultPolicy = "deny"
})
defer os.RemoveAll(dir1)

View File

@ -72,18 +72,21 @@ func (m *Internal) NodeDump(args *structs.DCSpecificRequest,
if err != nil {
return err
}
reply.Index, reply.Dump = index, dump
if err := m.srv.filterACL(args.Token, reply); err != nil {
return err
}
raw, err := filter.Execute(reply.Dump)
if err != nil {
return err
}
reply.Dump = raw.(structs.NodeDump)
// Note: we filter the results with ACLs *after* applying the user-supplied
// bexpr filter, to ensure QueryMeta.ResultsFilteredByACLs does not include
// results that would be filtered out even if the user did have permission.
if err := m.srv.filterACL(args.Token, reply); err != nil {
return err
}
return nil
})
}
@ -114,10 +117,6 @@ func (m *Internal) ServiceDump(args *structs.ServiceDumpRequest, reply *structs.
}
reply.Nodes = nodes
if err := m.srv.filterACL(args.Token, &reply.Nodes); err != nil {
return err
}
// Get, store, and filter gateway services
idx, gatewayServices, err := state.DumpGatewayServices(ws)
if err != nil {

View File

@ -461,7 +461,7 @@ func TestInternal_NodeInfo_FilterACL(t *testing.T) {
QueryOptions: structs.QueryOptions{Token: token},
}
reply := structs.IndexedNodeDump{}
if err := msgpackrpc.CallWithCodec(codec, "Health.NodeChecks", &opt, &reply); err != nil {
if err := msgpackrpc.CallWithCodec(codec, "Internal.NodeInfo", &opt, &reply); err != nil {
t.Fatalf("err: %s", err)
}
for _, info := range reply.Dump {
@ -492,6 +492,10 @@ func TestInternal_NodeInfo_FilterACL(t *testing.T) {
}
}
if !reply.QueryMeta.ResultsFilteredByACLs {
t.Fatal("ResultsFilteredByACLs should be true")
}
// We've already proven that we call the ACL filtering function so we
// test node filtering down in acl.go for node cases. This also proves
// that we respect the version 8 ACL flag, since the test server sets
@ -515,7 +519,7 @@ func TestInternal_NodeDump_FilterACL(t *testing.T) {
QueryOptions: structs.QueryOptions{Token: token},
}
reply := structs.IndexedNodeDump{}
if err := msgpackrpc.CallWithCodec(codec, "Health.NodeChecks", &opt, &reply); err != nil {
if err := msgpackrpc.CallWithCodec(codec, "Internal.NodeDump", &opt, &reply); err != nil {
t.Fatalf("err: %s", err)
}
for _, info := range reply.Dump {
@ -546,6 +550,10 @@ func TestInternal_NodeDump_FilterACL(t *testing.T) {
}
}
if !reply.QueryMeta.ResultsFilteredByACLs {
t.Fatal("ResultsFilteredByACLs should be true")
}
// We've already proven that we call the ACL filtering function so we
// test node filtering down in acl.go for node cases. This also proves
// that we respect the version 8 ACL flag, since the test server sets
@ -562,7 +570,7 @@ func TestInternal_EventFire_Token(t *testing.T) {
dir, srv := testServerWithConfig(t, func(c *Config) {
c.PrimaryDatacenter = "dc1"
c.ACLsEnabled = true
c.ACLMasterToken = "root"
c.ACLInitialManagementToken = "root"
c.ACLResolverSettings.ACLDownPolicy = "deny"
c.ACLResolverSettings.ACLDefaultPolicy = "deny"
})
@ -750,6 +758,217 @@ func TestInternal_ServiceDump_Kind(t *testing.T) {
})
}
func TestInternal_ServiceDump_ACL(t *testing.T) {
if testing.Short() {
t.Skip("too slow for testing.Short")
}
t.Parallel()
dir, s := testServerWithConfig(t, func(c *Config) {
c.PrimaryDatacenter = "dc1"
c.ACLsEnabled = true
c.ACLInitialManagementToken = "root"
c.ACLResolverSettings.ACLDefaultPolicy = "deny"
})
defer os.RemoveAll(dir)
defer s.Shutdown()
codec := rpcClient(t, s)
defer codec.Close()
testrpc.WaitForLeader(t, s.RPC, "dc1")
registrations := []*structs.RegisterRequest{
// Service `redis` on `node1`
{
Datacenter: "dc1",
Node: "node1",
ID: types.NodeID("e0155642-135d-4739-9853-a1ee6c9f945b"),
Address: "192.18.1.1",
Service: &structs.NodeService{
Kind: structs.ServiceKindTypical,
ID: "redis",
Service: "redis",
Port: 5678,
},
Check: &structs.HealthCheck{
Name: "redis check",
Status: api.HealthPassing,
ServiceID: "redis",
},
},
// Ingress gateway `igw` on `node2`
{
Datacenter: "dc1",
Node: "node2",
ID: types.NodeID("3a9d7530-20d4-443a-98d3-c10fe78f09f4"),
Address: "192.18.1.2",
Service: &structs.NodeService{
Kind: structs.ServiceKindIngressGateway,
ID: "igw",
Service: "igw",
},
Check: &structs.HealthCheck{
Name: "igw check",
Status: api.HealthPassing,
ServiceID: "igw",
},
},
}
for _, reg := range registrations {
reg.Token = "root"
err := msgpackrpc.CallWithCodec(codec, "Catalog.Register", reg, nil)
require.NoError(t, err)
}
{
req := structs.ConfigEntryRequest{
Datacenter: "dc1",
Entry: &structs.IngressGatewayConfigEntry{
Kind: structs.IngressGateway,
Name: "igw",
Listeners: []structs.IngressListener{
{
Port: 8765,
Protocol: "tcp",
Services: []structs.IngressService{
{Name: "redis"},
},
},
},
},
}
req.Token = "root"
var out bool
err := msgpackrpc.CallWithCodec(codec, "ConfigEntry.Apply", &req, &out)
require.NoError(t, err)
}
tokenWithRules := func(t *testing.T, rules string) string {
t.Helper()
tok, err := upsertTestTokenWithPolicyRules(codec, "root", "dc1", rules)
require.NoError(t, err)
return tok.SecretID
}
t.Run("can read all", func(t *testing.T) {
require := require.New(t)
token := tokenWithRules(t, `
node_prefix "" {
policy = "read"
}
service_prefix "" {
policy = "read"
}
`)
args := structs.DCSpecificRequest{
Datacenter: "dc1",
QueryOptions: structs.QueryOptions{Token: token},
}
var out structs.IndexedNodesWithGateways
err := msgpackrpc.CallWithCodec(codec, "Internal.ServiceDump", &args, &out)
require.NoError(err)
require.NotEmpty(out.Nodes)
require.NotEmpty(out.Gateways)
require.False(out.QueryMeta.ResultsFilteredByACLs, "ResultsFilteredByACLs should be false")
})
t.Run("cannot read service node", func(t *testing.T) {
require := require.New(t)
token := tokenWithRules(t, `
node "node1" {
policy = "deny"
}
service "redis" {
policy = "read"
}
`)
args := structs.DCSpecificRequest{
Datacenter: "dc1",
QueryOptions: structs.QueryOptions{Token: token},
}
var out structs.IndexedNodesWithGateways
err := msgpackrpc.CallWithCodec(codec, "Internal.ServiceDump", &args, &out)
require.NoError(err)
require.Empty(out.Nodes)
require.True(out.QueryMeta.ResultsFilteredByACLs, "ResultsFilteredByACLs should be true")
})
t.Run("cannot read service", func(t *testing.T) {
require := require.New(t)
token := tokenWithRules(t, `
node "node1" {
policy = "read"
}
service "redis" {
policy = "deny"
}
`)
args := structs.DCSpecificRequest{
Datacenter: "dc1",
QueryOptions: structs.QueryOptions{Token: token},
}
var out structs.IndexedNodesWithGateways
err := msgpackrpc.CallWithCodec(codec, "Internal.ServiceDump", &args, &out)
require.NoError(err)
require.Empty(out.Nodes)
require.True(out.QueryMeta.ResultsFilteredByACLs, "ResultsFilteredByACLs should be true")
})
t.Run("cannot read gateway node", func(t *testing.T) {
require := require.New(t)
token := tokenWithRules(t, `
node "node2" {
policy = "deny"
}
service "mgw" {
policy = "read"
}
`)
args := structs.DCSpecificRequest{
Datacenter: "dc1",
QueryOptions: structs.QueryOptions{Token: token},
}
var out structs.IndexedNodesWithGateways
err := msgpackrpc.CallWithCodec(codec, "Internal.ServiceDump", &args, &out)
require.NoError(err)
require.Empty(out.Gateways)
require.True(out.QueryMeta.ResultsFilteredByACLs, "ResultsFilteredByACLs should be true")
})
t.Run("cannot read gateway", func(t *testing.T) {
require := require.New(t)
token := tokenWithRules(t, `
node "node2" {
policy = "read"
}
service "mgw" {
policy = "deny"
}
`)
args := structs.DCSpecificRequest{
Datacenter: "dc1",
QueryOptions: structs.QueryOptions{Token: token},
}
var out structs.IndexedNodesWithGateways
err := msgpackrpc.CallWithCodec(codec, "Internal.ServiceDump", &args, &out)
require.NoError(err)
require.Empty(out.Gateways)
require.True(out.QueryMeta.ResultsFilteredByACLs, "ResultsFilteredByACLs should be true")
})
}
func TestInternal_GatewayServiceDump_Terminating(t *testing.T) {
if testing.Short() {
t.Skip("too slow for testing.Short")
@ -963,7 +1182,7 @@ func TestInternal_GatewayServiceDump_Terminating_ACL(t *testing.T) {
dir1, s1 := testServerWithConfig(t, func(c *Config) {
c.PrimaryDatacenter = "dc1"
c.ACLsEnabled = true
c.ACLMasterToken = "root"
c.ACLInitialManagementToken = "root"
c.ACLResolverSettings.ACLDefaultPolicy = "deny"
})
defer os.RemoveAll(dir1)
@ -1082,6 +1301,7 @@ func TestInternal_GatewayServiceDump_Terminating_ACL(t *testing.T) {
require.Equal(t, nodes[0].Node.Node, "bar")
require.Equal(t, nodes[0].Service.Service, "db")
require.Equal(t, nodes[0].Checks[0].Status, api.HealthWarning)
require.True(t, out.QueryMeta.ResultsFilteredByACLs, "ResultsFilteredByACLs should be true")
}
func TestInternal_GatewayServiceDump_Ingress(t *testing.T) {
@ -1308,7 +1528,7 @@ func TestInternal_GatewayServiceDump_Ingress_ACL(t *testing.T) {
dir1, s1 := testServerWithConfig(t, func(c *Config) {
c.PrimaryDatacenter = "dc1"
c.ACLsEnabled = true
c.ACLMasterToken = "root"
c.ACLInitialManagementToken = "root"
c.ACLResolverSettings.ACLDefaultPolicy = "deny"
})
defer os.RemoveAll(dir1)
@ -1972,7 +2192,7 @@ func TestInternal_ServiceTopology_ACL(t *testing.T) {
dir1, s1 := testServerWithConfig(t, func(c *Config) {
c.PrimaryDatacenter = "dc1"
c.ACLsEnabled = true
c.ACLMasterToken = TestDefaultMasterToken
c.ACLInitialManagementToken = TestDefaultMasterToken
c.ACLResolverSettings.ACLDefaultPolicy = "deny"
})
defer os.RemoveAll(dir1)
@ -2111,7 +2331,7 @@ func TestInternal_IntentionUpstreams_ACL(t *testing.T) {
dir1, s1 := testServerWithConfig(t, func(c *Config) {
c.PrimaryDatacenter = "dc1"
c.ACLsEnabled = true
c.ACLMasterToken = TestDefaultMasterToken
c.ACLInitialManagementToken = TestDefaultMasterToken
c.ACLResolverSettings.ACLDefaultPolicy = "deny"
})
defer os.RemoveAll(dir1)

View File

@ -84,7 +84,7 @@ func TestKVS_Apply_ACLDeny(t *testing.T) {
dir1, s1 := testServerWithConfig(t, func(c *Config) {
c.PrimaryDatacenter = "dc1"
c.ACLsEnabled = true
c.ACLMasterToken = "root"
c.ACLInitialManagementToken = "root"
c.ACLResolverSettings.ACLDefaultPolicy = "deny"
})
defer os.RemoveAll(dir1)
@ -189,7 +189,7 @@ func TestKVS_Get_ACLDeny(t *testing.T) {
dir1, s1 := testServerWithConfig(t, func(c *Config) {
c.PrimaryDatacenter = "dc1"
c.ACLsEnabled = true
c.ACLMasterToken = "root"
c.ACLInitialManagementToken = "root"
c.ACLResolverSettings.ACLDefaultPolicy = "deny"
})
defer os.RemoveAll(dir1)
@ -413,7 +413,7 @@ func TestKVSEndpoint_List_ACLDeny(t *testing.T) {
dir1, s1 := testServerWithConfig(t, func(c *Config) {
c.PrimaryDatacenter = "dc1"
c.ACLsEnabled = true
c.ACLMasterToken = "root"
c.ACLInitialManagementToken = "root"
c.ACLResolverSettings.ACLDefaultPolicy = "deny"
})
defer os.RemoveAll(dir1)
@ -492,7 +492,7 @@ func TestKVSEndpoint_List_ACLEnableKeyListPolicy(t *testing.T) {
dir1, s1 := testServerWithConfig(t, func(c *Config) {
c.PrimaryDatacenter = "dc1"
c.ACLsEnabled = true
c.ACLMasterToken = "root"
c.ACLInitialManagementToken = "root"
c.ACLResolverSettings.ACLDefaultPolicy = "deny"
c.ACLEnableKeyListPolicy = true
})
@ -684,7 +684,7 @@ func TestKVSEndpoint_ListKeys_ACLDeny(t *testing.T) {
dir1, s1 := testServerWithConfig(t, func(c *Config) {
c.PrimaryDatacenter = "dc1"
c.ACLsEnabled = true
c.ACLMasterToken = "root"
c.ACLInitialManagementToken = "root"
c.ACLResolverSettings.ACLDefaultPolicy = "deny"
})
defer os.RemoveAll(dir1)

View File

@ -431,28 +431,28 @@ func (s *Server) initializeACLs(ctx context.Context) error {
s.logger.Info("Created ACL 'global-management' policy")
}
// Check for configured master token.
if master := s.config.ACLMasterToken; len(master) > 0 {
// Check for configured initial management token.
if initialManagement := s.config.ACLInitialManagementToken; len(initialManagement) > 0 {
state := s.fsm.State()
if _, err := uuid.ParseUUID(master); err != nil {
s.logger.Warn("Configuring a non-UUID master token is deprecated")
if _, err := uuid.ParseUUID(initialManagement); err != nil {
s.logger.Warn("Configuring a non-UUID initial management token is deprecated")
}
_, token, err := state.ACLTokenGetBySecret(nil, master, nil)
_, token, err := state.ACLTokenGetBySecret(nil, initialManagement, nil)
if err != nil {
return fmt.Errorf("failed to get master token: %v", err)
return fmt.Errorf("failed to get initial management token: %v", err)
}
// Ignoring expiration times to avoid an insertion collision.
if token == nil {
accessor, err := lib.GenerateUUID(s.checkTokenUUID)
if err != nil {
return fmt.Errorf("failed to generate the accessor ID for the master token: %v", err)
return fmt.Errorf("failed to generate the accessor ID for the initial management token: %v", err)
}
token := structs.ACLToken{
AccessorID: accessor,
SecretID: master,
Description: "Master Token",
SecretID: initialManagement,
Description: "Initial Management Token",
Policies: []structs.ACLTokenPolicyLink{
{
ID: structs.ACLPolicyGlobalManagementID,
@ -472,12 +472,12 @@ func (s *Server) initializeACLs(ctx context.Context) error {
ResetIndex: 0,
}
if _, err := s.raftApply(structs.ACLBootstrapRequestType, &req); err == nil {
s.logger.Info("Bootstrapped ACL master token from configuration")
s.logger.Info("Bootstrapped ACL initial management token from configuration")
done = true
} else {
if err.Error() != structs.ACLBootstrapNotAllowedErr.Error() &&
err.Error() != structs.ACLBootstrapInvalidResetIndexErr.Error() {
return fmt.Errorf("failed to bootstrap master token: %v", err)
return fmt.Errorf("failed to bootstrap initial management token: %v", err)
}
}
}
@ -489,10 +489,10 @@ func (s *Server) initializeACLs(ctx context.Context) error {
CAS: false,
}
if _, err := s.raftApply(structs.ACLTokenSetRequestType, &req); err != nil {
return fmt.Errorf("failed to create master token: %v", err)
return fmt.Errorf("failed to create initial management token: %v", err)
}
s.logger.Info("Created ACL master token from configuration")
s.logger.Info("Created ACL initial management token from configuration")
}
}
}

View File

@ -204,7 +204,7 @@ func TestCAManager_Initialize_Secondary(t *testing.T) {
c.PrimaryDatacenter = "primary"
c.Build = "1.6.0"
c.ACLsEnabled = true
c.ACLMasterToken = masterToken
c.ACLInitialManagementToken = masterToken
c.ACLResolverSettings.ACLDefaultPolicy = "deny"
c.CAConfig.Config["PrivateKeyType"] = tc.keyType
c.CAConfig.Config["PrivateKeyBits"] = tc.keyBits

View File

@ -359,7 +359,7 @@ func TestLeader_FederationStateAntiEntropyPruning_ACLDeny(t *testing.T) {
c.PrimaryDatacenter = "dc1"
c.PrimaryDatacenter = "dc1"
c.ACLsEnabled = true
c.ACLMasterToken = "root"
c.ACLInitialManagementToken = "root"
c.ACLResolverSettings.ACLDefaultPolicy = "deny"
})
defer os.RemoveAll(dir1)
@ -373,7 +373,7 @@ func TestLeader_FederationStateAntiEntropyPruning_ACLDeny(t *testing.T) {
c.PrimaryDatacenter = "dc1"
c.PrimaryDatacenter = "dc1"
c.ACLsEnabled = true
c.ACLMasterToken = "root"
c.ACLInitialManagementToken = "root"
c.ACLResolverSettings.ACLDefaultPolicy = "deny"
})
defer os.RemoveAll(dir2)

View File

@ -29,7 +29,7 @@ func TestLeader_ReplicateIntentions(t *testing.T) {
c.Datacenter = "dc1"
c.PrimaryDatacenter = "dc1"
c.ACLsEnabled = true
c.ACLMasterToken = "root"
c.ACLInitialManagementToken = "root"
c.ACLResolverSettings.ACLDefaultPolicy = "deny"
c.Build = "1.6.0"
c.OverrideInitialSerfTags = func(tags map[string]string) {

View File

@ -31,7 +31,7 @@ func TestLeader_RegisterMember(t *testing.T) {
dir1, s1 := testServerWithConfig(t, func(c *Config) {
c.PrimaryDatacenter = "dc1"
c.ACLsEnabled = true
c.ACLMasterToken = "root"
c.ACLInitialManagementToken = "root"
c.ACLResolverSettings.ACLDefaultPolicy = "deny"
})
defer os.RemoveAll(dir1)
@ -106,7 +106,7 @@ func TestLeader_FailedMember(t *testing.T) {
dir1, s1 := testServerWithConfig(t, func(c *Config) {
c.PrimaryDatacenter = "dc1"
c.ACLsEnabled = true
c.ACLMasterToken = "root"
c.ACLInitialManagementToken = "root"
c.ACLResolverSettings.ACLDefaultPolicy = "deny"
})
defer os.RemoveAll(dir1)
@ -171,7 +171,7 @@ func TestLeader_LeftMember(t *testing.T) {
dir1, s1 := testServerWithConfig(t, func(c *Config) {
c.PrimaryDatacenter = "dc1"
c.ACLsEnabled = true
c.ACLMasterToken = "root"
c.ACLInitialManagementToken = "root"
c.ACLResolverSettings.ACLDefaultPolicy = "deny"
})
defer os.RemoveAll(dir1)
@ -221,7 +221,7 @@ func TestLeader_ReapMember(t *testing.T) {
dir1, s1 := testServerWithConfig(t, func(c *Config) {
c.PrimaryDatacenter = "dc1"
c.ACLsEnabled = true
c.ACLMasterToken = "root"
c.ACLInitialManagementToken = "root"
c.ACLResolverSettings.ACLDefaultPolicy = "deny"
})
defer os.RemoveAll(dir1)
@ -286,7 +286,7 @@ func TestLeader_CheckServersMeta(t *testing.T) {
dir1, s1 := testServerWithConfig(t, func(c *Config) {
c.PrimaryDatacenter = "dc1"
c.ACLsEnabled = true
c.ACLMasterToken = "root"
c.ACLInitialManagementToken = "root"
c.ACLResolverSettings.ACLDefaultPolicy = "allow"
c.Bootstrap = true
})
@ -296,7 +296,7 @@ func TestLeader_CheckServersMeta(t *testing.T) {
dir2, s2 := testServerWithConfig(t, func(c *Config) {
c.PrimaryDatacenter = "dc1"
c.ACLsEnabled = true
c.ACLMasterToken = "root"
c.ACLInitialManagementToken = "root"
c.ACLResolverSettings.ACLDefaultPolicy = "allow"
c.Bootstrap = false
})
@ -306,7 +306,7 @@ func TestLeader_CheckServersMeta(t *testing.T) {
dir3, s3 := testServerWithConfig(t, func(c *Config) {
c.PrimaryDatacenter = "dc1"
c.ACLsEnabled = true
c.ACLMasterToken = "root"
c.ACLInitialManagementToken = "root"
c.ACLResolverSettings.ACLDefaultPolicy = "allow"
c.Bootstrap = false
})
@ -394,7 +394,7 @@ func TestLeader_ReapServer(t *testing.T) {
dir1, s1 := testServerWithConfig(t, func(c *Config) {
c.PrimaryDatacenter = "dc1"
c.ACLsEnabled = true
c.ACLMasterToken = "root"
c.ACLInitialManagementToken = "root"
c.ACLResolverSettings.ACLDefaultPolicy = "allow"
c.Bootstrap = true
})
@ -404,7 +404,7 @@ func TestLeader_ReapServer(t *testing.T) {
dir2, s2 := testServerWithConfig(t, func(c *Config) {
c.PrimaryDatacenter = "dc1"
c.ACLsEnabled = true
c.ACLMasterToken = "root"
c.ACLInitialManagementToken = "root"
c.ACLResolverSettings.ACLDefaultPolicy = "allow"
c.Bootstrap = false
})
@ -414,7 +414,7 @@ func TestLeader_ReapServer(t *testing.T) {
dir3, s3 := testServerWithConfig(t, func(c *Config) {
c.PrimaryDatacenter = "dc1"
c.ACLsEnabled = true
c.ACLMasterToken = "root"
c.ACLInitialManagementToken = "root"
c.ACLResolverSettings.ACLDefaultPolicy = "allow"
c.Bootstrap = false
})
@ -473,7 +473,7 @@ func TestLeader_Reconcile_ReapMember(t *testing.T) {
dir1, s1 := testServerWithConfig(t, func(c *Config) {
c.PrimaryDatacenter = "dc1"
c.ACLsEnabled = true
c.ACLMasterToken = "root"
c.ACLInitialManagementToken = "root"
c.ACLResolverSettings.ACLDefaultPolicy = "deny"
})
defer os.RemoveAll(dir1)
@ -526,7 +526,7 @@ func TestLeader_Reconcile(t *testing.T) {
dir1, s1 := testServerWithConfig(t, func(c *Config) {
c.PrimaryDatacenter = "dc1"
c.ACLsEnabled = true
c.ACLMasterToken = "root"
c.ACLInitialManagementToken = "root"
c.ACLResolverSettings.ACLDefaultPolicy = "deny"
})
defer os.RemoveAll(dir1)
@ -875,7 +875,7 @@ func TestLeader_ReapTombstones(t *testing.T) {
dir1, s1 := testServerWithConfig(t, func(c *Config) {
c.PrimaryDatacenter = "dc1"
c.ACLsEnabled = true
c.ACLMasterToken = "root"
c.ACLInitialManagementToken = "root"
c.ACLResolverSettings.ACLDefaultPolicy = "deny"
c.TombstoneTTL = 50 * time.Millisecond
c.TombstoneTTLGranularity = 10 * time.Millisecond
@ -1180,7 +1180,7 @@ func TestLeader_ACL_Initialization(t *testing.T) {
c.Datacenter = "dc1"
c.PrimaryDatacenter = "dc1"
c.ACLsEnabled = true
c.ACLMasterToken = tt.master
c.ACLInitialManagementToken = tt.master
}
dir1, s1 := testServerWithConfig(t, conf)
defer os.RemoveAll(dir1)
@ -1225,7 +1225,7 @@ func TestLeader_ACLUpgrade_IsStickyEvenIfSerfTagsRegress(t *testing.T) {
c.Datacenter = "dc1"
c.PrimaryDatacenter = "dc1"
c.ACLsEnabled = true
c.ACLMasterToken = "root"
c.ACLInitialManagementToken = "root"
})
defer os.RemoveAll(dir1)
defer s1.Shutdown()

View File

@ -2,6 +2,7 @@ package consul
import (
"fmt"
"sync"
"github.com/hashicorp/go-version"
"github.com/hashicorp/serf/serf"
@ -86,14 +87,41 @@ func (md *lanMergeDelegate) NotifyMerge(members []*serf.Member) error {
// ring. We check that the peers are server nodes and abort the merge
// otherwise.
type wanMergeDelegate struct {
localDatacenter string
federationDisabledLock sync.Mutex
federationDisabled bool
}
// SetWANFederationDisabled selectively disables the wan pool from accepting
// non-local members. If the toggle changed the current value it returns true.
func (md *wanMergeDelegate) SetWANFederationDisabled(disabled bool) bool {
md.federationDisabledLock.Lock()
prior := md.federationDisabled
md.federationDisabled = disabled
md.federationDisabledLock.Unlock()
return prior != disabled
}
func (md *wanMergeDelegate) NotifyMerge(members []*serf.Member) error {
// Deliberately hold this lock during the entire merge so calls to
// SetWANFederationDisabled returning immediately imply that the flag takes
// effect for all future merges.
md.federationDisabledLock.Lock()
defer md.federationDisabledLock.Unlock()
for _, m := range members {
ok, _ := metadata.IsConsulServer(*m)
ok, srv := metadata.IsConsulServer(*m)
if !ok {
return fmt.Errorf("Member '%s' is not a server", m.Name)
}
if md.federationDisabled {
if srv.Datacenter != md.localDatacenter {
return fmt.Errorf("Member '%s' part of wrong datacenter '%s'; WAN federation is disabled", m.Name, srv.Datacenter)
}
}
}
return nil
}

View File

@ -138,10 +138,16 @@ func TestMerge_WAN(t *testing.T) {
type testcase struct {
members []*serf.Member
expect string
setupFn func(t *testing.T, delegate *wanMergeDelegate)
}
run := func(t *testing.T, tc testcase) {
delegate := &wanMergeDelegate{}
delegate := &wanMergeDelegate{
localDatacenter: "dc1",
}
if tc.setupFn != nil {
tc.setupFn(t, delegate)
}
err := delegate.NotifyMerge(tc.members)
if tc.expect == "" {
require.NoError(t, err)
@ -177,7 +183,33 @@ func TestMerge_WAN(t *testing.T) {
build: "0.7.5",
}),
},
expect: "",
},
"federation disabled and local join allowed": {
setupFn: func(t *testing.T, delegate *wanMergeDelegate) {
delegate.SetWANFederationDisabled(true)
},
members: []*serf.Member{
makeTestNode(t, testMember{
dc: "dc1",
name: "node1",
server: true,
build: "0.7.5",
}),
},
},
"federation disabled and remote join blocked": {
setupFn: func(t *testing.T, delegate *wanMergeDelegate) {
delegate.SetWANFederationDisabled(true)
},
members: []*serf.Member{
makeTestNode(t, testMember{
dc: "dc2",
name: "node1",
server: true,
build: "0.7.5",
}),
},
expect: `WAN federation is disabled`,
},
}

View File

@ -54,7 +54,7 @@ func TestOperator_Autopilot_GetConfiguration_ACLDeny(t *testing.T) {
dir1, s1 := testServerWithConfig(t, func(c *Config) {
c.PrimaryDatacenter = "dc1"
c.ACLsEnabled = true
c.ACLMasterToken = "root"
c.ACLInitialManagementToken = "root"
c.ACLResolverSettings.ACLDefaultPolicy = "deny"
c.AutopilotConfig.CleanupDeadServers = false
})
@ -138,7 +138,7 @@ func TestOperator_Autopilot_SetConfiguration_ACLDeny(t *testing.T) {
dir1, s1 := testServerWithConfig(t, func(c *Config) {
c.PrimaryDatacenter = "dc1"
c.ACLsEnabled = true
c.ACLMasterToken = "root"
c.ACLInitialManagementToken = "root"
c.ACLResolverSettings.ACLDefaultPolicy = "deny"
c.AutopilotConfig.CleanupDeadServers = false
})

View File

@ -72,7 +72,7 @@ func TestOperator_RaftGetConfiguration_ACLDeny(t *testing.T) {
dir1, s1 := testServerWithConfig(t, func(c *Config) {
c.PrimaryDatacenter = "dc1"
c.ACLsEnabled = true
c.ACLMasterToken = "root"
c.ACLInitialManagementToken = "root"
c.ACLResolverSettings.ACLDefaultPolicy = "deny"
})
defer os.RemoveAll(dir1)
@ -199,7 +199,7 @@ func TestOperator_RaftRemovePeerByAddress_ACLDeny(t *testing.T) {
dir1, s1 := testServerWithConfig(t, func(c *Config) {
c.PrimaryDatacenter = "dc1"
c.ACLsEnabled = true
c.ACLMasterToken = "root"
c.ACLInitialManagementToken = "root"
c.ACLResolverSettings.ACLDefaultPolicy = "deny"
})
defer os.RemoveAll(dir1)
@ -305,7 +305,7 @@ func TestOperator_RaftRemovePeerByID_ACLDeny(t *testing.T) {
dir1, s1 := testServerWithConfig(t, func(c *Config) {
c.PrimaryDatacenter = "dc1"
c.ACLsEnabled = true
c.ACLMasterToken = "root"
c.ACLInitialManagementToken = "root"
c.ACLResolverSettings.ACLDefaultPolicy = "deny"
c.RaftConfig.ProtocolVersion = 3
})

View File

@ -373,7 +373,7 @@ func (p *PreparedQuery) Execute(args *structs.PreparedQueryExecuteRequest,
if query.Token != "" {
token = query.Token
}
if err := p.srv.filterACL(token, &reply.Nodes); err != nil {
if err := p.srv.filterACL(token, reply); err != nil {
return err
}
@ -500,7 +500,7 @@ func (p *PreparedQuery) ExecuteRemote(args *structs.PreparedQueryExecuteRemoteRe
if args.Query.Token != "" {
token = args.Query.Token
}
if err := p.srv.filterACL(token, &reply.Nodes); err != nil {
if err := p.srv.filterACL(token, reply); err != nil {
return err
}

View File

@ -200,7 +200,7 @@ func TestPreparedQuery_Apply_ACLDeny(t *testing.T) {
dir1, s1 := testServerWithConfig(t, func(c *Config) {
c.PrimaryDatacenter = "dc1"
c.ACLsEnabled = true
c.ACLMasterToken = "root"
c.ACLInitialManagementToken = "root"
c.ACLResolverSettings.ACLDefaultPolicy = "deny"
})
defer os.RemoveAll(dir1)
@ -629,7 +629,7 @@ func TestPreparedQuery_ACLDeny_Catchall_Template(t *testing.T) {
dir1, s1 := testServerWithConfig(t, func(c *Config) {
c.PrimaryDatacenter = "dc1"
c.ACLsEnabled = true
c.ACLMasterToken = "root"
c.ACLInitialManagementToken = "root"
c.ACLResolverSettings.ACLDefaultPolicy = "deny"
})
defer os.RemoveAll(dir1)
@ -831,7 +831,7 @@ func TestPreparedQuery_Get(t *testing.T) {
dir1, s1 := testServerWithConfig(t, func(c *Config) {
c.PrimaryDatacenter = "dc1"
c.ACLsEnabled = true
c.ACLMasterToken = "root"
c.ACLInitialManagementToken = "root"
c.ACLResolverSettings.ACLDefaultPolicy = "deny"
})
defer os.RemoveAll(dir1)
@ -1072,7 +1072,7 @@ func TestPreparedQuery_List(t *testing.T) {
dir1, s1 := testServerWithConfig(t, func(c *Config) {
c.PrimaryDatacenter = "dc1"
c.ACLsEnabled = true
c.ACLMasterToken = "root"
c.ACLInitialManagementToken = "root"
c.ACLResolverSettings.ACLDefaultPolicy = "deny"
})
defer os.RemoveAll(dir1)
@ -1167,6 +1167,31 @@ func TestPreparedQuery_List(t *testing.T) {
}
}
// Same for a token without access to the query.
{
token := createTokenWithPolicyName(t, codec, "deny-queries", `
query_prefix "" {
policy = "deny"
}
`, "root")
req := &structs.DCSpecificRequest{
Datacenter: "dc1",
QueryOptions: structs.QueryOptions{Token: token},
}
var resp structs.IndexedPreparedQueries
if err := msgpackrpc.CallWithCodec(codec, "PreparedQuery.List", req, &resp); err != nil {
t.Fatalf("err: %v", err)
}
if len(resp.Queries) != 0 {
t.Fatalf("bad: %v", resp)
}
if !resp.QueryMeta.ResultsFilteredByACLs {
t.Fatal("ResultsFilteredByACLs should be true")
}
}
// But a management token should work, and be able to see the captured
// token.
query.Query.Token = "le-token"
@ -1268,7 +1293,7 @@ func TestPreparedQuery_Explain(t *testing.T) {
dir1, s1 := testServerWithConfig(t, func(c *Config) {
c.PrimaryDatacenter = "dc1"
c.ACLsEnabled = true
c.ACLMasterToken = "root"
c.ACLInitialManagementToken = "root"
c.ACLResolverSettings.ACLDefaultPolicy = "deny"
})
defer os.RemoveAll(dir1)
@ -1392,7 +1417,7 @@ func TestPreparedQuery_Execute(t *testing.T) {
dir1, s1 := testServerWithConfig(t, func(c *Config) {
c.PrimaryDatacenter = "dc1"
c.ACLsEnabled = true
c.ACLMasterToken = "root"
c.ACLInitialManagementToken = "root"
c.ACLResolverSettings.ACLDefaultPolicy = "deny"
})
defer os.RemoveAll(dir1)
@ -2124,6 +2149,7 @@ func TestPreparedQuery_Execute(t *testing.T) {
require.NoError(t, msgpackrpc.CallWithCodec(codec1, "PreparedQuery.Execute", &req, &reply))
expectNodes(t, &query, &reply, 0)
require.True(t, reply.QueryMeta.ResultsFilteredByACLs, "ResultsFilteredByACLs should be true")
})
t.Run("normal operation again with exec token", func(t *testing.T) {
@ -2246,6 +2272,20 @@ func TestPreparedQuery_Execute(t *testing.T) {
expectFailoverNodes(t, &query, &reply, 0)
})
t.Run("nodes in response from dc2 are filtered by ACL token", func(t *testing.T) {
req := structs.PreparedQueryExecuteRequest{
Datacenter: "dc1",
QueryIDOrName: query.Query.ID,
QueryOptions: structs.QueryOptions{Token: execNoNodesToken},
}
var reply structs.PreparedQueryExecuteResponse
require.NoError(t, msgpackrpc.CallWithCodec(codec1, "PreparedQuery.Execute", &req, &reply))
expectFailoverNodes(t, &query, &reply, 0)
require.True(t, reply.QueryMeta.ResultsFilteredByACLs, "ResultsFilteredByACLs should be true")
})
// Bake the exec token into the query.
query.Query.Token = execToken
require.NoError(t, msgpackrpc.CallWithCodec(codec1, "PreparedQuery.Apply", &query, &query.Query.ID))
@ -2659,7 +2699,7 @@ func TestPreparedQuery_Wrapper(t *testing.T) {
dir1, s1 := testServerWithConfig(t, func(c *Config) {
c.PrimaryDatacenter = "dc1"
c.ACLsEnabled = true
c.ACLMasterToken = "root"
c.ACLInitialManagementToken = "root"
c.ACLResolverSettings.ACLDefaultPolicy = "deny"
})
defer os.RemoveAll(dir1)
@ -2669,7 +2709,7 @@ func TestPreparedQuery_Wrapper(t *testing.T) {
c.Datacenter = "dc2"
c.PrimaryDatacenter = "dc1"
c.ACLsEnabled = true
c.ACLMasterToken = "root"
c.ACLInitialManagementToken = "root"
c.ACLResolverSettings.ACLDefaultPolicy = "deny"
})
defer os.RemoveAll(dir2)

View File

@ -882,7 +882,7 @@ func TestRPC_LocalTokenStrippedOnForward(t *testing.T) {
c.PrimaryDatacenter = "dc1"
c.ACLsEnabled = true
c.ACLResolverSettings.ACLDefaultPolicy = "deny"
c.ACLMasterToken = "root"
c.ACLInitialManagementToken = "root"
})
defer os.RemoveAll(dir1)
defer s1.Shutdown()
@ -1010,7 +1010,7 @@ func TestRPC_LocalTokenStrippedOnForward_GRPC(t *testing.T) {
c.PrimaryDatacenter = "dc1"
c.ACLsEnabled = true
c.ACLResolverSettings.ACLDefaultPolicy = "deny"
c.ACLMasterToken = "root"
c.ACLInitialManagementToken = "root"
c.RPCConfig.EnableStreaming = true
})
s1.tokens.UpdateAgentToken("root", tokenStore.TokenSourceConfig)

View File

@ -18,6 +18,7 @@ import (
"time"
"github.com/hashicorp/go-version"
"go.etcd.io/bbolt"
"github.com/armon/go-metrics"
connlimit "github.com/hashicorp/go-connlimit"
@ -25,7 +26,7 @@ import (
"github.com/hashicorp/go-memdb"
"github.com/hashicorp/raft"
autopilot "github.com/hashicorp/raft-autopilot"
raftboltdb "github.com/hashicorp/raft-boltdb"
raftboltdb "github.com/hashicorp/raft-boltdb/v2"
"github.com/hashicorp/serf/serf"
"golang.org/x/time/rate"
"google.golang.org/grpc"
@ -188,6 +189,12 @@ type Server struct {
// serf cluster that spans datacenters
eventChWAN chan serf.Event
// wanMembershipNotifyCh is used to receive notifications that the the
// serfWAN wan pool may have changed.
//
// If this is nil, notification is skipped.
wanMembershipNotifyCh chan struct{}
// fsm is the state machine used with Raft to provide
// strong consistency.
fsm *fsm.FSM
@ -265,6 +272,7 @@ type Server struct {
// serfWAN is the Serf cluster maintained between DC's
// which SHOULD only consist of Consul servers
serfWAN *serf.Serf
serfWANConfig *serf.Config
memberlistTransportWAN wanfed.IngestionAwareTransport
gatewayLocator *GatewayLocator
@ -492,7 +500,7 @@ func NewServer(config *Config, flat Deps) (*Server, error) {
// Initialize the WAN Serf if enabled
if config.SerfWANConfig != nil {
s.serfWAN, err = s.setupSerf(setupSerfOptions{
s.serfWAN, s.serfWANConfig, err = s.setupSerf(setupSerfOptions{
Config: config.SerfWANConfig,
EventCh: s.eventChWAN,
SnapshotPath: serfWANSnapshot,
@ -547,7 +555,7 @@ func NewServer(config *Config, flat Deps) (*Server, error) {
s.Shutdown()
return nil, fmt.Errorf("Failed to add WAN serf route: %v", err)
}
go router.HandleSerfEvents(s.logger, s.router, types.AreaWAN, s.serfWAN.ShutdownCh(), s.eventChWAN)
go router.HandleSerfEvents(s.logger, s.router, types.AreaWAN, s.serfWAN.ShutdownCh(), s.eventChWAN, s.wanMembershipNotifyCh)
// Fire up the LAN <-> WAN join flooder.
addrFn := func(s *metadata.Server) (string, error) {
@ -646,7 +654,7 @@ func newGRPCHandlerFromConfig(deps Deps, config *Config, s *Server) connHandler
s.registerEnterpriseGRPCServices(deps, srv)
}
return agentgrpc.NewHandler(config.RPCAddr, register)
return agentgrpc.NewHandler(deps.Logger, config.RPCAddr, register)
}
func (s *Server) connectCARootsMonitor(ctx context.Context) {
@ -729,13 +737,21 @@ func (s *Server) setupRaft() error {
}
// Create the backend raft store for logs and stable storage.
store, err := raftboltdb.NewBoltStore(filepath.Join(path, "raft.db"))
store, err := raftboltdb.New(raftboltdb.Options{
BoltOptions: &bbolt.Options{
NoFreelistSync: s.config.RaftBoltDBConfig.NoFreelistSync,
},
Path: filepath.Join(path, "raft.db"),
})
if err != nil {
return err
}
s.raftStore = store
stable = store
// start publishing boltdb metrics
go store.RunMetrics(&lib.StopChannelContext{StopCh: s.shutdownCh}, 0)
// Wrap the store in a LogCache to improve performance.
cacheStore, err := raft.NewLogCache(raftLogCacheSize, store)
if err != nil {
@ -1115,6 +1131,11 @@ func (s *Server) JoinWAN(addrs []string) (int, error) {
if s.serfWAN == nil {
return 0, ErrWANFederationDisabled
}
if err := s.enterpriseValidateJoinWAN(); err != nil {
return 0, err
}
return s.serfWAN.Join(addrs, true)
}

View File

@ -19,6 +19,10 @@ import (
func (s *Server) registerEnterpriseGRPCServices(deps Deps, srv *grpc.Server) {}
func (s *Server) enterpriseValidateJoinWAN() error {
return nil // no-op
}
// JoinLAN is used to have Consul join the inner-DC pool The target address
// should be another node inside the DC listening on the Serf LAN address
func (s *Server) JoinLAN(addrs []string, entMeta *structs.EnterpriseMeta) (int, error) {

View File

@ -48,12 +48,18 @@ type setupSerfOptions struct {
}
// setupSerf is used to setup and initialize a Serf
func (s *Server) setupSerf(opts setupSerfOptions) (*serf.Serf, error) {
func (s *Server) setupSerf(opts setupSerfOptions) (*serf.Serf, *serf.Config, error) {
conf, err := s.setupSerfConfig(opts)
if err != nil {
return nil, err
return nil, nil, err
}
return serf.Create(conf)
cluster, err := serf.Create(conf)
if err != nil {
return nil, nil, err
}
return cluster, conf, nil
}
func (s *Server) setupSerfConfig(opts setupSerfOptions) (*serf.Config, error) {
@ -152,7 +158,9 @@ func (s *Server) setupSerfConfig(opts setupSerfOptions) (*serf.Config, error) {
conf.ProtocolVersion = protocolVersionMap[s.config.ProtocolVersion]
conf.RejoinAfterLeave = s.config.RejoinAfterLeave
if opts.WAN {
conf.Merge = &wanMergeDelegate{}
conf.Merge = &wanMergeDelegate{
localDatacenter: s.config.Datacenter,
}
} else {
conf.Merge = &lanMergeDelegate{
dc: s.config.Datacenter,

View File

@ -74,7 +74,7 @@ func testServerACLConfig(cb func(*Config)) func(*Config) {
return func(c *Config) {
c.PrimaryDatacenter = "dc1"
c.ACLsEnabled = true
c.ACLMasterToken = TestDefaultMasterToken
c.ACLInitialManagementToken = TestDefaultMasterToken
c.ACLResolverSettings.ACLDefaultPolicy = "deny"
if cb != nil {

View File

@ -151,7 +151,7 @@ func (s *Session) Apply(args *structs.SessionRequest, reply *string) error {
if args.Op == structs.SessionCreate && args.Session.TTL != "" {
// If we created a session with a TTL, reset the expiration timer
s.srv.resetSessionTimer(args.Session.ID, &args.Session)
s.srv.resetSessionTimer(&args.Session)
} else if args.Op == structs.SessionDestroy {
// If we destroyed a session, it might potentially have a TTL,
// and we need to clear the timer
@ -308,7 +308,7 @@ func (s *Session) Renew(args *structs.SessionSpecificRequest,
// Reset the session TTL timer.
reply.Sessions = structs.Sessions{session}
if err := s.srv.resetSessionTimer(args.SessionID, session); err != nil {
if err := s.srv.resetSessionTimer(session); err != nil {
s.logger.Error("Session renew failed", "error", err)
return err
}

View File

@ -157,7 +157,7 @@ func TestSession_Apply_ACLDeny(t *testing.T) {
dir1, s1 := testServerWithConfig(t, func(c *Config) {
c.PrimaryDatacenter = "dc1"
c.ACLsEnabled = true
c.ACLMasterToken = "root"
c.ACLInitialManagementToken = "root"
c.ACLResolverSettings.ACLDefaultPolicy = "deny"
})
defer os.RemoveAll(dir1)
@ -382,7 +382,7 @@ func TestSession_Get_List_NodeSessions_ACLFilter(t *testing.T) {
dir1, s1 := testServerWithConfig(t, func(c *Config) {
c.PrimaryDatacenter = "dc1"
c.ACLsEnabled = true
c.ACLMasterToken = "root"
c.ACLInitialManagementToken = "root"
c.ACLResolverSettings.ACLDefaultPolicy = "deny"
})
defer os.RemoveAll(dir1)
@ -731,7 +731,7 @@ func TestSession_Renew_ACLDeny(t *testing.T) {
dir1, s1 := testServerWithConfig(t, func(c *Config) {
c.PrimaryDatacenter = "dc1"
c.ACLsEnabled = true
c.ACLMasterToken = "root"
c.ACLInitialManagementToken = "root"
c.ACLResolverSettings.ACLDefaultPolicy = "deny"
})
defer os.RemoveAll(dir1)

View File

@ -47,13 +47,12 @@ func (s *Server) initializeSessionTimers() error {
// Scan all sessions and reset their timer
state := s.fsm.State()
// TODO(partitions): track all session timers in all partitions
_, sessions, err := state.SessionList(nil, structs.WildcardEnterpriseMetaInDefaultPartition())
_, sessions, err := state.SessionListAll(nil)
if err != nil {
return err
}
for _, session := range sessions {
if err := s.resetSessionTimer(session.ID, session); err != nil {
if err := s.resetSessionTimer(session); err != nil {
return err
}
}
@ -63,20 +62,7 @@ func (s *Server) initializeSessionTimers() error {
// resetSessionTimer is used to renew the TTL of a session.
// This can be used for new sessions and existing ones. A session
// will be faulted in if not given.
func (s *Server) resetSessionTimer(id string, session *structs.Session) error {
// Fault the session in if not given
if session == nil {
state := s.fsm.State()
_, s, err := state.SessionGet(nil, id, nil)
if err != nil {
return err
}
if s == nil {
return fmt.Errorf("Session '%s' not found", id)
}
session = s
}
func (s *Server) resetSessionTimer(session *structs.Session) error {
// Bail if the session has no TTL, fast-path some common inputs
switch session.TTL {
case "", "0", "0s", "0m", "0h":

View File

@ -11,7 +11,7 @@ import (
"github.com/hashicorp/consul/sdk/testutil/retry"
"github.com/hashicorp/consul/testrpc"
"github.com/hashicorp/go-uuid"
"github.com/hashicorp/net-rpc-msgpackrpc"
msgpackrpc "github.com/hashicorp/net-rpc-msgpackrpc"
)
func generateUUID() (ret string) {
@ -59,50 +59,6 @@ func TestInitializeSessionTimers(t *testing.T) {
}
}
func TestResetSessionTimer_Fault(t *testing.T) {
if testing.Short() {
t.Skip("too slow for testing.Short")
}
t.Parallel()
dir1, s1 := testServer(t)
defer os.RemoveAll(dir1)
defer s1.Shutdown()
testrpc.WaitForLeader(t, s1.RPC, "dc1")
// Should not exist
err := s1.resetSessionTimer(generateUUID(), nil)
if err == nil || !strings.Contains(err.Error(), "not found") {
t.Fatalf("err: %v", err)
}
// Create a session
state := s1.fsm.State()
if err := state.EnsureNode(1, &structs.Node{Node: "foo", Address: "127.0.0.1"}); err != nil {
t.Fatalf("err: %s", err)
}
session := &structs.Session{
ID: generateUUID(),
Node: "foo",
TTL: "10s",
}
if err := state.SessionCreate(100, session); err != nil {
t.Fatalf("err: %v", err)
}
// Reset the session timer
err = s1.resetSessionTimer(session.ID, nil)
if err != nil {
t.Fatalf("err: %v", err)
}
// Check that we have a timer
if s1.sessionTimers.Get(session.ID) == nil {
t.Fatalf("missing session timer")
}
}
func TestResetSessionTimer_NoTTL(t *testing.T) {
if testing.Short() {
t.Skip("too slow for testing.Short")
@ -130,7 +86,7 @@ func TestResetSessionTimer_NoTTL(t *testing.T) {
}
// Reset the session timer
err := s1.resetSessionTimer(session.ID, session)
err := s1.resetSessionTimer(session)
if err != nil {
t.Fatalf("err: %v", err)
}
@ -155,7 +111,7 @@ func TestResetSessionTimer_InvalidTTL(t *testing.T) {
}
// Reset the session timer
err := s1.resetSessionTimer(session.ID, session)
err := s1.resetSessionTimer(session)
if err == nil || !strings.Contains(err.Error(), "Invalid Session TTL") {
t.Fatalf("err: %v", err)
}

View File

@ -271,7 +271,7 @@ func TestSnapshot_ACLDeny(t *testing.T) {
dir1, s1 := testServerWithConfig(t, func(c *Config) {
c.PrimaryDatacenter = "dc1"
c.ACLsEnabled = true
c.ACLMasterToken = "root"
c.ACLInitialManagementToken = "root"
c.ACLResolverSettings.ACLDefaultPolicy = "deny"
})
defer os.RemoveAll(dir1)

View File

@ -741,6 +741,9 @@ func ensureServiceTxn(tx WriteTxn, idx uint64, node string, preserveIndexes bool
if err = checkGatewayWildcardsAndUpdate(tx, idx, svc); err != nil {
return fmt.Errorf("failed updating gateway mapping: %s", err)
}
if err := upsertKindServiceName(tx, idx, svc.Kind, svc.CompoundServiceName()); err != nil {
return fmt.Errorf("failed to persist service name: %v", err)
}
// Update upstream/downstream mappings if it's a connect service
if svc.Kind == structs.ServiceKindConnectProxy || svc.Connect.Native {
@ -965,16 +968,14 @@ func (s *Store) Services(ws memdb.WatchSet, entMeta *structs.EnterpriseMeta) (ui
return idx, results, nil
}
func (s *Store) ServiceList(ws memdb.WatchSet,
include func(svc *structs.ServiceNode) bool, entMeta *structs.EnterpriseMeta) (uint64, structs.ServiceList, error) {
func (s *Store) ServiceList(ws memdb.WatchSet, entMeta *structs.EnterpriseMeta) (uint64, structs.ServiceList, error) {
tx := s.db.Txn(false)
defer tx.Abort()
return serviceListTxn(tx, ws, include, entMeta)
return serviceListTxn(tx, ws, entMeta)
}
func serviceListTxn(tx ReadTxn, ws memdb.WatchSet,
include func(svc *structs.ServiceNode) bool, entMeta *structs.EnterpriseMeta) (uint64, structs.ServiceList, error) {
func serviceListTxn(tx ReadTxn, ws memdb.WatchSet, entMeta *structs.EnterpriseMeta) (uint64, structs.ServiceList, error) {
idx := catalogServicesMaxIndex(tx, entMeta)
services, err := tx.Get(tableServices, indexID+"_prefix", entMeta)
@ -986,11 +987,7 @@ func serviceListTxn(tx ReadTxn, ws memdb.WatchSet,
unique := make(map[structs.ServiceName]struct{})
for service := services.Next(); service != nil; service = services.Next() {
svc := service.(*structs.ServiceNode)
// TODO (freddy) This is a hack to exclude certain kinds.
// Need a new index to query by kind and namespace, have to coordinate with consul foundations first
if include == nil || include(svc) {
unique[svc.CompoundServiceName()] = struct{}{}
}
unique[svc.CompoundServiceName()] = struct{}{}
}
results := make(structs.ServiceList, 0, len(unique))
@ -1691,6 +1688,9 @@ func (s *Store) deleteServiceTxn(tx WriteTxn, idx uint64, nodeName, serviceID st
if err := freeServiceVirtualIP(tx, svc.ServiceName, entMeta); err != nil {
return fmt.Errorf("failed to clean up virtual IP for %q: %v", name.String(), err)
}
if err := cleanupKindServiceName(tx, idx, svc.CompoundServiceName(), svc.ServiceKind); err != nil {
return fmt.Errorf("failed to persist service name: %v", err)
}
}
} else {
return fmt.Errorf("Could not find any service %s: %s", svc.ServiceName, err)
@ -2526,6 +2526,30 @@ func (s *Store) VirtualIPForService(sn structs.ServiceName) (string, error) {
return result.String(), nil
}
func (s *Store) ServiceNamesOfKind(ws memdb.WatchSet, kind structs.ServiceKind) (uint64, []*KindServiceName, error) {
tx := s.db.Txn(false)
defer tx.Abort()
return serviceNamesOfKindTxn(tx, ws, kind)
}
func serviceNamesOfKindTxn(tx ReadTxn, ws memdb.WatchSet, kind structs.ServiceKind) (uint64, []*KindServiceName, error) {
var names []*KindServiceName
iter, err := tx.Get(tableKindServiceNames, indexKindOnly, kind)
if err != nil {
return 0, nil, err
}
ws.Add(iter.WatchCh())
idx := kindServiceNamesMaxIndex(tx, ws, kind)
for name := iter.Next(); name != nil; name = iter.Next() {
ksn := name.(*KindServiceName)
names = append(names, ksn)
}
return idx, names, nil
}
// parseCheckServiceNodes is used to parse through a given set of services,
// and query for an associated node and a set of checks. This is the inner
// method used to return a rich set of results from a more simple query.
@ -3862,3 +3886,44 @@ func truncateGatewayServiceTopologyMappings(tx WriteTxn, idx uint64, gateway str
return nil
}
func upsertKindServiceName(tx WriteTxn, idx uint64, kind structs.ServiceKind, name structs.ServiceName) error {
q := KindServiceNameQuery{Name: name.Name, Kind: kind, EnterpriseMeta: name.EnterpriseMeta}
existing, err := tx.First(tableKindServiceNames, indexID, q)
if err != nil {
return err
}
// Service name is already known. Nothing to do.
if existing != nil {
return nil
}
ksn := KindServiceName{
Kind: kind,
Service: name,
RaftIndex: structs.RaftIndex{
CreateIndex: idx,
ModifyIndex: idx,
},
}
if err := tx.Insert(tableKindServiceNames, &ksn); err != nil {
return fmt.Errorf("failed inserting %s/%s into %s: %s", kind, name.String(), tableKindServiceNames, err)
}
if err := indexUpdateMaxTxn(tx, idx, kindServiceNameIndexName(kind)); err != nil {
return fmt.Errorf("failed updating %s index: %v", tableKindServiceNames, err)
}
return nil
}
func cleanupKindServiceName(tx WriteTxn, idx uint64, name structs.ServiceName, kind structs.ServiceKind) error {
q := KindServiceNameQuery{Name: name.Name, Kind: kind, EnterpriseMeta: name.EnterpriseMeta}
if _, err := tx.DeleteAll(tableKindServiceNames, indexID, q); err != nil {
return fmt.Errorf("failed to delete %s from %s: %s", name, tableKindServiceNames, err)
}
if err := indexUpdateMaxTxn(tx, idx, kindServiceNameIndexName(kind)); err != nil {
return fmt.Errorf("failed updating %s index: %v", tableKindServiceNames, err)
}
return nil
}

View File

@ -5,6 +5,7 @@ package state
import (
"fmt"
"strings"
memdb "github.com/hashicorp/go-memdb"
@ -18,13 +19,7 @@ func serviceIndexName(name string, _ *structs.EnterpriseMeta) string {
}
func serviceKindIndexName(kind structs.ServiceKind, _ *structs.EnterpriseMeta) string {
switch kind {
case structs.ServiceKindTypical:
// needs a special case here
return "service_kind.typical"
default:
return "service_kind." + string(kind)
}
return "service_kind." + kind.Normalized()
}
func catalogUpdateNodesIndexes(tx WriteTxn, idx uint64, entMeta *structs.EnterpriseMeta) error {
@ -192,3 +187,22 @@ func validateRegisterRequestTxn(_ ReadTxn, _ *structs.RegisterRequest, _ bool) (
func (s *Store) ValidateRegisterRequest(_ *structs.RegisterRequest) (*structs.EnterpriseMeta, error) {
return nil, nil
}
func indexFromKindServiceName(arg interface{}) ([]byte, error) {
var b indexBuilder
switch n := arg.(type) {
case KindServiceNameQuery:
b.String(strings.ToLower(string(n.Kind)))
b.String(strings.ToLower(n.Name))
return b.Bytes(), nil
case *KindServiceName:
b.String(strings.ToLower(string(n.Kind)))
b.String(strings.ToLower(n.Service.Name))
return b.Bytes(), nil
default:
return nil, fmt.Errorf("type must be KindServiceNameQuery or *KindServiceName: %T", arg)
}
}

View File

@ -412,3 +412,40 @@ func testIndexerTableServiceVirtualIPs() map[string]indexerTestCase {
},
}
}
func testIndexerTableKindServiceNames() map[string]indexerTestCase {
obj := &KindServiceName{
Service: structs.ServiceName{
Name: "web-sidecar-proxy",
},
Kind: structs.ServiceKindConnectProxy,
}
return map[string]indexerTestCase{
indexID: {
read: indexValue{
source: &KindServiceName{
Service: structs.ServiceName{
Name: "web-sidecar-proxy",
},
Kind: structs.ServiceKindConnectProxy,
},
expected: []byte("connect-proxy\x00web-sidecar-proxy\x00"),
},
write: indexValue{
source: obj,
expected: []byte("connect-proxy\x00web-sidecar-proxy\x00"),
},
},
indexKind: {
read: indexValue{
source: structs.ServiceKindConnectProxy,
expected: []byte("connect-proxy\x00"),
},
write: indexValue{
source: obj,
expected: []byte("connect-proxy\x00"),
},
},
}
}

View File

@ -19,6 +19,7 @@ const (
tableMeshTopology = "mesh-topology"
tableServiceVirtualIPs = "service-virtual-ips"
tableFreeVirtualIPs = "free-virtual-ips"
tableKindServiceNames = "kind-service-names"
indexID = "id"
indexService = "service"
@ -661,3 +662,80 @@ func freeVirtualIPTableSchema() *memdb.TableSchema {
},
}
}
type KindServiceName struct {
Kind structs.ServiceKind
Service structs.ServiceName
structs.RaftIndex
}
func kindServiceNameTableSchema() *memdb.TableSchema {
return &memdb.TableSchema{
Name: tableKindServiceNames,
Indexes: map[string]*memdb.IndexSchema{
indexID: {
Name: indexID,
AllowMissing: false,
Unique: true,
Indexer: indexerSingle{
readIndex: indexFromKindServiceName,
writeIndex: indexFromKindServiceName,
},
},
indexKindOnly: {
Name: indexKindOnly,
AllowMissing: false,
Unique: false,
Indexer: indexerSingle{
readIndex: indexFromKindServiceNameKindOnly,
writeIndex: indexFromKindServiceNameKindOnly,
},
},
},
}
}
// KindServiceNameQuery is used to lookup service names by kind or enterprise meta.
type KindServiceNameQuery struct {
Kind structs.ServiceKind
Name string
structs.EnterpriseMeta
}
// NamespaceOrDefault exists because structs.EnterpriseMeta uses a pointer
// receiver for this method. Remove once that is fixed.
func (q KindServiceNameQuery) NamespaceOrDefault() string {
return q.EnterpriseMeta.NamespaceOrDefault()
}
// PartitionOrDefault exists because structs.EnterpriseMeta uses a pointer
// receiver for this method. Remove once that is fixed.
func (q KindServiceNameQuery) PartitionOrDefault() string {
return q.EnterpriseMeta.PartitionOrDefault()
}
func indexFromKindServiceNameKindOnly(raw interface{}) ([]byte, error) {
switch x := raw.(type) {
case *KindServiceName:
var b indexBuilder
b.String(strings.ToLower(string(x.Kind)))
return b.Bytes(), nil
case structs.ServiceKind:
var b indexBuilder
b.String(strings.ToLower(string(x)))
return b.Bytes(), nil
default:
return nil, fmt.Errorf("type must be *KindServiceName or structs.ServiceKind: %T", raw)
}
}
func kindServiceNamesMaxIndex(tx ReadTxn, ws memdb.WatchSet, kind structs.ServiceKind) uint64 {
return maxIndexWatchTxn(tx, ws, kindServiceNameIndexName(kind))
}
func kindServiceNameIndexName(kind structs.ServiceKind) string {
return "kind_service_names." + kind.Normalized()
}

View File

@ -7656,6 +7656,143 @@ func TestProtocolForIngressGateway(t *testing.T) {
}
}
func TestStateStore_EnsureService_ServiceNames(t *testing.T) {
s := testStateStore(t)
// Create the service registration.
entMeta := structs.DefaultEnterpriseMetaInDefaultPartition()
services := []structs.NodeService{
{
Kind: structs.ServiceKindIngressGateway,
ID: "ingress-gateway",
Service: "ingress-gateway",
Address: "2.2.2.2",
Port: 2222,
EnterpriseMeta: *entMeta,
},
{
Kind: structs.ServiceKindMeshGateway,
ID: "mesh-gateway",
Service: "mesh-gateway",
Address: "4.4.4.4",
Port: 4444,
EnterpriseMeta: *entMeta,
},
{
Kind: structs.ServiceKindConnectProxy,
ID: "connect-proxy",
Service: "connect-proxy",
Address: "1.1.1.1",
Port: 1111,
Proxy: structs.ConnectProxyConfig{DestinationServiceName: "foo"},
EnterpriseMeta: *entMeta,
},
{
Kind: structs.ServiceKindTerminatingGateway,
ID: "terminating-gateway",
Service: "terminating-gateway",
Address: "3.3.3.3",
Port: 3333,
EnterpriseMeta: *entMeta,
},
{
Kind: structs.ServiceKindTypical,
ID: "web",
Service: "web",
Address: "5.5.5.5",
Port: 5555,
EnterpriseMeta: *entMeta,
},
}
var idx uint64
testRegisterNode(t, s, idx, "node1")
for _, svc := range services {
idx++
require.NoError(t, s.EnsureService(idx, "node1", &svc))
// Ensure the service name was stored for all of them under the appropriate kind
gotIdx, gotNames, err := s.ServiceNamesOfKind(nil, svc.Kind)
require.NoError(t, err)
require.Equal(t, idx, gotIdx)
require.Len(t, gotNames, 1)
require.Equal(t, svc.CompoundServiceName(), gotNames[0].Service)
require.Equal(t, svc.Kind, gotNames[0].Kind)
}
// Register another ingress gateway and there should be two names under the kind index
newIngress := structs.NodeService{
Kind: structs.ServiceKindIngressGateway,
ID: "new-ingress-gateway",
Service: "new-ingress-gateway",
Address: "6.6.6.6",
Port: 6666,
EnterpriseMeta: *entMeta,
}
idx++
require.NoError(t, s.EnsureService(idx, "node1", &newIngress))
gotIdx, got, err := s.ServiceNamesOfKind(nil, structs.ServiceKindIngressGateway)
require.NoError(t, err)
require.Equal(t, idx, gotIdx)
expect := []*KindServiceName{
{
Kind: structs.ServiceKindIngressGateway,
Service: structs.NewServiceName("ingress-gateway", nil),
RaftIndex: structs.RaftIndex{
CreateIndex: 1,
ModifyIndex: 1,
},
},
{
Kind: structs.ServiceKindIngressGateway,
Service: structs.NewServiceName("new-ingress-gateway", nil),
RaftIndex: structs.RaftIndex{
CreateIndex: idx,
ModifyIndex: idx,
},
},
}
require.Equal(t, expect, got)
// Deregister an ingress gateway and the index should not slide back
idx++
require.NoError(t, s.DeleteService(idx, "node1", "new-ingress-gateway", entMeta))
gotIdx, got, err = s.ServiceNamesOfKind(nil, structs.ServiceKindIngressGateway)
require.NoError(t, err)
require.Equal(t, idx, gotIdx)
require.Equal(t, expect[:1], got)
// Registering another instance of a known service should not bump the kind index
newMGW := structs.NodeService{
Kind: structs.ServiceKindMeshGateway,
ID: "mesh-gateway-1",
Service: "mesh-gateway",
Address: "7.7.7.7",
Port: 7777,
EnterpriseMeta: *entMeta,
}
idx++
require.NoError(t, s.EnsureService(idx, "node1", &newMGW))
gotIdx, _, err = s.ServiceNamesOfKind(nil, structs.ServiceKindMeshGateway)
require.NoError(t, err)
require.Equal(t, uint64(2), gotIdx)
// Deregister the single typical service and the service name should also be dropped
idx++
require.NoError(t, s.DeleteService(idx, "node1", "web", entMeta))
gotIdx, got, err = s.ServiceNamesOfKind(nil, structs.ServiceKindTypical)
require.NoError(t, err)
require.Equal(t, idx, gotIdx)
require.Empty(t, got)
}
func runStep(t *testing.T, name string, fn func(t *testing.T)) {
t.Helper()
if !t.Run(name, fn) {

View File

@ -395,7 +395,7 @@ func validateProposedConfigEntryInGraph(
}
case structs.ServiceIntentions:
case structs.MeshConfig:
case structs.PartitionExports:
case structs.ExportedServices:
default:
return fmt.Errorf("unhandled kind %q during validation of %q", kindName.Kind, kindName.Name)
}
@ -880,24 +880,21 @@ func readDiscoveryChainConfigEntriesTxn(
sid := structs.NewServiceID(serviceName, entMeta)
// Grab the proxy defaults if they exist.
idx, proxy, err := getProxyConfigEntryTxn(tx, ws, structs.ProxyConfigGlobal, overrides, entMeta)
if err != nil {
return 0, nil, err
} else if proxy != nil {
res.GlobalProxy = proxy
}
// At every step we'll need service defaults.
// At every step we'll need service and proxy defaults.
todoDefaults[sid] = struct{}{}
var maxIdx uint64
// first fetch the router, of which we only collect 1 per chain eval
_, router, err := getRouterConfigEntryTxn(tx, ws, serviceName, overrides, entMeta)
idx, router, err := getRouterConfigEntryTxn(tx, ws, serviceName, overrides, entMeta)
if err != nil {
return 0, nil, err
} else if router != nil {
res.Routers[sid] = router
}
if idx > maxIdx {
maxIdx = idx
}
if router != nil {
for _, svc := range router.ListRelatedServices() {
@ -922,10 +919,13 @@ func readDiscoveryChainConfigEntriesTxn(
// Yes, even for splitters.
todoDefaults[splitID] = struct{}{}
_, splitter, err := getSplitterConfigEntryTxn(tx, ws, splitID.ID, overrides, &splitID.EnterpriseMeta)
idx, splitter, err := getSplitterConfigEntryTxn(tx, ws, splitID.ID, overrides, &splitID.EnterpriseMeta)
if err != nil {
return 0, nil, err
}
if idx > maxIdx {
maxIdx = idx
}
if splitter == nil {
res.Splitters[splitID] = nil
@ -959,10 +959,13 @@ func readDiscoveryChainConfigEntriesTxn(
// And resolvers, too.
todoDefaults[resolverID] = struct{}{}
_, resolver, err := getResolverConfigEntryTxn(tx, ws, resolverID.ID, overrides, &resolverID.EnterpriseMeta)
idx, resolver, err := getResolverConfigEntryTxn(tx, ws, resolverID.ID, overrides, &resolverID.EnterpriseMeta)
if err != nil {
return 0, nil, err
}
if idx > maxIdx {
maxIdx = idx
}
if resolver == nil {
res.Resolvers[resolverID] = nil
@ -987,16 +990,31 @@ func readDiscoveryChainConfigEntriesTxn(
continue // already fetched
}
_, entry, err := getServiceConfigEntryTxn(tx, ws, svcID.ID, overrides, &svcID.EnterpriseMeta)
if _, ok := res.ProxyDefaults[svcID.PartitionOrDefault()]; !ok {
idx, proxy, err := getProxyConfigEntryTxn(tx, ws, structs.ProxyConfigGlobal, overrides, &svcID.EnterpriseMeta)
if err != nil {
return 0, nil, err
}
if idx > maxIdx {
maxIdx = idx
}
if proxy != nil {
res.ProxyDefaults[proxy.PartitionOrDefault()] = proxy
}
}
idx, entry, err := getServiceConfigEntryTxn(tx, ws, svcID.ID, overrides, &svcID.EnterpriseMeta)
if err != nil {
return 0, nil, err
}
if idx > maxIdx {
maxIdx = idx
}
if entry == nil {
res.Services[svcID] = nil
continue
}
res.Services[svcID] = entry
}
@ -1022,7 +1040,7 @@ func readDiscoveryChainConfigEntriesTxn(
}
}
return idx, res, nil
return maxIdx, res, nil
}
// anyKey returns any key from the provided map if any exist. Useful for using

View File

@ -1347,6 +1347,13 @@ func entrySetToKindNames(entrySet *structs.DiscoveryChainConfigEntries) []Config
&entry.EnterpriseMeta,
))
}
for _, entry := range entrySet.ProxyDefaults {
out = append(out, NewConfigEntryKindName(
entry.Kind,
entry.Name,
&entry.EnterpriseMeta,
))
}
return out
}

View File

@ -995,36 +995,29 @@ func (s *Store) intentionTopologyTxn(tx ReadTxn, ws memdb.WatchSet,
maxIdx = index
}
// Check for a wildcard intention (* -> *) since it overrides the default decision from ACLs
if len(intentions) > 0 {
// Intentions with wildcard source and destination have the lowest precedence, so they are last in the list
ixn := intentions[len(intentions)-1]
if ixn.HasWildcardSource() && ixn.HasWildcardDestination() {
defaultDecision = acl.Allow
if ixn.Action == structs.IntentionActionDeny {
defaultDecision = acl.Deny
}
}
}
index, allServices, err := serviceListTxn(tx, ws, func(svc *structs.ServiceNode) bool {
// Only include ingress gateways as downstreams, since they cannot receive service mesh traffic
// TODO(freddy): One remaining issue is that this includes non-Connect services (typical services without a proxy)
// Ideally those should be excluded as well, since they can't be upstreams/downstreams without a proxy.
// Maybe start tracking services represented by proxies? (both sidecar and ingress)
if svc.ServiceKind == structs.ServiceKindTypical || (svc.ServiceKind == structs.ServiceKindIngressGateway && downstreams) {
return true
}
return false
}, target.WithWildcardNamespace())
// TODO(tproxy): One remaining improvement is that this includes non-Connect services (typical services without a proxy)
// Ideally those should be excluded as well, since they can't be upstreams/downstreams without a proxy.
// Maybe narrow serviceNamesOfKindTxn to services represented by proxies? (ingress, sidecar-proxy, terminating)
index, services, err := serviceNamesOfKindTxn(tx, ws, structs.ServiceKindTypical)
if err != nil {
return index, nil, fmt.Errorf("failed to fetch catalog service list: %v", err)
return index, nil, fmt.Errorf("failed to list ingress service names: %v", err)
}
if index > maxIdx {
maxIdx = index
}
if downstreams {
// Ingress gateways can only ever be downstreams, since mesh services don't dial them.
index, ingress, err := serviceNamesOfKindTxn(tx, ws, structs.ServiceKindIngressGateway)
if err != nil {
return index, nil, fmt.Errorf("failed to list ingress service names: %v", err)
}
if index > maxIdx {
maxIdx = index
}
services = append(services, ingress...)
}
// When checking authorization to upstreams, the match type for the decision is `destination` because we are deciding
// if upstream candidates are covered by intentions that have the target service as a source.
// The reverse is true for downstreams.
@ -1032,11 +1025,13 @@ func (s *Store) intentionTopologyTxn(tx ReadTxn, ws memdb.WatchSet,
if downstreams {
decisionMatchType = structs.IntentionMatchSource
}
result := make([]ServiceWithDecision, 0, len(allServices))
for _, candidate := range allServices {
result := make([]ServiceWithDecision, 0, len(services))
for _, svc := range services {
candidate := svc.Service
if candidate.Name == structs.ConsulServiceName {
continue
}
opts := IntentionDecisionOpts{
Target: candidate.Name,
Namespace: candidate.NamespaceOrDefault(),

View File

@ -40,6 +40,7 @@ func newDBSchema() *memdb.DBSchema {
tombstonesTableSchema,
usageTableSchema,
freeVirtualIPTableSchema,
kindServiceNameTableSchema,
)
withEnterpriseSchema(db)
return db

View File

@ -50,6 +50,7 @@ func TestNewDBSchema_Indexers(t *testing.T) {
tableMeshTopology: testIndexerTableMeshTopology,
tableGatewayServices: testIndexerTableGatewayServices,
tableServiceVirtualIPs: testIndexerTableServiceVirtualIPs,
tableKindServiceNames: testIndexerTableKindServiceNames,
// KV
tableKVs: testIndexerTableKVs,
tableTombstones: testIndexerTableTombstones,

View File

@ -187,3 +187,7 @@ func (s *Store) SessionList(ws memdb.WatchSet, entMeta *structs.EnterpriseMeta)
func maxIndexTxnSessions(tx *memdb.Txn, _ *structs.EnterpriseMeta) uint64 {
return maxIndexTxn(tx, tableSessions)
}
func (s *Store) SessionListAll(ws memdb.WatchSet) (uint64, structs.Sessions, error) {
return s.SessionList(ws, nil)
}

View File

@ -319,7 +319,7 @@ func TestTxn_Apply_ACLDeny(t *testing.T) {
dir1, s1 := testServerWithConfig(t, func(c *Config) {
c.PrimaryDatacenter = "dc1"
c.ACLsEnabled = true
c.ACLMasterToken = "root"
c.ACLInitialManagementToken = "root"
c.ACLResolverSettings.ACLDefaultPolicy = "deny"
})
defer os.RemoveAll(dir1)
@ -838,7 +838,7 @@ func TestTxn_Read_ACLDeny(t *testing.T) {
dir1, s1 := testServerWithConfig(t, func(c *Config) {
c.PrimaryDatacenter = "dc1"
c.ACLsEnabled = true
c.ACLMasterToken = "root"
c.ACLInitialManagementToken = "root"
c.ACLResolverSettings.ACLDefaultPolicy = "deny"
})
defer os.RemoveAll(dir1)

View File

@ -178,12 +178,12 @@ func TestUsageReporter_emitNodeUsage_OSS(t *testing.T) {
{Name: "kind", Value: "terminating-gateway"},
},
},
"consul.usage.test.consul.state.config_entries;datacenter=dc1;kind=partition-exports": {
"consul.usage.test.consul.state.config_entries;datacenter=dc1;kind=exported-services": {
Name: "consul.usage.test.consul.state.config_entries",
Value: 0,
Labels: []metrics.Label{
{Name: "datacenter", Value: "dc1"},
{Name: "kind", Value: "partition-exports"},
{Name: "kind", Value: "exported-services"},
},
},
},
@ -363,12 +363,12 @@ func TestUsageReporter_emitNodeUsage_OSS(t *testing.T) {
{Name: "kind", Value: "terminating-gateway"},
},
},
"consul.usage.test.consul.state.config_entries;datacenter=dc1;kind=partition-exports": {
"consul.usage.test.consul.state.config_entries;datacenter=dc1;kind=exported-services": {
Name: "consul.usage.test.consul.state.config_entries",
Value: 0,
Labels: []metrics.Label{
{Name: "datacenter", Value: "dc1"},
{Name: "kind", Value: "partition-exports"},
{Name: "kind", Value: "exported-services"},
},
},
},
@ -576,12 +576,12 @@ func TestUsageReporter_emitServiceUsage_OSS(t *testing.T) {
{Name: "kind", Value: "terminating-gateway"},
},
},
"consul.usage.test.consul.state.config_entries;datacenter=dc1;kind=partition-exports": {
"consul.usage.test.consul.state.config_entries;datacenter=dc1;kind=exported-services": {
Name: "consul.usage.test.consul.state.config_entries",
Value: 0,
Labels: []metrics.Label{
{Name: "datacenter", Value: "dc1"},
{Name: "kind", Value: "partition-exports"},
{Name: "kind", Value: "exported-services"},
},
},
},
@ -803,12 +803,12 @@ func TestUsageReporter_emitServiceUsage_OSS(t *testing.T) {
{Name: "kind", Value: "terminating-gateway"},
},
},
"consul.usage.test.consul.state.config_entries;datacenter=dc1;kind=partition-exports": {
"consul.usage.test.consul.state.config_entries;datacenter=dc1;kind=exported-services": {
Name: "consul.usage.test.consul.state.config_entries",
Value: 0,
Labels: []metrics.Label{
{Name: "datacenter", Value: "dc1"},
{Name: "kind", Value: "partition-exports"},
{Name: "kind", Value: "exported-services"},
},
},
},
@ -1007,12 +1007,12 @@ func TestUsageReporter_emitKVUsage_OSS(t *testing.T) {
{Name: "kind", Value: "terminating-gateway"},
},
},
"consul.usage.test.consul.state.config_entries;datacenter=dc1;kind=partition-exports": {
"consul.usage.test.consul.state.config_entries;datacenter=dc1;kind=exported-services": {
Name: "consul.usage.test.consul.state.config_entries",
Value: 0,
Labels: []metrics.Label{
{Name: "datacenter", Value: "dc1"},
{Name: "kind", Value: "partition-exports"},
{Name: "kind", Value: "exported-services"},
},
},
},
@ -1201,12 +1201,12 @@ func TestUsageReporter_emitKVUsage_OSS(t *testing.T) {
{Name: "kind", Value: "terminating-gateway"},
},
},
"consul.usage.test.consul.state.config_entries;datacenter=dc1;kind=partition-exports": {
"consul.usage.test.consul.state.config_entries;datacenter=dc1;kind=exported-services": {
Name: "consul.usage.test.consul.state.config_entries",
Value: 0,
Labels: []metrics.Label{
{Name: "datacenter", Value: "dc1"},
{Name: "kind", Value: "partition-exports"},
{Name: "kind", Value: "exported-services"},
},
},
},

View File

@ -6224,11 +6224,18 @@ func TestDNS_ServiceLookup_FilterACL(t *testing.T) {
for _, tt := range tests {
t.Run("ACLToken == "+tt.token, func(t *testing.T) {
a := NewTestAgent(t, `
acl_token = "`+tt.token+`"
acl_master_token = "root"
acl_datacenter = "dc1"
acl_down_policy = "deny"
acl_default_policy = "deny"
primary_datacenter = "dc1"
acl {
enabled = true
default_policy = "deny"
down_policy = "deny"
tokens {
initial_management = "root"
default = "`+tt.token+`"
}
}
`)
defer a.Shutdown()
testrpc.WaitForLeader(t, a.RPC, "dc1")

View File

@ -20,6 +20,7 @@ import (
"github.com/hashicorp/consul/ipaddr"
"github.com/hashicorp/consul/sdk/freeport"
"github.com/hashicorp/consul/tlsutil"
"github.com/hashicorp/consul/types"
)
// useTLSForDcAlwaysTrue tell GRPC to always return the TLS is enabled
@ -33,7 +34,7 @@ func TestNewDialer_WithTLSWrapper(t *testing.T) {
t.Cleanup(logError(t, lis.Close))
builder := resolver.NewServerResolverBuilder(newConfig(t))
builder.AddServer(&metadata.Server{
builder.AddServer(types.AreaWAN, &metadata.Server{
Name: "server-1",
ID: "ID1",
Datacenter: "dc1",
@ -84,14 +85,14 @@ func TestNewDialer_WithALPNWrapper(t *testing.T) {
}()
builder := resolver.NewServerResolverBuilder(newConfig(t))
builder.AddServer(&metadata.Server{
builder.AddServer(types.AreaWAN, &metadata.Server{
Name: "server-1",
ID: "ID1",
Datacenter: "dc1",
Addr: lis1.Addr(),
UseTLS: true,
})
builder.AddServer(&metadata.Server{
builder.AddServer(types.AreaWAN, &metadata.Server{
Name: "server-2",
ID: "ID2",
Datacenter: "dc2",
@ -150,10 +151,10 @@ func TestNewDialer_IntegrationWithTLSEnabledHandler(t *testing.T) {
}, hclog.New(nil))
require.NoError(t, err)
srv := newTestServer(t, "server-1", "dc1", tlsConf)
srv := newSimpleTestServer(t, "server-1", "dc1", tlsConf)
md := srv.Metadata()
res.AddServer(md)
res.AddServer(types.AreaWAN, md)
t.Cleanup(srv.shutdown)
pool := NewClientConnPool(ClientConnPoolConfig{
@ -198,7 +199,7 @@ func TestNewDialer_IntegrationWithTLSEnabledHandler_viaMeshGateway(t *testing.T)
}, hclog.New(nil))
require.NoError(t, err)
srv := newTestServer(t, "bob", "dc1", tlsConf)
srv := newSimpleTestServer(t, "bob", "dc1", tlsConf)
// Send all of the traffic to dc1's server
var p tcpproxy.Proxy
@ -211,7 +212,7 @@ func TestNewDialer_IntegrationWithTLSEnabledHandler_viaMeshGateway(t *testing.T)
}()
md := srv.Metadata()
res.AddServer(md)
res.AddServer(types.AreaWAN, md)
t.Cleanup(srv.shutdown)
clientTLSConf, err := tlsutil.NewConfigurator(tlsutil.Config{
@ -265,8 +266,8 @@ func TestClientConnPool_IntegrationWithGRPCResolver_Failover(t *testing.T) {
for i := 0; i < count; i++ {
name := fmt.Sprintf("server-%d", i)
srv := newTestServer(t, name, "dc1", nil)
res.AddServer(srv.Metadata())
srv := newSimpleTestServer(t, name, "dc1", nil)
res.AddServer(types.AreaWAN, srv.Metadata())
t.Cleanup(srv.shutdown)
}
@ -280,7 +281,7 @@ func TestClientConnPool_IntegrationWithGRPCResolver_Failover(t *testing.T) {
first, err := client.Something(ctx, &testservice.Req{})
require.NoError(t, err)
res.RemoveServer(&metadata.Server{ID: first.ServerName, Datacenter: "dc1"})
res.RemoveServer(types.AreaWAN, &metadata.Server{ID: first.ServerName, Datacenter: "dc1"})
resp, err := client.Something(ctx, &testservice.Req{})
require.NoError(t, err)
@ -301,8 +302,8 @@ func TestClientConnPool_ForwardToLeader_Failover(t *testing.T) {
var servers []testServer
for i := 0; i < count; i++ {
name := fmt.Sprintf("server-%d", i)
srv := newTestServer(t, name, "dc1", nil)
res.AddServer(srv.Metadata())
srv := newSimpleTestServer(t, name, "dc1", nil)
res.AddServer(types.AreaWAN, srv.Metadata())
servers = append(servers, srv)
t.Cleanup(srv.shutdown)
}
@ -351,8 +352,8 @@ func TestClientConnPool_IntegrationWithGRPCResolver_Rebalance(t *testing.T) {
for i := 0; i < count; i++ {
name := fmt.Sprintf("server-%d", i)
srv := newTestServer(t, name, "dc1", nil)
res.AddServer(srv.Metadata())
srv := newSimpleTestServer(t, name, "dc1", nil)
res.AddServer(types.AreaWAN, srv.Metadata())
t.Cleanup(srv.shutdown)
}
@ -405,8 +406,8 @@ func TestClientConnPool_IntegrationWithGRPCResolver_MultiDC(t *testing.T) {
for _, dc := range dcs {
name := "server-0-" + dc
srv := newTestServer(t, name, dc, nil)
res.AddServer(srv.Metadata())
srv := newSimpleTestServer(t, name, dc, nil)
res.AddServer(types.AreaWAN, srv.Metadata())
t.Cleanup(srv.shutdown)
}

View File

@ -9,29 +9,73 @@ import (
"time"
"google.golang.org/grpc"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/keepalive"
"google.golang.org/grpc/status"
middleware "github.com/grpc-ecosystem/go-grpc-middleware"
recovery "github.com/grpc-ecosystem/go-grpc-middleware/recovery"
"github.com/hashicorp/go-hclog"
)
// NewHandler returns a gRPC server that accepts connections from Handle(conn).
// The register function will be called with the grpc.Server to register
// gRPC services with the server.
func NewHandler(addr net.Addr, register func(server *grpc.Server)) *Handler {
func NewHandler(logger Logger, addr net.Addr, register func(server *grpc.Server)) *Handler {
metrics := defaultMetrics()
// We don't need to pass tls.Config to the server since it's multiplexed
// behind the RPC listener, which already has TLS configured.
srv := grpc.NewServer(
recoveryOpts := PanicHandlerMiddlewareOpts(logger)
opts := []grpc.ServerOption{
grpc.StatsHandler(newStatsHandler(metrics)),
grpc.StreamInterceptor((&activeStreamCounter{metrics: metrics}).Intercept),
middleware.WithUnaryServerChain(
// Add middlware interceptors to recover in case of panics.
recovery.UnaryServerInterceptor(recoveryOpts...),
),
middleware.WithStreamServerChain(
// Add middlware interceptors to recover in case of panics.
recovery.StreamServerInterceptor(recoveryOpts...),
(&activeStreamCounter{metrics: metrics}).Intercept,
),
grpc.KeepaliveEnforcementPolicy(keepalive.EnforcementPolicy{
MinTime: 15 * time.Second,
}),
)
}
// We don't need to pass tls.Config to the server since it's multiplexed
// behind the RPC listener, which already has TLS configured.
srv := grpc.NewServer(opts...)
register(srv)
lis := &chanListener{addr: addr, conns: make(chan net.Conn), done: make(chan struct{})}
return &Handler{srv: srv, listener: lis}
}
// PanicHandlerMiddlewareOpts returns the []recovery.Option containing
// recovery handler function.
func PanicHandlerMiddlewareOpts(logger Logger) []recovery.Option {
return []recovery.Option{
recovery.WithRecoveryHandler(NewPanicHandler(logger)),
}
}
// NewPanicHandler returns a recovery.RecoveryHandlerFunc closure function
// to handle panic in GRPC server's handlers.
func NewPanicHandler(logger Logger) recovery.RecoveryHandlerFunc {
return func(p interface{}) (err error) {
// Log the panic and the stack trace of the Goroutine that caused the panic.
stacktrace := hclog.Stacktrace()
logger.Error("panic serving grpc request",
"panic", p,
"stack", stacktrace,
)
return status.Errorf(codes.Internal, "grpc: panic serving request")
}
}
// Handler implements a handler for the rpc server listener, and the
// agent.Component interface for managing the lifecycle of the grpc.Server.
type Handler struct {

View File

@ -0,0 +1,61 @@
package grpc
import (
"bytes"
"context"
"testing"
"time"
"github.com/hashicorp/consul/types"
"github.com/hashicorp/go-hclog"
"github.com/stretchr/testify/require"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
"github.com/hashicorp/consul/agent/grpc/internal/testservice"
"github.com/hashicorp/consul/agent/grpc/resolver"
)
func TestHandler_PanicRecoveryInterceptor(t *testing.T) {
// Prepare a logger with output to a buffer
// so we can check what it writes.
var buf bytes.Buffer
logger := hclog.New(&hclog.LoggerOptions{
Output: &buf,
})
res := resolver.NewServerResolverBuilder(newConfig(t))
registerWithGRPC(t, res)
srv := newPanicTestServer(t, logger, "server-1", "dc1", nil)
res.AddServer(types.AreaWAN, srv.Metadata())
t.Cleanup(srv.shutdown)
pool := NewClientConnPool(ClientConnPoolConfig{
Servers: res,
UseTLSForDC: useTLSForDcAlwaysTrue,
DialingFromServer: true,
DialingFromDatacenter: "dc1",
})
conn, err := pool.ClientConn("dc1")
require.NoError(t, err)
client := testservice.NewSimpleClient(conn)
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
t.Cleanup(cancel)
resp, err := client.Something(ctx, &testservice.Req{})
expectedErr := status.Errorf(codes.Internal, "grpc: panic serving request")
require.Equal(t, expectedErr, err)
require.Nil(t, resp)
// Read the log
strLog := buf.String()
// Checking the entire stack trace is not possible, let's
// make sure that it contains a couple of expected strings.
require.Contains(t, strLog, `[ERROR] panic serving grpc request: panic="panic from Something`)
require.Contains(t, strLog, `github.com/hashicorp/consul/agent/grpc.(*simplePanic).Something`)
}

View File

@ -10,6 +10,7 @@ import (
"google.golang.org/grpc/resolver"
"github.com/hashicorp/consul/agent/metadata"
"github.com/hashicorp/consul/types"
)
// ServerResolverBuilder tracks the current server list and keeps any
@ -18,9 +19,9 @@ type ServerResolverBuilder struct {
cfg Config
// leaderResolver is used to track the address of the leader in the local DC.
leaderResolver leaderResolver
// servers is an index of Servers by Server.ID. The map contains server IDs
// servers is an index of Servers by area and Server.ID. The map contains server IDs
// for all datacenters.
servers map[string]*metadata.Server
servers map[types.AreaID]map[string]*metadata.Server
// resolvers is an index of connections to the serverResolver which manages
// addresses of servers for that connection.
resolvers map[resolver.ClientConn]*serverResolver
@ -37,7 +38,7 @@ type Config struct {
func NewServerResolverBuilder(cfg Config) *ServerResolverBuilder {
return &ServerResolverBuilder{
cfg: cfg,
servers: make(map[string]*metadata.Server),
servers: make(map[types.AreaID]map[string]*metadata.Server),
resolvers: make(map[resolver.ClientConn]*serverResolver),
}
}
@ -72,9 +73,11 @@ func (s *ServerResolverBuilder) ServerForGlobalAddr(globalAddr string) (*metadat
s.lock.RLock()
defer s.lock.RUnlock()
for _, server := range s.servers {
if DCPrefix(server.Datacenter, server.Addr.String()) == globalAddr {
return server, nil
for _, areaServers := range s.servers {
for _, server := range areaServers {
if DCPrefix(server.Datacenter, server.Addr.String()) == globalAddr {
return server, nil
}
}
}
return nil, fmt.Errorf("failed to find Consul server for global address %q", globalAddr)
@ -138,11 +141,17 @@ func (s *ServerResolverBuilder) Authority() string {
}
// AddServer updates the resolvers' states to include the new server's address.
func (s *ServerResolverBuilder) AddServer(server *metadata.Server) {
func (s *ServerResolverBuilder) AddServer(areaID types.AreaID, server *metadata.Server) {
s.lock.Lock()
defer s.lock.Unlock()
s.servers[uniqueID(server)] = server
areaServers, ok := s.servers[areaID]
if !ok {
areaServers = make(map[string]*metadata.Server)
s.servers[areaID] = areaServers
}
areaServers[uniqueID(server)] = server
addrs := s.getDCAddrs(server.Datacenter)
for _, resolver := range s.resolvers {
@ -168,11 +177,19 @@ func DCPrefix(datacenter, suffix string) string {
}
// RemoveServer updates the resolvers' states with the given server removed.
func (s *ServerResolverBuilder) RemoveServer(server *metadata.Server) {
func (s *ServerResolverBuilder) RemoveServer(areaID types.AreaID, server *metadata.Server) {
s.lock.Lock()
defer s.lock.Unlock()
delete(s.servers, uniqueID(server))
areaServers, ok := s.servers[areaID]
if !ok {
return // already gone
}
delete(areaServers, uniqueID(server))
if len(areaServers) == 0 {
delete(s.servers, areaID)
}
addrs := s.getDCAddrs(server.Datacenter)
for _, resolver := range s.resolvers {
@ -185,18 +202,29 @@ func (s *ServerResolverBuilder) RemoveServer(server *metadata.Server) {
// getDCAddrs returns a list of the server addresses for the given datacenter.
// This method requires that lock is held for reads.
func (s *ServerResolverBuilder) getDCAddrs(dc string) []resolver.Address {
var addrs []resolver.Address
for _, server := range s.servers {
if server.Datacenter != dc {
continue
}
var (
addrs []resolver.Address
keptServerIDs = make(map[string]struct{})
)
for _, areaServers := range s.servers {
for _, server := range areaServers {
if server.Datacenter != dc {
continue
}
addrs = append(addrs, resolver.Address{
// NOTE: the address persisted here is only dialable using our custom dialer
Addr: DCPrefix(server.Datacenter, server.Addr.String()),
Type: resolver.Backend,
ServerName: server.Name,
})
// Servers may be part of multiple areas, so only include each one once.
if _, ok := keptServerIDs[server.ID]; ok {
continue
}
keptServerIDs[server.ID] = struct{}{}
addrs = append(addrs, resolver.Address{
// NOTE: the address persisted here is only dialable using our custom dialer
Addr: DCPrefix(server.Datacenter, server.Addr.String()),
Type: resolver.Backend,
ServerName: server.Name,
})
}
}
return addrs
}

View File

@ -18,6 +18,7 @@ import (
"github.com/hashicorp/consul/agent/metadata"
"github.com/hashicorp/consul/agent/pool"
"github.com/hashicorp/consul/tlsutil"
"github.com/hashicorp/go-hclog"
)
type testServer struct {
@ -39,11 +40,22 @@ func (s testServer) Metadata() *metadata.Server {
}
}
func newTestServer(t *testing.T, name string, dc string, tlsConf *tlsutil.Configurator) testServer {
addr := &net.IPAddr{IP: net.ParseIP("127.0.0.1")}
handler := NewHandler(addr, func(server *grpc.Server) {
func newSimpleTestServer(t *testing.T, name, dc string, tlsConf *tlsutil.Configurator) testServer {
return newTestServer(t, hclog.Default(), name, dc, tlsConf, func(server *grpc.Server) {
testservice.RegisterSimpleServer(server, &simple{name: name, dc: dc})
})
}
// newPanicTestServer sets up a simple server with handlers that panic.
func newPanicTestServer(t *testing.T, logger hclog.Logger, name, dc string, tlsConf *tlsutil.Configurator) testServer {
return newTestServer(t, logger, name, dc, tlsConf, func(server *grpc.Server) {
testservice.RegisterSimpleServer(server, &simplePanic{name: name, dc: dc})
})
}
func newTestServer(t *testing.T, logger hclog.Logger, name, dc string, tlsConf *tlsutil.Configurator, register func(server *grpc.Server)) testServer {
addr := &net.IPAddr{IP: net.ParseIP("127.0.0.1")}
handler := NewHandler(logger, addr, register)
lis, err := net.Listen("tcp", "127.0.0.1:0")
require.NoError(t, err)
@ -103,6 +115,23 @@ func (s *simple) Something(_ context.Context, _ *testservice.Req) (*testservice.
return &testservice.Resp{ServerName: s.name, Datacenter: s.dc}, nil
}
type simplePanic struct {
name, dc string
}
func (s *simplePanic) Flow(_ *testservice.Req, flow testservice.Simple_FlowServer) error {
for flow.Context().Err() == nil {
time.Sleep(time.Millisecond)
panic("panic from Flow")
}
return nil
}
func (s *simplePanic) Something(_ context.Context, _ *testservice.Req) (*testservice.Resp, error) {
time.Sleep(time.Millisecond)
panic("panic from Something")
}
// fakeRPCListener mimics agent/consul.Server.listen to handle the RPCType byte.
// In the future we should be able to refactor Server and extract this RPC
// handling logic so that we don't need to use a fake.

View File

@ -15,6 +15,7 @@ import (
"google.golang.org/grpc"
"github.com/hashicorp/consul/agent/grpc/internal/testservice"
"github.com/hashicorp/go-hclog"
)
func noopRegister(*grpc.Server) {}
@ -23,7 +24,7 @@ func TestHandler_EmitsStats(t *testing.T) {
sink, reset := patchGlobalMetrics(t)
addr := &net.IPAddr{IP: net.ParseIP("127.0.0.1")}
handler := NewHandler(addr, noopRegister)
handler := NewHandler(hclog.Default(), addr, noopRegister)
reset()
testservice.RegisterSimpleServer(handler.srv, &simple{})

Some files were not shown because too many files have changed in this diff Show More