|
|
|
@ -20,6 +20,65 @@ type Catalog struct {
|
|
|
|
|
srv *Server |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// nodePreApply does the verification of a node before it is applied to Raft.
|
|
|
|
|
func nodePreApply(nodeName, nodeID string) error { |
|
|
|
|
if nodeName == "" { |
|
|
|
|
return fmt.Errorf("Must provide node") |
|
|
|
|
} |
|
|
|
|
if nodeID != "" { |
|
|
|
|
if _, err := uuid.ParseUUID(nodeID); err != nil { |
|
|
|
|
return fmt.Errorf("Bad node ID: %v", err) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
return nil |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func servicePreApply(service *structs.NodeService, rule acl.Authorizer) error { |
|
|
|
|
// Validate the service. This is in addition to the below since
|
|
|
|
|
// the above just hasn't been moved over yet. We should move it over
|
|
|
|
|
// in time.
|
|
|
|
|
if err := service.Validate(); err != nil { |
|
|
|
|
return err |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// If no service id, but service name, use default
|
|
|
|
|
if service.ID == "" && service.Service != "" { |
|
|
|
|
service.ID = service.Service |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Verify ServiceName provided if ID.
|
|
|
|
|
if service.ID != "" && service.Service == "" { |
|
|
|
|
return fmt.Errorf("Must provide service name with ID") |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Check the service address here and in the agent endpoint
|
|
|
|
|
// since service registration isn't synchronous.
|
|
|
|
|
if ipaddr.IsAny(service.Address) { |
|
|
|
|
return fmt.Errorf("Invalid service address") |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Apply the ACL policy if any. The 'consul' service is excluded
|
|
|
|
|
// since it is managed automatically internally (that behavior
|
|
|
|
|
// is going away after version 0.8). We check this same policy
|
|
|
|
|
// later if version 0.8 is enabled, so we can eventually just
|
|
|
|
|
// delete this and do all the ACL checks down there.
|
|
|
|
|
if service.Service != structs.ConsulServiceName { |
|
|
|
|
if rule != nil && !rule.ServiceWrite(service.Service, nil) { |
|
|
|
|
return acl.ErrPermissionDenied |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Proxies must have write permission on their destination
|
|
|
|
|
if service.Kind == structs.ServiceKindConnectProxy { |
|
|
|
|
if rule != nil && !rule.ServiceWrite(service.Proxy.DestinationServiceName, nil) { |
|
|
|
|
return acl.ErrPermissionDenied |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
return nil |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// checkPreApply does the verification of a check before it is applied to Raft.
|
|
|
|
|
func checkPreApply(check *structs.HealthCheck) { |
|
|
|
|
if check.CheckID == "" && check.Name != "" { |
|
|
|
@ -34,67 +93,25 @@ func (c *Catalog) Register(args *structs.RegisterRequest, reply *struct{}) error
|
|
|
|
|
} |
|
|
|
|
defer metrics.MeasureSince([]string{"catalog", "register"}, time.Now()) |
|
|
|
|
|
|
|
|
|
// Verify the args.
|
|
|
|
|
if args.Node == "" { |
|
|
|
|
return fmt.Errorf("Must provide node") |
|
|
|
|
} |
|
|
|
|
if args.Address == "" && !args.SkipNodeUpdate { |
|
|
|
|
return fmt.Errorf("Must provide address if SkipNodeUpdate is not set") |
|
|
|
|
} |
|
|
|
|
if args.ID != "" { |
|
|
|
|
if _, err := uuid.ParseUUID(string(args.ID)); err != nil { |
|
|
|
|
return fmt.Errorf("Bad node ID: %v", err) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Fetch the ACL token, if any.
|
|
|
|
|
rule, err := c.srv.ResolveToken(args.Token) |
|
|
|
|
if err != nil { |
|
|
|
|
return err |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Verify the args.
|
|
|
|
|
if err := nodePreApply(args.Node, string(args.ID)); err != nil { |
|
|
|
|
return err |
|
|
|
|
} |
|
|
|
|
if args.Address == "" && !args.SkipNodeUpdate { |
|
|
|
|
return fmt.Errorf("Must provide address if SkipNodeUpdate is not set") |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Handle a service registration.
|
|
|
|
|
if args.Service != nil { |
|
|
|
|
// Validate the service. This is in addition to the below since
|
|
|
|
|
// the above just hasn't been moved over yet. We should move it over
|
|
|
|
|
// in time.
|
|
|
|
|
if err := args.Service.Validate(); err != nil { |
|
|
|
|
if err := servicePreApply(args.Service, rule); err != nil { |
|
|
|
|
return err |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// If no service id, but service name, use default
|
|
|
|
|
if args.Service.ID == "" && args.Service.Service != "" { |
|
|
|
|
args.Service.ID = args.Service.Service |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Verify ServiceName provided if ID.
|
|
|
|
|
if args.Service.ID != "" && args.Service.Service == "" { |
|
|
|
|
return fmt.Errorf("Must provide service name with ID") |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Check the service address here and in the agent endpoint
|
|
|
|
|
// since service registration isn't synchronous.
|
|
|
|
|
if ipaddr.IsAny(args.Service.Address) { |
|
|
|
|
return fmt.Errorf("Invalid service address") |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Apply the ACL policy if any. The 'consul' service is excluded
|
|
|
|
|
// since it is managed automatically internally (that behavior
|
|
|
|
|
// is going away after version 0.8). We check this same policy
|
|
|
|
|
// later if version 0.8 is enabled, so we can eventually just
|
|
|
|
|
// delete this and do all the ACL checks down there.
|
|
|
|
|
if args.Service.Service != structs.ConsulServiceName { |
|
|
|
|
if rule != nil && !rule.ServiceWrite(args.Service.Service, nil) { |
|
|
|
|
return acl.ErrPermissionDenied |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Proxies must have write permission on their destination
|
|
|
|
|
if args.Service.Kind == structs.ServiceKindConnectProxy { |
|
|
|
|
if rule != nil && !rule.ServiceWrite(args.Service.Proxy.DestinationServiceName, nil) { |
|
|
|
|
return acl.ErrPermissionDenied |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Move the old format single check into the slice, and fixup IDs.
|
|
|
|
|