Files
nomad/client/allocrunner/networking_bridge_linux.go
Daniel Bennett 2f5cf8efae networking: option to enable ipv6 on bridge network (#23882)
by setting bridge_network_subnet_ipv6 in client config

Co-authored-by: Martina Santangelo <martina.santangelo@hashicorp.com>
2024-09-04 10:17:10 -05:00

147 lines
4.0 KiB
Go

// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: BUSL-1.1
package allocrunner
import (
"context"
"fmt"
hclog "github.com/hashicorp/go-hclog"
"github.com/hashicorp/nomad/client/allocrunner/cni"
"github.com/hashicorp/nomad/nomad/structs"
"github.com/hashicorp/nomad/plugins/drivers"
)
const (
// defaultNomadBridgeName is the name of the bridge to use when not set by
// the client
defaultNomadBridgeName = "nomad"
// bridgeNetworkAllocIfPrefix is the prefix that is used for the interface
// name created inside of the alloc network which is connected to the bridge
bridgeNetworkAllocIfPrefix = "eth"
// defaultNomadAllocSubnet is the subnet to use for host local ip address
// allocation when not specified by the client
defaultNomadAllocSubnet = "172.26.64.0/20" // end 172.26.79.255
)
// bridgeNetworkConfigurator is a NetworkConfigurator which adds the alloc to a
// shared bridge, configures masquerading for egress traffic and port mapping
// for ingress
type bridgeNetworkConfigurator struct {
cni *cniNetworkConfigurator
allocSubnetIPv6 string
allocSubnetIPv4 string
bridgeName string
hairpinMode bool
newIPTables func(structs.NodeNetworkAF) (IPTablesChain, error)
logger hclog.Logger
}
func newBridgeNetworkConfigurator(log hclog.Logger, alloc *structs.Allocation, bridgeName, ipv4Range, ipv6Range, cniPath string, hairpinMode, ignorePortMappingHostIP bool, node *structs.Node) (*bridgeNetworkConfigurator, error) {
b := &bridgeNetworkConfigurator{
bridgeName: bridgeName,
hairpinMode: hairpinMode,
allocSubnetIPv4: ipv4Range,
allocSubnetIPv6: ipv6Range,
newIPTables: newIPTablesChain,
logger: log,
}
if b.bridgeName == "" {
b.bridgeName = defaultNomadBridgeName
}
if b.allocSubnetIPv4 == "" {
b.allocSubnetIPv4 = defaultNomadAllocSubnet
}
var netCfg []byte
var err error
tg := alloc.Job.LookupTaskGroup(alloc.TaskGroup)
for _, svc := range tg.Services {
if svc.Connect.HasTransparentProxy() {
netCfg, err = buildNomadBridgeNetConfig(*b, true)
if err != nil {
return nil, err
}
break
}
}
if netCfg == nil {
netCfg, err = buildNomadBridgeNetConfig(*b, false)
if err != nil {
return nil, err
}
}
parser := &cniConfParser{
listBytes: netCfg,
}
c, err := newCNINetworkConfiguratorWithConf(log, cniPath, bridgeNetworkAllocIfPrefix, ignorePortMappingHostIP, parser, node)
if err != nil {
return nil, err
}
b.cni = c
return b, nil
}
// ensureForwardingRules ensures that a forwarding rule is added to iptables
// to allow traffic inbound to the bridge network
func (b *bridgeNetworkConfigurator) ensureForwardingRules() error {
if b.allocSubnetIPv6 != "" {
ip6t, err := b.newIPTables(structs.NodeNetworkAF_IPv6)
if err != nil {
return err
}
if err = ensureChainRule(ip6t, b.bridgeName, b.allocSubnetIPv6); err != nil {
return err
}
}
ipt, err := b.newIPTables(structs.NodeNetworkAF_IPv4)
if err != nil {
return err
}
if err = ensureChainRule(ipt, b.bridgeName, b.allocSubnetIPv4); err != nil {
return err
}
return nil
}
// Setup calls the CNI plugins with the add action
func (b *bridgeNetworkConfigurator) Setup(ctx context.Context, alloc *structs.Allocation, spec *drivers.NetworkIsolationSpec) (*structs.AllocNetworkStatus, error) {
if err := b.ensureForwardingRules(); err != nil {
return nil, fmt.Errorf("failed to initialize table forwarding rules: %v", err)
}
return b.cni.Setup(ctx, alloc, spec)
}
// Teardown calls the CNI plugins with the delete action
func (b *bridgeNetworkConfigurator) Teardown(ctx context.Context, alloc *structs.Allocation, spec *drivers.NetworkIsolationSpec) error {
return b.cni.Teardown(ctx, alloc, spec)
}
func buildNomadBridgeNetConfig(b bridgeNetworkConfigurator, withConsulCNI bool) ([]byte, error) {
conf := cni.NewNomadBridgeConflist(cni.NomadBridgeConfig{
BridgeName: b.bridgeName,
AdminChainName: cniAdminChainName,
IPv4Subnet: b.allocSubnetIPv4,
IPv6Subnet: b.allocSubnetIPv6,
HairpinMode: b.hairpinMode,
ConsulCNI: withConsulCNI,
})
return conf.Json()
}