aboutsummaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--service/firewall/blocker.go41
-rw-r--r--service/firewall/rules.go189
-rw-r--r--service/ifaceconfig.go5
3 files changed, 186 insertions, 49 deletions
diff --git a/service/firewall/blocker.go b/service/firewall/blocker.go
index b47ef094..7e4af0ca 100644
--- a/service/firewall/blocker.go
+++ b/service/firewall/blocker.go
@@ -7,6 +7,7 @@ package firewall
import (
"errors"
+ "net"
"unsafe"
"golang.org/x/sys/windows"
@@ -106,7 +107,7 @@ func registerBaseObjects(session uintptr) (*baseObjects, error) {
}, nil
}
-func EnableFirewall(luid uint64, restrictDNS bool, restrictAll bool) error {
+func EnableFirewall(luid uint64, restrictToDNSServers []net.IP, restrictAll bool) error {
if wfpSession != 0 {
return errors.New("The firewall has already been enabled")
}
@@ -122,54 +123,56 @@ func EnableFirewall(luid uint64, restrictDNS bool, restrictAll bool) error {
return wrapErr(err)
}
- err = permitTunInterface(session, baseObjects, 15, luid)
+ if len(restrictToDNSServers) > 0 {
+ err = blockDns(restrictToDNSServers, session, baseObjects, 15, 14)
+ if err != nil {
+ return wrapErr(err)
+ }
+ }
+
+ if restrictAll {
+ err = permitLoopback(session, baseObjects, 13)
+ if err != nil {
+ return wrapErr(err)
+ }
+ }
+
+ err = permitTunInterface(session, baseObjects, 12, luid)
if err != nil {
return wrapErr(err)
}
- err = permitWireGuardService(session, baseObjects, 15)
+ err = permitWireGuardService(session, baseObjects, 12)
if err != nil {
return wrapErr(err)
}
if restrictAll {
- err = permitDhcpIpv4(session, baseObjects, 15)
+ err = permitDhcpIpv4(session, baseObjects, 12)
if err != nil {
return wrapErr(err)
}
- err = permitDhcpIpv6(session, baseObjects, 15)
+ err = permitDhcpIpv6(session, baseObjects, 12)
if err != nil {
return wrapErr(err)
}
- err = permitNdp(session, baseObjects, 15)
+ err = permitNdp(session, baseObjects, 12)
if err != nil {
return wrapErr(err)
}
/* TODO: actually evaluate if this does anything and if we need this. It's layer 2; our other rules are layer 3.
* In other words, if somebody complains, try enabling it. For now, keep it off.
- err = permitHyperV(session, baseObjects, 15)
+ err = permitHyperV(session, baseObjects, 12)
if err != nil {
return wrapErr(err)
}
*/
}
- if restrictDNS {
- err = blockDns(session, baseObjects, 14)
- if err != nil {
- return wrapErr(err)
- }
- }
-
if restrictAll {
- err = permitLoopback(session, baseObjects, 13)
- if err != nil {
- return wrapErr(err)
- }
-
err = blockAll(session, baseObjects, 0)
if err != nil {
return wrapErr(err)
diff --git a/service/firewall/rules.go b/service/firewall/rules.go
index 2a9eb11a..bab3ed2b 100644
--- a/service/firewall/rules.go
+++ b/service/firewall/rules.go
@@ -6,8 +6,12 @@
package firewall
import (
+ "encoding/binary"
+ "errors"
"golang.org/x/sys/windows"
"golang.zx2c4.com/wireguard/windows/version"
+ "net"
+ "runtime"
"unsafe"
)
@@ -984,42 +988,46 @@ func blockAll(session uintptr, baseObjects *baseObjects, weight uint8) error {
return nil
}
-// Block all DNS except what is matched by a permissive rule.
-func blockDns(session uintptr, baseObjects *baseObjects, weight uint8) error {
- var conditions [3]wtFwpmFilterCondition0
+// Block all DNS traffic except towards specified DNS servers.
+func blockDns(except []net.IP, session uintptr, baseObjects *baseObjects, weightAllow uint8, weightDeny uint8) error {
+ if weightDeny <= weightAllow {
+ return errors.New("The allow weight must be greater than the deny weight")
+ }
- conditions[0] = wtFwpmFilterCondition0{
- fieldKey: cFWPM_CONDITION_IP_REMOTE_PORT,
- matchType: cFWP_MATCH_EQUAL,
- conditionValue: wtFwpConditionValue0{
- _type: cFWP_UINT16,
- value: uintptr(53),
+ denyConditions := []wtFwpmFilterCondition0{
+ {
+ fieldKey: cFWPM_CONDITION_IP_REMOTE_PORT,
+ matchType: cFWP_MATCH_EQUAL,
+ conditionValue: wtFwpConditionValue0{
+ _type: cFWP_UINT16,
+ value: uintptr(53),
+ },
},
- }
- conditions[1] = wtFwpmFilterCondition0{
- fieldKey: cFWPM_CONDITION_IP_PROTOCOL,
- matchType: cFWP_MATCH_EQUAL,
- conditionValue: wtFwpConditionValue0{
- _type: cFWP_UINT8,
- value: uintptr(cIPPROTO_UDP),
+ {
+ fieldKey: cFWPM_CONDITION_IP_PROTOCOL,
+ matchType: cFWP_MATCH_EQUAL,
+ conditionValue: wtFwpConditionValue0{
+ _type: cFWP_UINT8,
+ value: uintptr(cIPPROTO_UDP),
+ },
},
- }
- // Repeat the condition type for logical OR.
- conditions[2] = wtFwpmFilterCondition0{
- fieldKey: cFWPM_CONDITION_IP_PROTOCOL,
- matchType: cFWP_MATCH_EQUAL,
- conditionValue: wtFwpConditionValue0{
- _type: cFWP_UINT8,
- value: uintptr(cIPPROTO_TCP),
+ // Repeat the condition type for logical OR.
+ {
+ fieldKey: cFWPM_CONDITION_IP_PROTOCOL,
+ matchType: cFWP_MATCH_EQUAL,
+ conditionValue: wtFwpConditionValue0{
+ _type: cFWP_UINT8,
+ value: uintptr(cIPPROTO_TCP),
+ },
},
}
filter := wtFwpmFilter0{
providerKey: &baseObjects.provider,
subLayerKey: baseObjects.filters,
- weight: filterWeight(weight),
- numFilterConditions: uint32(len(conditions)),
- filterCondition: (*wtFwpmFilterCondition0)(unsafe.Pointer(&conditions[0])),
+ weight: filterWeight(weightDeny),
+ numFilterConditions: uint32(len(denyConditions)),
+ filterCondition: (*wtFwpmFilterCondition0)(unsafe.Pointer(&denyConditions[0])),
action: wtFwpmAction0{
_type: cFWP_ACTION_BLOCK,
},
@@ -1099,5 +1107,132 @@ func blockDns(session uintptr, baseObjects *baseObjects, weight uint8) error {
}
}
+ allowConditionsV4 := make([]wtFwpmFilterCondition0, 0, len(denyConditions)+len(except))
+ allowConditionsV4 = append(allowConditionsV4, denyConditions...)
+ for _, ip := range except {
+ ip4 := ip.To4()
+ if ip4 == nil {
+ continue
+ }
+ allowConditionsV4 = append(allowConditionsV4, wtFwpmFilterCondition0{
+ fieldKey: cFWPM_CONDITION_IP_REMOTE_ADDRESS,
+ matchType: cFWP_MATCH_EQUAL,
+ conditionValue: wtFwpConditionValue0{
+ _type: cFWP_UINT32,
+ value: uintptr(binary.BigEndian.Uint32(ip4)),
+ },
+ })
+ }
+
+ storedPointers := make([]*wtFwpByteArray16, 0, len(except))
+ allowConditionsV6 := make([]wtFwpmFilterCondition0, 0, len(denyConditions)+len(except))
+ allowConditionsV6 = append(allowConditionsV6, denyConditions...)
+ for _, ip := range except {
+ if ip.To4() != nil {
+ continue
+ }
+ var address wtFwpByteArray16
+ copy(address.byteArray16[:], ip)
+ allowConditionsV6 = append(allowConditionsV6, wtFwpmFilterCondition0{
+ fieldKey: cFWPM_CONDITION_IP_REMOTE_ADDRESS,
+ matchType: cFWP_MATCH_EQUAL,
+ conditionValue: wtFwpConditionValue0{
+ _type: cFWP_BYTE_ARRAY16_TYPE,
+ value: uintptr(unsafe.Pointer(&address)),
+ },
+ })
+ storedPointers = append(storedPointers, &address)
+ }
+
+ filter = wtFwpmFilter0{
+ providerKey: &baseObjects.provider,
+ subLayerKey: baseObjects.filters,
+ weight: filterWeight(weightAllow),
+ numFilterConditions: uint32(len(allowConditionsV4)),
+ filterCondition: (*wtFwpmFilterCondition0)(unsafe.Pointer(&allowConditionsV4[0])),
+ action: wtFwpmAction0{
+ _type: cFWP_ACTION_PERMIT,
+ },
+ }
+
+ filterId = uint64(0)
+
+ //
+ // #5 Allow IPv4 outbound DNS.
+ //
+ {
+ displayData, err := createWtFwpmDisplayData0("Allow DNS outbound (IPv4)", "")
+ if err != nil {
+ return wrapErr(err)
+ }
+
+ filter.displayData = *displayData
+ filter.layerKey = cFWPM_LAYER_ALE_AUTH_CONNECT_V4
+
+ err = fwpmFilterAdd0(session, &filter, 0, &filterId)
+ if err != nil {
+ return wrapErr(err)
+ }
+ }
+
+ //
+ // #6 Allow IPv4 inbound DNS.
+ //
+ {
+ displayData, err := createWtFwpmDisplayData0("Allow DNS inbound (IPv4)", "")
+ if err != nil {
+ return wrapErr(err)
+ }
+
+ filter.displayData = *displayData
+ filter.layerKey = cFWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4
+
+ err = fwpmFilterAdd0(session, &filter, 0, &filterId)
+ if err != nil {
+ return wrapErr(err)
+ }
+ }
+
+ filter.filterCondition = (*wtFwpmFilterCondition0)(unsafe.Pointer(&allowConditionsV6[0]))
+ filter.numFilterConditions = uint32(len(allowConditionsV6))
+
+ //
+ // #7 Allow IPv6 outbound DNS.
+ //
+ {
+ displayData, err := createWtFwpmDisplayData0("Allow DNS outbound (IPv6)", "")
+ if err != nil {
+ return wrapErr(err)
+ }
+
+ filter.displayData = *displayData
+ filter.layerKey = cFWPM_LAYER_ALE_AUTH_CONNECT_V6
+
+ err = fwpmFilterAdd0(session, &filter, 0, &filterId)
+ if err != nil {
+ return wrapErr(err)
+ }
+ }
+
+ //
+ // #8 Allow IPv6 inbound DNS.
+ //
+ {
+ displayData, err := createWtFwpmDisplayData0("Allow DNS inbound (IPv6)", "")
+ if err != nil {
+ return wrapErr(err)
+ }
+
+ filter.displayData = *displayData
+ filter.layerKey = cFWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6
+
+ err = fwpmFilterAdd0(session, &filter, 0, &filterId)
+ if err != nil {
+ return wrapErr(err)
+ }
+ }
+
+ runtime.KeepAlive(storedPointers)
+
return nil
}
diff --git a/service/ifaceconfig.go b/service/ifaceconfig.go
index c6458b4e..2e4307cf 100644
--- a/service/ifaceconfig.go
+++ b/service/ifaceconfig.go
@@ -314,7 +314,6 @@ func configureInterface(conf *conf.Config, tun *tun.NativeTun) error {
}
func enableFirewall(conf *conf.Config, tun *tun.NativeTun) error {
- restrictDNS := len(conf.Interface.Dns) > 0
restrictAll := false
if len(conf.Peers) == 1 {
nextallowedip:
@@ -330,9 +329,9 @@ func enableFirewall(conf *conf.Config, tun *tun.NativeTun) error {
}
}
}
- if restrictAll && !restrictDNS {
+ if restrictAll && len(conf.Interface.Dns) == 0 {
name, _ := tun.Name()
log.Printf("[%s] Warning: no DNS server specified, despite having an allowed IPs of 0.0.0.0/0 or ::/0. There may be connectivity issues.", name)
}
- return firewall.EnableFirewall(tun.LUID(), restrictDNS, restrictAll)
+ return firewall.EnableFirewall(tun.LUID(), conf.Interface.Dns, restrictAll)
}