From patchwork Wed May 14 13:53:27 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Gert Doering X-Patchwork-Id: 4249 Return-Path: Delivered-To: patchwork@openvpn.net Received: by 2002:a05:7000:a32a:b0:656:592e:a137 with SMTP id jh42csp344508mab; Wed, 14 May 2025 08:04:40 -0700 (PDT) X-Forwarded-Encrypted: i=2; AJvYcCUkc4dV6gShYAOqJW6DW+FIYvFEM7tq2x+hQP7egWp4iqcvnPN3zYtBaNSmnNov0WH8A0H79Jp+Ujo=@openvpn.net X-Google-Smtp-Source: AGHT+IFNhECDrwxS4qGSCFN+m0ZZYRs6Zuti6vMe0f7dmPhLRIDqLAZRtuZTZ0gtyyYT6C7VflWX X-Received: by 2002:a05:6602:381b:b0:85b:3c49:8811 with SMTP id ca18e2360f4ac-86a08ddbbcfmr500129339f.4.1747235080457; Wed, 14 May 2025 08:04:40 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1747235080; cv=none; d=google.com; s=arc-20240605; b=YkGc7yX+sBP5mrYq0TMwgJC5bz5s6UgAQ7ItYz0vncsJ9DHSMjop1Rsu+rdvoI5e5R UtL6wPvepPTp75kMdmT9hYdO+263WSxZ3IC0UvKt9h6dt4LubDV/5TI3ODGXz6qGanmG axgdK1vuQWuekA26+v/s43cpLSVh/5u+mCOxH/d7bp/4IXMoKDcE835pRB0oO7sL5WEJ CTGCmPiIqFkQfe6gqiVilWvas8OE94h+eUSIySe3GSaqEVbPemZhIn7GNhZYNNFD46MZ HqxNa790YTvYTplcISAxxRBV4RJQ5Wxl4upvJFMJXkeMT22atxHY2cmJSDGOyRW7VisF VN4Q== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20240605; h=errors-to:content-transfer-encoding:list-subscribe:list-help :list-post:list-archive:list-unsubscribe:list-id:precedence:subject :mime-version:references:in-reply-to:message-id:date:to:from :dkim-signature:dkim-signature:dkim-signature; bh=76H3FWZUKmadvWAr6BkVEqnZOnNAVd6/+2/Yo/8evns=; fh=4NbAC/LsuMLI0S0hprUlLSLCiHwg6SCAifhH718Jh0Q=; b=g+4haM/Lz052s/bLzdTI0b1wAUwQbZADtFxU1z3c1bwVOAdMK/aWfFAOBoYaGsuccE JrwNyboTqZ1a/OYPtli/03UU66bMBgz4y9L8U38XdvpwhMn6rgFoAKGJbQLE+5p9MAaN VZAV0Ml7Q4xv2L77Lr2F7WLUIX0n39InGeti6+kELrB6Kw+nwg4Fs6/oJEEcfFsnNCMi uifFUSAXnEnDNk86xtKQPwZZkQuxxtnQQJiaTBrKWGUBJgqx6R6GFUzwQ6LwX65t1P0g FHBLfRydiH6HAfHd/oEQwi8FN066YsH+RscHzdwaGE74je4xeNEGs8pnnRBYAZcwalh4 3ljA==; dara=google.com ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@lists.sourceforge.net header.s=beta header.b=HSD5PV5n; dkim=neutral (body hash did not verify) header.i=@sourceforge.net header.s=x header.b=U+90iJrv; dkim=neutral (body hash did not verify) header.i=@sf.net header.s=x header.b=cfYFIU68; spf=pass (google.com: domain of openvpn-devel-bounces@lists.sourceforge.net designates 216.105.38.7 as permitted sender) smtp.mailfrom=openvpn-devel-bounces@lists.sourceforge.net; dmarc=fail (p=NONE sp=NONE dis=NONE) header.from=muc.de Received: from lists.sourceforge.net (lists.sourceforge.net. [216.105.38.7]) by mx.google.com with ESMTPS id ca18e2360f4ac-8676358217bsi1345247339f.43.2025.05.14.08.04.39 (version=TLS1_2 cipher=ECDHE-ECDSA-AES128-GCM-SHA256 bits=128/128); Wed, 14 May 2025 08:04:40 -0700 (PDT) Received-SPF: pass (google.com: domain of openvpn-devel-bounces@lists.sourceforge.net designates 216.105.38.7 as permitted sender) client-ip=216.105.38.7; Authentication-Results: mx.google.com; dkim=pass header.i=@lists.sourceforge.net header.s=beta header.b=HSD5PV5n; dkim=neutral (body hash did not verify) header.i=@sourceforge.net header.s=x header.b=U+90iJrv; dkim=neutral (body hash did not verify) header.i=@sf.net header.s=x header.b=cfYFIU68; spf=pass (google.com: domain of openvpn-devel-bounces@lists.sourceforge.net designates 216.105.38.7 as permitted sender) smtp.mailfrom=openvpn-devel-bounces@lists.sourceforge.net; dmarc=fail (p=NONE sp=NONE dis=NONE) header.from=muc.de DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.sourceforge.net; s=beta; h=Content-Transfer-Encoding:Content-Type: List-Subscribe:List-Help:List-Post:List-Archive:List-Unsubscribe:List-Id: Subject:MIME-Version:References:In-Reply-To:Message-ID:Date:To:From:Sender: Reply-To:Cc:Content-ID:Content-Description:Resent-Date:Resent-From: Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID:List-Owner; bh=76H3FWZUKmadvWAr6BkVEqnZOnNAVd6/+2/Yo/8evns=; b=HSD5PV5nWPVzIUUH8aEMcood3d EbYByFBgwqzu9YF2q+9xY1pSWAmwwC+3BBMqsF8gqWtfyPy2aLXhe6+ztk3U9ZLfmOXBk5J0Aea1w HyIPI1ww06qGe6B69ABubHClrfZNSP361dh6BopD1umq8KdCl1DkcT7WBna57TlETdjg=; Received: from [127.0.0.1] (helo=sfs-ml-1.v29.lw.sourceforge.com) by sfs-ml-1.v29.lw.sourceforge.com with esmtp (Exim 4.95) (envelope-from ) id 1uFDer-0004V4-Gr; Wed, 14 May 2025 15:04:33 +0000 Received: from [172.30.29.66] (helo=mx.sourceforge.net) by sfs-ml-1.v29.lw.sourceforge.com with esmtps (TLS1.2) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.95) (envelope-from ) id 1uFDel-0004Uq-8c for openvpn-devel@lists.sourceforge.net; Wed, 14 May 2025 15:04:27 +0000 DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=sourceforge.net; s=x; h=Content-Transfer-Encoding:MIME-Version:References: In-Reply-To:Message-ID:Date:Subject:To:From:Sender:Reply-To:Cc:Content-Type: Content-ID:Content-Description:Resent-Date:Resent-From:Resent-Sender: Resent-To:Resent-Cc:Resent-Message-ID:List-Id:List-Help:List-Unsubscribe: List-Subscribe:List-Post:List-Owner:List-Archive; bh=6Z/rb4E2B5PUWjYRkIqWY9cIoHs5xRyk70UJwS5qyqw=; b=U+90iJrviK5GGtfhJubf/eG7+e wqaMSDtT198uOeTxAyHHJI5Uqfkh9aeerjb7RnZnxRXxQjTTQ1eSKnCjYPeRK3yDAFCj4DzTXeqfL hoXMumMySH2Fn2MZvAb+Z2vcPLIzE+8fpdNNg3aYgBX4bNCP3KMRJMnxbU+jxAw28cJo=; DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=sf.net; s=x ; h=Content-Transfer-Encoding:MIME-Version:References:In-Reply-To:Message-ID: Date:Subject:To:From:Sender:Reply-To:Cc:Content-Type:Content-ID: Content-Description:Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc :Resent-Message-ID:List-Id:List-Help:List-Unsubscribe:List-Subscribe: List-Post:List-Owner:List-Archive; bh=6Z/rb4E2B5PUWjYRkIqWY9cIoHs5xRyk70UJwS5qyqw=; b=cfYFIU68PGuSlRhX4rzd7HqrkP GoYt/r/sHD4Ic1fhWn8ZTxrHzTCZ2L9gAD0JV0+KMa1V2S+FgW6UsGzTnxACupJolFNzMfgo0VOi8 IbkG43jSFG4w3ykIo6cOW9kpu3JjRLNeyVP0esr/iprbdCLlCo0oTT+Ie27Qzo+8DwqM=; Received: from [193.149.48.143] (helo=blue.greenie.muc.de) by sfi-mx-2.v28.lw.sourceforge.com with esmtps (TLS1.2:ECDHE-RSA-AES256-GCM-SHA384:256) (Exim 4.95) id 1uFDej-0006re-1l for openvpn-devel@lists.sourceforge.net; Wed, 14 May 2025 15:04:27 +0000 Received: from blue.greenie.muc.de (localhost [127.0.0.1]) by blue.greenie.muc.de (8.17.1.9/8.17.1.9) with ESMTP id 54EDrZuR014401 for ; Wed, 14 May 2025 15:53:35 +0200 Received: (from gert@localhost) by blue.greenie.muc.de (8.17.1.9/8.17.1.9/Submit) id 54EDrZ8v014400 for openvpn-devel@lists.sourceforge.net; Wed, 14 May 2025 15:53:35 +0200 From: Gert Doering To: openvpn-devel@lists.sourceforge.net Date: Wed, 14 May 2025 15:53:27 +0200 Message-ID: <20250514135334.14377-1-gert@greenie.muc.de> X-Mailer: git-send-email 2.49.0 In-Reply-To: References: MIME-Version: 1.0 X-Spam-Score: 1.3 (+) X-Spam-Report: Spam detection software, running on the system "6901ab67b84d", has NOT identified this incoming email as spam. The original message has been attached to this so you can view it or label similar future email. If you have any questions, see the administrator of that system for details. Content preview: From: Heiko Hund This introduces a new script hook, the dns-updown, and implements such a command script for a few popular systems (and a default for the not so popular ones). Like the name suggests this hook is solel [...] Content analysis details: (1.3 points, 5.0 required) pts rule name description ---- ---------------------- -------------------------------------------------- 1.3 RDNS_NONE Delivered to internal network by a host with no rDNS X-Headers-End: 1uFDej-0006re-1l Subject: [Openvpn-devel] [PATCH v25] dns: apply settings via script on unixoid systems X-BeenThere: openvpn-devel@lists.sourceforge.net X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: openvpn-devel-bounces@lists.sourceforge.net X-getmail-retrieved-from-mailbox: Inbox X-GMAIL-THRID: =?utf-8?q?1832108771969944830?= X-GMAIL-MSGID: =?utf-8?q?1832108771969944830?= From: Heiko Hund This introduces a new script hook, the dns-updown, and implements such a command script for a few popular systems (and a default for the not so popular ones). Like the name suggests this hook is soleley for dealing with modifying how names are resolved when the VPN pushes some --dns settings. The default dns updown command is part of the distribution and is installed with openvpn. You can change the path the command is located at as a compile time option, defaults to libexecdir. You can compile-time disable that the default dns-updown hook is run by passing --disable-dns-updown-by-default to configure or ccmake ENABLE_DNS_UPDOWN_BY_DEFAULT to OFF. There's also a new runtime option --dns-updown, which can run a custom command, force running the default when disabled or disable execution of the dns-updown altogether. Change-Id: Ifbe4ffb44d3bfcaa50adb38cacb3436fcdc71b10 Signed-off-by: Heiko Hund Acked-by: Gert Doering --- This change was reviewed on Gerrit and approved by at least one developer. I request to merge it to master. Gerrit URL: https://gerrit.openvpn.net/c/openvpn/+/838 This mail reflects revision 25 of this Change. Acked-by according to Gerrit (reflected above): Gert Doering diff --git a/.gitignore b/.gitignore index db8bb73..04523af 100644 --- a/.gitignore +++ b/.gitignore @@ -49,6 +49,7 @@ /doc/doxygen/latex/ /doc/doxygen/openvpn.doxyfile distro/systemd/*.service +distro/dns-scripts/dns-updown sample/sample-keys/sample-ca/ vendor/cmocka_build vendor/dist diff --git a/CMakeLists.txt b/CMakeLists.txt index ae818c3..40bffd4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -41,7 +41,10 @@ option(USE_WERROR "Treat compiler warnings as errors (-Werror)" ON) option(FAKE_ANDROID "Target Android but do not use actual cross compile/Android cmake to build for simple compile checks on Linux") -set(PLUGIN_DIR /usr/local/lib/openvpn/plugins CACHE FILEPATH "Location of the plugin directory") +option(ENABLE_DNS_UPDOWN_BY_DEFAULT "Run --dns-updown hook by default" ON) +set(DNS_UPDOWN_PATH "${CMAKE_INSTALL_PREFIX}/libexec/openvpn/dns-updown" CACHE STRING "Default location for the DNS up/down script") + +set(PLUGIN_DIR "${CMAKE_INSTALL_PREFIX}/lib/openvpn/plugins" CACHE FILEPATH "Location of the plugin directory") # Create machine readable compile commands option(ENABLE_COMPILE_COMMANDS "Generate compile_commands.json and a symlink for clangd to find it" OFF) @@ -601,6 +604,8 @@ add_library_deps(openvpn) +target_compile_options(openvpn PRIVATE -DDEFAULT_DNS_UPDOWN=\"${DNS_UPDOWN_PATH}\") + if(MINGW) target_compile_options(openvpn PRIVATE -municode -UUNICODE) target_link_options(openvpn PRIVATE -municode) diff --git a/config.h.cmake.in b/config.h.cmake.in index 2f7b43d..5164ce3 100644 --- a/config.h.cmake.in +++ b/config.h.cmake.in @@ -35,6 +35,9 @@ /* Enable LZO compression library */ #cmakedefine ENABLE_LZO +/* Enable dns-updown script hook */ +#cmakedefine ENABLE_DNS_UPDOWN + /* Enable NTLMv2 proxy support */ #define ENABLE_NTLM 1 diff --git a/configure.ac b/configure.ac index 9777e36..75367e8 100644 --- a/configure.ac +++ b/configure.ac @@ -96,6 +96,13 @@ ) AC_ARG_ENABLE( + [dns-updown-by-default], + [AS_HELP_STRING([--disable-dns-updown-by-default], [disable running --dns-updown by default @<:@default=yes@:>@])], + , + [enable_dns_updown_by_default="yes"] +) + +AC_ARG_ENABLE( [ntlm], [AS_HELP_STRING([--disable-ntlm], [disable NTLMv2 proxy support @<:@default=yes@:>@])], , @@ -315,37 +322,50 @@ plugindir="\${libdir}/openvpn/plugins" fi +AC_ARG_VAR([SCRIPTDIR], [Path of script directory @<:@default=PKGLIBEXECDIR@:>@]) +if test -n "${SCRIPTDIR}"; then + scriptdir="${SCRIPTDIR}" +else + scriptdir="\${pkglibexecdir}" +fi + AC_DEFINE_UNQUOTED([TARGET_ALIAS], ["${host}"], [A string representing our host]) -AM_CONDITIONAL([TARGET_LINUX], [false]) +AM_CONDITIONAL([ENABLE_DNS_UPDOWN],[true]) case "$host" in *-*-linux*) AC_DEFINE([TARGET_LINUX], [1], [Are we running on Linux?]) - AM_CONDITIONAL([TARGET_LINUX], [true]) AC_DEFINE_UNQUOTED([TARGET_PREFIX], ["L"], [Target prefix]) + AC_SUBST([DNS_UPDOWN_TYPE], ["systemd"]) have_sitnl="yes" pkg_config_required="yes" ;; *-*-solaris*) AC_DEFINE([TARGET_SOLARIS], [1], [Are we running on Solaris?]) AC_DEFINE_UNQUOTED([TARGET_PREFIX], ["S"], [Target prefix]) + AC_SUBST([DNS_UPDOWN_TYPE], ["resolvconf_file"]) CPPFLAGS="$CPPFLAGS -D_XPG4_2" test -x /bin/bash && SHELL="/bin/bash" ;; *-*-openbsd*) AC_DEFINE([TARGET_OPENBSD], [1], [Are we running on OpenBSD?]) AC_DEFINE_UNQUOTED([TARGET_PREFIX], ["O"], [Target prefix]) + AC_SUBST([DNS_UPDOWN_TYPE], ["resolvconf_file"]) ;; *-*-freebsd*) AC_DEFINE([TARGET_FREEBSD], [1], [Are we running on FreeBSD?]) AC_DEFINE_UNQUOTED([TARGET_PREFIX], ["F"], [Target prefix]) + AC_SUBST([DNS_UPDOWN_TYPE], ["openresolv"]) ;; *-*-netbsd*) AC_DEFINE([TARGET_NETBSD], [1], [Are we running NetBSD?]) AC_DEFINE_UNQUOTED([TARGET_PREFIX], ["N"], [Target prefix]) + AC_SUBST([DNS_UPDOWN_TYPE], ["openresolv"]) ;; *-*-darwin*) AC_DEFINE([TARGET_DARWIN], [1], [Are we running on Mac OS X?]) AC_DEFINE_UNQUOTED([TARGET_PREFIX], ["M"], [Target prefix]) + AM_CONDITIONAL([ENABLE_DNS_UPDOWN], [false]) + AC_SUBST([DNS_UPDOWN_TYPE], ["resolvconf_file"]) have_tap_header="yes" ac_cv_type_struct_in_pktinfo=no ;; @@ -353,6 +373,8 @@ AC_DEFINE([TARGET_WIN32], [1], [Are we running WIN32?]) AC_DEFINE([ENABLE_DCO], [1], [DCO is always enabled on Windows]) AC_DEFINE_UNQUOTED([TARGET_PREFIX], ["W"], [Target prefix]) + AM_CONDITIONAL([ENABLE_DNS_UPDOWN], [false]) + AC_SUBST([DNS_UPDOWN_TYPE], ["windows"]) CPPFLAGS="${CPPFLAGS} -DWIN32_LEAN_AND_MEAN" CPPFLAGS="${CPPFLAGS} -DNTDDI_VERSION=NTDDI_VISTA -D_WIN32_WINNT=_WIN32_WINNT_VISTA" WIN32=yes @@ -360,10 +382,12 @@ *-*-dragonfly*) AC_DEFINE([TARGET_DRAGONFLY], [1], [Are we running on DragonFlyBSD?]) AC_DEFINE_UNQUOTED([TARGET_PREFIX], ["D"], [Target prefix]) + AC_SUBST([DNS_UPDOWN_TYPE], ["openresolv"]) ;; *-aix*) AC_DEFINE([TARGET_AIX], [1], [Are we running AIX?]) AC_DEFINE_UNQUOTED([TARGET_PREFIX], ["A"], [Target prefix]) + AC_SUBST([DNS_UPDOWN_TYPE], ["resolvconf_file"]) ROUTE="/usr/sbin/route" have_tap_header="yes" ac_cv_header_net_if_h="no" # exists, but breaks things @@ -371,10 +395,12 @@ *-*-haiku*) AC_DEFINE([TARGET_HAIKU], [1], [Are we running Haiku?]) AC_DEFINE_UNQUOTED([TARGET_PREFIX], ["H"], [Target prefix]) + AC_SUBST([DNS_UPDOWN_TYPE], ["haikuos_file"]) LIBS="${LIBS} -lnetwork" ;; *) AC_DEFINE_UNQUOTED([TARGET_PREFIX], ["X"], [Target prefix]) + AC_SUBST([DNS_UPDOWN_TYPE], ["resolvconf_file"]) have_tap_header="yes" ;; esac @@ -1317,7 +1343,7 @@ test "${enable_small}" = "yes" && AC_DEFINE([ENABLE_SMALL], [1], [Enable smaller executable size]) test "${enable_fragment}" = "yes" && AC_DEFINE([ENABLE_FRAGMENT], [1], [Enable internal fragmentation support]) test "${enable_port_share}" = "yes" && AC_DEFINE([ENABLE_PORT_SHARE], [1], [Enable TCP Server port sharing]) - +test "${enable_dns_updown_by_default}" = "yes" && AC_DEFINE([ENABLE_DNS_UPDOWN_BY_DEFAULT], [1], [Enable dns-updown hook by default]) test "${enable_ntlm}" = "yes" && AC_DEFINE([ENABLE_NTLM], [1], [Enable NTLMv2 proxy support]) test "${enable_crypto_ofb_cfb}" = "yes" && AC_DEFINE([ENABLE_OFB_CFB_MODE], [1], [Enable OFB and CFB cipher modes]) if test "${have_export_keying_material}" = "yes"; then @@ -1505,6 +1531,7 @@ sampledir="\$(docdir)/sample" AC_SUBST([plugindir]) +AC_SUBST([scriptdir]) AC_SUBST([sampledir]) AC_SUBST([systemdunitdir]) @@ -1541,6 +1568,7 @@ Makefile distro/Makefile distro/systemd/Makefile + distro/dns-scripts/Makefile doc/Makefile doc/doxygen/Makefile doc/doxygen/openvpn.doxyfile diff --git a/distro/Makefile.am b/distro/Makefile.am index 7a588da..26f577b 100644 --- a/distro/Makefile.am +++ b/distro/Makefile.am @@ -13,3 +13,7 @@ $(srcdir)/Makefile.in SUBDIRS = systemd + +if ENABLE_DNS_UPDOWN +SUBDIRS += dns-scripts +endif diff --git a/distro/dns-scripts/Makefile.am b/distro/dns-scripts/Makefile.am new file mode 100644 index 0000000..fc2db08 --- /dev/null +++ b/distro/dns-scripts/Makefile.am @@ -0,0 +1,29 @@ +# +# OpenVPN -- An application to securely tunnel IP networks +# over a single UDP port, with support for SSL/TLS-based +# session authentication and key exchange, +# packet encryption, packet authentication, and +# packet compression. +# +# Copyright (C) 2002-2024 OpenVPN Inc +# + +MAINTAINERCLEANFILES = \ + $(srcdir)/Makefile.in + +EXTRA_DIST = \ + systemd-dns-updown.sh \ + openresolv-dns-updown.sh \ + haikuos_file-dns-updown.sh \ + resolvconf_file-dns-updown.sh + +script_SCRIPTS = \ + dns-updown + +CLEANFILES = $(script_SCRIPTS) + +dns-updown: @DNS_UPDOWN_TYPE@-dns-updown.sh + cp ${srcdir}/@DNS_UPDOWN_TYPE@-dns-updown.sh $@ + chmod +x $@ + +all: $(script_SCRIPTS) diff --git a/distro/dns-scripts/haikuos_file-dns-updown.sh b/distro/dns-scripts/haikuos_file-dns-updown.sh new file mode 100644 index 0000000..748804e --- /dev/null +++ b/distro/dns-scripts/haikuos_file-dns-updown.sh @@ -0,0 +1,85 @@ +#!/bin/sh +# +# Simple OpenVPN up/down script for modifying Haiku OS resolv.conf +# (C) Copyright 2024 OpenVPN Inc +# +# SPDX-License-Identifier: BSD-2-Clause +# +# Example env from openvpn (most are not applied): +# +# dev tun0 +# script_type dns-up +# dns_search_domain_1 mycorp.in +# dns_search_domain_2 eu.mycorp.com +# dns_server_1_address_1 192.168.99.254 +# dns_server_1_address_2 fd00::99:53 +# dns_server_1_port_1 53 +# dns_server_1_port_2 53 +# dns_server_1_resolve_domain_1 mycorp.in +# dns_server_1_resolve_domain_2 eu.mycorp.com +# dns_server_1_dnssec true +# dns_server_1_transport DoH +# dns_server_1_sni dns.mycorp.in +# + +set -e +u + +conly_standard_server_ports() { + i=1 + while true; do + eval addr=\"\$dns_server_${n}_address_${i}\" + [ -n "$addr" ] || return 0 + + eval port=\"\$dns_server_${n}_port_${i}\" + [ -z "$port" -o "$port" = "53" ] || return 1 + + i=$(expr $i + 1) + done +} + +onf=/boot/system/settings/network/resolv.conf +test -e "$conf" || exit 1 +case "${script_type}" in +dns-up) + n=1 + while :; do + eval addr=\"\$dns_server_${n}_address_1\" + [ -n "$addr" ] || { + echo "setting DNS failed, no compatible server profile" + exit 1 + } + + # Skip server profiles which require DNSSEC, + # secure transport or use a custom port + eval dnssec=\"\$dns_server_${n}_dnssec\" + eval transport=\"\$dns_server_${n}_transport\" + [ -z "$transport" -o "$transport" = "plain" ] \ + && [ -z "$dnssec" -o "$dnssec" = "no" ] \ + && only_standard_server_ports && break + + n=$(expr $n + 1) + done + + eval addr1=\"\$dns_server_${n}_address_1\" + eval addr2=\"\$dns_server_${n}_address_2\" + eval addr3=\"\$dns_server_${n}_address_3\" + text="### openvpn ${dev} begin ###\n" + text="${text}nameserver $addr1\n" + test -z "$addr2" || text="${text}nameserver $addr2\n" + test -z "$addr3" || text="${text}nameserver $addr3\n" + + test -z "$dns_search_domain_1" || { + for i in $(seq 1 6); do + eval domains=\"$domains\$dns_search_domain_${i} \" || break + done + text="${text}search $domains\n" + } + text="${text}### openvpn ${dev} end ###" + text="${text}\n$(cat ${conf})" + + echo "${text}" > "${conf}" + ;; +dns-down) + sed -i'' -e "/### openvpn ${dev} begin ###/,/### openvpn ${dev} end ###/d" "$conf" + ;; +esac diff --git a/distro/dns-scripts/openresolv-dns-updown.sh b/distro/dns-scripts/openresolv-dns-updown.sh new file mode 100644 index 0000000..e50398c --- /dev/null +++ b/distro/dns-scripts/openresolv-dns-updown.sh @@ -0,0 +1,89 @@ +#!/bin/sh +# +# Simple OpenVPN up/down script for openresolv integration +# (C) Copyright 2016 Baptiste Daroussin +# 2024 OpenVPN Inc +# +# SPDX-License-Identifier: BSD-2-Clause +# +# Example env from openvpn (most are not applied): +# +# dev tun0 +# script_type dns-up +# dns_search_domain_1 mycorp.in +# dns_search_domain_2 eu.mycorp.com +# dns_server_1_address_1 192.168.99.254 +# dns_server_1_address_2 fd00::99:53 +# dns_server_1_port_1 53 +# dns_server_1_port_2 53 +# dns_server_1_resolve_domain_1 mycorp.in +# dns_server_1_resolve_domain_2 eu.mycorp.com +# dns_server_1_dnssec true +# dns_server_1_transport DoH +# dns_server_1_sni dns.mycorp.in +# + +set -e +u + +only_standard_server_ports() { + i=1 + while true; do + eval addr=\"\$dns_server_${n}_address_${i}\" + [ -n "$addr" ] || return 0 + + eval port=\"\$dns_server_${n}_port_${i}\" + [ -z "$port" -o "$port" = "53" ] || return 1 + + i=$(expr $i + 1) + done +} + +: ${script_type:=dns-down} +case "${script_type}" in +dns-up) + n=1 + while :; do + eval addr=\"\$dns_server_${n}_address_1\" + [ -n "$addr" ] || { + echo "setting DNS failed, no compatible server profile" + exit 1 + } + + # Skip server profiles which require DNSSEC, + # secure transport or use a custom port + eval dnssec=\"\$dns_server_${n}_dnssec\" + eval transport=\"\$dns_server_${n}_transport\" + [ -z "$transport" -o "$transport" = "plain" ] \ + && [ -z "$dnssec" -o "$dnssec" = "no" ] \ + && only_standard_server_ports && break + + n=$(expr $n + 1) + done + + { + i=1 + maxns=3 + while :; do + maxns=$((maxns - 1)) + [ $maxns -gt 0 ] || break + eval option=\"\$dns_server_${n}_address_${i}\" || break + [ "${option}" ] || break + i=$((i + 1)) + echo "nameserver ${option}" + done + i=1 + maxdom=6 + while :; do + maxdom=$((maxdom - 1)) + [ $maxdom -gt 0 ] || break + eval option=\"\$dns_search_domain_${i}\" || break + [ "${option}" ] || break + i=$((i + 1)) + echo "search ${option}" + done + } | /sbin/resolvconf -a "${dev}" + ;; +dns-down) + /sbin/resolvconf -d "${dev}" -f + ;; +esac diff --git a/distro/dns-scripts/resolvconf_file-dns-updown.sh b/distro/dns-scripts/resolvconf_file-dns-updown.sh new file mode 100644 index 0000000..567b402 --- /dev/null +++ b/distro/dns-scripts/resolvconf_file-dns-updown.sh @@ -0,0 +1,85 @@ +#!/bin/sh +# +# Simple OpenVPN up/down script for modifying /etc/resolv.conf +# (C) Copyright 2024 OpenVPN Inc +# +# SPDX-License-Identifier: BSD-2-Clause +# +# Example env from openvpn (most are not applied): +# +# dev tun0 +# script_type dns-up +# dns_search_domain_1 mycorp.in +# dns_search_domain_2 eu.mycorp.com +# dns_server_1_address_1 192.168.99.254 +# dns_server_1_address_2 fd00::99:53 +# dns_server_1_port_1 53 +# dns_server_1_port_2 53 +# dns_server_1_resolve_domain_1 mycorp.in +# dns_server_1_resolve_domain_2 eu.mycorp.com +# dns_server_1_dnssec true +# dns_server_1_transport DoH +# dns_server_1_sni dns.mycorp.in +# + +set -e +u + +only_standard_server_ports() { + i=1 + while true; do + eval addr=\"\$dns_server_${n}_address_${i}\" + [ -n "$addr" ] || return 0 + + eval port=\"\$dns_server_${n}_port_${i}\" + [ -z "$port" -o "$port" = "53" ] || return 1 + + i=$(expr $i + 1) + done +} + +conf=/etc/resolv.conf +test -e "$conf" || exit 1 +case "${script_type}" in +dns-up) + n=1 + while :; do + eval addr=\"\$dns_server_${n}_address_1\" + [ -n "$addr" ] || { + echo "setting DNS failed, no compatible server profile" + exit 1 + } + + # Skip server profiles which require DNSSEC, + # secure transport or use a custom port + eval dnssec=\"\$dns_server_${n}_dnssec\" + eval transport=\"\$dns_server_${n}_transport\" + [ -z "$transport" -o "$transport" = "plain" ] \ + && [ -z "$dnssec" -o "$dnssec" = "no" ] \ + && only_standard_server_ports && break + + n=$(expr $n + 1) + done + + eval addr1=\"\$dns_server_${n}_address_1\" + eval addr2=\"\$dns_server_${n}_address_2\" + eval addr3=\"\$dns_server_${n}_address_3\" + text="### openvpn ${dev} begin ###\n" + text="${text}nameserver $addr1\n" + test -z "$addr2" || text="${text}nameserver $addr2\n" + test -z "$addr3" || text="${text}nameserver $addr3\n" + + test -z "$dns_search_domain_1" || { + for i in $(seq 1 6); do + eval domains=\"$domains\$dns_search_domain_${i} \" || break + done + text="${text}search $domains\n" + } + text="${text}### openvpn ${dev} end ###" + text="${text}\n$(cat ${conf})" + + echo "${text}" > "${conf}" + ;; +dns-down) + sed -i'' -e "/### openvpn ${dev} begin ###/,/### openvpn ${dev} end ###/d" "$conf" + ;; +esac diff --git a/distro/dns-scripts/systemd-dns-updown.sh b/distro/dns-scripts/systemd-dns-updown.sh new file mode 100644 index 0000000..ecadd29 --- /dev/null +++ b/distro/dns-scripts/systemd-dns-updown.sh @@ -0,0 +1,240 @@ +#!/bin/bash +# +# dns-updown - add/remove openvpn provided DNS information +# +# Copyright (C) 2024 OpenVPN Inc +# +# SPDX-License-Identifier: GPL-2.0 +# +# Add/remove openvpn DNS settings from the env into/from +# the system. Supported backends in this order: +# +# * systemd-resolved +# * resolvconf +# * /etc/resolv.conf file +# +# Example env from openvpn (not all are always applied): +# +# dev tun0 +# script_type dns-up +# dns_search_domain_1 mycorp.in +# dns_search_domain_2 eu.mycorp.com +# dns_server_1_address_1 192.168.99.254 +# dns_server_1_address_2 fd00::99:53 +# dns_server_1_port_1 53 +# dns_server_1_port_2 53 +# dns_server_1_resolve_domain_1 mycorp.in +# dns_server_1_resolve_domain_2 eu.mycorp.com +# dns_server_1_dnssec true +# dns_server_1_transport DoH +# dns_server_1_sni dns.mycorp.in +# + +function do_resolved_servers { + local sni="" + local transport_var=dns_server_${n}_transport + local sni_var=dns_server_${n}_sni + [ "${!transport_var}" = "DoT" ] && sni="#${!sni_var}" + + local i=1 + local addrs="" + while :; do + local addr_var=dns_server_${n}_address_${i} + local addr="${!addr_var}" + [ -n "$addr" ] || break + + local port_var=dns_server_${n}_port_${i} + if [ -n "${!port_var}" ]; then + if [[ "$addr" =~ : ]]; then + addr="[$addr]" + fi + addrs+="${addr}:${!port_var}${sni} " + else + addrs+="${addr}${sni} " + fi + i=$((i+1)) + done + + resolvectl dns "$dev" $addrs +} + +function do_resolved_domains { + local list="" + for domain_var in ${!dns_search_domain_*}; do + list+="${!domain_var} " + done + local domain_var=dns_server_${n}_resolve_domain_1 + if [ -z "${!domain_var}" ]; then + resolvectl default-route "$dev" true + list+="~." + else + resolvectl default-route "$dev" false + local i=1 + while :; do + domain_var=dns_server_${n}_resolve_domain_${i} + [ -n "${!domain_var}" ] || break + # Add as split domain (~ prefix), if it doesn't already exist + [[ "$list" =~ (^| )"${!domain_var}"( |$) ]] \ + || list+="~${!domain_var} " + i=$((i+1)) + done + fi + + resolvectl domain "$dev" $list +} + +function do_resolved_dnssec { + local dnssec_var=dns_server_${n}_dnssec + if [ "${!dnssec_var}" = "optional" ]; then + resolvectl dnssec "$dev" allow-downgrade + elif [ "${!dnssec_var}" = "yes" ]; then + resolvectl dnssec "$dev" true + else + resolvectl dnssec "$dev" false + fi +} + +function do_resolved_dnsovertls { + local transport_var=dns_server_${n}_transport + if [ "${!transport_var}" = "DoT" ]; then + resolvectl dnsovertls "$dev" true + else + resolvectl dnsovertls "$dev" false + fi +} + +function do_resolved { + [[ "$(readlink /etc/resolv.conf)" =~ systemd ]] || return 1 + + n=1 + while :; do + local addr_var=dns_server_${n}_address_1 + [ -n "${!addr_var}" ] || { + echo "setting DNS failed, no compatible server profile" + return 1 + } + + # Skip server profiles which require DNS-over-HTTPS + local transport_var=dns_server_${n}_transport + [ -n "${!transport_var}" -a "${!transport_var}" = "DoH" ] || break + + n=$((n+1)) + done + + if [ "$script_type" = "dns-up" ]; then + echo "setting DNS using resolvectl" + do_resolved_servers + do_resolved_domains + do_resolved_dnssec + do_resolved_dnsovertls + else + echo "unsetting DNS using resolvectl" + resolvectl revert "$dev" + fi + + return 0 +} + +function only_standard_server_ports { + local i=1 + while :; do + local addr_var=dns_server_${n}_address_${i} + [ -n "${!addr_var}" ] || return 0 + + local port_var=dns_server_${n}_port_${i} + [ -z "${!port_var}" -o "${!port_var}" = "53" ] || return 1 + + i=$((i+1)) + done +} + +function resolv_conf_compat_profile { + local n=1 + while :; do + local server_addr_var=dns_server_${n}_address_1 + [ -n "${!server_addr_var}" ] || { + echo "setting DNS failed, no compatible server profile" + exit 1 + } + + # Skip server profiles which require DNSSEC, + # secure transport or use a custom port + local dnssec_var=dns_server_${n}_dnssec + local transport_var=dns_server_${n}_transport + [ -z "${!transport_var}" -o "${!transport_var}" = "plain" ] \ + && [ -z "${!dnssec_var}" -o "${!dnssec_var}" = "no" ] \ + && only_standard_server_ports && break + + n=$((n+1)) + done + return $n +} + +function do_resolvconf { + [ -x /sbin/resolvconf ] || return 1 + + resolv_conf_compat_profile + local n=$? + + if [ "$script_type" = "dns-up" ]; then + echo "setting DNS using resolvconf" + local domains="" + for domain_var in ${!dns_search_domain_*}; do + domains+="${!domain_var} " + done + { + local maxns=3 + local server_var=dns_server_${n}_address_* + for addr_var in ${!server_var}; do + [ $((maxns--)) -gt 0 ] || break + echo "nameserver ${!addr_var}" + done + [ -z "$domains" ] || echo "search $domains" + } | /sbin/resolvconf -a "$dev" + else + echo "unsetting DNS using resolvconf" + /sbin/resolvconf -d "$dev" + fi + + return 0 +} + +function do_resolv_conf_file { + conf=/etc/resolv.conf + test -e "$conf" || exit 1 + + resolv_conf_compat_profile + local n=$? + + if [ "$script_type" = "dns-up" ]; then + echo "setting DNS using resolv.conf file" + + local addr1_var=dns_server_${n}_address_1 + local addr2_var=dns_server_${n}_address_2 + local addr3_var=dns_server_${n}_address_3 + text="### openvpn ${dev} begin ###\n" + text="${text}nameserver ${!addr1_var}\n" + test -z "${!addr2_var}" || text="${text}nameserver ${!addr2_var}\n" + test -z "${!addr3_var}" || text="${text}nameserver ${!addr3_var}\n" + + test -z "$dns_search_domain_1" || { + for i in $(seq 1 6); do + eval domains=\"$domains\$dns_search_domain_${i} \" || break + done + text="${text}search $domains\n" + } + text="${text}### openvpn ${dev} end ###" + + sed -i "1i${text}" "$conf" + else + echo "unsetting DNS using resolv.conf file" + sed -i "/### openvpn ${dev} begin ###/,/### openvpn ${dev} end ###/d" "$conf" + fi + + return 0 +} + +do_resolved || do_resolvconf || do_resolv_conf_file || { + echo "setting DNS failed, no method succeeded" + exit 1 +} diff --git a/doc/man-sections/script-options.rst b/doc/man-sections/script-options.rst index e48710b..bd5ecd4 100644 --- a/doc/man-sections/script-options.rst +++ b/doc/man-sections/script-options.rst @@ -8,9 +8,13 @@ Script Order of Execution ------------------------- +#. ``--dns-updown`` + + Executed after TCP/UDP socket bind and TUN/TAP open, before ``--up``. + #. ``--up`` - Executed after TCP/UDP socket bind and TUN/TAP open. + Executed after TCP/UDP socket bind and TUN/TAP open, after ``--dns-updown``. #. ``--tls-verify`` @@ -38,9 +42,13 @@ Executed in ``--mode server`` mode on client instance shutdown. +#. ``--dns-updown`` + + Executed before TCP/UDP and TUN/TAP close, before ``--down``. + #. ``--down`` - Executed after TCP/UDP and TUN/TAP close. + Executed after TCP/UDP and TUN/TAP close, after ``--dns-updown``. #. ``--learn-address`` @@ -171,7 +179,7 @@ client-crresponse cmd OpenVPN will write the response of the client into a temporary file. - The filename will be passed as an argument to ``cmd``, and the file will be + The filename will be passed as an argument to ``cmd``, and the file will automatically deleted by OpenVPN after the script returns. The response is passed as is from the client. The script needs to check @@ -233,6 +241,31 @@ The ``--client-disconnect`` command is not passed any extra arguments (only those arguments specified in cmd, if any). +--dns-updown cmd + Run command ``cmd``, instead of the default DNS up/down command that comes + with openvpn. If ``cmd`` is ``disable`` the ``--dns-updown`` command is not run. + + If you write your own command, please make sure to ignore ``--dns`` + server profiles that cannot be applied. Port, DNSSEC and secure transport + settings need to be adhered to. If split DNS is not possible a full redirect + can be used as a fallback. If not all of the server addresses or search domains + can be configured, apply them in the order they are listed in. + + Note that ``--dns-updown`` is not supported on all platforms. On Windows DNS + will always be set by the service. On Android DNS will be passed via management + interface. + + Note that DNS-related ``--dhcp-option``\ s might be converted so that they are + available to this hook if no ``--dns`` options exist. If any ``--dns server`` + option is present, DNS-related ``--dhcp-option``\ s will always be ignored. + If an ``--up`` script is defined, foreign_option env vars will be generated + from ``--dns`` options and passed to the script. The default ``--dns-updown`` + command is not run if an ``--up`` script is defined. Both is done for backward + compatibility. In case you want to run the ``--dns-updown`` command even if + there is an ``--up`` defined, you can define a custom command or use ``force`` + as ``cmd`` to run the default command. No DNS env vars will be passed to ``--up`` + in this case. + --down cmd Run command ``cmd`` after TUN/TAP device close (post ``--user`` UID change and/or ``--chroot`` ). ``cmd`` consists of a path to script (or @@ -659,7 +692,7 @@ names). Set prior to ``--up`` or ``--down`` script execution. :code:`dns_*` - The ``--dns`` configuration options will be made available to script + The ``--dns`` configuration options will be made available to ``--dns-updown`` execution through this set of environment variables. Variables appear only if the corresponding option has a value assigned. For the semantics of each individual variable, please refer to the documentation for ``--dns``. diff --git a/src/openvpn/Makefile.am b/src/openvpn/Makefile.am index 37af683..2e93ebb 100644 --- a/src/openvpn/Makefile.am +++ b/src/openvpn/Makefile.am @@ -30,7 +30,8 @@ $(OPTIONAL_LZ4_CFLAGS) \ $(OPTIONAL_PKCS11_HELPER_CFLAGS) \ $(OPTIONAL_INOTIFY_CFLAGS) \ - -DPLUGIN_LIBDIR=\"${plugindir}\" + -DPLUGIN_LIBDIR=\"${plugindir}\" \ + -DDEFAULT_DNS_UPDOWN=\"${scriptdir}/dns-updown\" if WIN32 # we want unicode entry point but not the macro diff --git a/src/openvpn/dns.c b/src/openvpn/dns.c index b6e524f..4da0747 100644 --- a/src/openvpn/dns.c +++ b/src/openvpn/dns.c @@ -30,6 +30,7 @@ #include "dns.h" #include "socket.h" #include "options.h" +#include "run_command.h" #ifdef _WIN32 #include "win32.h" @@ -262,6 +263,8 @@ clone.search_domains = clone_dns_domains(o->search_domains, gc); clone.servers = clone_dns_servers(o->servers, gc); clone.servers_prepull = clone_dns_servers(o->servers_prepull, gc); + clone.updown = o->updown; + clone.user_set_updown = o->user_set_updown; return clone; } @@ -548,6 +551,54 @@ send_msg_iservice(o->msg_channel, &nrpt, sizeof(nrpt), &ack, "DNS"); } +#else /* ifdef _WIN32 */ + +static void +updown_env_set(bool up, const struct dns_options *o, const struct tuntap *tt, struct env_set *es) +{ + setenv_str(es, "dev", tt->actual_name); + setenv_str(es, "script_type", up ? "dns-up" : "dns-down"); + setenv_dns_options(o, es); +} + +static int +do_run_up_down_command(bool up, const struct dns_options *o, const struct tuntap *tt) +{ + struct gc_arena gc = gc_new(); + struct argv argv = argv_new(); + struct env_set *es = env_set_create(&gc); + + updown_env_set(up, o, tt, es); + + argv_printf(&argv, "%s", o->updown); + argv_msg(M_INFO, &argv); + int res; + if (o->user_set_updown) + { + res = openvpn_run_script(&argv, es, S_EXITCODE, "dns updown"); + } + else + { + res = openvpn_execve_check(&argv, es, S_EXITCODE, "WARNING: Failed running dns updown"); + } + argv_free(&argv); + gc_free(&gc); + return res; +} + +static void +run_up_down_command(bool up, struct options *o, const struct tuntap *tt) +{ + if (!o->dns_options.updown) + { + return; + } + + int status; + status = do_run_up_down_command(up, &o->dns_options, tt); + msg(M_INFO, "dns %s command exited with status %d", up ? "up" : "down", status); +} + #endif /* _WIN32 */ void @@ -666,5 +717,7 @@ #ifdef _WIN32 run_up_down_service(up, o, tt); +#else + run_up_down_command(up, o, tt); #endif /* ifdef _WIN32 */ } diff --git a/src/openvpn/dns.h b/src/openvpn/dns.h index f24e30b..c4d19ff 100644 --- a/src/openvpn/dns.h +++ b/src/openvpn/dns.h @@ -73,6 +73,8 @@ struct dns_server *servers_prepull; struct dns_server *servers; struct gc_arena gc; + const char *updown; + bool user_set_updown; }; /** diff --git a/src/openvpn/options.c b/src/openvpn/options.c index 02970a7..1d1daea 100644 --- a/src/openvpn/options.c +++ b/src/openvpn/options.c @@ -526,10 +526,12 @@ " address [addr[:port] ...] : server addresses 4/6\n" " resolve-domains [domain ...] : split domains\n" " dnssec : option to use DNSSEC\n" - " type : query server over HTTPS / TLS\n" + " transport : query server over HTTPS / TLS\n" " sni : DNS server name indication\n" "--dns search-domains [domain ...]:\n" " Add domains to DNS domain search list\n" + "--dns-updown cmd|force|disable : Run cmd as user defined dns config command,\n" + " force running the default script or disable running it.\n" "--auth-retry t : How to handle auth failures. Set t to\n" " none (default), interact, or nointeract.\n" "--static-challenge t e []: Enable static challenge/response protocol using\n" @@ -922,6 +924,10 @@ #ifndef ENABLE_DCO o->disable_dco = true; #endif /* ENABLE_DCO */ + +#ifdef ENABLE_DNS_UPDOWN_BY_DEFAULT + o->dns_options.updown = DEFAULT_DNS_UPDOWN; +#endif /* ENABLE_DNS_UPDOWN_BY_DEFAULT */ } void @@ -8088,6 +8094,39 @@ to->ip_win32_defined = true; } #endif /* ifdef _WIN32 */ + else if (streq(p[0], "dns-updown") && p[1]) + { + VERIFY_PERMISSION(OPT_P_SCRIPT); + if (!no_more_than_n_args(msglevel, p, 2, NM_QUOTE_HINT)) + { + goto err; + } + struct dns_options *dns = &options->dns_options; + if (streq(p[1], "disable")) + { + dns->updown = NULL; + dns->user_set_updown = false; + } + else if (streq(p[1], "force")) + { + /* force dns-updown run, even if a --up script is defined */ + if (dns->user_set_updown == false) + { + dns->updown = DEFAULT_DNS_UPDOWN; + dns->user_set_updown = true; + } + } + else + { + if (streq(dns->updown, DEFAULT_DNS_UPDOWN)) + { + /* Unset the default command to prevent warnings */ + dns->updown = NULL; + } + set_user_script(options, &dns->updown, p[1], p[0], false); + dns->user_set_updown = true; + } + } else if (streq(p[0], "dns") && p[1]) { VERIFY_PERMISSION(OPT_P_DHCPDNS);