github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/apiserver/facades/controller/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  	"github.com/juju/loggo"
     9  	"gopkg.in/juju/names.v2"
    10  
    11  	"github.com/juju/juju/apiserver/common"
    12  	"github.com/juju/juju/apiserver/common/cloudspec"
    13  	"github.com/juju/juju/apiserver/common/firewall"
    14  	"github.com/juju/juju/apiserver/facade"
    15  	"github.com/juju/juju/apiserver/params"
    16  	"github.com/juju/juju/core/network"
    17  	"github.com/juju/juju/core/status"
    18  	"github.com/juju/juju/state"
    19  	"github.com/juju/juju/state/watcher"
    20  )
    21  
    22  var logger = loggo.GetLogger("juju.apiserver.firewaller")
    23  
    24  // FirewallerAPIV3 provides access to the Firewaller v3 API facade.
    25  type FirewallerAPIV3 struct {
    26  	*common.LifeGetter
    27  	*common.ModelWatcher
    28  	*common.AgentEntityWatcher
    29  	*common.UnitsWatcher
    30  	*common.ModelMachinesWatcher
    31  	*common.InstanceIdGetter
    32  	cloudspec.CloudSpecAPI
    33  
    34  	st                State
    35  	resources         facade.Resources
    36  	authorizer        facade.Authorizer
    37  	accessUnit        common.GetAuthFunc
    38  	accessApplication common.GetAuthFunc
    39  	accessMachine     common.GetAuthFunc
    40  	accessModel       common.GetAuthFunc
    41  }
    42  
    43  // FirewallerAPIV4 provides access to the Firewaller v4 API facade.
    44  type FirewallerAPIV4 struct {
    45  	*FirewallerAPIV3
    46  	*common.ControllerConfigAPI
    47  }
    48  
    49  // FirewallerAPIV5 provides access to the Firewaller v5 API facade.
    50  type FirewallerAPIV5 struct {
    51  	*FirewallerAPIV4
    52  }
    53  
    54  // NewStateFirewallerAPIV3 creates a new server-side FirewallerAPIV3 facade.
    55  func NewStateFirewallerAPIV3(context facade.Context) (*FirewallerAPIV3, error) {
    56  	st := context.State()
    57  
    58  	m, err := st.Model()
    59  	if err != nil {
    60  		return nil, errors.Trace(err)
    61  	}
    62  
    63  	cloudSpecAPI := cloudspec.NewCloudSpec(
    64  		cloudspec.MakeCloudSpecGetterForModel(st),
    65  		common.AuthFuncForTag(m.ModelTag()),
    66  	)
    67  	return NewFirewallerAPI(stateShim{st: st, State: firewall.StateShim(st, m)}, context.Resources(), context.Auth(), cloudSpecAPI)
    68  }
    69  
    70  // NewStateFirewallerAPIV4 creates a new server-side FirewallerAPIV4 facade.
    71  func NewStateFirewallerAPIV4(context facade.Context) (*FirewallerAPIV4, error) {
    72  	facadev3, err := NewStateFirewallerAPIV3(context)
    73  	if err != nil {
    74  		return nil, err
    75  	}
    76  	return &FirewallerAPIV4{
    77  		ControllerConfigAPI: common.NewStateControllerConfig(context.State()),
    78  		FirewallerAPIV3:     facadev3,
    79  	}, nil
    80  }
    81  
    82  // NewStateFirewallerAPIV5 creates a new server-side FirewallerAPIV5 facade.
    83  func NewStateFirewallerAPIV5(context facade.Context) (*FirewallerAPIV5, error) {
    84  	facadev4, err := NewStateFirewallerAPIV4(context)
    85  	if err != nil {
    86  		return nil, err
    87  	}
    88  	return &FirewallerAPIV5{
    89  		FirewallerAPIV4: facadev4,
    90  	}, nil
    91  }
    92  
    93  // NewFirewallerAPI creates a new server-side FirewallerAPIV3 facade.
    94  func NewFirewallerAPI(
    95  	st State,
    96  	resources facade.Resources,
    97  	authorizer facade.Authorizer,
    98  	cloudSpecAPI cloudspec.CloudSpecAPI,
    99  ) (*FirewallerAPIV3, error) {
   100  	if !authorizer.AuthController() {
   101  		// Firewaller must run as a controller.
   102  		return nil, common.ErrPerm
   103  	}
   104  	// Set up the various authorization checkers.
   105  	accessModel := common.AuthFuncForTagKind(names.ModelTagKind)
   106  	accessUnit := common.AuthFuncForTagKind(names.UnitTagKind)
   107  	accessApplication := common.AuthFuncForTagKind(names.ApplicationTagKind)
   108  	accessMachine := common.AuthFuncForTagKind(names.MachineTagKind)
   109  	accessRelation := common.AuthFuncForTagKind(names.RelationTagKind)
   110  	accessUnitApplicationOrMachineOrRelation := common.AuthAny(accessUnit, accessApplication, accessMachine, accessRelation)
   111  
   112  	// Life() is supported for units, applications or machines.
   113  	lifeGetter := common.NewLifeGetter(
   114  		st,
   115  		accessUnitApplicationOrMachineOrRelation,
   116  	)
   117  	// ModelConfig() and WatchForModelConfigChanges() are allowed
   118  	// with unrestricted access.
   119  	modelWatcher := common.NewModelWatcher(
   120  		st,
   121  		resources,
   122  		authorizer,
   123  	)
   124  	// Watch() is supported for applications only.
   125  	entityWatcher := common.NewAgentEntityWatcher(
   126  		st,
   127  		resources,
   128  		accessApplication,
   129  	)
   130  	// WatchUnits() is supported for machines.
   131  	unitsWatcher := common.NewUnitsWatcher(st,
   132  		resources,
   133  		accessMachine,
   134  	)
   135  	// WatchModelMachines() is allowed with unrestricted access.
   136  	machinesWatcher := common.NewModelMachinesWatcher(
   137  		st,
   138  		resources,
   139  		authorizer,
   140  	)
   141  	// InstanceId() is supported for machines.
   142  	instanceIdGetter := common.NewInstanceIdGetter(
   143  		st,
   144  		accessMachine,
   145  	)
   146  
   147  	return &FirewallerAPIV3{
   148  		LifeGetter:           lifeGetter,
   149  		ModelWatcher:         modelWatcher,
   150  		AgentEntityWatcher:   entityWatcher,
   151  		UnitsWatcher:         unitsWatcher,
   152  		ModelMachinesWatcher: machinesWatcher,
   153  		InstanceIdGetter:     instanceIdGetter,
   154  		CloudSpecAPI:         cloudSpecAPI,
   155  		st:                   st,
   156  		resources:            resources,
   157  		authorizer:           authorizer,
   158  		accessUnit:           accessUnit,
   159  		accessApplication:    accessApplication,
   160  		accessMachine:        accessMachine,
   161  		accessModel:          accessModel,
   162  	}, nil
   163  }
   164  
   165  // WatchOpenedPorts returns a new StringsWatcher for each given
   166  // model tag.
   167  func (f *FirewallerAPIV3) WatchOpenedPorts(args params.Entities) (params.StringsWatchResults, error) {
   168  	result := params.StringsWatchResults{
   169  		Results: make([]params.StringsWatchResult, len(args.Entities)),
   170  	}
   171  	if len(args.Entities) == 0 {
   172  		return result, nil
   173  	}
   174  	canWatch, err := f.accessModel()
   175  	if err != nil {
   176  		return params.StringsWatchResults{}, errors.Trace(err)
   177  	}
   178  	for i, entity := range args.Entities {
   179  		tag, err := names.ParseTag(entity.Tag)
   180  		if err != nil {
   181  			result.Results[i].Error = common.ServerError(common.ErrPerm)
   182  			continue
   183  		}
   184  		if !canWatch(tag) {
   185  			result.Results[i].Error = common.ServerError(common.ErrPerm)
   186  			continue
   187  		}
   188  		watcherId, initial, err := f.watchOneModelOpenedPorts(tag)
   189  		if err != nil {
   190  			result.Results[i].Error = common.ServerError(err)
   191  			continue
   192  		}
   193  		result.Results[i].StringsWatcherId = watcherId
   194  		result.Results[i].Changes = initial
   195  	}
   196  	return result, nil
   197  }
   198  
   199  func (f *FirewallerAPIV3) watchOneModelOpenedPorts(tag names.Tag) (string, []string, error) {
   200  	// NOTE: tag is ignored, as there is only one model in the
   201  	// state DB. Once this changes, change the code below accordingly.
   202  	watch := f.st.WatchOpenedPorts()
   203  	// Consume the initial event and forward it to the result.
   204  	if changes, ok := <-watch.Changes(); ok {
   205  		return f.resources.Register(watch), changes, nil
   206  	}
   207  	return "", nil, watcher.EnsureErr(watch)
   208  }
   209  
   210  // GetMachinePorts returns the port ranges opened on a machine for the specified
   211  // subnet as a map mapping port ranges to the tags of the units that opened
   212  // them.
   213  func (f *FirewallerAPIV3) GetMachinePorts(args params.MachinePortsParams) (params.MachinePortsResults, error) {
   214  	result := params.MachinePortsResults{
   215  		Results: make([]params.MachinePortsResult, len(args.Params)),
   216  	}
   217  	canAccess, err := f.accessMachine()
   218  	if err != nil {
   219  		return params.MachinePortsResults{}, err
   220  	}
   221  	for i, param := range args.Params {
   222  		machineTag, err := names.ParseMachineTag(param.MachineTag)
   223  		if err != nil {
   224  			result.Results[i].Error = common.ServerError(err)
   225  			continue
   226  		}
   227  		var subnetTag names.SubnetTag
   228  		if param.SubnetTag != "" {
   229  			subnetTag, err = names.ParseSubnetTag(param.SubnetTag)
   230  			if err != nil {
   231  				result.Results[i].Error = common.ServerError(err)
   232  				continue
   233  			}
   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.OpenedPorts(subnetTag.Id())
   241  		if err != nil {
   242  			result.Results[i].Error = common.ServerError(err)
   243  			continue
   244  		}
   245  		if ports != nil {
   246  			portRangeMap := ports.AllPortRanges()
   247  			var portRanges []network.PortRange
   248  			for portRange := range portRangeMap {
   249  				portRanges = append(portRanges, portRange)
   250  			}
   251  			network.SortPortRanges(portRanges)
   252  
   253  			for _, portRange := range portRanges {
   254  				unitTag := names.NewUnitTag(portRangeMap[portRange]).String()
   255  				result.Results[i].Ports = append(result.Results[i].Ports,
   256  					params.MachinePortRange{
   257  						UnitTag:   unitTag,
   258  						PortRange: params.FromNetworkPortRange(portRange),
   259  					})
   260  			}
   261  		}
   262  	}
   263  	return result, nil
   264  }
   265  
   266  // GetMachineActiveSubnets returns the tags of the all subnets that each machine
   267  // (in args) has open ports on.
   268  func (f *FirewallerAPIV3) GetMachineActiveSubnets(args params.Entities) (params.StringsResults, error) {
   269  	result := params.StringsResults{
   270  		Results: make([]params.StringsResult, len(args.Entities)),
   271  	}
   272  	canAccess, err := f.accessMachine()
   273  	if err != nil {
   274  		return params.StringsResults{}, err
   275  	}
   276  	for i, entity := range args.Entities {
   277  		machineTag, err := names.ParseMachineTag(entity.Tag)
   278  		if err != nil {
   279  			result.Results[i].Error = common.ServerError(err)
   280  			continue
   281  		}
   282  		machine, err := f.getMachine(canAccess, machineTag)
   283  		if err != nil {
   284  			result.Results[i].Error = common.ServerError(err)
   285  			continue
   286  		}
   287  		ports, err := machine.AllPorts()
   288  		if err != nil {
   289  			result.Results[i].Error = common.ServerError(err)
   290  			continue
   291  		}
   292  		for _, port := range ports {
   293  			subnetID := port.SubnetID()
   294  			if subnetID != "" && !names.IsValidSubnet(subnetID) {
   295  				// The error message below will look like e.g. `ports for
   296  				// machine "0", subnet "bad" not valid`.
   297  				err = errors.NotValidf("%s", ports)
   298  				result.Results[i].Error = common.ServerError(err)
   299  				continue
   300  			} else if subnetID != "" && names.IsValidSubnet(subnetID) {
   301  				subnetTag := names.NewSubnetTag(subnetID).String()
   302  				result.Results[i].Result = append(result.Results[i].Result, subnetTag)
   303  				continue
   304  			}
   305  			// TODO(dimitern): Empty subnet CIDRs for ports are still OK until
   306  			// we can enforce it across all providers.
   307  			result.Results[i].Result = append(result.Results[i].Result, "")
   308  		}
   309  	}
   310  	return result, nil
   311  }
   312  
   313  // GetExposed returns the exposed flag value for each given application.
   314  func (f *FirewallerAPIV3) GetExposed(args params.Entities) (params.BoolResults, error) {
   315  	result := params.BoolResults{
   316  		Results: make([]params.BoolResult, len(args.Entities)),
   317  	}
   318  	canAccess, err := f.accessApplication()
   319  	if err != nil {
   320  		return params.BoolResults{}, err
   321  	}
   322  	for i, entity := range args.Entities {
   323  		tag, err := names.ParseApplicationTag(entity.Tag)
   324  		if err != nil {
   325  			result.Results[i].Error = common.ServerError(common.ErrPerm)
   326  			continue
   327  		}
   328  		application, err := f.getApplication(canAccess, tag)
   329  		if err == nil {
   330  			result.Results[i].Result = application.IsExposed()
   331  		}
   332  		result.Results[i].Error = common.ServerError(err)
   333  	}
   334  	return result, nil
   335  }
   336  
   337  // GetAssignedMachine returns the assigned machine tag (if any) for
   338  // each given unit.
   339  func (f *FirewallerAPIV3) GetAssignedMachine(args params.Entities) (params.StringResults, error) {
   340  	result := params.StringResults{
   341  		Results: make([]params.StringResult, len(args.Entities)),
   342  	}
   343  	canAccess, err := f.accessUnit()
   344  	if err != nil {
   345  		return params.StringResults{}, err
   346  	}
   347  	for i, entity := range args.Entities {
   348  		tag, err := names.ParseUnitTag(entity.Tag)
   349  		if err != nil {
   350  			result.Results[i].Error = common.ServerError(common.ErrPerm)
   351  			continue
   352  		}
   353  		unit, err := f.getUnit(canAccess, tag)
   354  		if err == nil {
   355  			var machineId string
   356  			machineId, err = unit.AssignedMachineId()
   357  			if err == nil {
   358  				result.Results[i].Result = names.NewMachineTag(machineId).String()
   359  			}
   360  		}
   361  		result.Results[i].Error = common.ServerError(err)
   362  	}
   363  	return result, nil
   364  }
   365  
   366  func (f *FirewallerAPIV3) getEntity(canAccess common.AuthFunc, tag names.Tag) (state.Entity, error) {
   367  	if !canAccess(tag) {
   368  		return nil, common.ErrPerm
   369  	}
   370  	return f.st.FindEntity(tag)
   371  }
   372  
   373  func (f *FirewallerAPIV3) getUnit(canAccess common.AuthFunc, tag names.UnitTag) (*state.Unit, error) {
   374  	entity, err := f.getEntity(canAccess, tag)
   375  	if err != nil {
   376  		return nil, err
   377  	}
   378  	// The authorization function guarantees that the tag represents a
   379  	// unit.
   380  	return entity.(*state.Unit), nil
   381  }
   382  
   383  func (f *FirewallerAPIV3) getApplication(canAccess common.AuthFunc, tag names.ApplicationTag) (*state.Application, error) {
   384  	entity, err := f.getEntity(canAccess, tag)
   385  	if err != nil {
   386  		return nil, err
   387  	}
   388  	// The authorization function guarantees that the tag represents a
   389  	// application.
   390  	return entity.(*state.Application), nil
   391  }
   392  
   393  func (f *FirewallerAPIV3) getMachine(canAccess common.AuthFunc, tag names.MachineTag) (*state.Machine, error) {
   394  	entity, err := f.getEntity(canAccess, tag)
   395  	if err != nil {
   396  		return nil, err
   397  	}
   398  	// The authorization function guarantees that the tag represents a
   399  	// machine.
   400  	return entity.(*state.Machine), nil
   401  }
   402  
   403  // WatchEgressAddressesForRelations creates a watcher that notifies when addresses, from which
   404  // connections will originate for the relation, change.
   405  // Each event contains the entire set of addresses which are required for ingress for the relation.
   406  func (f *FirewallerAPIV4) WatchEgressAddressesForRelations(relations params.Entities) (params.StringsWatchResults, error) {
   407  	return firewall.WatchEgressAddressesForRelations(f.resources, f.st, relations)
   408  }
   409  
   410  // WatchIngressAddressesForRelations creates a watcher that returns the ingress networks
   411  // that have been recorded against the specified relations.
   412  func (f *FirewallerAPIV4) WatchIngressAddressesForRelations(relations params.Entities) (params.StringsWatchResults, error) {
   413  	results := params.StringsWatchResults{
   414  		make([]params.StringsWatchResult, len(relations.Entities)),
   415  	}
   416  
   417  	one := func(tag string) (id string, changes []string, _ error) {
   418  		logger.Debugf("Watching ingress addresses for %+v from model %v", tag, f.st.ModelUUID())
   419  
   420  		relationTag, err := names.ParseRelationTag(tag)
   421  		if err != nil {
   422  			return "", nil, errors.Trace(err)
   423  		}
   424  		rel, err := f.st.KeyRelation(relationTag.Id())
   425  		if err != nil {
   426  			return "", nil, errors.Trace(err)
   427  		}
   428  		w := rel.WatchRelationIngressNetworks()
   429  		changes, ok := <-w.Changes()
   430  		if !ok {
   431  			return "", nil, common.ServerError(watcher.EnsureErr(w))
   432  		}
   433  		return f.resources.Register(w), changes, nil
   434  	}
   435  
   436  	for i, e := range relations.Entities {
   437  		watcherId, changes, err := one(e.Tag)
   438  		if err != nil {
   439  			results.Results[i].Error = common.ServerError(err)
   440  			continue
   441  		}
   442  		results.Results[i].StringsWatcherId = watcherId
   443  		results.Results[i].Changes = changes
   444  	}
   445  	return results, nil
   446  }
   447  
   448  // MacaroonForRelations returns the macaroon for the specified relations.
   449  func (f *FirewallerAPIV4) MacaroonForRelations(args params.Entities) (params.MacaroonResults, error) {
   450  	var result params.MacaroonResults
   451  	result.Results = make([]params.MacaroonResult, len(args.Entities))
   452  	for i, entity := range args.Entities {
   453  		relationTag, err := names.ParseRelationTag(entity.Tag)
   454  		if err != nil {
   455  			result.Results[i].Error = common.ServerError(err)
   456  			continue
   457  		}
   458  		mac, err := f.st.GetMacaroon(relationTag)
   459  		if err != nil {
   460  			result.Results[i].Error = common.ServerError(err)
   461  			continue
   462  		}
   463  		result.Results[i].Result = mac
   464  	}
   465  	return result, nil
   466  }
   467  
   468  // SetRelationsStatus sets the status for the specified relations.
   469  func (f *FirewallerAPIV4) SetRelationsStatus(args params.SetStatus) (params.ErrorResults, error) {
   470  	var result params.ErrorResults
   471  	result.Results = make([]params.ErrorResult, len(args.Entities))
   472  	for i, entity := range args.Entities {
   473  		relationTag, err := names.ParseRelationTag(entity.Tag)
   474  		if err != nil {
   475  			result.Results[i].Error = common.ServerError(err)
   476  			continue
   477  		}
   478  		rel, err := f.st.KeyRelation(relationTag.Id())
   479  		if err != nil {
   480  			result.Results[i].Error = common.ServerError(err)
   481  			continue
   482  		}
   483  		err = rel.SetStatus(status.StatusInfo{
   484  			Status:  status.Status(entity.Status),
   485  			Message: entity.Info,
   486  		})
   487  		result.Results[i].Error = common.ServerError(err)
   488  	}
   489  	return result, nil
   490  }
   491  
   492  // FirewallRules returns the firewall rules for the specified well known service types.
   493  func (f *FirewallerAPIV4) FirewallRules(args params.KnownServiceArgs) (params.ListFirewallRulesResults, error) {
   494  	var result params.ListFirewallRulesResults
   495  	for _, knownService := range args.KnownServices {
   496  		rule, err := f.st.FirewallRule(state.WellKnownServiceType(knownService))
   497  		if err != nil && !errors.IsNotFound(err) {
   498  			return result, common.ServerError(err)
   499  		}
   500  		if err != nil {
   501  			continue
   502  		}
   503  		result.Rules = append(result.Rules, params.FirewallRule{
   504  			KnownService:   knownService,
   505  			WhitelistCIDRS: rule.WhitelistCIDRs,
   506  		})
   507  	}
   508  	return result, nil
   509  }
   510  
   511  // AreManuallyProvisioned returns whether each given entity is
   512  // manually provisioned or not. Only machine tags are accepted.
   513  func (f *FirewallerAPIV5) AreManuallyProvisioned(args params.Entities) (params.BoolResults, error) {
   514  	result := params.BoolResults{
   515  		Results: make([]params.BoolResult, len(args.Entities)),
   516  	}
   517  	canAccess, err := f.accessMachine()
   518  	if err != nil {
   519  		return result, err
   520  	}
   521  	for i, arg := range args.Entities {
   522  		machineTag, err := names.ParseMachineTag(arg.Tag)
   523  		if err != nil {
   524  			result.Results[i].Error = common.ServerError(err)
   525  			continue
   526  		}
   527  		machine, err := f.getMachine(canAccess, machineTag)
   528  		if err == nil {
   529  			result.Results[i].Result, err = machine.IsManual()
   530  		}
   531  		result.Results[i].Error = common.ServerError(err)
   532  	}
   533  	return result, nil
   534  }