github.imxd.top/hashicorp/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 }