github.com/DerekStrickland/consul@v1.4.5/agent/consul/acl_endpoint_legacy.go (about)

     1  package consul
     2  
     3  import (
     4  	"fmt"
     5  	"time"
     6  
     7  	"github.com/armon/go-metrics"
     8  	"github.com/hashicorp/consul/acl"
     9  	"github.com/hashicorp/consul/agent/consul/state"
    10  	"github.com/hashicorp/consul/agent/structs"
    11  	"github.com/hashicorp/consul/lib"
    12  	"github.com/hashicorp/go-memdb"
    13  )
    14  
    15  // Bootstrap is used to perform a one-time ACL bootstrap operation on
    16  // a cluster to get the first management token.
    17  func (a *ACL) Bootstrap(args *structs.DCSpecificRequest, reply *structs.ACL) error {
    18  	if done, err := a.srv.forward("ACL.Bootstrap", args, args, reply); done {
    19  		return err
    20  	}
    21  
    22  	// Verify we are allowed to serve this request
    23  	if !a.srv.InACLDatacenter() {
    24  		return acl.ErrDisabled
    25  	}
    26  
    27  	// By doing some pre-checks we can head off later bootstrap attempts
    28  	// without having to run them through Raft, which should curb abuse.
    29  	state := a.srv.fsm.State()
    30  	allowed, _, err := state.CanBootstrapACLToken()
    31  	if err != nil {
    32  		return err
    33  	}
    34  	if !allowed {
    35  		return structs.ACLBootstrapNotAllowedErr
    36  	}
    37  
    38  	// Propose a new token.
    39  	token, err := lib.GenerateUUID(a.srv.checkTokenUUID)
    40  	if err != nil {
    41  		return fmt.Errorf("failed to make random token: %v", err)
    42  	}
    43  
    44  	// Attempt a bootstrap.
    45  	req := structs.ACLRequest{
    46  		Datacenter: a.srv.config.ACLDatacenter,
    47  		Op:         structs.ACLBootstrapNow,
    48  		ACL: structs.ACL{
    49  			ID:   token,
    50  			Name: "Bootstrap Token",
    51  			Type: structs.ACLTokenTypeManagement,
    52  		},
    53  	}
    54  	resp, err := a.srv.raftApply(structs.ACLRequestType, &req)
    55  	if err != nil {
    56  		return err
    57  	}
    58  	switch v := resp.(type) {
    59  	case error:
    60  		return v
    61  
    62  	case *structs.ACL:
    63  		*reply = *v
    64  
    65  	default:
    66  		// Just log this, since it looks like the bootstrap may have
    67  		// completed.
    68  		a.srv.logger.Printf("[ERR] consul.acl: Unexpected response during bootstrap: %T", v)
    69  	}
    70  
    71  	a.srv.logger.Printf("[INFO] consul.acl: ACL bootstrap completed")
    72  	return nil
    73  }
    74  
    75  // aclApplyInternal is used to apply an ACL request after it has been vetted that
    76  // this is a valid operation. It is used when users are updating ACLs, in which
    77  // case we check their token to make sure they have management privileges. It is
    78  // also used for ACL replication. We want to run the replicated ACLs through the
    79  // same checks on the change itself.
    80  func aclApplyInternal(srv *Server, args *structs.ACLRequest, reply *string) error {
    81  	// All ACLs must have an ID by this point.
    82  	if args.ACL.ID == "" {
    83  		return fmt.Errorf("Missing ACL ID")
    84  	}
    85  
    86  	switch args.Op {
    87  	case structs.ACLSet:
    88  		// Verify the ACL type
    89  		switch args.ACL.Type {
    90  		case structs.ACLTokenTypeClient:
    91  		case structs.ACLTokenTypeManagement:
    92  		default:
    93  			return fmt.Errorf("Invalid ACL Type")
    94  		}
    95  
    96  		_, existing, _ := srv.fsm.State().ACLTokenGetBySecret(nil, args.ACL.ID)
    97  		if existing != nil && len(existing.Policies) > 0 {
    98  			return fmt.Errorf("Cannot use legacy endpoint to modify a non-legacy token")
    99  		}
   100  
   101  		// Verify this is not a root ACL
   102  		if acl.RootAuthorizer(args.ACL.ID) != nil {
   103  			return acl.PermissionDeniedError{Cause: "Cannot modify root ACL"}
   104  		}
   105  
   106  		// Ensure that we allow more permissive rule formats for legacy tokens,
   107  		// but that we correct them on the way into the system.
   108  		//
   109  		// DEPRECATED (ACL-Legacy-Compat)
   110  		correctedRules := structs.SanitizeLegacyACLTokenRules(args.ACL.Rules)
   111  		if correctedRules != "" {
   112  			args.ACL.Rules = correctedRules
   113  		}
   114  
   115  		// Validate the rules compile
   116  		_, err := acl.NewPolicyFromSource("", 0, args.ACL.Rules, acl.SyntaxLegacy, srv.sentinel)
   117  		if err != nil {
   118  			return fmt.Errorf("ACL rule compilation failed: %v", err)
   119  		}
   120  
   121  	case structs.ACLDelete:
   122  		if args.ACL.ID == anonymousToken {
   123  			return acl.PermissionDeniedError{Cause: "Cannot delete anonymous token"}
   124  		}
   125  
   126  	default:
   127  		return fmt.Errorf("Invalid ACL Operation")
   128  	}
   129  
   130  	// Apply the update
   131  	resp, err := srv.raftApply(structs.ACLRequestType, args)
   132  	if err != nil {
   133  		srv.logger.Printf("[ERR] consul.acl: Apply failed: %v", err)
   134  		return err
   135  	}
   136  	if respErr, ok := resp.(error); ok {
   137  		return respErr
   138  	}
   139  
   140  	// Check if the return type is a string
   141  	if respString, ok := resp.(string); ok {
   142  		*reply = respString
   143  	}
   144  
   145  	return nil
   146  }
   147  
   148  // Apply is used to apply a modifying request to the data store. This should
   149  // only be used for operations that modify the data
   150  func (a *ACL) Apply(args *structs.ACLRequest, reply *string) error {
   151  	if done, err := a.srv.forward("ACL.Apply", args, args, reply); done {
   152  		return err
   153  	}
   154  	defer metrics.MeasureSince([]string{"acl", "apply"}, time.Now())
   155  
   156  	// Verify we are allowed to serve this request
   157  	if !a.srv.ACLsEnabled() {
   158  		return acl.ErrDisabled
   159  	}
   160  
   161  	// Verify token is permitted to modify ACLs
   162  	if rule, err := a.srv.ResolveToken(args.Token); err != nil {
   163  		return err
   164  	} else if rule == nil || !rule.ACLWrite() {
   165  		return acl.ErrPermissionDenied
   166  	}
   167  
   168  	// If no ID is provided, generate a new ID. This must be done prior to
   169  	// appending to the Raft log, because the ID is not deterministic. Once
   170  	// the entry is in the log, the state update MUST be deterministic or
   171  	// the followers will not converge.
   172  	if args.Op == structs.ACLSet && args.ACL.ID == "" {
   173  		var err error
   174  		args.ACL.ID, err = lib.GenerateUUID(a.srv.checkTokenUUID)
   175  		if err != nil {
   176  			return err
   177  		}
   178  	}
   179  
   180  	// Do the apply now that this update is vetted.
   181  	if err := aclApplyInternal(a.srv, args, reply); err != nil {
   182  		return err
   183  	}
   184  
   185  	// Clear the cache if applicable
   186  	if args.ACL.ID != "" {
   187  		a.srv.acls.cache.RemoveIdentity(args.ACL.ID)
   188  	}
   189  
   190  	return nil
   191  }
   192  
   193  // Get is used to retrieve a single ACL
   194  func (a *ACL) Get(args *structs.ACLSpecificRequest,
   195  	reply *structs.IndexedACLs) error {
   196  	if done, err := a.srv.forward("ACL.Get", args, args, reply); done {
   197  		return err
   198  	}
   199  
   200  	// Verify we are allowed to serve this request
   201  	if !a.srv.ACLsEnabled() {
   202  		return acl.ErrDisabled
   203  	}
   204  
   205  	return a.srv.blockingQuery(&args.QueryOptions,
   206  		&reply.QueryMeta,
   207  		func(ws memdb.WatchSet, state *state.Store) error {
   208  			index, token, err := state.ACLTokenGetBySecret(ws, args.ACL)
   209  			if err != nil {
   210  				return err
   211  			}
   212  
   213  			// converting an ACLToken to an ACL will return nil and an error
   214  			// (which we ignore) when it is unconvertible.
   215  			var acl *structs.ACL
   216  			if token != nil {
   217  				acl, _ = token.Convert()
   218  			}
   219  
   220  			reply.Index = index
   221  			if acl != nil {
   222  				reply.ACLs = structs.ACLs{acl}
   223  			} else {
   224  				reply.ACLs = nil
   225  			}
   226  			return nil
   227  		})
   228  }
   229  
   230  // List is used to list all the ACLs
   231  func (a *ACL) List(args *structs.DCSpecificRequest,
   232  	reply *structs.IndexedACLs) error {
   233  	if done, err := a.srv.forward("ACL.List", args, args, reply); done {
   234  		return err
   235  	}
   236  
   237  	// Verify we are allowed to serve this request
   238  	if !a.srv.ACLsEnabled() {
   239  		return acl.ErrDisabled
   240  	}
   241  
   242  	// Verify token is permitted to list ACLs
   243  	if rule, err := a.srv.ResolveToken(args.Token); err != nil {
   244  		return err
   245  	} else if rule == nil || !rule.ACLWrite() {
   246  		return acl.ErrPermissionDenied
   247  	}
   248  
   249  	return a.srv.blockingQuery(&args.QueryOptions,
   250  		&reply.QueryMeta,
   251  		func(ws memdb.WatchSet, state *state.Store) error {
   252  			index, tokens, err := state.ACLTokenList(ws, false, true, "")
   253  			if err != nil {
   254  				return err
   255  			}
   256  
   257  			var acls structs.ACLs
   258  			for _, token := range tokens {
   259  				if acl, err := token.Convert(); err == nil && acl != nil {
   260  					acls = append(acls, acl)
   261  				}
   262  			}
   263  
   264  			reply.Index, reply.ACLs = index, acls
   265  			return nil
   266  		})
   267  }