backport of commit c983a8f0ad9a6a75dd0f74cd4e8fd4f929052fe0 (#19428)
Co-authored-by: Tom Davies <tom@t-davies.com>
This commit is contained in:
parent
04a5cb69f7
commit
2e8c36e698
|
@ -0,0 +1,3 @@
|
||||||
|
```release-note:bug
|
||||||
|
consul: uses token namespace to fetch policies for verification
|
||||||
|
```
|
|
@ -30,6 +30,7 @@ type ConnectACLsE2ETest struct {
|
||||||
jobIDs []string
|
jobIDs []string
|
||||||
consulPolicyIDs []string
|
consulPolicyIDs []string
|
||||||
consulTokenIDs []string
|
consulTokenIDs []string
|
||||||
|
consulNamespace string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (tc *ConnectACLsE2ETest) BeforeAll(f *framework.F) {
|
func (tc *ConnectACLsE2ETest) BeforeAll(f *framework.F) {
|
||||||
|
@ -72,14 +73,28 @@ func (tc *ConnectACLsE2ETest) AfterEach(f *framework.F) {
|
||||||
// cleanup consul tokens
|
// cleanup consul tokens
|
||||||
for _, id := range tc.consulTokenIDs {
|
for _, id := range tc.consulTokenIDs {
|
||||||
t.Log("cleanup: delete consul token id:", id)
|
t.Log("cleanup: delete consul token id:", id)
|
||||||
_, err := tc.Consul().ACL().TokenDelete(id, &consulapi.WriteOptions{Token: tc.consulManagementToken})
|
_, err := tc.Consul().ACL().TokenDelete(id, &consulapi.WriteOptions{
|
||||||
|
Token: tc.consulManagementToken,
|
||||||
|
Namespace: tc.consulNamespace,
|
||||||
|
})
|
||||||
f.NoError(err)
|
f.NoError(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// cleanup consul policies
|
// cleanup consul policies
|
||||||
for _, id := range tc.consulPolicyIDs {
|
for _, id := range tc.consulPolicyIDs {
|
||||||
t.Log("cleanup: delete consul policy id:", id)
|
t.Log("cleanup: delete consul policy id:", id)
|
||||||
_, err := tc.Consul().ACL().PolicyDelete(id, &consulapi.WriteOptions{Token: tc.consulManagementToken})
|
_, err := tc.Consul().ACL().PolicyDelete(id, &consulapi.WriteOptions{
|
||||||
|
Token: tc.consulManagementToken,
|
||||||
|
Namespace: tc.consulNamespace,
|
||||||
|
})
|
||||||
|
f.NoError(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if tc.consulNamespace != "" {
|
||||||
|
t.Log("cleanup: delete consul namespace:", tc.consulNamespace)
|
||||||
|
_, err := tc.Consul().Namespaces().Delete(tc.consulNamespace, &consulapi.WriteOptions{
|
||||||
|
Token: tc.consulManagementToken,
|
||||||
|
})
|
||||||
f.NoError(err)
|
f.NoError(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -98,12 +113,14 @@ func (tc *ConnectACLsE2ETest) AfterEach(f *framework.F) {
|
||||||
tc.jobIDs = []string{}
|
tc.jobIDs = []string{}
|
||||||
tc.consulTokenIDs = []string{}
|
tc.consulTokenIDs = []string{}
|
||||||
tc.consulPolicyIDs = []string{}
|
tc.consulPolicyIDs = []string{}
|
||||||
|
tc.consulNamespace = ""
|
||||||
}
|
}
|
||||||
|
|
||||||
// todo(shoenig): follow up refactor with e2eutil.ConsulPolicy
|
// todo(shoenig): follow up refactor with e2eutil.ConsulPolicy
|
||||||
type consulPolicy struct {
|
type consulPolicy struct {
|
||||||
Name string // e.g. nomad-operator
|
Name string // e.g. nomad-operator
|
||||||
Rules string // e.g. service "" { policy="write" }
|
Rules string // e.g. service "" { policy="write" }
|
||||||
|
Namespace string // e.g. default
|
||||||
}
|
}
|
||||||
|
|
||||||
// todo(shoenig): follow up refactor with e2eutil.ConsulPolicy
|
// todo(shoenig): follow up refactor with e2eutil.ConsulPolicy
|
||||||
|
@ -112,17 +129,31 @@ func (tc *ConnectACLsE2ETest) createConsulPolicy(p consulPolicy, f *framework.F)
|
||||||
Name: p.Name,
|
Name: p.Name,
|
||||||
Description: "test policy " + p.Name,
|
Description: "test policy " + p.Name,
|
||||||
Rules: p.Rules,
|
Rules: p.Rules,
|
||||||
|
Namespace: p.Namespace,
|
||||||
}, &consulapi.WriteOptions{Token: tc.consulManagementToken})
|
}, &consulapi.WriteOptions{Token: tc.consulManagementToken})
|
||||||
f.NoError(err, "failed to create consul policy")
|
f.NoError(err, "failed to create consul policy")
|
||||||
tc.consulPolicyIDs = append(tc.consulPolicyIDs, result.ID)
|
tc.consulPolicyIDs = append(tc.consulPolicyIDs, result.ID)
|
||||||
return result.ID
|
return result.ID
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (tc *ConnectACLsE2ETest) createConsulNamespace(namespace string, f *framework.F) string {
|
||||||
|
result, _, err := tc.Consul().Namespaces().Create(&consulapi.Namespace{
|
||||||
|
Name: namespace,
|
||||||
|
}, &consulapi.WriteOptions{Token: tc.consulManagementToken})
|
||||||
|
f.NoError(err, "failed to create consul namespace")
|
||||||
|
return result.Name
|
||||||
|
}
|
||||||
|
|
||||||
// todo(shoenig): follow up refactor with e2eutil.ConsulPolicy
|
// todo(shoenig): follow up refactor with e2eutil.ConsulPolicy
|
||||||
func (tc *ConnectACLsE2ETest) createOperatorToken(policyID string, f *framework.F) string {
|
func (tc *ConnectACLsE2ETest) createOperatorToken(policyID string, f *framework.F) string {
|
||||||
|
return tc.createOperatorTokenNamespaced(policyID, "default", f)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tc *ConnectACLsE2ETest) createOperatorTokenNamespaced(policyID string, namespace string, f *framework.F) string {
|
||||||
token, _, err := tc.Consul().ACL().TokenCreate(&consulapi.ACLToken{
|
token, _, err := tc.Consul().ACL().TokenCreate(&consulapi.ACLToken{
|
||||||
Description: "operator token",
|
Description: "operator token",
|
||||||
Policies: []*consulapi.ACLTokenPolicyLink{{ID: policyID}},
|
Policies: []*consulapi.ACLTokenPolicyLink{{ID: policyID}},
|
||||||
|
Namespace: namespace,
|
||||||
}, &consulapi.WriteOptions{Token: tc.consulManagementToken})
|
}, &consulapi.WriteOptions{Token: tc.consulManagementToken})
|
||||||
f.NoError(err, "failed to create operator token")
|
f.NoError(err, "failed to create operator token")
|
||||||
tc.consulTokenIDs = append(tc.consulTokenIDs, token.AccessorID)
|
tc.consulTokenIDs = append(tc.consulTokenIDs, token.AccessorID)
|
||||||
|
@ -250,6 +281,48 @@ func (tc *ConnectACLsE2ETest) TestConnectACLsConnectDemo(f *framework.F) {
|
||||||
t.Log("connect legacy job with ACLs enable finished")
|
t.Log("connect legacy job with ACLs enable finished")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (tc *ConnectACLsE2ETest) TestConnectACLsConnectDemoNamespaced(f *framework.F) {
|
||||||
|
t := f.T()
|
||||||
|
|
||||||
|
t.Log("test register Connect job w/ ACLs enabled w/ operator token")
|
||||||
|
|
||||||
|
// === Setup ACL policy within a namespace and mint Operator token ===
|
||||||
|
|
||||||
|
// create a namespace
|
||||||
|
namespace := tc.createConsulNamespace("ns-"+uuid.Short(), f)
|
||||||
|
tc.consulNamespace = namespace
|
||||||
|
t.Log("created namespace:", namespace)
|
||||||
|
|
||||||
|
// create a policy allowing writes of services "count-api" and "count-dashboard"
|
||||||
|
policyID := tc.createConsulPolicy(consulPolicy{
|
||||||
|
Name: "nomad-operator-policy-" + uuid.Short(),
|
||||||
|
Rules: `service "count-api" { policy = "write" } service "count-dashboard" { policy = "write" }`,
|
||||||
|
Namespace: namespace,
|
||||||
|
}, f)
|
||||||
|
t.Log("created operator policy:", policyID)
|
||||||
|
|
||||||
|
// create a Consul "operator token" blessed with the above policy
|
||||||
|
operatorToken := tc.createOperatorTokenNamespaced(policyID, namespace, f)
|
||||||
|
t.Log("created operator token:", operatorToken)
|
||||||
|
|
||||||
|
jobID := connectJobID()
|
||||||
|
tc.jobIDs = append(tc.jobIDs, jobID)
|
||||||
|
|
||||||
|
allocs := e2eutil.RegisterAndWaitForAllocs(t, tc.Nomad(), demoConnectJob, jobID, operatorToken)
|
||||||
|
f.Equal(2, len(allocs), "expected 2 allocs for connect demo", allocs)
|
||||||
|
allocIDs := e2eutil.AllocIDsFromAllocationListStubs(allocs)
|
||||||
|
f.Equal(2, len(allocIDs), "expected 2 allocIDs for connect demo", allocIDs)
|
||||||
|
e2eutil.WaitForAllocsRunning(t, tc.Nomad(), allocIDs)
|
||||||
|
|
||||||
|
// === Check Consul SI tokens were generated for sidecars ===
|
||||||
|
foundSITokens := tc.countSITokens(t)
|
||||||
|
f.Equal(2, len(foundSITokens), "expected 2 SI tokens total: %v", foundSITokens)
|
||||||
|
f.Equal(1, foundSITokens["connect-proxy-count-api"], "expected 1 SI token for connect-proxy-count-api: %v", foundSITokens)
|
||||||
|
f.Equal(1, foundSITokens["connect-proxy-count-dashboard"], "expected 1 SI token for connect-proxy-count-dashboard: %v", foundSITokens)
|
||||||
|
|
||||||
|
t.Log("connect legacy job with ACLs enable finished")
|
||||||
|
}
|
||||||
|
|
||||||
func (tc *ConnectACLsE2ETest) TestConnectACLsConnectNativeDemo(f *framework.F) {
|
func (tc *ConnectACLsE2ETest) TestConnectACLsConnectNativeDemo(f *framework.F) {
|
||||||
t := f.T()
|
t := f.T()
|
||||||
|
|
||||||
|
|
|
@ -122,7 +122,7 @@ func (c *consulACLsAPI) canReadKeystore(namespace string, token *api.ACLToken) (
|
||||||
|
|
||||||
// check each policy directly attached to the token
|
// check each policy directly attached to the token
|
||||||
for _, policyRef := range token.Policies {
|
for _, policyRef := range token.Policies {
|
||||||
if allowable, err := c.policyAllowsKeystoreRead(matches, namespace, policyRef.ID); err != nil {
|
if allowable, err := c.policyAllowsKeystoreRead(matches, namespace, policyRef.ID, token.Namespace); err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
} else if allowable {
|
} else if allowable {
|
||||||
return true, nil
|
return true, nil
|
||||||
|
@ -133,13 +133,14 @@ func (c *consulACLsAPI) canReadKeystore(namespace string, token *api.ACLToken) (
|
||||||
for _, roleLink := range token.Roles {
|
for _, roleLink := range token.Roles {
|
||||||
role, _, err := c.aclClient.RoleRead(roleLink.ID, &api.QueryOptions{
|
role, _, err := c.aclClient.RoleRead(roleLink.ID, &api.QueryOptions{
|
||||||
AllowStale: false,
|
AllowStale: false,
|
||||||
|
Namespace: token.Namespace,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, policyLink := range role.Policies {
|
for _, policyLink := range role.Policies {
|
||||||
allowable, err := c.policyAllowsKeystoreRead(matches, namespace, policyLink.ID)
|
allowable, err := c.policyAllowsKeystoreRead(matches, namespace, policyLink.ID, token.Namespace)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
} else if allowable {
|
} else if allowable {
|
||||||
|
@ -173,7 +174,7 @@ func (c *consulACLsAPI) canWriteService(namespace, service string, token *api.AC
|
||||||
|
|
||||||
// check each policy directly attached to the token
|
// check each policy directly attached to the token
|
||||||
for _, policyRef := range token.Policies {
|
for _, policyRef := range token.Policies {
|
||||||
if allowable, err := c.policyAllowsServiceWrite(matches, namespace, service, policyRef.ID); err != nil {
|
if allowable, err := c.policyAllowsServiceWrite(matches, namespace, service, policyRef.ID, token.Namespace); err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
} else if allowable {
|
} else if allowable {
|
||||||
return true, nil
|
return true, nil
|
||||||
|
@ -190,7 +191,7 @@ func (c *consulACLsAPI) canWriteService(namespace, service string, token *api.AC
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, policyLink := range role.Policies {
|
for _, policyLink := range role.Policies {
|
||||||
allowable, wErr := c.policyAllowsServiceWrite(matches, namespace, service, policyLink.ID)
|
allowable, wErr := c.policyAllowsServiceWrite(matches, namespace, service, policyLink.ID, token.Namespace)
|
||||||
if wErr != nil {
|
if wErr != nil {
|
||||||
return false, wErr
|
return false, wErr
|
||||||
} else if allowable {
|
} else if allowable {
|
||||||
|
@ -202,9 +203,10 @@ func (c *consulACLsAPI) canWriteService(namespace, service string, token *api.AC
|
||||||
return false, nil
|
return false, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *consulACLsAPI) policyAllowsServiceWrite(matches bool, namespace, service string, policyID string) (bool, error) {
|
func (c *consulACLsAPI) policyAllowsServiceWrite(matches bool, namespace, service string, policyID string, tokenNamespace string) (bool, error) {
|
||||||
policy, _, err := c.aclClient.PolicyRead(policyID, &api.QueryOptions{
|
policy, _, err := c.aclClient.PolicyRead(policyID, &api.QueryOptions{
|
||||||
AllowStale: false,
|
AllowStale: false,
|
||||||
|
Namespace: tokenNamespace,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
|
@ -287,9 +289,10 @@ func (cp *ConsulPolicy) allowsServiceWrite(matches bool, namespace, task string)
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *consulACLsAPI) policyAllowsKeystoreRead(matches bool, namespace, policyID string) (bool, error) {
|
func (c *consulACLsAPI) policyAllowsKeystoreRead(matches bool, namespace, policyID string, tokenNamespace string) (bool, error) {
|
||||||
policy, _, err := c.aclClient.PolicyRead(policyID, &api.QueryOptions{
|
policy, _, err := c.aclClient.PolicyRead(policyID, &api.QueryOptions{
|
||||||
AllowStale: false,
|
AllowStale: false,
|
||||||
|
Namespace: tokenNamespace,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
|
|
Loading…
Reference in New Issue