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 }