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