mirror of
https://github.com/amnezia-vpn/openvpn3.git
synced 2026-05-17 08:26:28 +03:00
Win: support for local DNS resolvers
Local DNS resolvers, such as Umbrella Roaming Client, change DNS settings on adapters to 127.0.0.1. This may not work with openvpn3 because: - NRPT rule might be created for "." zone, which redirects all DNS requests to the server specified in rule. This takes precendence over adapters' DNS settings. - DNS requests might be blocked on all adapters except TAP (tap-windows6/wintun/ovpn-dco-win) to prevent DNS leaks. To enable compatibility with local DNS resolvers, add "allowLocalDnsResolvers" core config option, which, when enabled, makes core to - avoid creating NRPT rule for "." zone - permit DNS requests to 127.0.0.1 / ::1 Signed-off-by: Lev Stipakov <lev@openvpn.net>
This commit is contained in:
@@ -437,6 +437,7 @@ namespace openvpn {
|
||||
int conn_timeout = 0;
|
||||
bool tun_persist = false;
|
||||
bool wintun = false;
|
||||
bool allow_local_dns_resolvers = false;
|
||||
bool google_dns_fallback = false;
|
||||
bool synchronous_dns_lookup = false;
|
||||
bool autologin_sessions = false;
|
||||
@@ -682,6 +683,7 @@ namespace openvpn {
|
||||
state->conn_timeout = config.connTimeout;
|
||||
state->tun_persist = config.tunPersist;
|
||||
state->wintun = config.wintun;
|
||||
state->allow_local_dns_resolvers = config.allowLocalDnsResolvers;
|
||||
state->google_dns_fallback = config.googleDnsFallback;
|
||||
state->synchronous_dns_lookup = config.synchronousDnsLookup;
|
||||
state->autologin_sessions = config.autologinSessions;
|
||||
@@ -964,6 +966,7 @@ namespace openvpn {
|
||||
cc.conn_timeout = state->conn_timeout;
|
||||
cc.tun_persist = state->tun_persist;
|
||||
cc.wintun = state->wintun;
|
||||
cc.allow_local_dns_resolvers = state->allow_local_dns_resolvers;
|
||||
cc.google_dns_fallback = state->google_dns_fallback;
|
||||
cc.synchronous_dns_lookup = state->synchronous_dns_lookup;
|
||||
cc.autologin_sessions = state->autologin_sessions;
|
||||
|
||||
@@ -320,6 +320,10 @@ namespace openvpn {
|
||||
|
||||
// Use wintun instead of tap-windows6 on Windows
|
||||
bool wintun = false;
|
||||
|
||||
// On Windows allow DNS resolvers on localhost, such as Umbrella Roaming Client
|
||||
// This disables adding NRPT rule for "." zone and permits DNS requests to localhost
|
||||
bool allowLocalDnsResolvers = false;
|
||||
};
|
||||
|
||||
// used to communicate VPN events such as connect, disconnect, etc.
|
||||
|
||||
@@ -138,6 +138,7 @@ namespace openvpn {
|
||||
bool info = false;
|
||||
bool tun_persist = false;
|
||||
bool wintun = false;
|
||||
bool allow_local_dns_resolvers = false;
|
||||
bool google_dns_fallback = false;
|
||||
bool synchronous_dns_lookup = false;
|
||||
std::string private_key_password;
|
||||
@@ -441,6 +442,7 @@ namespace openvpn {
|
||||
tunconf->stats = cli_stats;
|
||||
tunconf->stop = config.stop;
|
||||
tunconf->wintun = config.wintun;
|
||||
tunconf->allow_local_dns_resolvers = config.allow_local_dns_resolvers;
|
||||
if (config.tun_persist)
|
||||
{
|
||||
tunconf->tun_persist.reset(new TunWin::TunPersist(true, false, nullptr));
|
||||
|
||||
@@ -100,6 +100,7 @@ namespace openvpn {
|
||||
std::string client_exe; // for validation
|
||||
int debug_level;
|
||||
bool wintun;
|
||||
bool allow_local_dns_resolvers = false;
|
||||
};
|
||||
|
||||
class SetupClient : public TunWin::SetupBase
|
||||
@@ -174,6 +175,7 @@ namespace openvpn {
|
||||
ring_buffer->serialize(jreq);
|
||||
|
||||
jreq["wintun"] = config->wintun;
|
||||
jreq["allow_local_dns_resolvers"] = config->allow_local_dns_resolvers;
|
||||
jreq["confirm_event"] = confirm_event.duplicate_local();
|
||||
jreq["destroy_event"] = destroy_event.duplicate_local();
|
||||
jreq["tun"] = pull.to_json(); // convert TunBuilderCapture to JSON
|
||||
@@ -294,15 +296,16 @@ namespace openvpn {
|
||||
Win::DestroyEvent destroy_event;
|
||||
};
|
||||
|
||||
virtual TunWin::SetupBase::Ptr new_setup_obj(openvpn_io::io_context& io_context, bool wintun) override
|
||||
virtual TunWin::SetupBase::Ptr new_setup_obj(openvpn_io::io_context& io_context, bool wintun, bool allow_local_dns_resolvers) override
|
||||
{
|
||||
if (config)
|
||||
{
|
||||
config->wintun = wintun;
|
||||
config->allow_local_dns_resolvers = allow_local_dns_resolvers;
|
||||
return new SetupClient(io_context, config);
|
||||
}
|
||||
else
|
||||
return new TunWin::Setup(io_context, wintun);
|
||||
return new TunWin::Setup(io_context, wintun, allow_local_dns_resolvers);
|
||||
}
|
||||
|
||||
WinCommandAgent(const OptionList& opt_parent)
|
||||
|
||||
@@ -139,10 +139,11 @@ public:
|
||||
const std::wstring& openvpn_app_path,
|
||||
Stop* stop,
|
||||
std::ostream& os,
|
||||
bool wintun)
|
||||
bool wintun,
|
||||
bool allow_local_dns_resolvers)
|
||||
{
|
||||
if (!tun)
|
||||
tun.reset(new TunWin::Setup(io_context_, wintun));
|
||||
tun.reset(new TunWin::Setup(io_context_, wintun, allow_local_dns_resolvers));
|
||||
auto th = tun->establish(tbc, openvpn_app_path, stop, os, ring_buffer);
|
||||
// store VPN interface index to be able to exclude it
|
||||
// when next time adding bypass route
|
||||
@@ -578,6 +579,8 @@ private:
|
||||
|
||||
bool wintun = json::get_bool_optional(root, "wintun");
|
||||
|
||||
bool allow_local_dns_resolvers = json::get_bool_optional(root, "allow_local_dns_resolvers");
|
||||
|
||||
// get remote event handles for tun object confirmation/destruction
|
||||
const std::string confirm_event_hex = json::get_string(root, "confirm_event");
|
||||
const std::string destroy_event_hex = json::get_string(root, "destroy_event");
|
||||
@@ -616,7 +619,7 @@ private:
|
||||
}
|
||||
|
||||
// establish the tun setup object
|
||||
Win::ScopedHANDLE tap_handle(parent()->establish_tun(*tbc, client_exe, nullptr, os, wintun));
|
||||
Win::ScopedHANDLE tap_handle(parent()->establish_tun(*tbc, client_exe, nullptr, os, wintun, allow_local_dns_resolvers));
|
||||
|
||||
// post-establish impersonation
|
||||
{
|
||||
|
||||
@@ -56,6 +56,7 @@ namespace openvpn {
|
||||
TunProp::Config tun_prop;
|
||||
int n_parallel = 8; // number of parallel async reads on tun socket
|
||||
bool wintun = false;
|
||||
bool allow_local_dns_resolvers = false;
|
||||
|
||||
Frame::Ptr frame;
|
||||
SessionStats::Ptr stats;
|
||||
@@ -69,9 +70,9 @@ namespace openvpn {
|
||||
TunWin::SetupBase::Ptr new_setup_obj(openvpn_io::io_context& io_context)
|
||||
{
|
||||
if (tun_setup_factory)
|
||||
return tun_setup_factory->new_setup_obj(io_context, wintun);
|
||||
return tun_setup_factory->new_setup_obj(io_context, wintun, allow_local_dns_resolvers);
|
||||
else
|
||||
return new TunWin::Setup(io_context, wintun);
|
||||
return new TunWin::Setup(io_context, wintun, allow_local_dns_resolvers);
|
||||
}
|
||||
|
||||
static Ptr new_obj()
|
||||
|
||||
@@ -70,7 +70,7 @@ namespace openvpn {
|
||||
{
|
||||
typedef RCPtr<SetupFactory> Ptr;
|
||||
|
||||
virtual SetupBase::Ptr new_setup_obj(openvpn_io::io_context& io_context, bool wintun) = 0;
|
||||
virtual SetupBase::Ptr new_setup_obj(openvpn_io::io_context& io_context, bool wintun, bool allow_local_dns_resolvers) = 0;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -68,9 +68,10 @@ namespace openvpn {
|
||||
public:
|
||||
typedef RCPtr<Setup> Ptr;
|
||||
|
||||
Setup(openvpn_io::io_context& io_context_arg, bool wintun_arg=false)
|
||||
Setup(openvpn_io::io_context& io_context_arg, bool wintun_arg, bool allow_local_dns_resolvers_arg)
|
||||
: delete_route_timer(io_context_arg),
|
||||
wintun(wintun_arg) {}
|
||||
wintun(wintun_arg),
|
||||
allow_local_dns_resolvers(allow_local_dns_resolvers_arg) {}
|
||||
|
||||
// Set up the TAP device
|
||||
virtual HANDLE establish(const TunBuilderCapture& pull,
|
||||
@@ -636,7 +637,7 @@ namespace openvpn {
|
||||
}
|
||||
}
|
||||
}
|
||||
if (dsfx.empty())
|
||||
if (dsfx.empty() && !allow_local_dns_resolvers)
|
||||
dsfx.emplace_back(".");
|
||||
|
||||
// DNS server list
|
||||
@@ -653,8 +654,8 @@ namespace openvpn {
|
||||
// the TAP adapter.
|
||||
if (use_wfp && !split_dns && !openvpn_app_path.empty() && (dns.ipv4() || dns.ipv6()))
|
||||
{
|
||||
create.add(new ActionWFP(openvpn_app_path, tap.index, true, wfp));
|
||||
destroy.add(new ActionWFP(openvpn_app_path, tap.index, false, wfp));
|
||||
create.add(new ActionWFP(openvpn_app_path, tap.index, true, allow_local_dns_resolvers ,wfp));
|
||||
destroy.add(new ActionWFP(openvpn_app_path, tap.index, false, allow_local_dns_resolvers, wfp));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -945,6 +946,8 @@ namespace openvpn {
|
||||
AsioTimer delete_route_timer;
|
||||
|
||||
bool wintun = false;
|
||||
|
||||
bool allow_local_dns_resolvers = false;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -121,6 +121,7 @@ namespace openvpn {
|
||||
// Derived from https://github.com/ValdikSS/openvpn-with-patches/commit/3bd4d503d21aa34636e4f97b3e32ae0acca407f0
|
||||
void block_dns(const std::wstring& openvpn_app_path,
|
||||
const NET_IFINDEX tap_index,
|
||||
const bool allow_local_dns_resolvers,
|
||||
std::ostream& log)
|
||||
{
|
||||
// WFP filter/conditions
|
||||
@@ -156,7 +157,40 @@ namespace openvpn {
|
||||
filter.weight.uint8 = 0xF;
|
||||
filter.filterCondition = condition;
|
||||
|
||||
// Filter #1 -- permit IPv4 DNS requests from OpenVPN app
|
||||
if (allow_local_dns_resolvers)
|
||||
{
|
||||
// Filter #1 -- permit IPv4 DNS requests to 127.0.0.1
|
||||
filter.layerKey = FWPM_LAYER_ALE_AUTH_CONNECT_V4;
|
||||
filter.action.type = FWP_ACTION_PERMIT;
|
||||
filter.numFilterConditions = 2;
|
||||
|
||||
condition[0].fieldKey = FWPM_CONDITION_IP_REMOTE_PORT;
|
||||
condition[0].matchType = FWP_MATCH_EQUAL;
|
||||
condition[0].conditionValue.type = FWP_UINT16;
|
||||
condition[0].conditionValue.uint16 = 53;
|
||||
|
||||
UINT8 localhost[4] = {1, 0, 0, 127};
|
||||
|
||||
condition[1].fieldKey = FWPM_CONDITION_IP_REMOTE_ADDRESS;
|
||||
condition[1].matchType = FWP_MATCH_EQUAL;
|
||||
condition[1].conditionValue.type = FWP_UINT32;
|
||||
condition[1].conditionValue.uint32 = *(UINT32 *)localhost;
|
||||
|
||||
add_filter(&filter, NULL, &filterid);
|
||||
log << "permit IPv4 DNS requests to 127.0.0.1" << std::endl;
|
||||
|
||||
// Filter #2 -- permit IPv6 DNS requests to ::1
|
||||
filter.layerKey = FWPM_LAYER_ALE_AUTH_CONNECT_V6;
|
||||
UINT8 localhostv6[16] = {1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
|
||||
|
||||
condition[1].conditionValue.type = FWP_BYTE_ARRAY16_TYPE;
|
||||
condition[1].conditionValue.byteArray16 = (FWP_BYTE_ARRAY16*)localhostv6;
|
||||
|
||||
add_filter(&filter, NULL, &filterid);
|
||||
log << "permit IPv6 DNS requests to ::1" << std::endl;
|
||||
}
|
||||
|
||||
// Filter #3 -- permit IPv4 DNS requests from OpenVPN app
|
||||
filter.layerKey = FWPM_LAYER_ALE_AUTH_CONNECT_V4;
|
||||
filter.action.type = FWP_ACTION_PERMIT;
|
||||
filter.numFilterConditions = 2;
|
||||
@@ -174,13 +208,13 @@ namespace openvpn {
|
||||
add_filter(&filter, NULL, &filterid);
|
||||
log << "permit IPv4 DNS requests from OpenVPN app" << std::endl;
|
||||
|
||||
// Filter #2 -- permit IPv6 DNS requests from OpenVPN app
|
||||
// Filter #4 -- permit IPv6 DNS requests from OpenVPN app
|
||||
filter.layerKey = FWPM_LAYER_ALE_AUTH_CONNECT_V6;
|
||||
|
||||
add_filter(&filter, NULL, &filterid);
|
||||
log << "permit IPv6 DNS requests from OpenVPN app" << std::endl;
|
||||
|
||||
// Filter #3 -- block IPv4 DNS requests from other apps
|
||||
// Filter #5 -- block IPv4 DNS requests from other apps
|
||||
filter.layerKey = FWPM_LAYER_ALE_AUTH_CONNECT_V4;
|
||||
filter.action.type = FWP_ACTION_BLOCK;
|
||||
filter.weight.type = FWP_EMPTY;
|
||||
@@ -189,13 +223,13 @@ namespace openvpn {
|
||||
add_filter(&filter, NULL, &filterid);
|
||||
log << "block IPv4 DNS requests from other apps" << std::endl;
|
||||
|
||||
// Filter #4 -- block IPv6 DNS requests from other apps
|
||||
// Filter #6 -- block IPv6 DNS requests from other apps
|
||||
filter.layerKey = FWPM_LAYER_ALE_AUTH_CONNECT_V6;
|
||||
|
||||
add_filter(&filter, NULL, &filterid);
|
||||
log << "block IPv6 DNS requests from other apps" << std::endl;
|
||||
|
||||
// Filter #5 -- allow IPv4 traffic from TAP
|
||||
// Filter #7 -- allow IPv4 traffic from TAP
|
||||
filter.layerKey = FWPM_LAYER_ALE_AUTH_CONNECT_V4;
|
||||
filter.action.type = FWP_ACTION_PERMIT;
|
||||
filter.numFilterConditions = 2;
|
||||
@@ -208,7 +242,7 @@ namespace openvpn {
|
||||
add_filter(&filter, NULL, &filterid);
|
||||
log << "allow IPv4 traffic from TAP" << std::endl;
|
||||
|
||||
// Filter #6 -- allow IPv6 traffic from TAP
|
||||
// Filter #8 -- allow IPv6 traffic from TAP
|
||||
filter.layerKey = FWPM_LAYER_ALE_AUTH_CONNECT_V6;
|
||||
|
||||
add_filter(&filter, NULL, &filterid);
|
||||
@@ -326,11 +360,12 @@ namespace openvpn {
|
||||
|
||||
void block(const std::wstring& openvpn_app_path,
|
||||
const NET_IFINDEX tap_index,
|
||||
const bool allow_local_dns_resolvers,
|
||||
std::ostream& log)
|
||||
{
|
||||
unblock(log);
|
||||
wfp.reset(new WFP());
|
||||
wfp->block_dns(openvpn_app_path, tap_index, log);
|
||||
wfp->block_dns(openvpn_app_path, tap_index, allow_local_dns_resolvers, log);
|
||||
}
|
||||
|
||||
void unblock(std::ostream& log)
|
||||
@@ -351,11 +386,13 @@ namespace openvpn {
|
||||
ActionWFP(const std::wstring& openvpn_app_path_arg,
|
||||
const NET_IFINDEX tap_index_arg,
|
||||
const bool enable_arg,
|
||||
const bool allow_local_dns_resolvers_arg,
|
||||
const WFPContext::Ptr& wfp_arg)
|
||||
: openvpn_app_path(openvpn_app_path_arg),
|
||||
tap_index(tap_index_arg),
|
||||
enable(enable_arg),
|
||||
wfp(wfp_arg)
|
||||
wfp(wfp_arg),
|
||||
allow_local_dns_resolvers(allow_local_dns_resolvers_arg)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -363,7 +400,7 @@ namespace openvpn {
|
||||
{
|
||||
log << to_string() << std::endl;
|
||||
if (enable)
|
||||
wfp->block(openvpn_app_path, tap_index, log);
|
||||
wfp->block(openvpn_app_path, tap_index, allow_local_dns_resolvers, log);
|
||||
else
|
||||
wfp->unblock(log);
|
||||
}
|
||||
@@ -377,6 +414,7 @@ namespace openvpn {
|
||||
const std::wstring openvpn_app_path;
|
||||
const NET_IFINDEX tap_index;
|
||||
const bool enable;
|
||||
const bool allow_local_dns_resolvers;
|
||||
WFPContext::Ptr wfp;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -717,6 +717,7 @@ int openvpn_client(int argc, char *argv[], const std::string* profile_content)
|
||||
{ "google-dns", no_argument, nullptr, 'g' },
|
||||
{ "persist-tun", no_argument, nullptr, 'j' },
|
||||
{ "wintun", no_argument, nullptr, 'w' },
|
||||
{ "allow-local-dns-resolvers", no_argument, nullptr, 'l' },
|
||||
{ "def-keydir", required_argument, nullptr, 'k' },
|
||||
{ "merge", no_argument, nullptr, 'm' },
|
||||
{ "version", no_argument, nullptr, 'v' },
|
||||
@@ -776,6 +777,7 @@ int openvpn_client(int argc, char *argv[], const std::string* profile_content)
|
||||
bool retryOnAuthFailed = false;
|
||||
bool tunPersist = false;
|
||||
bool wintun = false;
|
||||
bool allowLocalDnsResolvers = false;
|
||||
bool merge = false;
|
||||
bool version = false;
|
||||
bool altProxy = false;
|
||||
@@ -790,7 +792,7 @@ int openvpn_client(int argc, char *argv[], const std::string* profile_content)
|
||||
|
||||
int ch;
|
||||
optind = 1;
|
||||
while ((ch = getopt_long(argc, argv, "BAdeTCxfgjwmvaYu:p:r:D:P:6:s:S:t:c:z:M:h:q:U:W:I:G:k:X:R:Z:", longopts, nullptr)) != -1)
|
||||
while ((ch = getopt_long(argc, argv, "BAdeTCxfgjwmlvaYu:p:r:D:P:6:s:S:t:c:z:M:h:q:U:W:I:G:k:X:R:Z:", longopts, nullptr)) != -1)
|
||||
{
|
||||
switch (ch)
|
||||
{
|
||||
@@ -901,6 +903,9 @@ int openvpn_client(int argc, char *argv[], const std::string* profile_content)
|
||||
case 'w':
|
||||
wintun = true;
|
||||
break;
|
||||
case 'l':
|
||||
allowLocalDnsResolvers = true;
|
||||
break;
|
||||
case 'm':
|
||||
merge = true;
|
||||
break;
|
||||
@@ -1006,6 +1011,7 @@ int openvpn_client(int argc, char *argv[], const std::string* profile_content)
|
||||
config.gremlinConfig = gremlin;
|
||||
config.info = true;
|
||||
config.wintun = wintun;
|
||||
config.allowLocalDnsResolvers = allowLocalDnsResolvers;
|
||||
config.ssoMethods =ssoMethods;
|
||||
#if defined(OPENVPN_OVPNCLI_SINGLE_THREAD)
|
||||
config.clockTickMS = 250;
|
||||
|
||||
Reference in New Issue
Block a user