feat(firewall): use all default routes
- Accept output traffic from all default routes through VPN interface - Accept output from all default routes to outbound subnets - Accept all input traffic on ports for all default routes - Add IP rules for all default routes
This commit is contained in:
@@ -96,13 +96,9 @@ func (c *Config) enable(ctx context.Context) (err error) {
|
||||
if err = c.acceptEstablishedRelatedTraffic(ctx, remove); err != nil {
|
||||
return err
|
||||
}
|
||||
if c.vpnConnection.IP != nil {
|
||||
if err = c.acceptOutputTrafficToVPN(ctx, c.defaultInterface, c.vpnConnection, remove); err != nil {
|
||||
return err
|
||||
}
|
||||
if err = c.acceptOutputThroughInterface(ctx, c.vpnIntf, remove); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err = c.allowVPNIP(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, network := range c.localNetworks {
|
||||
@@ -111,10 +107,8 @@ func (c *Config) enable(ctx context.Context) (err error) {
|
||||
}
|
||||
}
|
||||
|
||||
for _, subnet := range c.outboundSubnets {
|
||||
if err := c.acceptOutputFromIPToSubnet(ctx, c.defaultInterface, c.localIP, subnet, remove); err != nil {
|
||||
return err
|
||||
}
|
||||
if err = c.allowOutboundSubnets(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Allows packets from any IP address to go through eth0 / local network
|
||||
@@ -125,10 +119,8 @@ func (c *Config) enable(ctx context.Context) (err error) {
|
||||
}
|
||||
}
|
||||
|
||||
for port, intf := range c.allowedInputPorts {
|
||||
if err := c.acceptInputToPort(ctx, intf, port, remove); err != nil {
|
||||
return err
|
||||
}
|
||||
if err = c.allowInputPorts(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := c.runUserPostRules(ctx, c.customRulesPath, remove); err != nil {
|
||||
@@ -137,3 +129,47 @@ func (c *Config) enable(ctx context.Context) (err error) {
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Config) allowVPNIP(ctx context.Context) (err error) {
|
||||
if c.vpnConnection.IP == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
const remove = false
|
||||
for _, defaultRoute := range c.defaultRoutes {
|
||||
err = c.acceptOutputTrafficToVPN(ctx, defaultRoute.NetInterface, c.vpnConnection, remove)
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannot accept output traffic through VPN: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Config) allowOutboundSubnets(ctx context.Context) (err error) {
|
||||
for _, subnet := range c.outboundSubnets {
|
||||
for _, defaultRoute := range c.defaultRoutes {
|
||||
const remove = false
|
||||
err := c.acceptOutputFromIPToSubnet(ctx, defaultRoute.NetInterface,
|
||||
defaultRoute.AssignedIP, subnet, remove)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Config) allowInputPorts(ctx context.Context) (err error) {
|
||||
for port, netInterfaces := range c.allowedInputPorts {
|
||||
for netInterface := range netInterfaces {
|
||||
const remove = false
|
||||
err = c.acceptInputToPort(ctx, netInterface, port, remove)
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannot accept input port %d on interface %s: %w",
|
||||
port, netInterface, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -23,14 +23,12 @@ type Configurator interface {
|
||||
}
|
||||
|
||||
type Config struct { //nolint:maligned
|
||||
runner command.Runner
|
||||
logger Logger
|
||||
iptablesMutex sync.Mutex
|
||||
ip6tablesMutex sync.Mutex
|
||||
defaultInterface string
|
||||
defaultGateway net.IP
|
||||
localNetworks []routing.LocalNetwork
|
||||
localIP net.IP
|
||||
runner command.Runner
|
||||
logger Logger
|
||||
iptablesMutex sync.Mutex
|
||||
ip6tablesMutex sync.Mutex
|
||||
defaultRoutes []routing.DefaultRoute
|
||||
localNetworks []routing.LocalNetwork
|
||||
|
||||
// Fixed state
|
||||
ipTables string
|
||||
@@ -42,16 +40,15 @@ type Config struct { //nolint:maligned
|
||||
vpnConnection models.Connection
|
||||
vpnIntf string
|
||||
outboundSubnets []net.IPNet
|
||||
allowedInputPorts map[uint16]string // port to interface mapping
|
||||
allowedInputPorts map[uint16]map[string]struct{} // port to interfaces set mapping
|
||||
stateMutex sync.Mutex
|
||||
}
|
||||
|
||||
// NewConfig creates a new Config instance and returns an error
|
||||
// if no iptables implementation is available.
|
||||
func NewConfig(ctx context.Context, logger Logger,
|
||||
runner command.Runner, defaultInterface string,
|
||||
defaultGateway net.IP, localNetworks []routing.LocalNetwork,
|
||||
localIP net.IP) (config *Config, err error) {
|
||||
runner command.Runner, defaultRoutes []routing.DefaultRoute,
|
||||
localNetworks []routing.LocalNetwork) (config *Config, err error) {
|
||||
iptables, err := findIptablesSupported(ctx, runner)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -60,14 +57,12 @@ func NewConfig(ctx context.Context, logger Logger,
|
||||
return &Config{
|
||||
runner: runner,
|
||||
logger: logger,
|
||||
allowedInputPorts: make(map[uint16]string),
|
||||
allowedInputPorts: make(map[uint16]map[string]struct{}),
|
||||
ipTables: iptables,
|
||||
ip6Tables: findIP6tablesSupported(ctx, runner),
|
||||
customRulesPath: "/iptables/post-rules.txt",
|
||||
// Obtained from routing
|
||||
defaultInterface: defaultInterface,
|
||||
defaultGateway: defaultGateway,
|
||||
localNetworks: localNetworks,
|
||||
localIP: localIP,
|
||||
defaultRoutes: defaultRoutes,
|
||||
localNetworks: localNetworks,
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -41,9 +41,13 @@ func (c *Config) SetOutboundSubnets(ctx context.Context, subnets []net.IPNet) (e
|
||||
func (c *Config) removeOutboundSubnets(ctx context.Context, subnets []net.IPNet) {
|
||||
const remove = true
|
||||
for _, subNet := range subnets {
|
||||
if err := c.acceptOutputFromIPToSubnet(ctx, c.defaultInterface, c.localIP, subNet, remove); err != nil {
|
||||
c.logger.Error("cannot remove outdated outbound subnet: " + err.Error())
|
||||
continue
|
||||
for _, defaultRoute := range c.defaultRoutes {
|
||||
err := c.acceptOutputFromIPToSubnet(ctx, defaultRoute.NetInterface,
|
||||
defaultRoute.AssignedIP, subNet, remove)
|
||||
if err != nil {
|
||||
c.logger.Error("cannot remove outdated outbound subnet: " + err.Error())
|
||||
continue
|
||||
}
|
||||
}
|
||||
c.outboundSubnets = subnet.RemoveSubnetFromSubnets(c.outboundSubnets, subNet)
|
||||
}
|
||||
@@ -52,8 +56,12 @@ func (c *Config) removeOutboundSubnets(ctx context.Context, subnets []net.IPNet)
|
||||
func (c *Config) addOutboundSubnets(ctx context.Context, subnets []net.IPNet) error {
|
||||
const remove = false
|
||||
for _, subnet := range subnets {
|
||||
if err := c.acceptOutputFromIPToSubnet(ctx, c.defaultInterface, c.localIP, subnet, remove); err != nil {
|
||||
return err
|
||||
for _, defaultRoute := range c.defaultRoutes {
|
||||
err := c.acceptOutputFromIPToSubnet(ctx, defaultRoute.NetInterface,
|
||||
defaultRoute.AssignedIP, subnet, remove)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
c.outboundSubnets = append(c.outboundSubnets, subnet)
|
||||
}
|
||||
|
||||
@@ -21,27 +21,30 @@ func (c *Config) SetAllowedPort(ctx context.Context, port uint16, intf string) (
|
||||
|
||||
if !c.enabled {
|
||||
c.logger.Info("firewall disabled, only updating allowed ports internal state")
|
||||
c.allowedInputPorts[port] = intf
|
||||
existingInterfaces, ok := c.allowedInputPorts[port]
|
||||
if !ok {
|
||||
existingInterfaces = make(map[string]struct{})
|
||||
}
|
||||
existingInterfaces[intf] = struct{}{}
|
||||
c.allowedInputPorts[port] = existingInterfaces
|
||||
return nil
|
||||
}
|
||||
|
||||
netInterfaces, has := c.allowedInputPorts[port]
|
||||
if !has {
|
||||
netInterfaces = make(map[string]struct{})
|
||||
} else if _, exists := netInterfaces[intf]; exists {
|
||||
return nil
|
||||
}
|
||||
|
||||
c.logger.Info("setting allowed input port " + fmt.Sprint(port) + " through interface " + intf + "...")
|
||||
|
||||
if existingIntf, ok := c.allowedInputPorts[port]; ok {
|
||||
if intf == existingIntf {
|
||||
return nil
|
||||
}
|
||||
const remove = true
|
||||
if err := c.acceptInputToPort(ctx, existingIntf, port, remove); err != nil {
|
||||
return fmt.Errorf("cannot remove old allowed port %d: %w", port, err)
|
||||
}
|
||||
}
|
||||
|
||||
const remove = false
|
||||
if err := c.acceptInputToPort(ctx, intf, port, remove); err != nil {
|
||||
return fmt.Errorf("cannot allow input to port %d: %w", port, err)
|
||||
return fmt.Errorf("cannot allow input to port %d through interface %s: %w",
|
||||
port, intf, err)
|
||||
}
|
||||
c.allowedInputPorts[port] = intf
|
||||
netInterfaces[intf] = struct{}{}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -60,17 +63,24 @@ func (c *Config) RemoveAllowedPort(ctx context.Context, port uint16) (err error)
|
||||
return nil
|
||||
}
|
||||
|
||||
c.logger.Info("removing allowed port " + strconv.Itoa(int(port)) + " ...")
|
||||
c.logger.Info("removing allowed port " + strconv.Itoa(int(port)) + "...")
|
||||
|
||||
intf, ok := c.allowedInputPorts[port]
|
||||
interfacesSet, ok := c.allowedInputPorts[port]
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
|
||||
const remove = true
|
||||
if err := c.acceptInputToPort(ctx, intf, port, remove); err != nil {
|
||||
return fmt.Errorf("cannot remove allowed port %d: %w", port, err)
|
||||
for netInterface := range interfacesSet {
|
||||
err := c.acceptInputToPort(ctx, netInterface, port, remove)
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannot remove allowed port %d on interface %s: %w",
|
||||
port, netInterface, err)
|
||||
}
|
||||
delete(interfacesSet, netInterface)
|
||||
}
|
||||
|
||||
// All interfaces were removed successfully, so remove the port entry.
|
||||
delete(c.allowedInputPorts, port)
|
||||
|
||||
return nil
|
||||
|
||||
@@ -31,8 +31,10 @@ func (c *Config) SetVPNConnection(ctx context.Context,
|
||||
|
||||
remove := true
|
||||
if c.vpnConnection.IP != nil {
|
||||
if err := c.acceptOutputTrafficToVPN(ctx, c.defaultInterface, c.vpnConnection, remove); err != nil {
|
||||
c.logger.Error("cannot remove outdated VPN connection rule: " + err.Error())
|
||||
for _, defaultRoute := range c.defaultRoutes {
|
||||
if err := c.acceptOutputTrafficToVPN(ctx, defaultRoute.NetInterface, c.vpnConnection, remove); err != nil {
|
||||
c.logger.Error("cannot remove outdated VPN connection rule: " + err.Error())
|
||||
}
|
||||
}
|
||||
}
|
||||
c.vpnConnection = models.Connection{}
|
||||
@@ -46,8 +48,10 @@ func (c *Config) SetVPNConnection(ctx context.Context,
|
||||
|
||||
remove = false
|
||||
|
||||
if err := c.acceptOutputTrafficToVPN(ctx, c.defaultInterface, connection, remove); err != nil {
|
||||
return fmt.Errorf("cannot allow output traffic through VPN connection: %w", err)
|
||||
for _, defaultRoute := range c.defaultRoutes {
|
||||
if err := c.acceptOutputTrafficToVPN(ctx, defaultRoute.NetInterface, connection, remove); err != nil {
|
||||
return fmt.Errorf("cannot allow output traffic through VPN connection: %w", err)
|
||||
}
|
||||
}
|
||||
c.vpnConnection = connection
|
||||
|
||||
|
||||
Reference in New Issue
Block a user