github.com/wallyworld/juju@v0.0.0-20161013125918-6cf1bc9d917a/apiserver/firewaller/firewaller.go (about)

     1  // Copyright 2014 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package firewaller
     5  
     6  import (
     7  	"github.com/juju/errors"
     8  	"gopkg.in/juju/names.v2"
     9  
    10  	"github.com/juju/juju/apiserver/common"
    11  	"github.com/juju/juju/apiserver/common/cloudspec"
    12  	"github.com/juju/juju/apiserver/facade"
    13  	"github.com/juju/juju/apiserver/params"
    14  	"github.com/juju/juju/network"
    15  	"github.com/juju/juju/state"
    16  	"github.com/juju/juju/state/stateenvirons"
    17  	"github.com/juju/juju/state/watcher"
    18  )
    19  
    20  func init() {
    21  	// Version 0 is no longer supported.
    22  	common.RegisterStandardFacade("Firewaller", 3, NewFirewallerAPI)
    23  }
    24  
    25  // FirewallerAPI provides access to the Firewaller API facade.
    26  type FirewallerAPI struct {
    27  	*common.LifeGetter
    28  	*common.ModelWatcher
    29  	*common.AgentEntityWatcher
    30  	*common.UnitsWatcher
    31  	*common.ModelMachinesWatcher
    32  	*common.InstanceIdGetter
    33  	cloudspec.CloudSpecAPI
    34  
    35  	st            *state.State
    36  	resources     facade.Resources
    37  	authorizer    facade.Authorizer
    38  	accessUnit    common.GetAuthFunc
    39  	accessService common.GetAuthFunc
    40  	accessMachine common.GetAuthFunc
    41  	accessEnviron common.GetAuthFunc
    42  }
    43  
    44  // NewFirewallerAPI creates a new server-side FirewallerAPI facade.
    45  func NewFirewallerAPI(
    46  	st *state.State,
    47  	resources facade.Resources,
    48  	authorizer facade.Authorizer,
    49  ) (*FirewallerAPI, error) {
    50  	if !authorizer.AuthModelManager() {
    51  		// Firewaller must run as environment manager.
    52  		return nil, common.ErrPerm
    53  	}
    54  	// Set up the various authorization checkers.
    55  	accessEnviron := common.AuthFuncForTagKind(names.ModelTagKind)
    56  	accessUnit := common.AuthFuncForTagKind(names.UnitTagKind)
    57  	accessService := common.AuthFuncForTagKind(names.ApplicationTagKind)
    58  	accessMachine := common.AuthFuncForTagKind(names.MachineTagKind)
    59  	accessUnitOrService := common.AuthEither(accessUnit, accessService)
    60  	accessUnitServiceOrMachine := common.AuthEither(accessUnitOrService, accessMachine)
    61  
    62  	// Life() is supported for units, services or machines.
    63  	lifeGetter := common.NewLifeGetter(
    64  		st,
    65  		accessUnitServiceOrMachine,
    66  	)
    67  	// ModelConfig() and WatchForModelConfigChanges() are allowed
    68  	// with unrestriced access.
    69  	modelWatcher := common.NewModelWatcher(
    70  		st,
    71  		resources,
    72  		authorizer,
    73  	)
    74  	// Watch() is supported for applications only.
    75  	entityWatcher := common.NewAgentEntityWatcher(
    76  		st,
    77  		resources,
    78  		accessService,
    79  	)
    80  	// WatchUnits() is supported for machines.
    81  	unitsWatcher := common.NewUnitsWatcher(st,
    82  		resources,
    83  		accessMachine,
    84  	)
    85  	// WatchModelMachines() is allowed with unrestricted access.
    86  	machinesWatcher := common.NewModelMachinesWatcher(
    87  		st,
    88  		resources,
    89  		authorizer,
    90  	)
    91  	// InstanceId() is supported for machines.
    92  	instanceIdGetter := common.NewInstanceIdGetter(
    93  		st,
    94  		accessMachine,
    95  	)
    96  
    97  	environConfigGetter := stateenvirons.EnvironConfigGetter{st}
    98  	cloudSpecAPI := cloudspec.NewCloudSpec(environConfigGetter.CloudSpec, common.AuthFuncForTag(st.ModelTag()))
    99  
   100  	return &FirewallerAPI{
   101  		LifeGetter:           lifeGetter,
   102  		ModelWatcher:         modelWatcher,
   103  		AgentEntityWatcher:   entityWatcher,
   104  		UnitsWatcher:         unitsWatcher,
   105  		ModelMachinesWatcher: machinesWatcher,
   106  		InstanceIdGetter:     instanceIdGetter,
   107  		CloudSpecAPI:         cloudSpecAPI,
   108  		st:                   st,
   109  		resources:            resources,
   110  		authorizer:           authorizer,
   111  		accessUnit:           accessUnit,
   112  		accessService:        accessService,
   113  		accessMachine:        accessMachine,
   114  		accessEnviron:        accessEnviron,
   115  	}, nil
   116  }
   117  
   118  // WatchOpenedPorts returns a new StringsWatcher for each given
   119  // environment tag.
   120  func (f *FirewallerAPI) WatchOpenedPorts(args params.Entities) (params.StringsWatchResults, error) {
   121  	result := params.StringsWatchResults{
   122  		Results: make([]params.StringsWatchResult, len(args.Entities)),
   123  	}
   124  	if len(args.Entities) == 0 {
   125  		return result, nil
   126  	}
   127  	canWatch, err := f.accessEnviron()
   128  	if err != nil {
   129  		return params.StringsWatchResults{}, errors.Trace(err)
   130  	}
   131  	for i, entity := range args.Entities {
   132  		tag, err := names.ParseTag(entity.Tag)
   133  		if err != nil {
   134  			result.Results[i].Error = common.ServerError(common.ErrPerm)
   135  			continue
   136  		}
   137  		if !canWatch(tag) {
   138  			result.Results[i].Error = common.ServerError(common.ErrPerm)
   139  			continue
   140  		}
   141  		watcherId, initial, err := f.watchOneEnvironOpenedPorts(tag)
   142  		if err != nil {
   143  			result.Results[i].Error = common.ServerError(err)
   144  			continue
   145  		}
   146  		result.Results[i].StringsWatcherId = watcherId
   147  		result.Results[i].Changes = initial
   148  	}
   149  	return result, nil
   150  }
   151  
   152  func (f *FirewallerAPI) watchOneEnvironOpenedPorts(tag names.Tag) (string, []string, error) {
   153  	// NOTE: tag is ignored, as there is only one environment in the
   154  	// state DB. Once this changes, change the code below accordingly.
   155  	watch := f.st.WatchOpenedPorts()
   156  	// Consume the initial event and forward it to the result.
   157  	if changes, ok := <-watch.Changes(); ok {
   158  		return f.resources.Register(watch), changes, nil
   159  	}
   160  	return "", nil, watcher.EnsureErr(watch)
   161  }
   162  
   163  // GetMachinePorts returns the port ranges opened on a machine for the specified
   164  // subnet as a map mapping port ranges to the tags of the units that opened
   165  // them.
   166  func (f *FirewallerAPI) GetMachinePorts(args params.MachinePortsParams) (params.MachinePortsResults, error) {
   167  	result := params.MachinePortsResults{
   168  		Results: make([]params.MachinePortsResult, len(args.Params)),
   169  	}
   170  	canAccess, err := f.accessMachine()
   171  	if err != nil {
   172  		return params.MachinePortsResults{}, err
   173  	}
   174  	for i, param := range args.Params {
   175  		machineTag, err := names.ParseMachineTag(param.MachineTag)
   176  		if err != nil {
   177  			result.Results[i].Error = common.ServerError(err)
   178  			continue
   179  		}
   180  		var subnetTag names.SubnetTag
   181  		if param.SubnetTag != "" {
   182  			subnetTag, err = names.ParseSubnetTag(param.SubnetTag)
   183  			if err != nil {
   184  				result.Results[i].Error = common.ServerError(err)
   185  				continue
   186  			}
   187  		}
   188  		machine, err := f.getMachine(canAccess, machineTag)
   189  		if err != nil {
   190  			result.Results[i].Error = common.ServerError(err)
   191  			continue
   192  		}
   193  		ports, err := machine.OpenedPorts(subnetTag.Id())
   194  		if err != nil {
   195  			result.Results[i].Error = common.ServerError(err)
   196  			continue
   197  		}
   198  		if ports != nil {
   199  			portRangeMap := ports.AllPortRanges()
   200  			var portRanges []network.PortRange
   201  			for portRange := range portRangeMap {
   202  				portRanges = append(portRanges, portRange)
   203  			}
   204  			network.SortPortRanges(portRanges)
   205  
   206  			for _, portRange := range portRanges {
   207  				unitTag := names.NewUnitTag(portRangeMap[portRange]).String()
   208  				result.Results[i].Ports = append(result.Results[i].Ports,
   209  					params.MachinePortRange{
   210  						UnitTag:   unitTag,
   211  						PortRange: params.FromNetworkPortRange(portRange),
   212  					})
   213  			}
   214  		}
   215  	}
   216  	return result, nil
   217  }
   218  
   219  // GetMachineActiveSubnets returns the tags of the all subnets that each machine
   220  // (in args) has open ports on.
   221  func (f *FirewallerAPI) GetMachineActiveSubnets(args params.Entities) (params.StringsResults, error) {
   222  	result := params.StringsResults{
   223  		Results: make([]params.StringsResult, len(args.Entities)),
   224  	}
   225  	canAccess, err := f.accessMachine()
   226  	if err != nil {
   227  		return params.StringsResults{}, err
   228  	}
   229  	for i, entity := range args.Entities {
   230  		machineTag, err := names.ParseMachineTag(entity.Tag)
   231  		if err != nil {
   232  			result.Results[i].Error = common.ServerError(err)
   233  			continue
   234  		}
   235  		machine, err := f.getMachine(canAccess, machineTag)
   236  		if err != nil {
   237  			result.Results[i].Error = common.ServerError(err)
   238  			continue
   239  		}
   240  		ports, err := machine.AllPorts()
   241  		if err != nil {
   242  			result.Results[i].Error = common.ServerError(err)
   243  			continue
   244  		}
   245  		for _, port := range ports {
   246  			subnetID := port.SubnetID()
   247  			if subnetID != "" && !names.IsValidSubnet(subnetID) {
   248  				// The error message below will look like e.g. `ports for
   249  				// machine "0", subnet "bad" not valid`.
   250  				err = errors.NotValidf("%s", ports)
   251  				result.Results[i].Error = common.ServerError(err)
   252  				continue
   253  			} else if subnetID != "" && names.IsValidSubnet(subnetID) {
   254  				subnetTag := names.NewSubnetTag(subnetID).String()
   255  				result.Results[i].Result = append(result.Results[i].Result, subnetTag)
   256  				continue
   257  			}
   258  			// TODO(dimitern): Empty subnet CIDRs for ports are still OK until
   259  			// we can enforce it across all providers.
   260  			result.Results[i].Result = append(result.Results[i].Result, "")
   261  		}
   262  	}
   263  	return result, nil
   264  }
   265  
   266  // GetExposed returns the exposed flag value for each given service.
   267  func (f *FirewallerAPI) GetExposed(args params.Entities) (params.BoolResults, error) {
   268  	result := params.BoolResults{
   269  		Results: make([]params.BoolResult, len(args.Entities)),
   270  	}
   271  	canAccess, err := f.accessService()
   272  	if err != nil {
   273  		return params.BoolResults{}, err
   274  	}
   275  	for i, entity := range args.Entities {
   276  		tag, err := names.ParseApplicationTag(entity.Tag)
   277  		if err != nil {
   278  			result.Results[i].Error = common.ServerError(common.ErrPerm)
   279  			continue
   280  		}
   281  		service, err := f.getService(canAccess, tag)
   282  		if err == nil {
   283  			result.Results[i].Result = service.IsExposed()
   284  		}
   285  		result.Results[i].Error = common.ServerError(err)
   286  	}
   287  	return result, nil
   288  }
   289  
   290  // GetAssignedMachine returns the assigned machine tag (if any) for
   291  // each given unit.
   292  func (f *FirewallerAPI) GetAssignedMachine(args params.Entities) (params.StringResults, error) {
   293  	result := params.StringResults{
   294  		Results: make([]params.StringResult, len(args.Entities)),
   295  	}
   296  	canAccess, err := f.accessUnit()
   297  	if err != nil {
   298  		return params.StringResults{}, err
   299  	}
   300  	for i, entity := range args.Entities {
   301  		tag, err := names.ParseUnitTag(entity.Tag)
   302  		if err != nil {
   303  			result.Results[i].Error = common.ServerError(common.ErrPerm)
   304  			continue
   305  		}
   306  		unit, err := f.getUnit(canAccess, tag)
   307  		if err == nil {
   308  			var machineId string
   309  			machineId, err = unit.AssignedMachineId()
   310  			if err == nil {
   311  				result.Results[i].Result = names.NewMachineTag(machineId).String()
   312  			}
   313  		}
   314  		result.Results[i].Error = common.ServerError(err)
   315  	}
   316  	return result, nil
   317  }
   318  
   319  func (f *FirewallerAPI) getEntity(canAccess common.AuthFunc, tag names.Tag) (state.Entity, error) {
   320  	if !canAccess(tag) {
   321  		return nil, common.ErrPerm
   322  	}
   323  	return f.st.FindEntity(tag)
   324  }
   325  
   326  func (f *FirewallerAPI) getUnit(canAccess common.AuthFunc, tag names.UnitTag) (*state.Unit, error) {
   327  	entity, err := f.getEntity(canAccess, tag)
   328  	if err != nil {
   329  		return nil, err
   330  	}
   331  	// The authorization function guarantees that the tag represents a
   332  	// unit.
   333  	return entity.(*state.Unit), nil
   334  }
   335  
   336  func (f *FirewallerAPI) getService(canAccess common.AuthFunc, tag names.ApplicationTag) (*state.Application, error) {
   337  	entity, err := f.getEntity(canAccess, tag)
   338  	if err != nil {
   339  		return nil, err
   340  	}
   341  	// The authorization function guarantees that the tag represents a
   342  	// service.
   343  	return entity.(*state.Application), nil
   344  }
   345  
   346  func (f *FirewallerAPI) getMachine(canAccess common.AuthFunc, tag names.MachineTag) (*state.Machine, error) {
   347  	entity, err := f.getEntity(canAccess, tag)
   348  	if err != nil {
   349  		return nil, err
   350  	}
   351  	// The authorization function guarantees that the tag represents a
   352  	// machine.
   353  	return entity.(*state.Machine), nil
   354  }