github.com/outbrain/consul@v1.4.5/agent/acl_endpoint.go (about) 1 package agent 2 3 import ( 4 "fmt" 5 "io/ioutil" 6 "net/http" 7 "strconv" 8 "strings" 9 "time" 10 11 "github.com/hashicorp/consul/acl" 12 "github.com/hashicorp/consul/agent/structs" 13 ) 14 15 // aclCreateResponse is used to wrap the ACL ID 16 type aclBootstrapResponse struct { 17 ID string 18 structs.ACLToken 19 } 20 21 // checkACLDisabled will return a standard response if ACLs are disabled. This 22 // returns true if they are disabled and we should not continue. 23 func (s *HTTPServer) checkACLDisabled(resp http.ResponseWriter, req *http.Request) bool { 24 if s.agent.delegate.ACLsEnabled() { 25 return false 26 } 27 28 resp.WriteHeader(http.StatusUnauthorized) 29 fmt.Fprint(resp, "ACL support disabled") 30 return true 31 } 32 33 // ACLBootstrap is used to perform a one-time ACL bootstrap operation on 34 // a cluster to get the first management token. 35 func (s *HTTPServer) ACLBootstrap(resp http.ResponseWriter, req *http.Request) (interface{}, error) { 36 if s.checkACLDisabled(resp, req) { 37 return nil, nil 38 } 39 40 args := structs.DCSpecificRequest{ 41 Datacenter: s.agent.config.Datacenter, 42 } 43 44 legacy := false 45 legacyStr := req.URL.Query().Get("legacy") 46 if legacyStr != "" { 47 legacy, _ = strconv.ParseBool(legacyStr) 48 } 49 50 if legacy && s.agent.delegate.UseLegacyACLs() { 51 var out structs.ACL 52 err := s.agent.RPC("ACL.Bootstrap", &args, &out) 53 if err != nil { 54 if strings.Contains(err.Error(), structs.ACLBootstrapNotAllowedErr.Error()) { 55 resp.WriteHeader(http.StatusForbidden) 56 fmt.Fprint(resp, acl.PermissionDeniedError{Cause: err.Error()}.Error()) 57 return nil, nil 58 } else { 59 return nil, err 60 } 61 } 62 return &aclBootstrapResponse{ID: out.ID}, nil 63 } else { 64 var out structs.ACLToken 65 err := s.agent.RPC("ACL.BootstrapTokens", &args, &out) 66 if err != nil { 67 if strings.Contains(err.Error(), structs.ACLBootstrapNotAllowedErr.Error()) { 68 resp.WriteHeader(http.StatusForbidden) 69 fmt.Fprint(resp, acl.PermissionDeniedError{Cause: err.Error()}.Error()) 70 return nil, nil 71 } else { 72 return nil, err 73 } 74 } 75 return &aclBootstrapResponse{ID: out.SecretID, ACLToken: out}, nil 76 } 77 } 78 79 func (s *HTTPServer) ACLReplicationStatus(resp http.ResponseWriter, req *http.Request) (interface{}, error) { 80 if s.checkACLDisabled(resp, req) { 81 return nil, nil 82 } 83 84 // Note that we do not forward to the ACL DC here. This is a query for 85 // any DC that's doing replication. 86 args := structs.DCSpecificRequest{} 87 s.parseSource(req, &args.Source) 88 if done := s.parse(resp, req, &args.Datacenter, &args.QueryOptions); done { 89 return nil, nil 90 } 91 92 // Make the request. 93 var out structs.ACLReplicationStatus 94 if err := s.agent.RPC("ACL.ReplicationStatus", &args, &out); err != nil { 95 return nil, err 96 } 97 return out, nil 98 } 99 100 func (s *HTTPServer) ACLRulesTranslate(resp http.ResponseWriter, req *http.Request) (interface{}, error) { 101 if s.checkACLDisabled(resp, req) { 102 return nil, nil 103 } 104 105 var token string 106 s.parseToken(req, &token) 107 rule, err := s.agent.resolveToken(token) 108 if err != nil { 109 return nil, err 110 } 111 // Should this require lesser permissions? Really the only reason to require authorization at all is 112 // to prevent external entities from DoS Consul with repeated rule translation requests 113 if rule != nil && !rule.ACLRead() { 114 return nil, acl.ErrPermissionDenied 115 } 116 117 policyBytes, err := ioutil.ReadAll(req.Body) 118 if err != nil { 119 return nil, BadRequestError{Reason: fmt.Sprintf("Failed to read body: %v", err)} 120 } 121 122 translated, err := acl.TranslateLegacyRules(policyBytes) 123 if err != nil { 124 return nil, BadRequestError{Reason: err.Error()} 125 } 126 127 resp.Write(translated) 128 return nil, nil 129 } 130 131 func (s *HTTPServer) ACLRulesTranslateLegacyToken(resp http.ResponseWriter, req *http.Request) (interface{}, error) { 132 if s.checkACLDisabled(resp, req) { 133 return nil, nil 134 } 135 136 tokenID := strings.TrimPrefix(req.URL.Path, "/v1/acl/rules/translate/") 137 if tokenID == "" { 138 return nil, BadRequestError{Reason: "Missing token ID"} 139 } 140 141 args := structs.ACLTokenGetRequest{ 142 Datacenter: s.agent.config.Datacenter, 143 TokenID: tokenID, 144 TokenIDType: structs.ACLTokenAccessor, 145 } 146 if done := s.parse(resp, req, &args.Datacenter, &args.QueryOptions); done { 147 return nil, nil 148 } 149 150 if args.Datacenter == "" { 151 args.Datacenter = s.agent.config.Datacenter 152 } 153 154 // Do not allow blocking 155 args.QueryOptions.MinQueryIndex = 0 156 157 var out structs.ACLTokenResponse 158 defer setMeta(resp, &out.QueryMeta) 159 if err := s.agent.RPC("ACL.TokenRead", &args, &out); err != nil { 160 return nil, err 161 } 162 163 if out.Token == nil { 164 return nil, acl.ErrNotFound 165 } 166 167 if out.Token.Rules == "" { 168 return nil, fmt.Errorf("The specified token does not have any rules set") 169 } 170 171 translated, err := acl.TranslateLegacyRules([]byte(out.Token.Rules)) 172 if err != nil { 173 return nil, fmt.Errorf("Failed to parse legacy rules: %v", err) 174 } 175 176 resp.Write(translated) 177 return nil, nil 178 } 179 180 func (s *HTTPServer) ACLPolicyList(resp http.ResponseWriter, req *http.Request) (interface{}, error) { 181 if s.checkACLDisabled(resp, req) { 182 return nil, nil 183 } 184 185 var args structs.ACLPolicyListRequest 186 if done := s.parse(resp, req, &args.Datacenter, &args.QueryOptions); done { 187 return nil, nil 188 } 189 190 if args.Datacenter == "" { 191 args.Datacenter = s.agent.config.Datacenter 192 } 193 194 var out structs.ACLPolicyListResponse 195 defer setMeta(resp, &out.QueryMeta) 196 if err := s.agent.RPC("ACL.PolicyList", &args, &out); err != nil { 197 return nil, err 198 } 199 200 // make sure we return an array and not nil 201 if out.Policies == nil { 202 out.Policies = make(structs.ACLPolicyListStubs, 0) 203 } 204 205 return out.Policies, nil 206 } 207 208 func (s *HTTPServer) ACLPolicyCRUD(resp http.ResponseWriter, req *http.Request) (interface{}, error) { 209 if s.checkACLDisabled(resp, req) { 210 return nil, nil 211 } 212 213 var fn func(resp http.ResponseWriter, req *http.Request, policyID string) (interface{}, error) 214 215 switch req.Method { 216 case "GET": 217 fn = s.ACLPolicyRead 218 219 case "PUT": 220 fn = s.ACLPolicyWrite 221 222 case "DELETE": 223 fn = s.ACLPolicyDelete 224 225 default: 226 return nil, MethodNotAllowedError{req.Method, []string{"GET", "PUT", "DELETE"}} 227 } 228 229 policyID := strings.TrimPrefix(req.URL.Path, "/v1/acl/policy/") 230 if policyID == "" && req.Method != "PUT" { 231 return nil, BadRequestError{Reason: "Missing policy ID"} 232 } 233 234 return fn(resp, req, policyID) 235 } 236 237 func (s *HTTPServer) ACLPolicyRead(resp http.ResponseWriter, req *http.Request, policyID string) (interface{}, error) { 238 args := structs.ACLPolicyGetRequest{ 239 Datacenter: s.agent.config.Datacenter, 240 PolicyID: policyID, 241 } 242 if done := s.parse(resp, req, &args.Datacenter, &args.QueryOptions); done { 243 return nil, nil 244 } 245 246 if args.Datacenter == "" { 247 args.Datacenter = s.agent.config.Datacenter 248 } 249 250 var out structs.ACLPolicyResponse 251 defer setMeta(resp, &out.QueryMeta) 252 if err := s.agent.RPC("ACL.PolicyRead", &args, &out); err != nil { 253 return nil, err 254 } 255 256 if out.Policy == nil { 257 return nil, acl.ErrNotFound 258 } 259 260 return out.Policy, nil 261 } 262 263 func (s *HTTPServer) ACLPolicyCreate(resp http.ResponseWriter, req *http.Request) (interface{}, error) { 264 if s.checkACLDisabled(resp, req) { 265 return nil, nil 266 } 267 268 return s.ACLPolicyWrite(resp, req, "") 269 } 270 271 // fixCreateTimeAndHash is used to help in decoding the CreateTime and Hash 272 // attributes from the ACL Token/Policy create/update requests. It is needed 273 // to help mapstructure decode things properly when decodeBody is used. 274 func fixCreateTimeAndHash(raw interface{}) error { 275 rawMap, ok := raw.(map[string]interface{}) 276 if !ok { 277 return nil 278 } 279 280 if val, ok := rawMap["CreateTime"]; ok { 281 if sval, ok := val.(string); ok { 282 t, err := time.Parse(time.RFC3339, sval) 283 if err != nil { 284 return err 285 } 286 rawMap["CreateTime"] = t 287 } 288 } 289 290 if val, ok := rawMap["Hash"]; ok { 291 if sval, ok := val.(string); ok { 292 rawMap["Hash"] = []byte(sval) 293 } 294 } 295 return nil 296 } 297 298 func (s *HTTPServer) ACLPolicyWrite(resp http.ResponseWriter, req *http.Request, policyID string) (interface{}, error) { 299 args := structs.ACLPolicySetRequest{ 300 Datacenter: s.agent.config.Datacenter, 301 } 302 s.parseToken(req, &args.Token) 303 304 if err := decodeBody(req, &args.Policy, fixCreateTimeAndHash); err != nil { 305 return nil, BadRequestError{Reason: fmt.Sprintf("Policy decoding failed: %v", err)} 306 } 307 308 args.Policy.Syntax = acl.SyntaxCurrent 309 310 if args.Policy.ID != "" && args.Policy.ID != policyID { 311 return nil, BadRequestError{Reason: "Policy ID in URL and payload do not match"} 312 } else if args.Policy.ID == "" { 313 args.Policy.ID = policyID 314 } 315 316 var out structs.ACLPolicy 317 if err := s.agent.RPC("ACL.PolicySet", args, &out); err != nil { 318 return nil, err 319 } 320 321 return &out, nil 322 } 323 324 func (s *HTTPServer) ACLPolicyDelete(resp http.ResponseWriter, req *http.Request, policyID string) (interface{}, error) { 325 args := structs.ACLPolicyDeleteRequest{ 326 Datacenter: s.agent.config.Datacenter, 327 PolicyID: policyID, 328 } 329 s.parseToken(req, &args.Token) 330 331 var ignored string 332 if err := s.agent.RPC("ACL.PolicyDelete", args, &ignored); err != nil { 333 return nil, err 334 } 335 336 return true, nil 337 } 338 339 func (s *HTTPServer) ACLTokenList(resp http.ResponseWriter, req *http.Request) (interface{}, error) { 340 if s.checkACLDisabled(resp, req) { 341 return nil, nil 342 } 343 344 args := &structs.ACLTokenListRequest{ 345 IncludeLocal: true, 346 IncludeGlobal: true, 347 } 348 if done := s.parse(resp, req, &args.Datacenter, &args.QueryOptions); done { 349 return nil, nil 350 } 351 352 if args.Datacenter == "" { 353 args.Datacenter = s.agent.config.Datacenter 354 } 355 356 args.Policy = req.URL.Query().Get("policy") 357 358 var out structs.ACLTokenListResponse 359 defer setMeta(resp, &out.QueryMeta) 360 if err := s.agent.RPC("ACL.TokenList", &args, &out); err != nil { 361 return nil, err 362 } 363 364 return out.Tokens, nil 365 } 366 367 func (s *HTTPServer) ACLTokenCRUD(resp http.ResponseWriter, req *http.Request) (interface{}, error) { 368 if s.checkACLDisabled(resp, req) { 369 return nil, nil 370 } 371 372 var fn func(resp http.ResponseWriter, req *http.Request, tokenID string) (interface{}, error) 373 374 switch req.Method { 375 case "GET": 376 fn = s.ACLTokenGet 377 378 case "PUT": 379 fn = s.ACLTokenSet 380 381 case "DELETE": 382 fn = s.ACLTokenDelete 383 384 default: 385 return nil, MethodNotAllowedError{req.Method, []string{"GET", "PUT", "DELETE"}} 386 } 387 388 tokenID := strings.TrimPrefix(req.URL.Path, "/v1/acl/token/") 389 if strings.HasSuffix(tokenID, "/clone") && req.Method == "PUT" { 390 tokenID = tokenID[:len(tokenID)-6] 391 fn = s.ACLTokenClone 392 } 393 if tokenID == "" && req.Method != "PUT" { 394 return nil, BadRequestError{Reason: "Missing token ID"} 395 } 396 397 return fn(resp, req, tokenID) 398 } 399 400 func (s *HTTPServer) ACLTokenSelf(resp http.ResponseWriter, req *http.Request) (interface{}, error) { 401 if s.checkACLDisabled(resp, req) { 402 return nil, nil 403 } 404 405 args := structs.ACLTokenGetRequest{ 406 TokenIDType: structs.ACLTokenSecret, 407 } 408 409 if done := s.parse(resp, req, &args.Datacenter, &args.QueryOptions); done { 410 return nil, nil 411 } 412 413 // copy the token parameter to the ID 414 args.TokenID = args.Token 415 416 if args.Datacenter == "" { 417 args.Datacenter = s.agent.config.Datacenter 418 } 419 420 var out structs.ACLTokenResponse 421 defer setMeta(resp, &out.QueryMeta) 422 if err := s.agent.RPC("ACL.TokenRead", &args, &out); err != nil { 423 return nil, err 424 } 425 426 if out.Token == nil { 427 return nil, acl.ErrNotFound 428 } 429 430 return out.Token, nil 431 } 432 433 func (s *HTTPServer) ACLTokenCreate(resp http.ResponseWriter, req *http.Request) (interface{}, error) { 434 if s.checkACLDisabled(resp, req) { 435 return nil, nil 436 } 437 438 return s.ACLTokenSet(resp, req, "") 439 } 440 441 func (s *HTTPServer) ACLTokenGet(resp http.ResponseWriter, req *http.Request, tokenID string) (interface{}, error) { 442 args := structs.ACLTokenGetRequest{ 443 Datacenter: s.agent.config.Datacenter, 444 TokenID: tokenID, 445 TokenIDType: structs.ACLTokenAccessor, 446 } 447 448 if done := s.parse(resp, req, &args.Datacenter, &args.QueryOptions); done { 449 return nil, nil 450 } 451 452 if args.Datacenter == "" { 453 args.Datacenter = s.agent.config.Datacenter 454 } 455 456 var out structs.ACLTokenResponse 457 defer setMeta(resp, &out.QueryMeta) 458 if err := s.agent.RPC("ACL.TokenRead", &args, &out); err != nil { 459 return nil, err 460 } 461 462 if out.Token == nil { 463 return nil, acl.ErrNotFound 464 } 465 466 return out.Token, nil 467 } 468 469 func (s *HTTPServer) ACLTokenSet(resp http.ResponseWriter, req *http.Request, tokenID string) (interface{}, error) { 470 args := structs.ACLTokenSetRequest{ 471 Datacenter: s.agent.config.Datacenter, 472 } 473 s.parseToken(req, &args.Token) 474 475 if err := decodeBody(req, &args.ACLToken, fixCreateTimeAndHash); err != nil { 476 return nil, BadRequestError{Reason: fmt.Sprintf("Token decoding failed: %v", err)} 477 } 478 479 if args.ACLToken.AccessorID != "" && args.ACLToken.AccessorID != tokenID { 480 return nil, BadRequestError{Reason: "Token Accessor ID in URL and payload do not match"} 481 } else if args.ACLToken.AccessorID == "" { 482 args.ACLToken.AccessorID = tokenID 483 } 484 485 var out structs.ACLToken 486 if err := s.agent.RPC("ACL.TokenSet", args, &out); err != nil { 487 return nil, err 488 } 489 490 return &out, nil 491 } 492 493 func (s *HTTPServer) ACLTokenDelete(resp http.ResponseWriter, req *http.Request, tokenID string) (interface{}, error) { 494 args := structs.ACLTokenDeleteRequest{ 495 Datacenter: s.agent.config.Datacenter, 496 TokenID: tokenID, 497 } 498 s.parseToken(req, &args.Token) 499 500 var ignored string 501 if err := s.agent.RPC("ACL.TokenDelete", args, &ignored); err != nil { 502 return nil, err 503 } 504 return true, nil 505 } 506 507 func (s *HTTPServer) ACLTokenClone(resp http.ResponseWriter, req *http.Request, tokenID string) (interface{}, error) { 508 if s.checkACLDisabled(resp, req) { 509 return nil, nil 510 } 511 512 args := structs.ACLTokenSetRequest{ 513 Datacenter: s.agent.config.Datacenter, 514 } 515 516 if err := decodeBody(req, &args.ACLToken, fixCreateTimeAndHash); err != nil && err.Error() != "EOF" { 517 return nil, BadRequestError{Reason: fmt.Sprintf("Token decoding failed: %v", err)} 518 } 519 s.parseToken(req, &args.Token) 520 521 // Set this for the ID to clone 522 args.ACLToken.AccessorID = tokenID 523 524 var out structs.ACLToken 525 if err := s.agent.RPC("ACL.TokenClone", args, &out); err != nil { 526 return nil, err 527 } 528 529 return &out, nil 530 }