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 }