github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/apiserver/common/firewall/firewall.go (about) 1 // Copyright 2018 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package firewall 5 6 import ( 7 "net" 8 9 "github.com/juju/errors" 10 "github.com/juju/loggo" 11 "gopkg.in/juju/names.v2" 12 13 "github.com/juju/juju/apiserver/common" 14 "github.com/juju/juju/apiserver/facade" 15 "github.com/juju/juju/apiserver/params" 16 "github.com/juju/juju/state/watcher" 17 "gopkg.in/juju/charm.v6" 18 ) 19 20 var logger = loggo.GetLogger("juju.apiserver.crossmodelrelations") 21 22 // WatchEgressAddressesForRelations creates a watcher that notifies when addresses, from which 23 // connections will originate for the relation, change. 24 // Each event contains the entire set of addresses which are required for ingress for the relation. 25 func WatchEgressAddressesForRelations(resources facade.Resources, st State, relations params.Entities) (params.StringsWatchResults, error) { 26 results := params.StringsWatchResults{ 27 make([]params.StringsWatchResult, len(relations.Entities)), 28 } 29 30 one := func(tag string) (id string, changes []string, _ error) { 31 logger.Debugf("Watching egress addresses for %+v", tag) 32 33 relationTag, err := names.ParseRelationTag(tag) 34 if err != nil { 35 return "", nil, errors.Trace(err) 36 } 37 38 // Load the relation details for the current token. 39 localEndpoint, err := localApplication(st, relationTag) 40 if err != nil { 41 return "", nil, errors.Trace(err) 42 } 43 44 w, err := NewEgressAddressWatcher(st, localEndpoint.relation, localEndpoint.application) 45 if err != nil { 46 return "", nil, errors.Trace(err) 47 } 48 49 // TODO(wallyworld) - we will need to watch subnets too, but only 50 // when we support using cloud local addresses 51 //filter := func(id interface{}) bool { 52 // include, err := includeAsIngressSubnet(id.(string)) 53 // if err != nil { 54 // logger.Warningf("invalid CIDR %q", id) 55 // } 56 // return include 57 //} 58 //w := api.st.WatchSubnets(filter) 59 60 changes, ok := <-w.Changes() 61 if !ok { 62 return "", nil, common.ServerError(watcher.EnsureErr(w)) 63 } 64 return resources.Register(w), changes, nil 65 } 66 67 for i, e := range relations.Entities { 68 watcherId, changes, err := one(e.Tag) 69 if err != nil { 70 results.Results[i].Error = common.ServerError(err) 71 continue 72 } 73 results.Results[i].StringsWatcherId = watcherId 74 results.Results[i].Changes = changes 75 } 76 return results, nil 77 } 78 79 type localEndpointInfo struct { 80 relation Relation 81 application string 82 name string 83 role charm.RelationRole 84 } 85 86 func localApplication(st State, relationTag names.RelationTag) (*localEndpointInfo, error) { 87 rel, err := st.KeyRelation(relationTag.Id()) 88 if err != nil { 89 return nil, errors.Trace(err) 90 } 91 92 // Gather info about the local (this model) application of the relation. 93 // We'll use the info to figure out what addresses/subnets to include. 94 localEndpoint := localEndpointInfo{relation: rel} 95 for _, ep := range rel.Endpoints() { 96 // Try looking up the info for the local application. 97 _, err = st.Application(ep.ApplicationName) 98 if err != nil && !errors.IsNotFound(err) { 99 return nil, errors.Trace(err) 100 } else if err == nil { 101 localEndpoint.application = ep.ApplicationName 102 localEndpoint.name = ep.Name 103 localEndpoint.role = ep.Role 104 break 105 } 106 } 107 // Until networking support becomes more sophisticated, for now 108 // we only care about opening access to applications with an endpoint 109 // having the "provider" role. The assumption is that such endpoints listen 110 // to incoming connections and thus require ingress. An exception to this 111 // would be applications which accept connections onto an endpoint which 112 // has a "requirer" role. 113 // We are operating in the model hosting the "consuming" application, so check 114 // that its endpoint has the "requirer" role, meaning that we need to notify 115 // the offering model of subnets from this model required for ingress. 116 if localEndpoint.role != charm.RoleRequirer { 117 return nil, errors.NotSupportedf( 118 "egress network for application %v without requires endpoint", localEndpoint.application) 119 } 120 return &localEndpoint, nil 121 } 122 123 // TODO(wallyworld) - this is unused until we query subnets again 124 func includeAsEgressSubnet(cidr string) (bool, error) { 125 ip, _, err := net.ParseCIDR(cidr) 126 if err != nil { 127 return false, errors.Trace(err) 128 } 129 if ip.IsLoopback() || ip.IsMulticast() { 130 return false, nil 131 } 132 // TODO(wallyworld) - We only support IPv4 addresses as not all providers support IPv6. 133 if ip.To4() == nil { 134 return false, nil 135 } 136 return true, nil 137 }