github.imxd.top/hashicorp/consul@v1.4.5/agent/acl_test.go (about) 1 package agent 2 3 import ( 4 "fmt" 5 "io" 6 "log" 7 "os" 8 "testing" 9 "time" 10 11 "github.com/armon/go-metrics" 12 "github.com/hashicorp/consul/acl" 13 "github.com/hashicorp/consul/agent/config" 14 "github.com/hashicorp/consul/agent/consul" 15 "github.com/hashicorp/consul/agent/local" 16 "github.com/hashicorp/consul/agent/structs" 17 "github.com/hashicorp/consul/lib" 18 "github.com/hashicorp/consul/logger" 19 "github.com/hashicorp/consul/types" 20 "github.com/hashicorp/serf/serf" 21 22 "github.com/stretchr/testify/require" 23 ) 24 25 type TestACLAgent struct { 26 // Name is an optional name of the agent. 27 Name string 28 29 HCL string 30 31 // Config is the agent configuration. If Config is nil then 32 // TestConfig() is used. If Config.DataDir is set then it is 33 // the callers responsibility to clean up the data directory. 34 // Otherwise, a temporary data directory is created and removed 35 // when Shutdown() is called. 36 Config *config.RuntimeConfig 37 38 // LogOutput is the sink for the logs. If nil, logs are written 39 // to os.Stderr. 40 LogOutput io.Writer 41 42 // LogWriter is used for streaming logs. 43 LogWriter *logger.LogWriter 44 45 // DataDir is the data directory which is used when Config.DataDir 46 // is not set. It is created automatically and removed when 47 // Shutdown() is called. 48 DataDir string 49 50 resolveTokenFn func(string) (acl.Authorizer, error) 51 52 *Agent 53 } 54 55 // NewTestACLAGent does just enough so that all the code within agent/acl.go can work 56 // Basically it needs a local state for some of the vet* functions, a logger and a delegate. 57 // The key is that we are the delegate so we can control the ResolveToken responses 58 func NewTestACLAgent(name string, hcl string, resolveFn func(string) (acl.Authorizer, error)) *TestACLAgent { 59 a := &TestACLAgent{Name: name, HCL: hcl, resolveTokenFn: resolveFn} 60 hclDataDir := `data_dir = "acl-agent"` 61 62 a.Config = TestConfig( 63 config.Source{Name: a.Name, Format: "hcl", Data: a.HCL}, 64 config.Source{Name: a.Name + ".data_dir", Format: "hcl", Data: hclDataDir}, 65 ) 66 67 agent, err := New(a.Config) 68 if err != nil { 69 panic(fmt.Sprintf("Error creating agent: %v", err)) 70 } 71 a.Agent = agent 72 73 logOutput := a.LogOutput 74 if logOutput == nil { 75 logOutput = os.Stderr 76 } 77 agent.LogOutput = logOutput 78 agent.LogWriter = a.LogWriter 79 agent.logger = log.New(logOutput, a.Name+" - ", log.LstdFlags|log.Lmicroseconds) 80 agent.MemSink = metrics.NewInmemSink(1*time.Second, time.Minute) 81 82 a.Agent.delegate = a 83 a.Agent.State = local.NewState(LocalConfig(a.Config), a.Agent.logger, a.Agent.tokens) 84 a.Agent.State.TriggerSyncChanges = func() {} 85 return a 86 } 87 88 func (a *TestACLAgent) ACLsEnabled() bool { 89 // the TestACLAgent always has ACLs enabled 90 return true 91 } 92 93 func (a *TestACLAgent) UseLegacyACLs() bool { 94 return false 95 } 96 97 func (a *TestACLAgent) ResolveToken(secretID string) (acl.Authorizer, error) { 98 if a.resolveTokenFn == nil { 99 panic("This agent is useless without providing a token resolution function") 100 } 101 102 return a.resolveTokenFn(secretID) 103 } 104 105 // All of these are stubs to satisfy the interface 106 func (a *TestACLAgent) Encrypted() bool { 107 return false 108 } 109 func (a *TestACLAgent) GetLANCoordinate() (lib.CoordinateSet, error) { 110 return nil, fmt.Errorf("Unimplemented") 111 } 112 func (a *TestACLAgent) Leave() error { 113 return fmt.Errorf("Unimplemented") 114 } 115 func (a *TestACLAgent) LANMembers() []serf.Member { 116 return nil 117 } 118 func (a *TestACLAgent) LANMembersAllSegments() ([]serf.Member, error) { 119 return nil, fmt.Errorf("Unimplemented") 120 } 121 func (a *TestACLAgent) LANSegmentMembers(segment string) ([]serf.Member, error) { 122 return nil, fmt.Errorf("Unimplemented") 123 } 124 func (a *TestACLAgent) LocalMember() serf.Member { 125 return serf.Member{} 126 } 127 func (a *TestACLAgent) JoinLAN(addrs []string) (n int, err error) { 128 return 0, fmt.Errorf("Unimplemented") 129 } 130 func (a *TestACLAgent) RemoveFailedNode(node string) error { 131 return fmt.Errorf("Unimplemented") 132 } 133 134 func (a *TestACLAgent) RPC(method string, args interface{}, reply interface{}) error { 135 return fmt.Errorf("Unimplemented") 136 } 137 func (a *TestACLAgent) SnapshotRPC(args *structs.SnapshotRequest, in io.Reader, out io.Writer, replyFn structs.SnapshotReplyFn) error { 138 return fmt.Errorf("Unimplemented") 139 } 140 func (a *TestACLAgent) Shutdown() error { 141 return fmt.Errorf("Unimplemented") 142 } 143 func (a *TestACLAgent) Stats() map[string]map[string]string { 144 return nil 145 } 146 func (a *TestACLAgent) ReloadConfig(config *consul.Config) error { 147 return fmt.Errorf("Unimplemented") 148 } 149 150 func TestACL_Version8(t *testing.T) { 151 t.Parallel() 152 153 t.Run("version 8 disabled", func(t *testing.T) { 154 resolveFn := func(string) (acl.Authorizer, error) { 155 require.Fail(t, "should not have called delegate.ResolveToken") 156 return nil, fmt.Errorf("should not have called delegate.ResolveToken") 157 } 158 159 a := NewTestACLAgent(t.Name(), TestACLConfig()+` 160 acl_enforce_version_8 = false 161 `, resolveFn) 162 163 token, err := a.resolveToken("nope") 164 require.Nil(t, token) 165 require.Nil(t, err) 166 }) 167 168 t.Run("version 8 enabled", func(t *testing.T) { 169 called := false 170 resolveFn := func(string) (acl.Authorizer, error) { 171 called = true 172 return nil, acl.ErrNotFound 173 } 174 a := NewTestACLAgent(t.Name(), TestACLConfig()+` 175 acl_enforce_version_8 = true 176 `, resolveFn) 177 178 _, err := a.resolveToken("nope") 179 require.Error(t, err) 180 require.True(t, called) 181 }) 182 } 183 184 func TestACL_AgentMasterToken(t *testing.T) { 185 t.Parallel() 186 187 resolveFn := func(string) (acl.Authorizer, error) { 188 require.Fail(t, "should not have called delegate.ResolveToken") 189 return nil, fmt.Errorf("should not have called delegate.ResolveToken") 190 } 191 192 a := NewTestACLAgent(t.Name(), TestACLConfig(), resolveFn) 193 a.loadTokens(a.config) 194 authz, err := a.resolveToken("towel") 195 require.NotNil(t, authz) 196 require.Nil(t, err) 197 198 require.True(t, authz.AgentRead(a.config.NodeName)) 199 require.True(t, authz.AgentWrite(a.config.NodeName)) 200 require.True(t, authz.NodeRead("foobarbaz")) 201 require.False(t, authz.NodeWrite("foobarbaz", nil)) 202 } 203 204 func TestACL_RootAuthorizersDenied(t *testing.T) { 205 t.Parallel() 206 207 resolveFn := func(string) (acl.Authorizer, error) { 208 require.Fail(t, "should not have called delegate.ResolveToken") 209 return nil, fmt.Errorf("should not have called delegate.ResolveToken") 210 } 211 212 a := NewTestACLAgent(t.Name(), TestACLConfig(), resolveFn) 213 authz, err := a.resolveToken("deny") 214 require.Nil(t, authz) 215 require.Error(t, err) 216 require.True(t, acl.IsErrRootDenied(err)) 217 authz, err = a.resolveToken("allow") 218 require.Nil(t, authz) 219 require.Error(t, err) 220 require.True(t, acl.IsErrRootDenied(err)) 221 authz, err = a.resolveToken("manage") 222 require.Nil(t, authz) 223 require.Error(t, err) 224 require.True(t, acl.IsErrRootDenied(err)) 225 } 226 227 func authzFromPolicy(policy *acl.Policy) (acl.Authorizer, error) { 228 return acl.NewPolicyAuthorizer(acl.DenyAll(), []*acl.Policy{policy}, nil) 229 } 230 231 // catalogPolicy supplies some standard policies to help with testing the 232 // catalog-related vet and filter functions. 233 func catalogPolicy(token string) (acl.Authorizer, error) { 234 switch token { 235 236 case "node-ro": 237 return authzFromPolicy(&acl.Policy{ 238 NodePrefixes: []*acl.NodePolicy{ 239 &acl.NodePolicy{Name: "Node", Policy: "read"}, 240 }, 241 }) 242 case "node-rw": 243 return authzFromPolicy(&acl.Policy{ 244 NodePrefixes: []*acl.NodePolicy{ 245 &acl.NodePolicy{Name: "Node", Policy: "write"}, 246 }, 247 }) 248 case "service-ro": 249 return authzFromPolicy(&acl.Policy{ 250 ServicePrefixes: []*acl.ServicePolicy{ 251 &acl.ServicePolicy{Name: "service", Policy: "read"}, 252 }, 253 }) 254 case "service-rw": 255 return authzFromPolicy(&acl.Policy{ 256 ServicePrefixes: []*acl.ServicePolicy{ 257 &acl.ServicePolicy{Name: "service", Policy: "write"}, 258 }, 259 }) 260 case "other-rw": 261 return authzFromPolicy(&acl.Policy{ 262 ServicePrefixes: []*acl.ServicePolicy{ 263 &acl.ServicePolicy{Name: "other", Policy: "write"}, 264 }, 265 }) 266 default: 267 return nil, fmt.Errorf("unknown token %q", token) 268 } 269 } 270 271 func TestACL_vetServiceRegister(t *testing.T) { 272 t.Parallel() 273 a := NewTestACLAgent(t.Name(), TestACLConfig(), catalogPolicy) 274 275 // Register a new service, with permission. 276 err := a.vetServiceRegister("service-rw", &structs.NodeService{ 277 ID: "my-service", 278 Service: "service", 279 }) 280 require.NoError(t, err) 281 282 // Register a new service without write privs. 283 err = a.vetServiceRegister("service-ro", &structs.NodeService{ 284 ID: "my-service", 285 Service: "service", 286 }) 287 require.True(t, acl.IsErrPermissionDenied(err)) 288 289 // Try to register over a service without write privs to the existing 290 // service. 291 a.State.AddService(&structs.NodeService{ 292 ID: "my-service", 293 Service: "other", 294 }, "") 295 err = a.vetServiceRegister("service-rw", &structs.NodeService{ 296 ID: "my-service", 297 Service: "service", 298 }) 299 require.True(t, acl.IsErrPermissionDenied(err)) 300 } 301 302 func TestACL_vetServiceUpdate(t *testing.T) { 303 t.Parallel() 304 a := NewTestACLAgent(t.Name(), TestACLConfig(), catalogPolicy) 305 306 // Update a service that doesn't exist. 307 err := a.vetServiceUpdate("service-rw", "my-service") 308 require.Error(t, err) 309 require.Contains(t, err.Error(), "Unknown service") 310 311 // Update with write privs. 312 a.State.AddService(&structs.NodeService{ 313 ID: "my-service", 314 Service: "service", 315 }, "") 316 err = a.vetServiceUpdate("service-rw", "my-service") 317 require.NoError(t, err) 318 319 // Update without write privs. 320 err = a.vetServiceUpdate("service-ro", "my-service") 321 require.Error(t, err) 322 require.True(t, acl.IsErrPermissionDenied(err)) 323 } 324 325 func TestACL_vetCheckRegister(t *testing.T) { 326 t.Parallel() 327 a := NewTestACLAgent(t.Name(), TestACLConfig(), catalogPolicy) 328 329 // Register a new service check with write privs. 330 err := a.vetCheckRegister("service-rw", &structs.HealthCheck{ 331 CheckID: types.CheckID("my-check"), 332 ServiceID: "my-service", 333 ServiceName: "service", 334 }) 335 require.NoError(t, err) 336 337 // Register a new service check without write privs. 338 err = a.vetCheckRegister("service-ro", &structs.HealthCheck{ 339 CheckID: types.CheckID("my-check"), 340 ServiceID: "my-service", 341 ServiceName: "service", 342 }) 343 require.Error(t, err) 344 require.True(t, acl.IsErrPermissionDenied(err)) 345 346 // Register a new node check with write privs. 347 err = a.vetCheckRegister("node-rw", &structs.HealthCheck{ 348 CheckID: types.CheckID("my-check"), 349 }) 350 require.NoError(t, err) 351 352 // Register a new node check without write privs. 353 err = a.vetCheckRegister("node-ro", &structs.HealthCheck{ 354 CheckID: types.CheckID("my-check"), 355 }) 356 require.Error(t, err) 357 require.True(t, acl.IsErrPermissionDenied(err)) 358 359 // Try to register over a service check without write privs to the 360 // existing service. 361 a.State.AddService(&structs.NodeService{ 362 ID: "my-service", 363 Service: "service", 364 }, "") 365 a.State.AddCheck(&structs.HealthCheck{ 366 CheckID: types.CheckID("my-check"), 367 ServiceID: "my-service", 368 ServiceName: "other", 369 }, "") 370 err = a.vetCheckRegister("service-rw", &structs.HealthCheck{ 371 CheckID: types.CheckID("my-check"), 372 ServiceID: "my-service", 373 ServiceName: "service", 374 }) 375 require.Error(t, err) 376 require.True(t, acl.IsErrPermissionDenied(err)) 377 378 // Try to register over a node check without write privs to the node. 379 a.State.AddCheck(&structs.HealthCheck{ 380 CheckID: types.CheckID("my-node-check"), 381 }, "") 382 err = a.vetCheckRegister("service-rw", &structs.HealthCheck{ 383 CheckID: types.CheckID("my-node-check"), 384 ServiceID: "my-service", 385 ServiceName: "service", 386 }) 387 require.Error(t, err) 388 require.True(t, acl.IsErrPermissionDenied(err)) 389 } 390 391 func TestACL_vetCheckUpdate(t *testing.T) { 392 t.Parallel() 393 a := NewTestACLAgent(t.Name(), TestACLConfig(), catalogPolicy) 394 395 // Update a check that doesn't exist. 396 err := a.vetCheckUpdate("node-rw", "my-check") 397 require.Error(t, err) 398 require.Contains(t, err.Error(), "Unknown check") 399 400 // Update service check with write privs. 401 a.State.AddService(&structs.NodeService{ 402 ID: "my-service", 403 Service: "service", 404 }, "") 405 a.State.AddCheck(&structs.HealthCheck{ 406 CheckID: types.CheckID("my-service-check"), 407 ServiceID: "my-service", 408 ServiceName: "service", 409 }, "") 410 err = a.vetCheckUpdate("service-rw", "my-service-check") 411 require.NoError(t, err) 412 413 // Update service check without write privs. 414 err = a.vetCheckUpdate("service-ro", "my-service-check") 415 require.Error(t, err) 416 require.True(t, acl.IsErrPermissionDenied(err)) 417 418 // Update node check with write privs. 419 a.State.AddCheck(&structs.HealthCheck{ 420 CheckID: types.CheckID("my-node-check"), 421 }, "") 422 err = a.vetCheckUpdate("node-rw", "my-node-check") 423 require.NoError(t, err) 424 425 // Update without write privs. 426 err = a.vetCheckUpdate("node-ro", "my-node-check") 427 require.Error(t, err) 428 require.True(t, acl.IsErrPermissionDenied(err)) 429 } 430 431 func TestACL_filterMembers(t *testing.T) { 432 t.Parallel() 433 a := NewTestACLAgent(t.Name(), TestACLConfig(), catalogPolicy) 434 435 var members []serf.Member 436 require.NoError(t, a.filterMembers("node-ro", &members)) 437 require.Len(t, members, 0) 438 439 members = []serf.Member{ 440 serf.Member{Name: "Node 1"}, 441 serf.Member{Name: "Nope"}, 442 serf.Member{Name: "Node 2"}, 443 } 444 require.NoError(t, a.filterMembers("node-ro", &members)) 445 require.Len(t, members, 2) 446 require.Equal(t, members[0].Name, "Node 1") 447 require.Equal(t, members[1].Name, "Node 2") 448 } 449 450 func TestACL_filterServices(t *testing.T) { 451 t.Parallel() 452 a := NewTestACLAgent(t.Name(), TestACLConfig(), catalogPolicy) 453 454 services := make(map[string]*structs.NodeService) 455 require.NoError(t, a.filterServices("node-ro", &services)) 456 457 services["my-service"] = &structs.NodeService{ID: "my-service", Service: "service"} 458 services["my-other"] = &structs.NodeService{ID: "my-other", Service: "other"} 459 require.NoError(t, a.filterServices("service-ro", &services)) 460 require.Contains(t, services, "my-service") 461 require.NotContains(t, services, "my-other") 462 } 463 464 func TestACL_filterChecks(t *testing.T) { 465 t.Parallel() 466 a := NewTestACLAgent(t.Name(), TestACLConfig(), catalogPolicy) 467 468 checks := make(map[types.CheckID]*structs.HealthCheck) 469 require.NoError(t, a.filterChecks("node-ro", &checks)) 470 471 checks["my-node"] = &structs.HealthCheck{} 472 checks["my-service"] = &structs.HealthCheck{ServiceName: "service"} 473 checks["my-other"] = &structs.HealthCheck{ServiceName: "other"} 474 require.NoError(t, a.filterChecks("service-ro", &checks)) 475 fmt.Printf("filtered: %#v", checks) 476 _, ok := checks["my-node"] 477 require.False(t, ok) 478 _, ok = checks["my-service"] 479 require.True(t, ok) 480 _, ok = checks["my-other"] 481 require.False(t, ok) 482 483 checks["my-node"] = &structs.HealthCheck{} 484 checks["my-service"] = &structs.HealthCheck{ServiceName: "service"} 485 checks["my-other"] = &structs.HealthCheck{ServiceName: "other"} 486 require.NoError(t, a.filterChecks("node-ro", &checks)) 487 _, ok = checks["my-node"] 488 require.True(t, ok) 489 _, ok = checks["my-service"] 490 require.False(t, ok) 491 _, ok = checks["my-other"] 492 require.False(t, ok) 493 }