Skip to content

Commit

Permalink
Add support for IPsec to UBI based image (antrea-io#4244)
Browse files Browse the repository at this point in the history
1. Do not install strongswan in UBI-based image.
2. Add support for libreswan to `start_ovs_ipsec` start script.

Fixes: antrea-io#4243

Signed-off-by: Xu Liu <[email protected]>
  • Loading branch information
xliuxu authored and qiyueyao committed Nov 2, 2022
1 parent b36a884 commit 89e0b43
Show file tree
Hide file tree
Showing 4 changed files with 219 additions and 12 deletions.
7 changes: 5 additions & 2 deletions build/images/ovs/Dockerfile.ubi
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,6 @@ LABEL description="A Docker image based on UBI8 which includes Open vSwitch buil
# Change Repository from UBI8’s to CentOS because UBI8's repository does not contain
# enough packages required by OVS installation.
# Using the official RHEL repository would be the best choice but it's not publicly accessible.
# TODO: update the strongSwan logging config.
COPY CentOS.repo /tmp/CentOS.repo
COPY charon-logging.conf /tmp
COPY --from=ovs-rpms /tmp/ovs-rpms/* /tmp/ovs-rpms/
Expand All @@ -54,7 +53,11 @@ RUN rm -f /etc/yum.repos.d/* && mv /tmp/CentOS.repo /etc/yum.repos.d/CentOS.repo
subscription-manager config --rhsm.manage_repos=0 && \
yum clean all -y && yum reinstall yum -y && \
yum install /tmp/ovs-rpms/* -y && yum install epel-release -y && \
yum install iptables logrotate strongswan -y && \
yum install iptables logrotate -y && \
mv /etc/logrotate.d/openvswitch /etc/logrotate.d/openvswitch-switch && \
sed -i "/rotate /a\ #size 100M" /etc/logrotate.d/openvswitch-switch && \
# https://github.com/libreswan/libreswan/blob/main/programs/setup/setup.in
# The init system is configured to systemd by default. Change it to namespaces
# to spawn the ipsec process directly.
sed -i 's/^initsystem=.*$/initsystem="namespaces"/' /usr/libexec/ipsec/setup && \
rm -rf /tmp/* && yum clean all
20 changes: 13 additions & 7 deletions build/images/scripts/start_ovs_ipsec
Original file line number Diff line number Diff line change
Expand Up @@ -21,27 +21,33 @@ CONTAINER_NAME="antrea-ipsec"

set -euo pipefail

# TODO: we assume that StrongSwan is used to provide IPsec for OVS, but in
# theory LibreSwan is also supported.

log_info $CONTAINER_NAME "Checking for StrongSwan prerequisites"
log_info $CONTAINER_NAME "Checking for IPsec prerequisites"

command -v ipsec >/dev/null 2>&1 || { log_error $CONTAINER_NAME "'ipsec' command not available - are the StrongSwan packages installed?"; exit 1; }

IKE_DAEMON="strongswan"
IPSEC_VERSION=$(ipsec --version)

if [[ ${IPSEC_VERSION,,} =~ "libreswan" ]]; then
IKE_DAEMON="libreswan"
# Check the NSS database and initialize it when it is not present.
ipsec checknss
fi

# OVS IPsec requires that the GCM module be loaded (/etc/strongswan.d/ovs.conf),
# and we use the presence of /etc/strongswan.d/charon/gcm.conf to determine
# whether this is the case (this should be independent of the Linux distribution
# used). Just in case, we only perform the check if the /etc/strongswan.d/charon
# directory exists. We do not use "ipsec listplugins" as it requires the IKE
# daemon to be running already.
if [[ -d "/etc/strongswan.d/charon" && ! -f "/etc/strongswan.d/charon/gcm.conf" ]]; then
if [[ ${IKE_DAEMON} == "strongswan" && -d "/etc/strongswan.d/charon" && ! -f "/etc/strongswan.d/charon/gcm.conf" ]]; then
log_error $CONTAINER_NAME "Cannot detect 'gcm' plugin for StrongSwan, make sure it is installed (libstrongswan-standard-plugins package on Debian systems)"
exit 1
fi

function start_agents {
log_info $CONTAINER_NAME "Starting ovs-monitor-ipsec and strongSwan agents"
/usr/share/openvswitch/scripts/ovs-ctl --ike-daemon=strongswan start-ovs-ipsec
log_info $CONTAINER_NAME "Starting ovs-monitor-ipsec and "${IKE_DAEMON}" agents"
/usr/share/openvswitch/scripts/ovs-ctl --ike-daemon="${IKE_DAEMON}" start-ovs-ipsec
}

function stop_agents {
Expand Down
6 changes: 4 additions & 2 deletions pkg/agent/util/net.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@ const (
bridgedUplinkSuffix = "~"
)

var interfaceByName = net.InterfaceByName

func generateInterfaceName(key string, name string, useHead bool) string {
hash := sha1.New() // #nosec G401: not used for security purposes
io.WriteString(hash, key)
Expand Down Expand Up @@ -166,7 +168,7 @@ func GetIPNetDeviceFromIP(localIPs *ip.DualStackIPs, ignoredInterfaces sets.Stri
}

func GetIPNetDeviceByName(ifaceName string) (v4IPNet *net.IPNet, v6IPNet *net.IPNet, link *net.Interface, err error) {
link, err = net.InterfaceByName(ifaceName)
link, err = interfaceByName(ifaceName)
if err != nil {
return nil, nil, nil, err
}
Expand Down Expand Up @@ -246,7 +248,7 @@ func GetIPNetDeviceByCIDRs(cidrsList []string) (v4IPNet, v6IPNet *net.IPNet, lin

func GetAllIPNetsByName(ifaceName string) ([]*net.IPNet, error) {
ips := []*net.IPNet{}
adapter, err := net.InterfaceByName(ifaceName)
adapter, err := interfaceByName(ifaceName)
if err != nil {
return nil, err
}
Expand Down
198 changes: 197 additions & 1 deletion pkg/agent/util/net_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,15 @@
package util

import (
"errors"
"fmt"
"net"
"os"
"path/filepath"
"strings"
"testing"

"github.com/stretchr/testify/assert"
"k8s.io/apimachinery/pkg/util/sets"

"antrea.io/antrea/pkg/util/ip"
Expand Down Expand Up @@ -51,7 +55,95 @@ func TestGenerateContainerInterfaceName(t *testing.T) {
}
}

func TestGetDefaultLocalNodeAddr(t *testing.T) {
func TestNewLinkNotFoundError(t *testing.T) {
testName := "testLink"
wantResult := LinkNotFound{
fmt.Errorf("link %s not found", testName),
}
gotResult := newLinkNotFoundError(testName)
assert.Equal(t, wantResult, gotResult)
}

func TestUnixSocket(t *testing.T) {
testAddress := "/tmp/echo.sock"
_ = os.Remove(testAddress)
_ = os.MkdirAll(filepath.Dir(testAddress), 0750)
gotListener, err := listenUnix(testAddress)
assert.NoError(t, err)
assert.Equal(t, testAddress, gotListener.Addr().String())
gotConn, err := dialUnix(testAddress)
assert.NoError(t, err)
defer gotConn.Close()
assert.Equal(t, testAddress, gotConn.RemoteAddr().String())
_ = os.Remove(testAddress)
}

func TestGetIPNetDevice(t *testing.T) {
// IPv4
conn, err := net.Dial("udp", "8.8.8.8:80")
if err != nil {
t.Error(err)
}
localAddr := conn.LocalAddr().(*net.UDPAddr).IP

nodeIPs := &ip.DualStackIPs{IPv4: localAddr}
_, _, dev, err := GetIPNetDeviceFromIP(nodeIPs, sets.NewString())
if err != nil {
t.Error(err)
}
t.Logf("IP obtained %s, %v", localAddr, dev)
nodeCIDRs := []string{localAddr.String() + "/32"}
_, _, link, err := GetIPNetDeviceByCIDRs(nodeCIDRs)
assert.NoError(t, err)
assert.Equal(t, dev, link)
_, _, link, err = GetIPNetDeviceByName(dev.Name)
assert.NoError(t, err)
assert.Equal(t, dev, link)
ipNets, err := GetIPNetsByLink(dev)
assert.NoError(t, err)
addrList, _ := dev.Addrs()
assert.Equal(t, len(addrList), len(ipNets))
conn.Close()

// IPv6
if supportsIPv6() {
conn, _ = net.Dial("udp6", "[2001:4860:4860::8888]:80")
defer conn.Close()
localAddr = conn.LocalAddr().(*net.UDPAddr).IP
nodeIPs = &ip.DualStackIPs{IPv6: localAddr}
_, _, dev, err = GetIPNetDeviceFromIP(nodeIPs, sets.NewString())
if err != nil {
t.Error(err)
}
t.Logf("IP obtained %s, %v", localAddr, dev)
nodeCIDRs = []string{localAddr.String() + "/128"}
_, _, link, err = GetIPNetDeviceByCIDRs(nodeCIDRs)
assert.NoError(t, err)
assert.Equal(t, dev, link)
_, _, link, err = GetIPNetDeviceByName(dev.Name)
assert.NoError(t, err)
assert.Equal(t, dev, link)
ipNets, err = GetIPNetsByLink(dev)
assert.NoError(t, err)
addrList, _ = dev.Addrs()
assert.Equal(t, len(addrList), len(ipNets))
}

// Error handling
linkList, _ := net.Interfaces()
ignoredInterfacesTest := sets.NewString()
for _, link := range linkList {
ignoredInterfacesTest.Insert(link.Name)
}
_, _, dev, err = GetIPNetDeviceFromIP(nodeIPs, ignoredInterfacesTest)
assert.Error(t, err)
assert.Empty(t, dev)
_, _, link, err = GetIPNetDeviceByName("")
assert.Error(t, err)
assert.Empty(t, link)
}

func TestGetAllIPNetsByName(t *testing.T) {
conn, err := net.Dial("udp", "8.8.8.8:80")
if err != nil {
t.Error(err)
Expand All @@ -65,4 +157,108 @@ func TestGetDefaultLocalNodeAddr(t *testing.T) {
t.Error(err)
}
t.Logf("IP obtained %s, %v", localAddr, dev)
gotIPNets, _ := GetAllIPNetsByName(dev.Name)
gotConn := false
for _, ipNet := range gotIPNets {
gotConn = gotConn && ipNet.Contains(localAddr)
}
assert.False(t, gotConn)
}

func TestGetIPv4Addr(t *testing.T) {
testIPs := []net.IP{net.IPv4zero, net.IPv6zero}
gotIP := GetIPv4Addr(testIPs)
assert.Equal(t, net.IPv4zero, gotIP)
gotIP = GetIPv4Addr([]net.IP{})
assert.Equal(t, nil, gotIP)
}

func TestGetIPWithFamily(t *testing.T) {
tests := []struct {
name string
testIPs []net.IP
testFamily uint8
wantIP net.IP
wantErr error
}{
{
name: "IPv6 AddressFamily",
testIPs: []net.IP{net.IPv4zero, net.IPv6zero},
testFamily: FamilyIPv6,
wantIP: net.IPv6zero,
wantErr: nil,
},
{
name: "IPv4 AddressFamily",
testIPs: []net.IP{net.IPv4zero, net.IPv6zero},
testFamily: FamilyIPv4,
wantIP: net.IPv4zero,
wantErr: nil,
},
{
name: "IPv6 AddressFamily not found",
testIPs: []net.IP{net.IPv4zero},
testFamily: FamilyIPv6,
wantIP: nil,
wantErr: errors.New("no IP found with IPv6 AddressFamily"),
},
{
name: "IPv4 AddressFamily not found",
testIPs: []net.IP{net.IPv6zero},
testFamily: FamilyIPv4,
wantIP: nil,
wantErr: errors.New("no IP found with IPv4 AddressFamily"),
},
}

for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
gotIP, err := GetIPWithFamily(tc.testIPs, tc.testFamily)
assert.Equal(t, tc.wantIP, gotIP)
assert.Equal(t, tc.wantErr, err)
})
}
}

func TestGetAllNodeAddresses(t *testing.T) {
nodeAddressIPv4, nodeAddressIPv6, err := GetAllNodeAddresses([]string{})
assert.NotEmpty(t, nodeAddressIPv4)
assert.Contains(t, nodeAddressIPv4, net.IPv4(127, 0, 0, 1), net.IPv4(192, 168, 1, 73))
if supportsIPv6() {
assert.NotEmpty(t, nodeAddressIPv6)
}
assert.NoError(t, err)
}

func TestNewIPNet(t *testing.T) {
gotIPNet := NewIPNet(net.IPv4allrouter)
assert.Equal(t, &net.IPNet{IP: net.IPv4allrouter, Mask: net.CIDRMask(32, 32)}, gotIPNet)
gotIPNet = NewIPNet(net.IPv6linklocalallrouters)
assert.Equal(t, &net.IPNet{IP: net.IPv6linklocalallrouters, Mask: net.CIDRMask(128, 128)}, gotIPNet)
}

func TestPortToUint16(t *testing.T) {
gotUint16 := PortToUint16(1)
assert.Equal(t, uint16(1), gotUint16)
gotUint16 = PortToUint16(-1)
assert.Equal(t, uint16(0), gotUint16)
}

func TestGenerateUplinkInterfaceName(t *testing.T) {
testUplinkName := "t0"
gotName := GenerateUplinkInterfaceName(testUplinkName)
assert.Equal(t, "t0~", gotName)
}

func TestGenerateRandomMAC(t *testing.T) {
gotMacAddress := GenerateRandomMAC()
assert.NotEmpty(t, gotMacAddress)
}

func supportsIPv6() bool {
if ln, err := net.Listen("udp6", "[2001:4860:4860::8888]:80"); err == nil {
ln.Close()
return true
}
return false
}

0 comments on commit 89e0b43

Please sign in to comment.