agent: return the default ACL policy to callers as a header (#9101)
Header is: X-Consul-Default-ACL-Policy=<allow|deny> This is of particular utility when fetching matching intentions, as the fallthrough for a request that doesn't match any intentions is to enforce using the default acl policy.
This commit is contained in:
parent
990134371b
commit
a5bd1ba323
|
@ -0,0 +1,3 @@
|
||||||
|
```release-note:feature
|
||||||
|
agent: return the default ACL policy to callers as a header
|
||||||
|
```
|
|
@ -77,8 +77,8 @@ type RuntimeConfig struct {
|
||||||
|
|
||||||
// ACLDefaultPolicy is used to control the ACL interaction when
|
// ACLDefaultPolicy is used to control the ACL interaction when
|
||||||
// there is no defined policy. This can be "allow" which means
|
// there is no defined policy. This can be "allow" which means
|
||||||
// ACLs are used to black-list, or "deny" which means ACLs are
|
// ACLs are used to deny-list, or "deny" which means ACLs are
|
||||||
// white-lists.
|
// allow-lists.
|
||||||
//
|
//
|
||||||
// hcl: acl.default_policy = ("allow"|"deny")
|
// hcl: acl.default_policy = ("allow"|"deny")
|
||||||
ACLDefaultPolicy string
|
ACLDefaultPolicy string
|
||||||
|
|
|
@ -268,8 +268,8 @@ type Config struct {
|
||||||
|
|
||||||
// ACLDefaultPolicy is used to control the ACL interaction when
|
// ACLDefaultPolicy is used to control the ACL interaction when
|
||||||
// there is no defined policy. This can be "allow" which means
|
// there is no defined policy. This can be "allow" which means
|
||||||
// ACLs are used to black-list, or "deny" which means ACLs are
|
// ACLs are used to deny-list, or "deny" which means ACLs are
|
||||||
// white-lists.
|
// allow-lists.
|
||||||
ACLDefaultPolicy string
|
ACLDefaultPolicy string
|
||||||
|
|
||||||
// ACLDownPolicy controls the behavior of ACLs if the ACLDatacenter
|
// ACLDownPolicy controls the behavior of ACLs if the ACLDatacenter
|
||||||
|
|
|
@ -357,6 +357,7 @@ func (s *HTTPHandlers) wrap(handler endpoint, methods []string) http.HandlerFunc
|
||||||
return func(resp http.ResponseWriter, req *http.Request) {
|
return func(resp http.ResponseWriter, req *http.Request) {
|
||||||
setHeaders(resp, s.agent.config.HTTPResponseHeaders)
|
setHeaders(resp, s.agent.config.HTTPResponseHeaders)
|
||||||
setTranslateAddr(resp, s.agent.config.TranslateWANAddrs)
|
setTranslateAddr(resp, s.agent.config.TranslateWANAddrs)
|
||||||
|
setACLDefaultPolicy(resp, s.agent.config.ACLDefaultPolicy)
|
||||||
|
|
||||||
// Obfuscate any tokens from appearing in the logs
|
// Obfuscate any tokens from appearing in the logs
|
||||||
formVals, err := url.ParseQuery(req.URL.RawQuery)
|
formVals, err := url.ParseQuery(req.URL.RawQuery)
|
||||||
|
@ -697,6 +698,12 @@ func setConsistency(resp http.ResponseWriter, consistency string) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func setACLDefaultPolicy(resp http.ResponseWriter, aclDefaultPolicy string) {
|
||||||
|
if aclDefaultPolicy != "" {
|
||||||
|
resp.Header().Set("X-Consul-Default-ACL-Policy", aclDefaultPolicy)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// setLastContact is used to set the last contact header
|
// setLastContact is used to set the last contact header
|
||||||
func setLastContact(resp http.ResponseWriter, last time.Duration) {
|
func setLastContact(resp http.ResponseWriter, last time.Duration) {
|
||||||
if last < 0 {
|
if last < 0 {
|
||||||
|
|
|
@ -415,6 +415,54 @@ func TestHTTPAPI_TranslateAddrHeader(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestHTTPAPI_DefaultACLPolicy(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
type testcase struct {
|
||||||
|
name string
|
||||||
|
hcl string
|
||||||
|
expect string
|
||||||
|
}
|
||||||
|
|
||||||
|
cases := []testcase{
|
||||||
|
{
|
||||||
|
name: "default is allow",
|
||||||
|
hcl: ``,
|
||||||
|
expect: "allow",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "explicit allow",
|
||||||
|
hcl: `acl { default_policy = "allow" }`,
|
||||||
|
expect: "allow",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "explicit deny",
|
||||||
|
hcl: `acl { default_policy = "deny" }`,
|
||||||
|
expect: "deny",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range cases {
|
||||||
|
tc := tc
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
a := NewTestAgent(t, tc.hcl)
|
||||||
|
defer a.Shutdown()
|
||||||
|
|
||||||
|
resp := httptest.NewRecorder()
|
||||||
|
handler := func(resp http.ResponseWriter, req *http.Request) (interface{}, error) {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
req, _ := http.NewRequest("GET", "/v1/agent/self", nil)
|
||||||
|
a.srv.wrap(handler, []string{"GET"})(resp, req)
|
||||||
|
|
||||||
|
require.Equal(t, tc.expect, resp.Header().Get("X-Consul-Default-ACL-Policy"))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestHTTPAPIResponseHeaders(t *testing.T) {
|
func TestHTTPAPIResponseHeaders(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
a := NewTestAgent(t, `
|
a := NewTestAgent(t, `
|
||||||
|
|
11
api/api.go
11
api/api.go
|
@ -254,6 +254,11 @@ type QueryMeta struct {
|
||||||
// CacheAge is set if request was ?cached and indicates how stale the cached
|
// CacheAge is set if request was ?cached and indicates how stale the cached
|
||||||
// response is.
|
// response is.
|
||||||
CacheAge time.Duration
|
CacheAge time.Duration
|
||||||
|
|
||||||
|
// DefaultACLPolicy is used to control the ACL interaction when there is no
|
||||||
|
// defined policy. This can be "allow" which means ACLs are used to
|
||||||
|
// deny-list, or "deny" which means ACLs are allow-lists.
|
||||||
|
DefaultACLPolicy string
|
||||||
}
|
}
|
||||||
|
|
||||||
// WriteMeta is used to return meta data about a write
|
// WriteMeta is used to return meta data about a write
|
||||||
|
@ -962,6 +967,12 @@ func parseQueryMeta(resp *http.Response, q *QueryMeta) error {
|
||||||
q.AddressTranslationEnabled = false
|
q.AddressTranslationEnabled = false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Parse X-Consul-Default-ACL-Policy
|
||||||
|
switch v := header.Get("X-Consul-Default-ACL-Policy"); v {
|
||||||
|
case "allow", "deny":
|
||||||
|
q.DefaultACLPolicy = v
|
||||||
|
}
|
||||||
|
|
||||||
// Parse Cache info
|
// Parse Cache info
|
||||||
if cacheStr := header.Get("X-Cache"); cacheStr != "" {
|
if cacheStr := header.Get("X-Cache"); cacheStr != "" {
|
||||||
q.CacheHit = strings.EqualFold(cacheStr, "HIT")
|
q.CacheHit = strings.EqualFold(cacheStr, "HIT")
|
||||||
|
|
|
@ -840,6 +840,7 @@ func TestAPI_ParseQueryMeta(t *testing.T) {
|
||||||
resp.Header.Set("X-Consul-LastContact", "80")
|
resp.Header.Set("X-Consul-LastContact", "80")
|
||||||
resp.Header.Set("X-Consul-KnownLeader", "true")
|
resp.Header.Set("X-Consul-KnownLeader", "true")
|
||||||
resp.Header.Set("X-Consul-Translate-Addresses", "true")
|
resp.Header.Set("X-Consul-Translate-Addresses", "true")
|
||||||
|
resp.Header.Set("X-Consul-Default-ACL-Policy", "deny")
|
||||||
|
|
||||||
qm := &QueryMeta{}
|
qm := &QueryMeta{}
|
||||||
if err := parseQueryMeta(resp, qm); err != nil {
|
if err := parseQueryMeta(resp, qm); err != nil {
|
||||||
|
@ -858,6 +859,9 @@ func TestAPI_ParseQueryMeta(t *testing.T) {
|
||||||
if !qm.AddressTranslationEnabled {
|
if !qm.AddressTranslationEnabled {
|
||||||
t.Fatalf("Bad: %v", qm)
|
t.Fatalf("Bad: %v", qm)
|
||||||
}
|
}
|
||||||
|
if qm.DefaultACLPolicy != "deny" {
|
||||||
|
t.Fatalf("Bad: %v", qm)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestAPI_UnixSocket(t *testing.T) {
|
func TestAPI_UnixSocket(t *testing.T) {
|
||||||
|
|
|
@ -86,6 +86,18 @@ to allow clients to know if address translation is in effect, the
|
||||||
and will have a value of `true`. If translation is not enabled then this header
|
and will have a value of `true`. If translation is not enabled then this header
|
||||||
will not be present.
|
will not be present.
|
||||||
|
|
||||||
|
## Default ACL Policy
|
||||||
|
|
||||||
|
All API responses for Consul versions after 1.9 will include an HTTP response
|
||||||
|
header `X-Consul-Default-ACL-Policy` set to either "allow" or "deny" which
|
||||||
|
mirrors the current value of the agent's
|
||||||
|
[`acl.default_policy`](/docs/agent/options#acl_default_policy) option.
|
||||||
|
|
||||||
|
This is also the default [intention](/docs/connect/intentions) enforcement
|
||||||
|
action if no intention matches.
|
||||||
|
|
||||||
|
This is returned even if ACLs are disabled.
|
||||||
|
|
||||||
## UUID Format
|
## UUID Format
|
||||||
|
|
||||||
UUID-format identifiers generated by the Consul API use the
|
UUID-format identifiers generated by the Consul API use the
|
||||||
|
|
Loading…
Reference in New Issue