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  }