mirror of
https://github.com/optim-enterprises-bv/vault.git
synced 2025-11-26 14:55:01 +00:00
Move cidrutil to sdk
This commit is contained in:
217
sdk/helper/cidrutil/cidr.go
Normal file
217
sdk/helper/cidrutil/cidr.go
Normal file
@@ -0,0 +1,217 @@
|
||||
package cidrutil
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"strings"
|
||||
|
||||
"github.com/hashicorp/errwrap"
|
||||
sockaddr "github.com/hashicorp/go-sockaddr"
|
||||
"github.com/hashicorp/vault/sdk/helper/strutil"
|
||||
)
|
||||
|
||||
// RemoteAddrIsOk checks if the given remote address is either:
|
||||
// - OK because there's no CIDR whitelist
|
||||
// - OK because it's in the CIDR whitelist
|
||||
func RemoteAddrIsOk(remoteAddr string, boundCIDRs []*sockaddr.SockAddrMarshaler) bool {
|
||||
if len(boundCIDRs) == 0 {
|
||||
// There's no CIDR whitelist.
|
||||
return true
|
||||
}
|
||||
remoteSockAddr, err := sockaddr.NewSockAddr(remoteAddr)
|
||||
if err != nil {
|
||||
// Can't tell, err on the side of less access.
|
||||
return false
|
||||
}
|
||||
for _, cidr := range boundCIDRs {
|
||||
if cidr.Contains(remoteSockAddr) {
|
||||
// Whitelisted.
|
||||
return true
|
||||
}
|
||||
}
|
||||
// Not whitelisted.
|
||||
return false
|
||||
}
|
||||
|
||||
// IPBelongsToCIDR checks if the given IP is encompassed by the given CIDR block
|
||||
func IPBelongsToCIDR(ipAddr string, cidr string) (bool, error) {
|
||||
if ipAddr == "" {
|
||||
return false, fmt.Errorf("missing IP address")
|
||||
}
|
||||
|
||||
ip := net.ParseIP(ipAddr)
|
||||
if ip == nil {
|
||||
return false, fmt.Errorf("invalid IP address")
|
||||
}
|
||||
|
||||
_, ipnet, err := net.ParseCIDR(cidr)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
if !ipnet.Contains(ip) {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// IPBelongsToCIDRBlocksSlice checks if the given IP is encompassed by any of the given
|
||||
// CIDR blocks
|
||||
func IPBelongsToCIDRBlocksSlice(ipAddr string, cidrs []string) (bool, error) {
|
||||
if ipAddr == "" {
|
||||
return false, fmt.Errorf("missing IP address")
|
||||
}
|
||||
|
||||
if len(cidrs) == 0 {
|
||||
return false, fmt.Errorf("missing CIDR blocks to be checked against")
|
||||
}
|
||||
|
||||
if ip := net.ParseIP(ipAddr); ip == nil {
|
||||
return false, fmt.Errorf("invalid IP address")
|
||||
}
|
||||
|
||||
for _, cidr := range cidrs {
|
||||
belongs, err := IPBelongsToCIDR(ipAddr, cidr)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
if belongs {
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// ValidateCIDRListString checks if the list of CIDR blocks are valid, given
|
||||
// that the input is a string composed by joining all the CIDR blocks using a
|
||||
// separator. The input is separated based on the given separator and validity
|
||||
// of each is checked.
|
||||
func ValidateCIDRListString(cidrList string, separator string) (bool, error) {
|
||||
if cidrList == "" {
|
||||
return false, fmt.Errorf("missing CIDR list that needs validation")
|
||||
}
|
||||
if separator == "" {
|
||||
return false, fmt.Errorf("missing separator")
|
||||
}
|
||||
|
||||
return ValidateCIDRListSlice(strutil.ParseDedupLowercaseAndSortStrings(cidrList, separator))
|
||||
}
|
||||
|
||||
// ValidateCIDRListSlice checks if the given list of CIDR blocks are valid
|
||||
func ValidateCIDRListSlice(cidrBlocks []string) (bool, error) {
|
||||
if len(cidrBlocks) == 0 {
|
||||
return false, fmt.Errorf("missing CIDR blocks that needs validation")
|
||||
}
|
||||
|
||||
for _, block := range cidrBlocks {
|
||||
if _, _, err := net.ParseCIDR(strings.TrimSpace(block)); err != nil {
|
||||
return false, err
|
||||
}
|
||||
}
|
||||
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// Subset checks if the IPs belonging to a given CIDR block is a subset of IPs
|
||||
// belonging to another CIDR block.
|
||||
func Subset(cidr1, cidr2 string) (bool, error) {
|
||||
if cidr1 == "" {
|
||||
return false, fmt.Errorf("missing CIDR to be checked against")
|
||||
}
|
||||
|
||||
if cidr2 == "" {
|
||||
return false, fmt.Errorf("missing CIDR that needs to be checked")
|
||||
}
|
||||
|
||||
ip1, net1, err := net.ParseCIDR(cidr1)
|
||||
if err != nil {
|
||||
return false, errwrap.Wrapf("failed to parse the CIDR to be checked against: {{err}}", err)
|
||||
}
|
||||
|
||||
zeroAddr := false
|
||||
if ip := ip1.To4(); ip != nil && ip.Equal(net.IPv4zero) {
|
||||
zeroAddr = true
|
||||
}
|
||||
if ip := ip1.To16(); ip != nil && ip.Equal(net.IPv6zero) {
|
||||
zeroAddr = true
|
||||
}
|
||||
|
||||
maskLen1, _ := net1.Mask.Size()
|
||||
if !zeroAddr && maskLen1 == 0 {
|
||||
return false, fmt.Errorf("CIDR to be checked against is not in its canonical form")
|
||||
}
|
||||
|
||||
ip2, net2, err := net.ParseCIDR(cidr2)
|
||||
if err != nil {
|
||||
return false, errwrap.Wrapf("failed to parse the CIDR that needs to be checked: {{err}}", err)
|
||||
}
|
||||
|
||||
zeroAddr = false
|
||||
if ip := ip2.To4(); ip != nil && ip.Equal(net.IPv4zero) {
|
||||
zeroAddr = true
|
||||
}
|
||||
if ip := ip2.To16(); ip != nil && ip.Equal(net.IPv6zero) {
|
||||
zeroAddr = true
|
||||
}
|
||||
|
||||
maskLen2, _ := net2.Mask.Size()
|
||||
if !zeroAddr && maskLen2 == 0 {
|
||||
return false, fmt.Errorf("CIDR that needs to be checked is not in its canonical form")
|
||||
}
|
||||
|
||||
// If the mask length of the CIDR that needs to be checked is smaller
|
||||
// then the mask length of the CIDR to be checked against, then the
|
||||
// former will encompass more IPs than the latter, and hence can't be a
|
||||
// subset of the latter.
|
||||
if maskLen2 < maskLen1 {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
belongs, err := IPBelongsToCIDR(net2.IP.String(), cidr1)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
return belongs, nil
|
||||
}
|
||||
|
||||
// SubsetBlocks checks if each CIDR block of a given set of CIDR blocks, is a
|
||||
// subset of at least one CIDR block belonging to another set of CIDR blocks.
|
||||
// First parameter is the set of CIDR blocks to check against and the second
|
||||
// parameter is the set of CIDR blocks that needs to be checked.
|
||||
func SubsetBlocks(cidrBlocks1, cidrBlocks2 []string) (bool, error) {
|
||||
if len(cidrBlocks1) == 0 {
|
||||
return false, fmt.Errorf("missing CIDR blocks to be checked against")
|
||||
}
|
||||
|
||||
if len(cidrBlocks2) == 0 {
|
||||
return false, fmt.Errorf("missing CIDR blocks that needs to be checked")
|
||||
}
|
||||
|
||||
// Check if all the elements of cidrBlocks2 is a subset of at least one
|
||||
// element of cidrBlocks1
|
||||
for _, cidrBlock2 := range cidrBlocks2 {
|
||||
isSubset := false
|
||||
for _, cidrBlock1 := range cidrBlocks1 {
|
||||
subset, err := Subset(cidrBlock1, cidrBlock2)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
// If CIDR is a subset of any of the CIDR block, its
|
||||
// good enough. Break out.
|
||||
if subset {
|
||||
isSubset = true
|
||||
break
|
||||
}
|
||||
}
|
||||
// CIDR block was not a subset of any of the CIDR blocks in the
|
||||
// set of blocks to check against
|
||||
if !isSubset {
|
||||
return false, nil
|
||||
}
|
||||
}
|
||||
|
||||
return true, nil
|
||||
}
|
||||
226
sdk/helper/cidrutil/cidr_test.go
Normal file
226
sdk/helper/cidrutil/cidr_test.go
Normal file
@@ -0,0 +1,226 @@
|
||||
package cidrutil
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
sockaddr "github.com/hashicorp/go-sockaddr"
|
||||
)
|
||||
|
||||
func TestCIDRUtil_IPBelongsToCIDR(t *testing.T) {
|
||||
ip := "192.168.25.30"
|
||||
cidr := "192.168.26.30/16"
|
||||
|
||||
belongs, err := IPBelongsToCIDR(ip, cidr)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !belongs {
|
||||
t.Fatalf("expected IP %q to belong to CIDR %q", ip, cidr)
|
||||
}
|
||||
|
||||
ip = "10.197.192.6"
|
||||
cidr = "10.197.192.0/18"
|
||||
belongs, err = IPBelongsToCIDR(ip, cidr)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !belongs {
|
||||
t.Fatalf("expected IP %q to belong to CIDR %q", ip, cidr)
|
||||
}
|
||||
|
||||
ip = "192.168.25.30"
|
||||
cidr = "192.168.26.30/24"
|
||||
belongs, err = IPBelongsToCIDR(ip, cidr)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if belongs {
|
||||
t.Fatalf("expected IP %q to not belong to CIDR %q", ip, cidr)
|
||||
}
|
||||
|
||||
ip = "192.168.25.30.100"
|
||||
cidr = "192.168.26.30/24"
|
||||
belongs, err = IPBelongsToCIDR(ip, cidr)
|
||||
if err == nil {
|
||||
t.Fatalf("expected an error")
|
||||
}
|
||||
}
|
||||
|
||||
func TestCIDRUtil_IPBelongsToCIDRBlocksSlice(t *testing.T) {
|
||||
ip := "192.168.27.29"
|
||||
cidrList := []string{"172.169.100.200/18", "192.168.0.0/16", "10.10.20.20/24"}
|
||||
|
||||
belongs, err := IPBelongsToCIDRBlocksSlice(ip, cidrList)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !belongs {
|
||||
t.Fatalf("expected IP %q to belong to one of the CIDRs in %q", ip, cidrList)
|
||||
}
|
||||
|
||||
ip = "192.168.27.29"
|
||||
cidrList = []string{"172.169.100.200/18", "192.168.0.0.0/16", "10.10.20.20/24"}
|
||||
|
||||
belongs, err = IPBelongsToCIDRBlocksSlice(ip, cidrList)
|
||||
if err == nil {
|
||||
t.Fatalf("expected an error")
|
||||
}
|
||||
|
||||
ip = "30.40.50.60"
|
||||
cidrList = []string{"172.169.100.200/18", "192.168.0.0/16", "10.10.20.20/24"}
|
||||
|
||||
belongs, err = IPBelongsToCIDRBlocksSlice(ip, cidrList)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if belongs {
|
||||
t.Fatalf("expected IP %q to not belong to one of the CIDRs in %q", ip, cidrList)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCIDRUtil_ValidateCIDRListString(t *testing.T) {
|
||||
cidrList := "172.169.100.200/18,192.168.0.0/16,10.10.20.20/24"
|
||||
|
||||
valid, err := ValidateCIDRListString(cidrList, ",")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !valid {
|
||||
t.Fatalf("expected CIDR list %q to be valid", cidrList)
|
||||
}
|
||||
|
||||
cidrList = "172.169.100.200,192.168.0.0/16,10.10.20.20/24"
|
||||
valid, err = ValidateCIDRListString(cidrList, ",")
|
||||
if err == nil {
|
||||
t.Fatal("expected an error")
|
||||
}
|
||||
|
||||
cidrList = "172.169.100.200/18,192.168.0.0.0/16,10.10.20.20/24"
|
||||
valid, err = ValidateCIDRListString(cidrList, ",")
|
||||
if err == nil {
|
||||
t.Fatal("expected an error")
|
||||
}
|
||||
}
|
||||
|
||||
func TestCIDRUtil_ValidateCIDRListSlice(t *testing.T) {
|
||||
cidrList := []string{"172.169.100.200/18", "192.168.0.0/16", "10.10.20.20/24"}
|
||||
|
||||
valid, err := ValidateCIDRListSlice(cidrList)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !valid {
|
||||
t.Fatalf("expected CIDR list %q to be valid", cidrList)
|
||||
}
|
||||
|
||||
cidrList = []string{"172.169.100.200", "192.168.0.0/16", "10.10.20.20/24"}
|
||||
valid, err = ValidateCIDRListSlice(cidrList)
|
||||
if err == nil {
|
||||
t.Fatal("expected an error")
|
||||
}
|
||||
|
||||
cidrList = []string{"172.169.100.200/18", "192.168.0.0.0/16", "10.10.20.20/24"}
|
||||
valid, err = ValidateCIDRListSlice(cidrList)
|
||||
if err == nil {
|
||||
t.Fatal("expected an error")
|
||||
}
|
||||
}
|
||||
|
||||
func TestCIDRUtil_Subset(t *testing.T) {
|
||||
cidr1 := "192.168.27.29/24"
|
||||
cidr2 := "192.168.27.29/24"
|
||||
subset, err := Subset(cidr1, cidr2)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !subset {
|
||||
t.Fatalf("expected CIDR %q to be a subset of CIDR %q", cidr2, cidr1)
|
||||
}
|
||||
|
||||
cidr1 = "192.168.27.29/16"
|
||||
cidr2 = "192.168.27.29/24"
|
||||
subset, err = Subset(cidr1, cidr2)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !subset {
|
||||
t.Fatalf("expected CIDR %q to be a subset of CIDR %q", cidr2, cidr1)
|
||||
}
|
||||
|
||||
cidr1 = "192.168.27.29/24"
|
||||
cidr2 = "192.168.27.29/16"
|
||||
subset, err = Subset(cidr1, cidr2)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if subset {
|
||||
t.Fatalf("expected CIDR %q to not be a subset of CIDR %q", cidr2, cidr1)
|
||||
}
|
||||
|
||||
cidr1 = "192.168.0.128/25"
|
||||
cidr2 = "192.168.0.0/24"
|
||||
subset, err = Subset(cidr1, cidr2)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if subset {
|
||||
t.Fatalf("expected CIDR %q to not be a subset of CIDR %q", cidr2, cidr1)
|
||||
}
|
||||
subset, err = Subset(cidr2, cidr1)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !subset {
|
||||
t.Fatalf("expected CIDR %q to be a subset of CIDR %q", cidr1, cidr2)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCIDRUtil_SubsetBlocks(t *testing.T) {
|
||||
cidrBlocks1 := []string{"192.168.27.29/16", "172.245.30.40/24", "10.20.30.40/30"}
|
||||
cidrBlocks2 := []string{"192.168.27.29/20", "172.245.30.40/25", "10.20.30.40/32"}
|
||||
|
||||
subset, err := SubsetBlocks(cidrBlocks1, cidrBlocks2)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !subset {
|
||||
t.Fatalf("expected CIDR blocks %q to be a subset of CIDR blocks %q", cidrBlocks2, cidrBlocks1)
|
||||
}
|
||||
|
||||
cidrBlocks1 = []string{"192.168.27.29/16", "172.245.30.40/25", "10.20.30.40/30"}
|
||||
cidrBlocks2 = []string{"192.168.27.29/20", "172.245.30.40/24", "10.20.30.40/32"}
|
||||
|
||||
subset, err = SubsetBlocks(cidrBlocks1, cidrBlocks2)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if subset {
|
||||
t.Fatalf("expected CIDR blocks %q to not be a subset of CIDR blocks %q", cidrBlocks2, cidrBlocks1)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCIDRUtil_RemoteAddrIsOk_NegativeTest(t *testing.T) {
|
||||
addr, err := sockaddr.NewSockAddr("127.0.0.1/8")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
boundCIDRs := []*sockaddr.SockAddrMarshaler{
|
||||
{addr},
|
||||
}
|
||||
if RemoteAddrIsOk("123.0.0.1", boundCIDRs) {
|
||||
t.Fatal("remote address of 123.0.0.1/2 should not be allowed for 127.0.0.1/8")
|
||||
}
|
||||
}
|
||||
|
||||
func TestCIDRUtil_RemoteAddrIsOk_PositiveTest(t *testing.T) {
|
||||
addr, err := sockaddr.NewSockAddr("127.0.0.1/8")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
boundCIDRs := []*sockaddr.SockAddrMarshaler{
|
||||
{addr},
|
||||
}
|
||||
if !RemoteAddrIsOk("127.0.0.1", boundCIDRs) {
|
||||
t.Fatal("remote address of 127.0.0.1 should be allowed for 127.0.0.1/8")
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user