|
|
|
@ -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.
|
|
|
|
|