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