Skip to content

Commit

Permalink
Add access control to varnish (#7700)
Browse files Browse the repository at this point in the history
* Add access control to Varnish

* Add license
  • Loading branch information
AbdelrahmanElawady authored Aug 25, 2023
1 parent 0fb433d commit 2a220b4
Show file tree
Hide file tree
Showing 6 changed files with 567 additions and 142 deletions.
320 changes: 188 additions & 132 deletions lib/go-atscfg/ipallowdotyaml.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ package atscfg
*/

import (
"errors"
"net"
"sort"
"strconv"
Expand Down Expand Up @@ -73,62 +74,20 @@ func MakeIPAllowDotYAML(
return Cfg{}, makeErr(warnings, "this server missing HostName")
}

params := paramsToMultiMap(filterParams(serverParams, IPAllowConfigFileName, "", "", ""))

ipAllowDat := []ipAllowYAMLData{}

// localhost is trusted.
ipAllowDat = append([]ipAllowYAMLData{yamlAllowAll(`127.0.0.1`)}, ipAllowDat...)
ipAllowDat = append([]ipAllowYAMLData{yamlAllowAll(`::1`)}, ipAllowDat...)

// default for coalesce_ipv4 = 24, 5 and for ipv6 48, 5; override with the parameters in the server profile.
coalesceMaskLenV4 := DefaultCoalesceMaskLenV4
coalesceNumberV4 := DefaultCoalesceNumberV4
coalesceMaskLenV6 := DefaultCoalesceMaskLenV6
coalesceNumberV6 := DefaultCoalesceNumberV6
ips := GetPurgeIPs(serverParams)

for name, vals := range params {
for _, val := range vals {
switch name {
case ParamPurgeAllowIP:
for _, ip := range strings.Split(val, ",") {
ipAllowDat = append(ipAllowDat, yamlAllowAll(strings.TrimSpace(ip)))
}
case ParamCoalesceMaskLenV4:
if vi, err := strconv.Atoi(val); err != nil {
warnings = append(warnings, "got param '"+name+"' val '"+val+"' not a number, ignoring!")
} else if coalesceMaskLenV4 != DefaultCoalesceMaskLenV4 {
warnings = append(warnings, "got multiple param '"+name+"' - ignoring val '"+val+"'!")
} else {
coalesceMaskLenV4 = vi
}
case ParamCoalesceNumberV4:
if vi, err := strconv.Atoi(val); err != nil {
warnings = append(warnings, "got param '"+name+"' val '"+val+"' not a number, ignoring!")
} else if coalesceNumberV4 != DefaultCoalesceNumberV4 {
warnings = append(warnings, "got multiple param '"+name+"' - ignoring val '"+val+"'!")
} else {
coalesceNumberV4 = vi
}
case ParamCoalesceMaskLenV6:
if vi, err := strconv.Atoi(val); err != nil {
warnings = append(warnings, "got param '"+name+"' val '"+val+"' not a number, ignoring!")
} else if coalesceMaskLenV6 != DefaultCoalesceMaskLenV6 {
warnings = append(warnings, "got multiple param '"+name+"' - ignoring val '"+val+"'!")
} else {
coalesceMaskLenV6 = vi
}
case ParamCoalesceNumberV6:
if vi, err := strconv.Atoi(val); err != nil {
warnings = append(warnings, "got param '"+name+"' val '"+val+"' not a number, ignoring!")
} else if coalesceNumberV6 != DefaultCoalesceNumberV6 {
warnings = append(warnings, "got multiple param '"+name+"' - ignoring val '"+val+"'!")
} else {
coalesceNumberV6 = vi
}
}
}
for _, ip := range ips {
ipAllowDat = append(ipAllowDat, yamlAllowAll(strings.TrimSpace(ip)))
}
coalesceMaskLenV4, coalesceNumberV4, coalesceMaskLenV6, coalesceNumberV6, ws := GetCoalesceMaskAndNumber(serverParams)

warnings = append(warnings, ws...)

// for edges deny "PUSH|PURGE|DELETE", allow everything else to everyone.
isMid := strings.HasPrefix(server.Type, tc.MidTypePrefix)
Expand All @@ -137,92 +96,22 @@ func MakeIPAllowDotYAML(
ipAllowDat = append(ipAllowDat, yamlAllowAllButPushPurgeDelete(`::/0`))
} else {

ips := []*net.IPNet{}
ip6s := []*net.IPNet{}

cgMap := map[string]tc.CacheGroupNullable{}
for _, cg := range cacheGroups {
if cg.Name == nil {
return Cfg{}, makeErr(warnings, "got cachegroup with nil name!")
}
cgMap[*cg.Name] = cg
}

if server.Cachegroup == nil {
return Cfg{}, makeErr(warnings, "server had nil Cachegroup!")
}

serverCG, ok := cgMap[*server.Cachegroup]
if !ok {
return Cfg{}, makeErr(warnings, "server cachegroup not in cachegroups!")
}

childCGNames := getTopologyDirectChildren(tc.CacheGroupName(*server.Cachegroup), topologies)

childCGs := map[string]tc.CacheGroupNullable{}
for cgName, _ := range childCGNames {
childCGs[string(cgName)] = cgMap[string(cgName)]
}

for cgName, cg := range cgMap {
if (cg.ParentName != nil && *cg.ParentName == *serverCG.Name) || (cg.SecondaryParentName != nil && *cg.SecondaryParentName == *serverCG.Name) {
childCGs[cgName] = cg
}
}

// sort servers, to guarantee things like IP coalescing are deterministic
sort.Sort(serversSortByName(servers))
for _, childServer := range servers {
if childServer.Cachegroup == nil {
warnings = append(warnings, "Servers had server with nil Cachegroup, skipping!")
continue
} else if childServer.HostName == nil {
warnings = append(warnings, "Servers had server with nil HostName, skipping!")
continue
}

// We need to add IPs to the allow of
// - all children of this server
// - all monitors, if this server is a Mid
//
_, isChild := childCGs[*childServer.Cachegroup]
if !isChild && !strings.HasPrefix(server.Type, tc.MidTypePrefix) && string(childServer.Type) != tc.MonitorTypeName {
continue
}

for _, svInterface := range childServer.Interfaces {
for _, svAddr := range svInterface.IPAddresses {
if ip := net.ParseIP(svAddr.Address); ip != nil {
// got an IP - convert it to a CIDR and add it to the list
if ip4 := ip.To4(); ip4 != nil {
ips = append(ips, util.IPToCIDR(ip4))
} else {
ip6s = append(ip6s, util.IPToCIDR(ip))
}
} else {
// not an IP, try a CIDR
if ip, cidr, err := net.ParseCIDR(svAddr.Address); err != nil {
// not a CIDR or IP - error out
warnings = append(warnings, "server '"+*server.HostName+"' IP '"+svAddr.Address+" is not an IP address or CIDR - skipping!")
} else if ip == nil {
// not a CIDR or IP - error out
warnings = append(warnings, "server '"+*server.HostName+"' IP '"+svAddr.Address+" failed to parse as IP or CIDR - skipping!")
} else {
// got a valid CIDR - add it to the list
if ip4 := ip.To4(); ip4 != nil {
ips = append(ips, cidr)
} else {
ip6s = append(ip6s, cidr)
}
}
}
}
}
cidrs, cidr6s, ws, err := GetAllowedCIDRsForMid(
server,
servers,
cacheGroups,
topologies,
coalesceNumberV4,
coalesceMaskLenV4,
coalesceNumberV6,
coalesceMaskLenV6,
)
warnings = append(warnings, ws...)

if err != nil {
return Cfg{}, makeErr(warnings, err.Error())
}

cidrs := util.CoalesceCIDRs(ips, coalesceNumberV4, coalesceMaskLenV4)
cidr6s := util.CoalesceCIDRs(ip6s, coalesceNumberV6, coalesceMaskLenV6)

for _, cidr := range cidrs {
ipAllowDat = append(ipAllowDat, yamlAllowAllButPushPurge(cidr.String()))
}
Expand Down Expand Up @@ -350,3 +239,170 @@ func yamlDenyAll(rangeStr string) ipAllowYAMLData {
Methods: []string{YAMLMethodAll},
}
}

// GetPurgeIPs returns IPs allowed for PURGE requests.
func GetPurgeIPs(serverParams []tc.Parameter) []string {
ips := make([]string, 0)

params := paramsToMultiMap(filterParams(serverParams, IPAllowConfigFileName, "", "", ""))

for _, val := range params[ParamPurgeAllowIP] {
ips = append(ips, strings.Split(val, ",")...)
}
return ips
}

// GetCoalesceMaskAndNumber returns coalesce mask length and number for ipv4 and ipv6.
func GetCoalesceMaskAndNumber(serverParams []tc.Parameter) (int, int, int, int, []string) {
warnings := make([]string, 0)

// default for coalesce_ipv4 = 24, 5 and for ipv6 48, 5; override with the parameters in the server profile.
coalesceMaskLenV4 := DefaultCoalesceMaskLenV4
coalesceNumberV4 := DefaultCoalesceNumberV4
coalesceMaskLenV6 := DefaultCoalesceMaskLenV6
coalesceNumberV6 := DefaultCoalesceNumberV6

params := paramsToMultiMap(filterParams(serverParams, IPAllowConfigFileName, "", "", ""))

for name, vals := range params {
for _, val := range vals {
switch name {
case ParamCoalesceMaskLenV4:
if vi, err := strconv.Atoi(val); err != nil {
warnings = append(warnings, "got param '"+name+"' val '"+val+"' not a number, ignoring!")
} else if coalesceMaskLenV4 != DefaultCoalesceMaskLenV4 {
warnings = append(warnings, "got multiple param '"+name+"' - ignoring val '"+val+"'!")
} else {
coalesceMaskLenV4 = vi
}
case ParamCoalesceNumberV4:
if vi, err := strconv.Atoi(val); err != nil {
warnings = append(warnings, "got param '"+name+"' val '"+val+"' not a number, ignoring!")
} else if coalesceNumberV4 != DefaultCoalesceNumberV4 {
warnings = append(warnings, "got multiple param '"+name+"' - ignoring val '"+val+"'!")
} else {
coalesceNumberV4 = vi
}
case ParamCoalesceMaskLenV6:
if vi, err := strconv.Atoi(val); err != nil {
warnings = append(warnings, "got param '"+name+"' val '"+val+"' not a number, ignoring!")
} else if coalesceMaskLenV6 != DefaultCoalesceMaskLenV6 {
warnings = append(warnings, "got multiple param '"+name+"' - ignoring val '"+val+"'!")
} else {
coalesceMaskLenV6 = vi
}
case ParamCoalesceNumberV6:
if vi, err := strconv.Atoi(val); err != nil {
warnings = append(warnings, "got param '"+name+"' val '"+val+"' not a number, ignoring!")
} else if coalesceNumberV6 != DefaultCoalesceNumberV6 {
warnings = append(warnings, "got multiple param '"+name+"' - ignoring val '"+val+"'!")
} else {
coalesceNumberV6 = vi
}
}
}
}
return coalesceMaskLenV4, coalesceNumberV4, coalesceMaskLenV6, coalesceNumberV6, nil
}

// GetAllowedCIDRsForMid returns CIDRs allowed for all methods other than Push and Purge to mid servers.
func GetAllowedCIDRsForMid(
server *Server,
servers []Server,
cacheGroups []tc.CacheGroupNullable,
topologies []tc.Topology,
coalesceNumberV4 int,
coalesceMaskLenV4 int,
coalesceNumberV6 int,
coalesceMaskLenV6 int,
) ([]*net.IPNet, []*net.IPNet, []string, error) {

ips := []*net.IPNet{}
ip6s := []*net.IPNet{}
warnings := make([]string, 0)

cgMap := map[string]tc.CacheGroupNullable{}
for _, cg := range cacheGroups {
if cg.Name == nil {
return nil, nil, warnings, errors.New("got cachegroup with nil name!")
}
cgMap[*cg.Name] = cg
}

if server.Cachegroup == nil {
return nil, nil, warnings, errors.New("server had nil Cachegroup!")
}

serverCG, ok := cgMap[*server.Cachegroup]
if !ok {
return nil, nil, warnings, errors.New("server cachegroup not in cachegroups!")
}

childCGNames := getTopologyDirectChildren(tc.CacheGroupName(*server.Cachegroup), topologies)

childCGs := map[string]tc.CacheGroupNullable{}
for cgName, _ := range childCGNames {
childCGs[string(cgName)] = cgMap[string(cgName)]
}

for cgName, cg := range cgMap {
if (cg.ParentName != nil && *cg.ParentName == *serverCG.Name) || (cg.SecondaryParentName != nil && *cg.SecondaryParentName == *serverCG.Name) {
childCGs[cgName] = cg
}
}

// sort servers, to guarantee things like IP coalescing are deterministic
sort.Sort(serversSortByName(servers))
for _, childServer := range servers {
if childServer.Cachegroup == nil {
warnings = append(warnings, "Servers had server with nil Cachegroup, skipping!")
continue
} else if childServer.HostName == nil {
warnings = append(warnings, "Servers had server with nil HostName, skipping!")
continue
}

// We need to add IPs to the allow of
// - all children of this server
// - all monitors, if this server is a Mid
//
_, isChild := childCGs[*childServer.Cachegroup]
if !isChild && !strings.HasPrefix(server.Type, tc.MidTypePrefix) && string(childServer.Type) != tc.MonitorTypeName {
continue
}

for _, svInterface := range childServer.Interfaces {
for _, svAddr := range svInterface.IPAddresses {
if ip := net.ParseIP(svAddr.Address); ip != nil {
// got an IP - convert it to a CIDR and add it to the list
if ip4 := ip.To4(); ip4 != nil {
ips = append(ips, util.IPToCIDR(ip4))
} else {
ip6s = append(ip6s, util.IPToCIDR(ip))
}
} else {
// not an IP, try a CIDR
if ip, cidr, err := net.ParseCIDR(svAddr.Address); err != nil {
// not a CIDR or IP - error out
warnings = append(warnings, "server '"+*server.HostName+"' IP '"+svAddr.Address+" is not an IP address or CIDR - skipping!")
} else if ip == nil {
// not a CIDR or IP - error out
warnings = append(warnings, "server '"+*server.HostName+"' IP '"+svAddr.Address+" failed to parse as IP or CIDR - skipping!")
} else {
// got a valid CIDR - add it to the list
if ip4 := ip.To4(); ip4 != nil {
ips = append(ips, cidr)
} else {
ip6s = append(ip6s, cidr)
}
}
}
}
}
}

cidrs := util.CoalesceCIDRs(ips, coalesceNumberV4, coalesceMaskLenV4)
cidr6s := util.CoalesceCIDRs(ip6s, coalesceNumberV6, coalesceMaskLenV6)

return cidrs, cidr6s, warnings, nil
}
Loading

0 comments on commit 2a220b4

Please sign in to comment.