github.com/Ilhicas/nomad@v1.0.4-0.20210304152020-e86851182bc3/client/allocrunner/networking_bridge_linux.go (about) 1 package allocrunner 2 3 import ( 4 "context" 5 "fmt" 6 7 "github.com/coreos/go-iptables/iptables" 8 hclog "github.com/hashicorp/go-hclog" 9 "github.com/hashicorp/nomad/nomad/structs" 10 "github.com/hashicorp/nomad/plugins/drivers" 11 ) 12 13 const ( 14 // defaultNomadBridgeName is the name of the bridge to use when not set by 15 // the client 16 defaultNomadBridgeName = "nomad" 17 18 // bridgeNetworkAllocIfPrefix is the prefix that is used for the interface 19 // name created inside of the alloc network which is connected to the bridge 20 bridgeNetworkAllocIfPrefix = "eth" 21 22 // defaultNomadAllocSubnet is the subnet to use for host local ip address 23 // allocation when not specified by the client 24 defaultNomadAllocSubnet = "172.26.64.0/20" // end 172.26.79.255 25 26 // cniAdminChainName is the name of the admin iptables chain used to allow 27 // forwarding traffic to allocations 28 cniAdminChainName = "NOMAD-ADMIN" 29 ) 30 31 // bridgeNetworkConfigurator is a NetworkConfigurator which adds the alloc to a 32 // shared bridge, configures masquerading for egress traffic and port mapping 33 // for ingress 34 type bridgeNetworkConfigurator struct { 35 cni *cniNetworkConfigurator 36 allocSubnet string 37 bridgeName string 38 39 logger hclog.Logger 40 } 41 42 func newBridgeNetworkConfigurator(log hclog.Logger, bridgeName, ipRange, cniPath string, ignorePortMappingHostIP bool) (*bridgeNetworkConfigurator, error) { 43 b := &bridgeNetworkConfigurator{ 44 bridgeName: bridgeName, 45 allocSubnet: ipRange, 46 logger: log, 47 } 48 49 if b.bridgeName == "" { 50 b.bridgeName = defaultNomadBridgeName 51 } 52 53 if b.allocSubnet == "" { 54 b.allocSubnet = defaultNomadAllocSubnet 55 } 56 57 c, err := newCNINetworkConfiguratorWithConf(log, cniPath, bridgeNetworkAllocIfPrefix, ignorePortMappingHostIP, buildNomadBridgeNetConfig(b.bridgeName, b.allocSubnet)) 58 if err != nil { 59 return nil, err 60 } 61 b.cni = c 62 63 return b, nil 64 } 65 66 // ensureForwardingRules ensures that a forwarding rule is added to iptables 67 // to allow traffic inbound to the bridge network 68 func (b *bridgeNetworkConfigurator) ensureForwardingRules() error { 69 ipt, err := iptables.New() 70 if err != nil { 71 return err 72 } 73 74 if err = ensureChain(ipt, "filter", cniAdminChainName); err != nil { 75 return err 76 } 77 78 if err := ensureFirstChainRule(ipt, cniAdminChainName, b.generateAdminChainRule()); err != nil { 79 return err 80 } 81 82 return nil 83 } 84 85 // ensureChain ensures that the given chain exists, creating it if missing 86 func ensureChain(ipt *iptables.IPTables, table, chain string) error { 87 chains, err := ipt.ListChains(table) 88 if err != nil { 89 return fmt.Errorf("failed to list iptables chains: %v", err) 90 } 91 for _, ch := range chains { 92 if ch == chain { 93 return nil 94 } 95 } 96 97 err = ipt.NewChain(table, chain) 98 99 // if err is for chain already existing return as it is possible another 100 // goroutine created it first 101 if e, ok := err.(*iptables.Error); ok && e.ExitStatus() == 1 { 102 return nil 103 } 104 105 return err 106 } 107 108 // ensureFirstChainRule ensures the given rule exists as the first rule in the chain 109 func ensureFirstChainRule(ipt *iptables.IPTables, chain string, rule []string) error { 110 exists, err := ipt.Exists("filter", chain, rule...) 111 if !exists && err == nil { 112 // iptables rules are 1-indexed 113 err = ipt.Insert("filter", chain, 1, rule...) 114 } 115 return err 116 } 117 118 // generateAdminChainRule builds the iptables rule that is inserted into the 119 // CNI admin chain to ensure traffic forwarding to the bridge network 120 func (b *bridgeNetworkConfigurator) generateAdminChainRule() []string { 121 return []string{"-o", b.bridgeName, "-d", b.allocSubnet, "-j", "ACCEPT"} 122 } 123 124 // Setup calls the CNI plugins with the add action 125 func (b *bridgeNetworkConfigurator) Setup(ctx context.Context, alloc *structs.Allocation, spec *drivers.NetworkIsolationSpec) (*structs.AllocNetworkStatus, error) { 126 if err := b.ensureForwardingRules(); err != nil { 127 return nil, fmt.Errorf("failed to initialize table forwarding rules: %v", err) 128 } 129 130 return b.cni.Setup(ctx, alloc, spec) 131 } 132 133 // Teardown calls the CNI plugins with the delete action 134 func (b *bridgeNetworkConfigurator) Teardown(ctx context.Context, alloc *structs.Allocation, spec *drivers.NetworkIsolationSpec) error { 135 return b.cni.Teardown(ctx, alloc, spec) 136 } 137 138 func buildNomadBridgeNetConfig(bridgeName, subnet string) []byte { 139 return []byte(fmt.Sprintf(nomadCNIConfigTemplate, bridgeName, subnet, cniAdminChainName)) 140 } 141 142 const nomadCNIConfigTemplate = `{ 143 "cniVersion": "0.4.0", 144 "name": "nomad", 145 "plugins": [ 146 { 147 "type": "bridge", 148 "bridge": "%s", 149 "ipMasq": true, 150 "isGateway": true, 151 "forceAddress": true, 152 "ipam": { 153 "type": "host-local", 154 "ranges": [ 155 [ 156 { 157 "subnet": "%s" 158 } 159 ] 160 ], 161 "routes": [ 162 { "dst": "0.0.0.0/0" } 163 ] 164 } 165 }, 166 { 167 "type": "firewall", 168 "backend": "iptables", 169 "iptablesAdminChainName": "%s" 170 }, 171 { 172 "type": "portmap", 173 "capabilities": {"portMappings": true}, 174 "snat": true 175 } 176 ] 177 } 178 `