Rework network creation sanity checks

Fixes #18.
pull/20/head
Jack O'Sullivan 3 years ago
parent e9f925ba4c
commit 4f9e4aa925

@ -1 +1,3 @@
/bin/
/plugin/
/multiarch/

@ -1,6 +1,7 @@
package plugin
import (
"bytes"
"context"
"fmt"
"net"
@ -41,53 +42,63 @@ func (p *Plugin) CreateNetwork(r CreateNetworkRequest) error {
}
}
links, err := netlink.LinkList()
link, err := netlink.LinkByName(opts.Bridge)
if err != nil {
return fmt.Errorf("failed to retrieve list of network interfaces: %w", err)
return fmt.Errorf("failed to lookup interface %v: %w", opts.Bridge, err)
}
if link.Type() != "bridge" {
return util.ErrNotBridge
}
v4Addrs, err := netlink.AddrList(link, unix.AF_INET)
if err != nil {
return fmt.Errorf("failed to retrieve IPv4 addresses for %v: %w", opts.Bridge, err)
}
v6Addrs, err := netlink.AddrList(link, unix.AF_INET6)
if err != nil {
return fmt.Errorf("failed to retrieve IPv6 addresses for %v: %w", opts.Bridge, err)
}
bridgeAddrs := append(v4Addrs, v6Addrs...)
nets, err := p.docker.NetworkList(context.Background(), dTypes.NetworkListOptions{})
if err != nil {
return fmt.Errorf("failed to retrieve list of networks from Docker: %w", err)
}
found := false
for _, l := range links {
attrs := l.Attrs()
if l.Type() != "bridge" || attrs.Name != opts.Bridge {
// Make sure the addresses on this bridge aren't used by another network
for _, n := range nets {
if IsDHCPPlugin(n.Driver) {
otherOpts, err := decodeOpts(n.Options)
if err != nil {
log.
WithField("network", n.Name).
WithError(err).
Warn("Failed to parse other DHCP network's options")
} else if otherOpts.Bridge == opts.Bridge {
return util.ErrBridgeUsed
}
}
if n.IPAM.Driver == "null" {
// Null driver networks will have 0.0.0.0/0 which covers any address range!
continue
}
v4Addrs, err := netlink.AddrList(l, unix.AF_INET)
if err != nil {
return fmt.Errorf("failed to retrieve IPv4 addresses for %v: %w", attrs.Name, err)
}
v6Addrs, err := netlink.AddrList(l, unix.AF_INET6)
if err != nil {
return fmt.Errorf("failed to retrieve IPv6 addresses for %v: %w", attrs.Name, err)
}
addrs := append(v4Addrs, v6Addrs...)
// Make sure the addresses on this bridge aren't used by another network
for _, n := range nets {
for _, c := range n.IPAM.Config {
_, cidr, err := net.ParseCIDR(c.Subnet)
if err != nil {
return fmt.Errorf("failed to parse subnet %v on Docker network %v: %w", c.Subnet, n.ID, err)
}
for _, c := range n.IPAM.Config {
_, dockerCIDR, err := net.ParseCIDR(c.Subnet)
if err != nil {
return fmt.Errorf("failed to parse subnet %v on Docker network %v: %w", c.Subnet, n.ID, err)
}
if bytes.Equal(dockerCIDR.Mask, net.CIDRMask(0, 32)) || bytes.Equal(dockerCIDR.Mask, net.CIDRMask(0, 128)) {
// Last check to make sure the network isn't 0.0.0.0/0 or ::/0 (which would always pass the check below)
continue
}
for _, linkAddr := range addrs {
if linkAddr.IPNet.Contains(cidr.IP) || cidr.Contains(linkAddr.IP) {
return util.ErrBridgeUsed
}
for _, bridgeAddr := range bridgeAddrs {
if bridgeAddr.IPNet.Contains(dockerCIDR.IP) || dockerCIDR.Contains(bridgeAddr.IP) {
return util.ErrBridgeUsed
}
}
}
found = true
break
}
if !found {
return util.ErrBridgeNotFound
}
log.WithFields(log.Fields{

@ -4,6 +4,7 @@ import (
"fmt"
"net"
"net/http"
"regexp"
"time"
docker "github.com/docker/docker/client"
@ -19,6 +20,13 @@ const DriverName string = "net-dhcp"
const defaultLeaseTimeout = 10 * time.Second
var driverRegexp = regexp.MustCompile(`^ghcr\.io/devplayer0/docker-net-dhcp:.+$`)
// IsDHCPPlugin checks if a Docker network driver is an instance of this plugin
func IsDHCPPlugin(driver string) bool {
return driverRegexp.MatchString(driver)
}
// DHCPNetworkOptions contains options for the DHCP network driver
type DHCPNetworkOptions struct {
Bridge string

@ -10,8 +10,8 @@ var (
ErrIPAM = errors.New("only the null IPAM driver is supported")
// ErrBridgeRequired indicates a network bridge was not provided for network creation
ErrBridgeRequired = errors.New("bridge required")
// ErrBridgeNotFound indicates that a bridge could not be found
ErrBridgeNotFound = errors.New("bridge not found")
// ErrNotBridge indicates that the provided network interface is not a bridge
ErrNotBridge = errors.New("network interface is not a bridge")
// ErrBridgeUsed indicates that a bridge is already in use
ErrBridgeUsed = errors.New("bridge already in use by Docker")
// ErrMACAddress indicates an invalid MAC address
@ -30,7 +30,7 @@ var (
func ErrToStatus(err error) int {
switch {
case errors.Is(err, ErrIPAM), errors.Is(err, ErrBridgeRequired), errors.Is(err, ErrBridgeNotFound),
case errors.Is(err, ErrIPAM), errors.Is(err, ErrBridgeRequired), errors.Is(err, ErrNotBridge),
errors.Is(err, ErrBridgeUsed), errors.Is(err, ErrMACAddress):
return http.StatusBadRequest
default:

Loading…
Cancel
Save