363 lines
11 KiB
Rust
363 lines
11 KiB
Rust
use crate::startup::Interfaces;
|
|
|
|
static UNIVERSE: &'static str = "0.0.0.0/0";
|
|
|
|
pub(crate) fn firewall_rules(interfaces: &Interfaces) -> String {
|
|
filter(interfaces) + "\n" + &nat(interfaces)
|
|
}
|
|
|
|
// FILTER table rules
|
|
fn filter(interfaces: &Interfaces) -> String {
|
|
let mut filter = String::from(
|
|
r#"*filter
|
|
:INPUT DROP [0:0]
|
|
:FORWARD DROP [0:0]
|
|
:OUTPUT DROP [0:0]
|
|
|
|
"#,
|
|
);
|
|
|
|
// INPUT: Incoming traffic from various interfaces
|
|
|
|
// Accept everything on loopback
|
|
filter += &format!(
|
|
"-A INPUT -i lo -s {universe} -d {universe} -j ACCEPT\n",
|
|
universe = UNIVERSE
|
|
);
|
|
|
|
// Allow internal machines to connect to anything
|
|
for iface in interfaces.internal.iter().chain(&interfaces.vlan) {
|
|
filter += &format!(
|
|
"-A INPUT -i {intif} -s {intip}/{intmask} -d {universe} -j ACCEPT\n",
|
|
intif = iface.interface,
|
|
intip = iface.ip,
|
|
intmask = iface.mask,
|
|
universe = UNIVERSE
|
|
);
|
|
}
|
|
|
|
// Disallow IP spoofing, internal IPs should only come from the internal network
|
|
// If an internal IP is seen by the external interface, it's BAD!!!!
|
|
for iface in interfaces.internal.iter().chain(&interfaces.vlan) {
|
|
filter += &format!(
|
|
"-A INPUT -i {extif} -s {intip}/{intmask} -d {universe} -j REJECT\n",
|
|
extif = interfaces.external.interface,
|
|
intip = iface.ip,
|
|
intmask = iface.mask,
|
|
universe = UNIVERSE,
|
|
);
|
|
}
|
|
|
|
// Allow ICMP traffic on external interface
|
|
filter += &format!(
|
|
"-A INPUT -i {extif} -p ICMP -s {universe} -d {extip}/{extmask} -j ACCEPT\n",
|
|
extif = interfaces.external.interface,
|
|
universe = UNIVERSE,
|
|
extip = interfaces.external.ip,
|
|
extmask = interfaces.external.mask,
|
|
);
|
|
|
|
// Don't prevent existing connections for continuing
|
|
filter += &format!(
|
|
"-A INPUT -i {extif} -s {universe} -d {extip}/{extmask} -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT\n",
|
|
extif = interfaces.external.interface,
|
|
universe = UNIVERSE,
|
|
extip = interfaces.external.ip,
|
|
extmask = interfaces.external.mask,
|
|
);
|
|
|
|
// Allow DHCP traffic
|
|
for iface in interfaces.internal.iter().chain(&interfaces.vlan) {
|
|
filter += &format!(
|
|
"-A INPUT -i {intif} -p tcp --sport 68 --dport 67 -j ACCEPT\n",
|
|
intif = iface.interface,
|
|
);
|
|
|
|
filter += &format!(
|
|
"-A INPUT -i {intif} -p udp --sport 68 --dport 67 -j ACCEPT\n",
|
|
intif = iface.interface,
|
|
);
|
|
}
|
|
|
|
for iface in &interfaces.tunnel {
|
|
filter += &format!(
|
|
"-A INPUT -i {tunface} -j ACCEPT\n",
|
|
tunface = iface.interface,
|
|
);
|
|
}
|
|
|
|
filter += &format!(
|
|
"-A INPUT -s {universe} -d {universe} -j REJECT\n",
|
|
universe = UNIVERSE
|
|
);
|
|
|
|
// OUTPUT: Outgoing traffic from vairous interfaces
|
|
|
|
// netfilter bug workaround
|
|
filter += "-A OUTPUT -m conntrack -p icmp --ctstate INVALID -j DROP\n";
|
|
|
|
// Accept everything on loopback
|
|
filter += &format!(
|
|
"-A OUTPUT -o lo -s {universe} -d {universe} -j ACCEPT\n",
|
|
universe = UNIVERSE,
|
|
);
|
|
|
|
// Allow VLAN traffic only on VLAN
|
|
for iface in &interfaces.vlan {
|
|
// Allow internal traffic only on network associated with interface
|
|
filter += &format!(
|
|
"-A OUTPUT -o {intif} -s {extip}/{extmask} -d {intip}/{intmask} -j ACCEPT\n",
|
|
intif = iface.interface,
|
|
extip = interfaces.external.ip,
|
|
extmask = interfaces.external.mask,
|
|
intip = iface.ip,
|
|
intmask = iface.mask,
|
|
);
|
|
|
|
// Allow traffic from self to networks associated with interface
|
|
filter += &format!(
|
|
"-A OUTPUT -o {intif} -s {intip}/32 -d {intip}/{intmask} -j ACCEPT\n",
|
|
intif = iface.interface,
|
|
intip = iface.ip,
|
|
intmask = iface.mask,
|
|
);
|
|
}
|
|
|
|
for iface in &interfaces.internal {
|
|
// Allow internal traffic only on network associated with interface
|
|
filter += &format!(
|
|
"-A OUTPUT -o {intif} -s {extip}/{extmask} -d {intip}/{intmask} -j ACCEPT\n",
|
|
intif = iface.interface,
|
|
extip = interfaces.external.ip,
|
|
extmask = interfaces.external.mask,
|
|
intip = iface.ip,
|
|
intmask = iface.mask,
|
|
);
|
|
|
|
// Allow traffic from self to networks associated with interface
|
|
filter += &format!(
|
|
"-A OUTPUT -o {intif} -s {intip}/32 -d {intip}/{intmask} -j ACCEPT\n",
|
|
intif = iface.interface,
|
|
intip = iface.ip,
|
|
intmask = iface.mask,
|
|
);
|
|
}
|
|
|
|
for iface in interfaces.internal.iter().chain(&interfaces.vlan) {
|
|
// Deny traffic to internal networks on external interface
|
|
filter += &format!(
|
|
"-A OUTPUT -o {extif} -s {universe} -d {intip}/{intmask} -j REJECT\n",
|
|
extif = interfaces.external.interface,
|
|
universe = UNIVERSE,
|
|
intip = iface.ip,
|
|
intmask = iface.mask,
|
|
);
|
|
}
|
|
|
|
for iface in &interfaces.tunnel {
|
|
filter += &format!(
|
|
"-A OUTPUT -o {tunface} -j ACCEPT\n",
|
|
tunface = iface.interface,
|
|
);
|
|
}
|
|
|
|
// Allow traffic out from external interface to anywhere
|
|
filter += &format!(
|
|
"-A OUTPUT -o {extif} -s {extip}/{extmask} -d {universe} -j ACCEPT\n",
|
|
extif = interfaces.external.interface,
|
|
extip = interfaces.external.ip,
|
|
extmask = interfaces.external.mask,
|
|
universe = UNIVERSE,
|
|
);
|
|
|
|
for iface in interfaces.internal.iter().chain(&interfaces.vlan) {
|
|
// Allow DHCP traffic out from internal interfaces
|
|
filter += &format!(
|
|
"-A OUTPUT -o {intif} -p tcp -s {intip} --sport 67 -d 255.255.255.255 --dport 68 -j ACCEPT\n",
|
|
intif = iface.interface,
|
|
intip = iface.ip,
|
|
);
|
|
|
|
filter += &format!(
|
|
"-A OUTPUT -o {intif} -p udp -s {intip} --sport 67 -d 255.255.255.255 --dport 68 -j ACCEPT\n",
|
|
intif = iface.interface,
|
|
intip = iface.ip,
|
|
);
|
|
}
|
|
|
|
// Reject traffic we don't care about
|
|
filter += &format!(
|
|
"-A OUTPUT -s {universe} -d {universe} -j REJECT\n",
|
|
universe = UNIVERSE
|
|
);
|
|
|
|
// FORWARD: Forwarding traffic across interfaces
|
|
|
|
for nat_iface in &interfaces.nats {
|
|
if let Some(iface) = interfaces
|
|
.internal
|
|
.iter()
|
|
.chain(interfaces.tunnel.iter())
|
|
.chain(interfaces.vlan.iter())
|
|
.find(|iface| iface.interface == *nat_iface)
|
|
{
|
|
filter += &format!(
|
|
"-A FORWARD -i {natif} -d {natip}/{natmask} -j ACCEPT\n",
|
|
natif = iface.interface,
|
|
natip = iface.ip,
|
|
natmask = iface.mask,
|
|
);
|
|
}
|
|
|
|
for iface in interfaces
|
|
.internal
|
|
.iter()
|
|
.chain(interfaces.tunnel.iter())
|
|
.chain(interfaces.vlan.iter())
|
|
{
|
|
let is_nat_iface = *nat_iface == iface.interface;
|
|
|
|
let has_nat_subnet = interfaces
|
|
.internal
|
|
.iter()
|
|
.chain(interfaces.tunnel.iter())
|
|
.chain(interfaces.vlan.iter())
|
|
.any(|other_iface| {
|
|
*nat_iface == other_iface.interface
|
|
&& other_iface.ip == iface.ip
|
|
&& other_iface.mask == iface.mask
|
|
});
|
|
|
|
if !is_nat_iface && !has_nat_subnet {
|
|
// Accept packets over NAT'd tunnel interfaces from pre-existing connections
|
|
filter += &format!(
|
|
"-A FORWARD -i {natif} -o {intif} -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT\n",
|
|
natif = nat_iface,
|
|
intif = iface.interface,
|
|
);
|
|
|
|
// Reject packets over NAT'd tunnel interfaces
|
|
filter += &format!(
|
|
"-A FORWARD -i {natif} -o {intif} -j REJECT --reject-with icmp-port-unreachable\n",
|
|
natif = nat_iface,
|
|
intif = iface.interface,
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Allow forwarding packets over tunnel interfaces
|
|
for iface in &interfaces.tunnel {
|
|
filter += &format!(
|
|
"-A FORWARD -i {tunface} -o {tunface} -j ACCEPT\n",
|
|
tunface = iface.interface,
|
|
);
|
|
}
|
|
|
|
// Allow VLANs to respond to related external traffic
|
|
for iface in interfaces.internal.iter().chain(&interfaces.vlan) {
|
|
filter += &format!(
|
|
"-A FORWARD -i {extif} -o {intif} -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT\n",
|
|
extif = interfaces.external.interface,
|
|
intif = iface.interface,
|
|
);
|
|
}
|
|
|
|
// Allow packets across VLAN interface
|
|
for iface in &interfaces.vlan {
|
|
filter += &format!(
|
|
"-A FORWARD -i {intif} -o {intif} -j ACCEPT\n",
|
|
intif = iface.interface,
|
|
);
|
|
}
|
|
|
|
for iface in &interfaces.internal {
|
|
// Allow packets across internal interface
|
|
filter += &format!(
|
|
"-A FORWARD -i {intif} -o {intif} -j ACCEPT\n",
|
|
intif = iface.interface,
|
|
);
|
|
}
|
|
|
|
// Bridge interfaces
|
|
for [left, right] in interfaces.bridges.iter() {
|
|
filter += &format!(
|
|
"-A FORWARD -i {left} -o {right} -j ACCEPT\n",
|
|
left = left,
|
|
right = right,
|
|
);
|
|
|
|
filter += &format!(
|
|
"-A FORWARD -i {right} -o {left} -j ACCEPT\n",
|
|
left = left,
|
|
right = right,
|
|
);
|
|
}
|
|
|
|
// Forward packets to the internet
|
|
for iface in interfaces.internal.iter().chain(&interfaces.vlan) {
|
|
filter += &format!(
|
|
"-A FORWARD -i {intif} -o {extif} -j ACCEPT\n",
|
|
intif = iface.interface,
|
|
extif = interfaces.external.interface,
|
|
);
|
|
}
|
|
|
|
filter += "-A FORWARD -j REJECT\n";
|
|
filter += "COMMIT\n";
|
|
|
|
filter
|
|
}
|
|
|
|
// NAT Table rules
|
|
fn nat(interfaces: &Interfaces) -> String {
|
|
let mut nat = String::from(
|
|
r#"*nat
|
|
:PREROUTING ACCEPT [0:0]
|
|
:POSTROUTING ACCEPT [0:0]
|
|
:OUTPUT ACCEPT [0:0]
|
|
|
|
"#,
|
|
);
|
|
|
|
nat += &format!(
|
|
"-A POSTROUTING -o {extif} -j MASQUERADE\n",
|
|
extif = interfaces.external.interface
|
|
);
|
|
|
|
for nat_iface in &interfaces.nats {
|
|
for iface in interfaces
|
|
.internal
|
|
.iter()
|
|
.chain(&interfaces.tunnel)
|
|
.chain(&interfaces.vlan)
|
|
{
|
|
let is_nat_iface = *nat_iface == iface.interface;
|
|
|
|
let has_nat_subnet = interfaces
|
|
.internal
|
|
.iter()
|
|
.chain(interfaces.tunnel.iter())
|
|
.chain(interfaces.vlan.iter())
|
|
.any(|other_iface| {
|
|
*nat_iface == other_iface.interface
|
|
&& other_iface.ip == iface.ip
|
|
&& other_iface.mask == iface.mask
|
|
});
|
|
|
|
if !is_nat_iface && !has_nat_subnet {
|
|
nat += &format!(
|
|
"-A POSTROUTING -s {intip}/{intmask} -o {natiface} -j MASQUERADE\n",
|
|
intip = iface.ip,
|
|
intmask = iface.mask,
|
|
natiface = nat_iface,
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
nat += "COMMIT\n";
|
|
|
|
nat
|
|
}
|