mirror of
https://github.com/3proxy/3proxy.git
synced 2026-09-02 21:05:49 +08:00
Transparent moved to main code (TransparentPlugin removed), BSD pf support added
This commit is contained in:
parent
85b753ce96
commit
88b3225bdf
@ -4,8 +4,20 @@
|
|||||||
|
|
||||||
cmake_minimum_required(VERSION 3.16)
|
cmake_minimum_required(VERSION 3.16)
|
||||||
|
|
||||||
# Read version from RELEASE file
|
# Read the version. A release branch carries RELEASE, a development branch
|
||||||
file(STRINGS "${CMAKE_CURRENT_SOURCE_DIR}/RELEASE" PROJECT_VERSION LIMIT_COUNT 1)
|
# DEVEL, whose version may have a suffix that project() will not take.
|
||||||
|
if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/RELEASE")
|
||||||
|
file(STRINGS "${CMAKE_CURRENT_SOURCE_DIR}/RELEASE" PROJECT_VERSION_FULL LIMIT_COUNT 1)
|
||||||
|
elseif(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/DEVEL")
|
||||||
|
file(STRINGS "${CMAKE_CURRENT_SOURCE_DIR}/DEVEL" PROJECT_VERSION_FULL LIMIT_COUNT 1)
|
||||||
|
else()
|
||||||
|
message(FATAL_ERROR "Neither RELEASE nor DEVEL found: cannot tell the version")
|
||||||
|
endif()
|
||||||
|
string(STRIP "${PROJECT_VERSION_FULL}" PROJECT_VERSION_FULL)
|
||||||
|
string(REGEX MATCH "^[0-9]+(\\.[0-9]+)*" PROJECT_VERSION "${PROJECT_VERSION_FULL}")
|
||||||
|
if(NOT PROJECT_VERSION)
|
||||||
|
message(FATAL_ERROR "No version number in '${PROJECT_VERSION_FULL}'")
|
||||||
|
endif()
|
||||||
|
|
||||||
project(3proxy
|
project(3proxy
|
||||||
VERSION ${PROJECT_VERSION}
|
VERSION ${PROJECT_VERSION}
|
||||||
@ -55,6 +67,7 @@ option(3PROXY_USE_SPLICE "Build Linux splice() support, slower than read/write f
|
|||||||
option(3PROXY_USE_POLL "Use poll() instead of select() (Unix only)" ON)
|
option(3PROXY_USE_POLL "Use poll() instead of select() (Unix only)" ON)
|
||||||
option(3PROXY_USE_WSAPOLL "Use WSAPoll instead of select() (Windows only)" ON)
|
option(3PROXY_USE_WSAPOLL "Use WSAPoll instead of select() (Windows only)" ON)
|
||||||
option(3PROXY_USE_NETFILTER "Enable Linux netfilter support (Linux only)" ON)
|
option(3PROXY_USE_NETFILTER "Enable Linux netfilter support (Linux only)" ON)
|
||||||
|
option(3PROXY_USE_TRANSPARENT "Build transparent proxying support (Linux and BSD only)" ON)
|
||||||
option(3PROXY_USE_UNIX_SOCKETS "Enable Unix domain socket support (Unix only)" ON)
|
option(3PROXY_USE_UNIX_SOCKETS "Enable Unix domain socket support (Unix only)" ON)
|
||||||
option(3PROXY_USE_HTTPSRV "Build the HTTP server and the admin interface on top of it" ON)
|
option(3PROXY_USE_HTTPSRV "Build the HTTP server and the admin interface on top of it" ON)
|
||||||
|
|
||||||
@ -193,7 +206,6 @@ elseif(CMAKE_SYSTEM_NAME STREQUAL "Linux")
|
|||||||
set(DEFAULT_PLUGINS
|
set(DEFAULT_PLUGINS
|
||||||
StringsPlugin
|
StringsPlugin
|
||||||
TrafficPlugin
|
TrafficPlugin
|
||||||
TransparentPlugin
|
|
||||||
FilePlugin
|
FilePlugin
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -215,7 +227,6 @@ elseif(CMAKE_SYSTEM_NAME MATCHES "FreeBSD|Darwin|OpenBSD|NetBSD")
|
|||||||
set(DEFAULT_PLUGINS
|
set(DEFAULT_PLUGINS
|
||||||
StringsPlugin
|
StringsPlugin
|
||||||
TrafficPlugin
|
TrafficPlugin
|
||||||
TransparentPlugin
|
|
||||||
FilePlugin
|
FilePlugin
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -232,7 +243,6 @@ else()
|
|||||||
set(DEFAULT_PLUGINS
|
set(DEFAULT_PLUGINS
|
||||||
StringsPlugin
|
StringsPlugin
|
||||||
TrafficPlugin
|
TrafficPlugin
|
||||||
TransparentPlugin
|
|
||||||
FilePlugin
|
FilePlugin
|
||||||
)
|
)
|
||||||
endif()
|
endif()
|
||||||
@ -241,6 +251,25 @@ if(3PROXY_USE_HTTPSRV)
|
|||||||
add_compile_definitions(WITH_HTTPSRV)
|
add_compile_definitions(WITH_HTTPSRV)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
# Transparent proxying needs a redirection that leaves the original
|
||||||
|
# destination where 3proxy reads it: the kernel on Linux, the socket on the
|
||||||
|
# BSDs. That means netfilter, OpenBSD divert-to or FreeBSD ipfw fwd. NetBSD
|
||||||
|
# and macOS rewrite the destination instead and are left out.
|
||||||
|
if(3PROXY_USE_TRANSPARENT AND (CMAKE_SYSTEM_NAME STREQUAL "Linux"
|
||||||
|
OR CMAKE_SYSTEM_NAME MATCHES "FreeBSD|OpenBSD|NetBSD"))
|
||||||
|
add_compile_definitions(WITH_TRANSPARENT)
|
||||||
|
set(3PROXY_TRANSPARENT_BUILT ON)
|
||||||
|
# pf keeps the original destination in its state table, which is read
|
||||||
|
# through /dev/pf. macOS has the device but ships no header for it.
|
||||||
|
include(CheckIncludeFiles)
|
||||||
|
check_include_files("sys/types.h;sys/socket.h;net/if.h;net/pfvar.h" HAVE_PFVAR_H)
|
||||||
|
if(HAVE_PFVAR_H)
|
||||||
|
add_compile_definitions(WITH_PF)
|
||||||
|
endif()
|
||||||
|
else()
|
||||||
|
set(3PROXY_TRANSPARENT_BUILT OFF)
|
||||||
|
endif()
|
||||||
|
|
||||||
# Unix domain sockets off: NO_UN also undefines WITH_UN if it arrives from
|
# Unix domain sockets off: NO_UN also undefines WITH_UN if it arrives from
|
||||||
# elsewhere, e.g. CFLAGS
|
# elsewhere, e.g. CFLAGS
|
||||||
if(NOT 3PROXY_USE_UNIX_SOCKETS)
|
if(NOT 3PROXY_USE_UNIX_SOCKETS)
|
||||||
@ -471,6 +500,10 @@ if(PCRE2_FOUND)
|
|||||||
target_sources(3proxy PRIVATE src/pcre.c)
|
target_sources(3proxy PRIVATE src/pcre.c)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
if(3PROXY_TRANSPARENT_BUILT)
|
||||||
|
target_sources(3proxy PRIVATE src/transparent.c)
|
||||||
|
endif()
|
||||||
|
|
||||||
target_include_directories(3proxy PRIVATE
|
target_include_directories(3proxy PRIVATE
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/src
|
${CMAKE_CURRENT_SOURCE_DIR}/src
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/src/libs
|
${CMAKE_CURRENT_SOURCE_DIR}/src/libs
|
||||||
@ -917,7 +950,7 @@ endif()
|
|||||||
# Summary
|
# Summary
|
||||||
message(STATUS "")
|
message(STATUS "")
|
||||||
message(STATUS "3proxy configuration summary:")
|
message(STATUS "3proxy configuration summary:")
|
||||||
message(STATUS " Version: ${PROJECT_VERSION}")
|
message(STATUS " Version: ${PROJECT_VERSION_FULL}")
|
||||||
message(STATUS " Platform: ${CMAKE_SYSTEM_NAME}")
|
message(STATUS " Platform: ${CMAKE_SYSTEM_NAME}")
|
||||||
message(STATUS " Compiler: ${CMAKE_C_COMPILER_ID} ${CMAKE_C_COMPILER_VERSION}")
|
message(STATUS " Compiler: ${CMAKE_C_COMPILER_ID} ${CMAKE_C_COMPILER_VERSION}")
|
||||||
message(STATUS " Build type: ${CMAKE_BUILD_TYPE}")
|
message(STATUS " Build type: ${CMAKE_BUILD_TYPE}")
|
||||||
@ -927,6 +960,7 @@ message(STATUS " BUILD_SHARED: ${3PROXY_BUILD_SHARED}")
|
|||||||
message(STATUS " USE_WOLFSSL: ${3PROXY_USE_WOLFSSL}")
|
message(STATUS " USE_WOLFSSL: ${3PROXY_USE_WOLFSSL}")
|
||||||
message(STATUS " USE_OPENSSL: ${3PROXY_USE_OPENSSL}")
|
message(STATUS " USE_OPENSSL: ${3PROXY_USE_OPENSSL}")
|
||||||
message(STATUS " USE_PCRE2: ${3PROXY_USE_PCRE2}")
|
message(STATUS " USE_PCRE2: ${3PROXY_USE_PCRE2}")
|
||||||
|
message(STATUS " TRANSPARENT: ${3PROXY_TRANSPARENT_BUILT}")
|
||||||
message(STATUS " USE_PAM: ${3PROXY_USE_PAM}")
|
message(STATUS " USE_PAM: ${3PROXY_USE_PAM}")
|
||||||
message(STATUS " USE_ODBC: ${3PROXY_USE_ODBC}")
|
message(STATUS " USE_ODBC: ${3PROXY_USE_ODBC}")
|
||||||
message(STATUS " USE_POLL: ${3PROXY_USE_POLL}")
|
message(STATUS " USE_POLL: ${3PROXY_USE_POLL}")
|
||||||
|
|||||||
@ -44,7 +44,20 @@ AFTERCLEAN = (find . -type f -name "*.o" -delete && find src/ -type f -name "Mak
|
|||||||
TYPECOMMAND = cat
|
TYPECOMMAND = cat
|
||||||
COMPATLIBS =
|
COMPATLIBS =
|
||||||
MAKEFILE = Makefile.FreeBSD
|
MAKEFILE = Makefile.FreeBSD
|
||||||
PLUGINS ?= StringsPlugin TrafficPlugin TransparentPlugin FilePlugin
|
PLUGINS ?= StringsPlugin TrafficPlugin FilePlugin
|
||||||
|
|
||||||
|
# Transparent proxying, built in. The destination of a redirected connection
|
||||||
|
# comes from pf where its headers are available, and from the socket where a
|
||||||
|
# redirection leaves it there (OpenBSD divert-to, FreeBSD ipfw fwd). macOS
|
||||||
|
# has /dev/pf but ships no pfvar.h, so only the socket route is built there
|
||||||
|
# and no macOS redirection leaves the address on the socket.
|
||||||
|
CFLAGS += -DWITH_TRANSPARENT
|
||||||
|
TRANSPARENT_OBJS = transparent$(OBJSUFFICS)
|
||||||
|
|
||||||
|
PF_CHECK ?= $(shell printf "\#include <sys/types.h>\\n\#include <sys/socket.h>\\n\#include <net/if.h>\\n\#include <net/pfvar.h>\\n int main(){return 0;}" | tr -d \\\\ | $(CC) -x c $(CFLAGS) -o testpf.o -c - 2>/dev/null && rm testpf.o && echo true||echo false)
|
||||||
|
ifeq ($(PF_CHECK), true)
|
||||||
|
CFLAGS += -DWITH_PF
|
||||||
|
endif
|
||||||
ifeq ($(STATIC), true)
|
ifeq ($(STATIC), true)
|
||||||
LDFLAGS += -static
|
LDFLAGS += -static
|
||||||
CFLAGS += -DNOPLUGINS -DNOSTDRESOLVE -DNOCRYPT
|
CFLAGS += -DNOPLUGINS -DNOSTDRESOLVE -DNOCRYPT
|
||||||
|
|||||||
@ -50,8 +50,12 @@ MAKEFILE = Makefile.Linux
|
|||||||
# PamAuth requires libpam, you may require pam-devel package to be installed
|
# PamAuth requires libpam, you may require pam-devel package to be installed
|
||||||
# SSLPlugin requires -lcrypto -lssl
|
# SSLPlugin requires -lcrypto -lssl
|
||||||
#LIBS = -lcrypto -lssl -ldl
|
#LIBS = -lcrypto -lssl -ldl
|
||||||
#PLUGINS = SSLPlugin StringsPlugin TrafficPlugin PCREPlugin TransparentPlugin PamAuth
|
#PLUGINS = StringsPlugin TrafficPlugin PamAuth LdapPlugin
|
||||||
PLUGINS ?= StringsPlugin TrafficPlugin TransparentPlugin FilePlugin
|
PLUGINS ?= StringsPlugin TrafficPlugin FilePlugin
|
||||||
|
|
||||||
|
# Transparent proxying, built in: it needs the packet filter of the platform
|
||||||
|
CFLAGS += -DWITH_TRANSPARENT
|
||||||
|
TRANSPARENT_OBJS = transparent$(OBJSUFFICS)
|
||||||
ifeq ($(STATIC), true)
|
ifeq ($(STATIC), true)
|
||||||
LDFLAGS += -static
|
LDFLAGS += -static
|
||||||
CFLAGS += -DNOPLUGINS -DNOSTDRESOLVE -DNOCRYPT
|
CFLAGS += -DNOPLUGINS -DNOSTDRESOLVE -DNOCRYPT
|
||||||
|
|||||||
@ -34,7 +34,7 @@ AFTERCLEAN = (find . -type f -name "*.o" -delete && find src/ -type f -name "Mak
|
|||||||
TYPECOMMAND = cat
|
TYPECOMMAND = cat
|
||||||
COMPATLIBS =
|
COMPATLIBS =
|
||||||
MAKEFILE = Makefile.Solaris
|
MAKEFILE = Makefile.Solaris
|
||||||
PLUGINS = StringsPlugin TrafficPlugin TransparentPlugin FilePlugin
|
PLUGINS = StringsPlugin TrafficPlugin FilePlugin
|
||||||
|
|
||||||
WOLFSSL_CHECK = $(shell printf "\#include <wolfssl/options.h>\\n\#include <wolfssl/openssl/ssl.h>\\n int main(){return 0;}" | tr -d \\\\ | $(CC) -x c $(CFLAGS) -o testwssl.o - 2>/dev/null && $(CC) $(LDFLAGS) -o testwssl testwssl.o -lwolfssl 2>/dev/null && rm testwssl testwssl.o && echo true||echo false)
|
WOLFSSL_CHECK = $(shell printf "\#include <wolfssl/options.h>\\n\#include <wolfssl/openssl/ssl.h>\\n int main(){return 0;}" | tr -d \\\\ | $(CC) -x c $(CFLAGS) -o testwssl.o - 2>/dev/null && $(CC) $(LDFLAGS) -o testwssl testwssl.o -lwolfssl 2>/dev/null && rm testwssl testwssl.o && echo true||echo false)
|
||||||
ifeq ($(WOLFSSL_CHECK), true)
|
ifeq ($(WOLFSSL_CHECK), true)
|
||||||
|
|||||||
@ -46,7 +46,13 @@ AFTERCLEAN = (find . -type f -name "*.o" -delete && find src/ -type f -name "Mak
|
|||||||
TYPECOMMAND = cat
|
TYPECOMMAND = cat
|
||||||
COMPATLIBS =
|
COMPATLIBS =
|
||||||
MAKEFILE = Makefile.unix
|
MAKEFILE = Makefile.unix
|
||||||
PLUGINS ?= StringsPlugin TrafficPlugin TransparentPlugin FilePlugin
|
PLUGINS ?= StringsPlugin TrafficPlugin FilePlugin
|
||||||
|
|
||||||
|
# Transparent proxying is not built here: this makefile is for the systems
|
||||||
|
# without a redirection 3proxy can read the original destination from. Where
|
||||||
|
# the platform has one - OpenBSD divert-to is the case that fits - uncomment:
|
||||||
|
#CFLAGS += -DWITH_TRANSPARENT
|
||||||
|
#TRANSPARENT_OBJS = transparent$(OBJSUFFICS)
|
||||||
ifeq ($(STATIC), true)
|
ifeq ($(STATIC), true)
|
||||||
LDFLAGS += -static
|
LDFLAGS += -static
|
||||||
CFLAGS += -DNOPLUGINS -DNOSTDRESOLVE -DNOCRYPT
|
CFLAGS += -DNOPLUGINS -DNOSTDRESOLVE -DNOCRYPT
|
||||||
|
|||||||
@ -37,6 +37,7 @@
|
|||||||
<li><a href="#SSLPLUGIN">How to set up TLS/SSL (https proxy, mTLS)</a></li>
|
<li><a href="#SSLPLUGIN">How to set up TLS/SSL (https proxy, mTLS)</a></li>
|
||||||
<li><a href="#CERTIFICATES">How to create CA and certificates for SSL</a></li>
|
<li><a href="#CERTIFICATES">How to create CA and certificates for SSL</a></li>
|
||||||
<li><a href="#PCRE">How to use PCRE filtering (regular expressions)</a></li>
|
<li><a href="#PCRE">How to use PCRE filtering (regular expressions)</a></li>
|
||||||
|
<li><a href="#TRANSPARENT">How to proxy transparently</a></li>
|
||||||
<li><A HREF="#AUTH">How to limit service access</a>
|
<li><A HREF="#AUTH">How to limit service access</a>
|
||||||
<li><A HREF="#USERS">How to create a user list</a>
|
<li><A HREF="#USERS">How to create a user list</a>
|
||||||
<li><A HREF="#ACL">How to limit user access to resources</a>
|
<li><A HREF="#ACL">How to limit user access to resources</a>
|
||||||
@ -981,6 +982,189 @@ openssl pkcs12 -export -out client.p12 -passout pass: \
|
|||||||
openssl verify -x509_strict -CAfile ca.crt server.crt
|
openssl verify -x509_strict -CAfile ca.crt server.crt
|
||||||
openssl verify -x509_strict -CAfile ca.crt client.crt
|
openssl verify -x509_strict -CAfile ca.crt client.crt
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
|
<li><a name="TRANSPARENT"><i>How to proxy transparently</i></a>
|
||||||
|
<p>
|
||||||
|
A transparent proxy serves clients that were never configured to use one. A
|
||||||
|
packet filter redirects their connections to 3proxy, and the
|
||||||
|
<b>transparent</b> command tells the service to take the destination from the
|
||||||
|
filter instead of from the request. Every other feature applies as usual:
|
||||||
|
access rules, parent proxies, limits and logging all see the real destination.
|
||||||
|
It works on Linux and on the BSDs. Since 1.0.1 it is part of the binary; before
|
||||||
|
that it was the separate TransparentPlugin, and the <b>plugin</b> line it
|
||||||
|
needed is no longer required.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
The command supplies both the address and the port the client was trying to
|
||||||
|
reach, so a service can serve whatever was redirected to it rather than one
|
||||||
|
port with one target.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Without it a service has to get a destination from somewhere else: an HTTP
|
||||||
|
proxy falls back to the <b>Host</b> header, and a port mapper uses the address
|
||||||
|
it was configured with. Taking the destination from the filter is what makes
|
||||||
|
the other protocols work, and what makes the address authoritative rather than
|
||||||
|
something the client claimed.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<b>tlspr</b> is the clearest case. Nothing reaches it at all unless traffic is
|
||||||
|
redirected to it, or the clients resolve the names to it through DNS. With a
|
||||||
|
redirection it gets the address as well as the name from the handshake, and
|
||||||
|
that is what lets access rules be written with host names: the name from the
|
||||||
|
handshake is matched, and the connection still goes to the address the client
|
||||||
|
was going to. Without the address it would have to resolve the name itself,
|
||||||
|
which is a second lookup and a second answer.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
A configuration for web and TLS traffic:
|
||||||
|
</p><pre>
|
||||||
|
log /var/log/3proxy.log D
|
||||||
|
auth iponly
|
||||||
|
allow *
|
||||||
|
|
||||||
|
# the destination comes from the redirection for the services below
|
||||||
|
transparent
|
||||||
|
|
||||||
|
# ordinary web traffic, redirected here from port 80
|
||||||
|
proxy -p3129 -e192.0.2.10
|
||||||
|
|
||||||
|
# TLS, redirected here from port 443: tlspr would otherwise have only the
|
||||||
|
# name in the handshake, and this gives it the address as well
|
||||||
|
tlspr -p3143 -e192.0.2.10
|
||||||
|
|
||||||
|
notransparent
|
||||||
|
</pre>
|
||||||
|
<p>
|
||||||
|
<b>-e</b> gives those services an address of their own to connect from. That
|
||||||
|
address is what the redirection rules exclude, and excluding it is what stops
|
||||||
|
the proxy's own connections from being redirected back into it. Without an
|
||||||
|
exclusion the connection 3proxy makes to the origin matches the same rule,
|
||||||
|
returns to 3proxy, and the traffic goes round until something gives out.
|
||||||
|
Running 3proxy as its own account and excluding that account works too, and is
|
||||||
|
the better choice when the machine has one address.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p><b>Linux, iptables</b>. For traffic the machine forwards for others:
|
||||||
|
</p><pre>
|
||||||
|
iptables -t nat -A PREROUTING -i eth0 -p tcp --dport 80 -j REDIRECT --to-ports 3129
|
||||||
|
iptables -t nat -A PREROUTING -i eth0 -p tcp --dport 443 -j REDIRECT --to-ports 3143
|
||||||
|
</pre>
|
||||||
|
<p>
|
||||||
|
Traffic the machine generates itself passes through OUTPUT instead, where the
|
||||||
|
proxy's own connections have to be excluded:
|
||||||
|
</p><pre>
|
||||||
|
# by the address the services connect from
|
||||||
|
iptables -t nat -A OUTPUT -p tcp --dport 80 ! -s 192.0.2.10 -j REDIRECT --to-ports 3129
|
||||||
|
|
||||||
|
# or by the account 3proxy runs as
|
||||||
|
iptables -t nat -A OUTPUT -p tcp --dport 80 -m owner ! --uid-owner proxy3 \
|
||||||
|
-j REDIRECT --to-ports 3129
|
||||||
|
</pre>
|
||||||
|
|
||||||
|
<p><b>Linux, nftables</b> (Debian 11+, Ubuntu 22.04+, RHEL 8+, Fedora, Arch,
|
||||||
|
where nftables is what iptables is a front end for):
|
||||||
|
</p><pre>
|
||||||
|
table ip proxy3 {
|
||||||
|
chain prerouting {
|
||||||
|
type nat hook prerouting priority dstnat; policy accept;
|
||||||
|
iif "eth0" tcp dport 80 redirect to :3129
|
||||||
|
iif "eth0" tcp dport 443 redirect to :3143
|
||||||
|
}
|
||||||
|
|
||||||
|
chain output {
|
||||||
|
type nat hook output priority dstnat; policy accept;
|
||||||
|
meta skuid != "proxy3" tcp dport 80 redirect to :3129
|
||||||
|
meta skuid != "proxy3" tcp dport 443 redirect to :3143
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</pre>
|
||||||
|
<p>
|
||||||
|
Load it with <b>nft -f</b>, and keep it across reboots in
|
||||||
|
<b>/etc/nftables.conf</b> (Debian, Ubuntu) or
|
||||||
|
<b>/etc/sysconfig/nftables.conf</b> (RHEL, Fedora). A table name cannot begin
|
||||||
|
with a digit, which is why the table above is not called 3proxy.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p><b>Linux, firewalld</b> (RHEL, CentOS Stream, Fedora, openSUSE). Redirect an
|
||||||
|
incoming port on a zone:
|
||||||
|
</p><pre>
|
||||||
|
firewall-cmd --permanent --zone=internal --add-forward-port=port=80:proto=tcp:toport=3129
|
||||||
|
firewall-cmd --permanent --zone=internal --add-forward-port=port=443:proto=tcp:toport=3143
|
||||||
|
firewall-cmd --reload
|
||||||
|
</pre>
|
||||||
|
<p>
|
||||||
|
firewalld has no exclusion for the proxy's own traffic in that form, so put
|
||||||
|
that part in a direct rule:
|
||||||
|
</p><pre>
|
||||||
|
firewall-cmd --permanent --direct --add-rule ipv4 nat OUTPUT 0 \
|
||||||
|
-p tcp --dport 80 -m owner ! --uid-owner proxy3 -j REDIRECT --to-ports 3129
|
||||||
|
firewall-cmd --reload
|
||||||
|
</pre>
|
||||||
|
|
||||||
|
<p><b>Linux, ufw</b> (Ubuntu, Debian). ufw has no command for redirection;
|
||||||
|
add the rules to <b>/etc/ufw/before.rules</b>, above the <b>*filter</b> block:
|
||||||
|
</p><pre>
|
||||||
|
*nat
|
||||||
|
:PREROUTING ACCEPT [0:0]
|
||||||
|
-A PREROUTING -i eth0 -p tcp --dport 80 -j REDIRECT --to-ports 3129
|
||||||
|
-A PREROUTING -i eth0 -p tcp --dport 443 -j REDIRECT --to-ports 3143
|
||||||
|
COMMIT
|
||||||
|
</pre>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
On Linux 3proxy asks the kernel where the connection was going. On the BSDs it
|
||||||
|
asks pf, which keeps the original destination in its state table, through
|
||||||
|
<b>/dev/pf</b> - so <b>rdr</b> rules work, and 3proxy has to be able to read
|
||||||
|
that device. Where a redirection leaves the destination on the socket instead,
|
||||||
|
that is used: OpenBSD <b>divert-to</b> and FreeBSD <b>ipfw fwd</b> both do.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
The mechanism is chosen automatically, and <b>transparent</b> takes an
|
||||||
|
argument for the installations that need to pin it: <b>auto</b> (the default),
|
||||||
|
<b>netfilter</b>, <b>pf</b>, or <b>socket</b> for reading it off the socket. A
|
||||||
|
mode the build has no code for is refused, so a configuration written for
|
||||||
|
another platform fails where it is wrong instead of quietly doing something
|
||||||
|
else.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p><b>FreeBSD, NetBSD and OpenBSD, pf</b>. Redirect in <b>/etc/pf.conf</b>,
|
||||||
|
excluding the address the proxy connects from:
|
||||||
|
</p><pre>
|
||||||
|
rdr pass on em0 inet proto tcp from ! 192.0.2.10 to any port 80 -> 127.0.0.1 port 3129
|
||||||
|
rdr pass on em0 inet proto tcp from ! 192.0.2.10 to any port 443 -> 127.0.0.1 port 3143
|
||||||
|
</pre>
|
||||||
|
<p>
|
||||||
|
Load with <b>pfctl -f /etc/pf.conf</b>. 3proxy looks the destination up in pf's
|
||||||
|
state table, so it needs to read <b>/dev/pf</b>: either run it as root, or give
|
||||||
|
its account access to that device.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p><b>OpenBSD, divert-to</b> is an alternative which leaves the destination on
|
||||||
|
the socket, and needs no access to <b>/dev/pf</b>:
|
||||||
|
</p><pre>
|
||||||
|
pass in on em0 inet proto tcp to any port 80 divert-to 127.0.0.1 port 3129
|
||||||
|
pass in on em0 inet proto tcp to any port 443 divert-to 127.0.0.1 port 3143
|
||||||
|
</pre>
|
||||||
|
|
||||||
|
<p><b>FreeBSD, ipfw</b>. <b>fwd</b> delivers the connection locally without
|
||||||
|
rewriting it, which also leaves the destination on the socket:
|
||||||
|
</p><pre>
|
||||||
|
ipfw add fwd 127.0.0.1,3129 tcp from any to any 80 in recv em0
|
||||||
|
ipfw add fwd 127.0.0.1,3143 tcp from any to any 443 in recv em0
|
||||||
|
</pre>
|
||||||
|
|
||||||
|
<p><b>macOS</b> has <b>/dev/pf</b> but ships no header for it, so a macOS build
|
||||||
|
has no pf lookup, and macOS has neither <b>divert-to</b> nor ipfw to leave the
|
||||||
|
address on the socket. Transparent proxying is not usable there, even though
|
||||||
|
the commands exist in a macOS build.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
Check the result by asking for a host through a redirected port and reading
|
||||||
|
the log: the request should appear with the address the client asked for,
|
||||||
|
which is what it would look like through a configured proxy.
|
||||||
|
</p>
|
||||||
|
|
||||||
<li><a name="PCRE"><i>How to use PCRE filtering (regular expressions)</i></a>
|
<li><a name="PCRE"><i>How to use PCRE filtering (regular expressions)</i></a>
|
||||||
<p>
|
<p>
|
||||||
Since version 0.9.7, PCRE (Perl Compatible Regular Expressions) filtering is built into
|
Since version 0.9.7, PCRE (Perl Compatible Regular Expressions) filtering is built into
|
||||||
|
|||||||
@ -37,6 +37,7 @@
|
|||||||
<li><a href="#SSLPLUGIN">Как настроить TLS/SSL (https прокси, mTLS)</a></li>
|
<li><a href="#SSLPLUGIN">Как настроить TLS/SSL (https прокси, mTLS)</a></li>
|
||||||
<li><a href="#CERTIFICATES">Как создать CA и сертификаты для SSL</a></li>
|
<li><a href="#CERTIFICATES">Как создать CA и сертификаты для SSL</a></li>
|
||||||
<li><a href="#PCRE">Как использовать PCRE-фильтрацию (регулярные выражения)</a></li>
|
<li><a href="#PCRE">Как использовать PCRE-фильтрацию (регулярные выражения)</a></li>
|
||||||
|
<li><a href="#TRANSPARENT">Как сделать транспарентный прокси</a></li>
|
||||||
<li><a href="#AUTH">Как ограничить доступ к службе</a>
|
<li><a href="#AUTH">Как ограничить доступ к службе</a>
|
||||||
<li><a href="#USERS">Как создать список пользователей</a>
|
<li><a href="#USERS">Как создать список пользователей</a>
|
||||||
<li><a href="#ACL">Как ограничить доступ пользователей к ресурсам</a>
|
<li><a href="#ACL">Как ограничить доступ пользователей к ресурсам</a>
|
||||||
@ -992,6 +993,176 @@ openssl verify -x509_strict -CAfile ca.crt server.crt
|
|||||||
openssl verify -x509_strict -CAfile ca.crt client.crt
|
openssl verify -x509_strict -CAfile ca.crt client.crt
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
|
|
||||||
|
<li><a name="TRANSPARENT"><i>Как сделать транспарентный прокси</i></a>
|
||||||
|
<p>
|
||||||
|
Транспарентный прокси обслуживает клиентов, которые не настроены на работу
|
||||||
|
через прокси. Пакетный фильтр перенаправляет их соединения на 3proxy, а команда
|
||||||
|
<b>transparent</b> указывает сервису брать адрес назначения у фильтра, а не из
|
||||||
|
запроса. Всё остальное работает как обычно: правила доступа, родительские
|
||||||
|
прокси, ограничения и логирование видят настоящий адрес назначения. Работает в
|
||||||
|
Linux и BSD. С версии 1.0.1 встроено в бинарник, раньше это был отдельный
|
||||||
|
TransparentPlugin, и строка <b>plugin</b> больше не нужна.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Команда даёт и адрес, и порт назначения, поэтому сервис обслуживает всё, что
|
||||||
|
на него перенаправлено, а не один порт с одним адресом назначения.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Без неё сервис берёт адрес откуда-то ещё: HTTP-прокси - из заголовка
|
||||||
|
<b>Host</b>, порт-маппер - из своей конфигурации. Именно получение адреса от
|
||||||
|
фильтра позволяет работать с остальными протоколами и делает адрес
|
||||||
|
достоверным, а не заявленным клиентом.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Нагляднее всего это с <b>tlspr</b>. Без перенаправления трафика (или без
|
||||||
|
резолва имён на него через DNS) на него вообще ничего не попадёт. С
|
||||||
|
перенаправлением он получает и имя из TLS handshake, и адрес назначения -
|
||||||
|
именно это позволяет писать правила доступа по именам хостов: имя из handshake
|
||||||
|
проверяется в ACL, а соединение идёт на тот адрес, куда шёл клиент. Без адреса
|
||||||
|
пришлось бы резолвить имя самостоятельно, то есть делать ещё один запрос и
|
||||||
|
получать ещё один ответ.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Конфигурация для веб- и TLS-трафика:
|
||||||
|
</p><pre>
|
||||||
|
log /var/log/3proxy.log D
|
||||||
|
auth iponly
|
||||||
|
allow *
|
||||||
|
|
||||||
|
# для сервисов ниже адрес назначения берётся из перенаправления
|
||||||
|
transparent
|
||||||
|
|
||||||
|
# обычный веб-трафик, перенаправленный сюда с порта 80
|
||||||
|
proxy -p3129 -e192.0.2.10
|
||||||
|
|
||||||
|
# TLS, перенаправленный сюда с порта 443: без этого у tlspr было бы только
|
||||||
|
# имя из handshake, а так есть и адрес
|
||||||
|
tlspr -p3143 -e192.0.2.10
|
||||||
|
|
||||||
|
notransparent
|
||||||
|
</pre>
|
||||||
|
<p>
|
||||||
|
<b>-e</b> задаёт сервисам собственный адрес для исходящих соединений. Именно
|
||||||
|
этот адрес исключается в правилах перенаправления, и это исключение не даёт
|
||||||
|
соединениям самого прокси попасть обратно в него. Без исключения соединение,
|
||||||
|
которое 3proxy устанавливает к серверу назначения, попадает под то же правило,
|
||||||
|
возвращается в 3proxy, и трафик зацикливается. Можно вместо этого запускать
|
||||||
|
3proxy под отдельной учётной записью и исключать её - так лучше, если у машины
|
||||||
|
один адрес.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p><b>Linux, iptables</b>. Для транзитного трафика:
|
||||||
|
</p><pre>
|
||||||
|
iptables -t nat -A PREROUTING -i eth0 -p tcp --dport 80 -j REDIRECT --to-ports 3129
|
||||||
|
iptables -t nat -A PREROUTING -i eth0 -p tcp --dport 443 -j REDIRECT --to-ports 3143
|
||||||
|
</pre>
|
||||||
|
<p>
|
||||||
|
Трафик самой машины проходит через цепочку OUTPUT, где соединения прокси нужно
|
||||||
|
исключить:
|
||||||
|
</p><pre>
|
||||||
|
# по адресу, с которого сервисы устанавливают соединения
|
||||||
|
iptables -t nat -A OUTPUT -p tcp --dport 80 ! -s 192.0.2.10 -j REDIRECT --to-ports 3129
|
||||||
|
|
||||||
|
# либо по учётной записи, под которой работает 3proxy
|
||||||
|
iptables -t nat -A OUTPUT -p tcp --dport 80 -m owner ! --uid-owner proxy3 \
|
||||||
|
-j REDIRECT --to-ports 3129
|
||||||
|
</pre>
|
||||||
|
|
||||||
|
<p><b>Linux, nftables</b> (Debian 11+, Ubuntu 22.04+, RHEL 8+, Fedora, Arch):
|
||||||
|
</p><pre>
|
||||||
|
table ip proxy3 {
|
||||||
|
chain prerouting {
|
||||||
|
type nat hook prerouting priority dstnat; policy accept;
|
||||||
|
iif "eth0" tcp dport 80 redirect to :3129
|
||||||
|
iif "eth0" tcp dport 443 redirect to :3143
|
||||||
|
}
|
||||||
|
|
||||||
|
chain output {
|
||||||
|
type nat hook output priority dstnat; policy accept;
|
||||||
|
meta skuid != "proxy3" tcp dport 80 redirect to :3129
|
||||||
|
meta skuid != "proxy3" tcp dport 443 redirect to :3143
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</pre>
|
||||||
|
<p>
|
||||||
|
Загружается через <b>nft -f</b>, сохраняется в <b>/etc/nftables.conf</b>
|
||||||
|
(Debian, Ubuntu) или <b>/etc/sysconfig/nftables.conf</b> (RHEL, Fedora). Имя
|
||||||
|
таблицы не может начинаться с цифры, поэтому таблица называется не 3proxy.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p><b>Linux, firewalld</b> (RHEL, CentOS Stream, Fedora, openSUSE):
|
||||||
|
</p><pre>
|
||||||
|
firewall-cmd --permanent --zone=internal --add-forward-port=port=80:proto=tcp:toport=3129
|
||||||
|
firewall-cmd --permanent --zone=internal --add-forward-port=port=443:proto=tcp:toport=3143
|
||||||
|
firewall-cmd --reload
|
||||||
|
</pre>
|
||||||
|
<p>
|
||||||
|
Исключение для трафика самого прокси в таком виде не задаётся, для него нужно
|
||||||
|
прямое правило:
|
||||||
|
</p><pre>
|
||||||
|
firewall-cmd --permanent --direct --add-rule ipv4 nat OUTPUT 0 \
|
||||||
|
-p tcp --dport 80 -m owner ! --uid-owner proxy3 -j REDIRECT --to-ports 3129
|
||||||
|
firewall-cmd --reload
|
||||||
|
</pre>
|
||||||
|
|
||||||
|
<p><b>Linux, ufw</b> (Ubuntu, Debian). В ufw нет команды для перенаправления,
|
||||||
|
правила добавляются в <b>/etc/ufw/before.rules</b> перед блоком <b>*filter</b>:
|
||||||
|
</p><pre>
|
||||||
|
*nat
|
||||||
|
:PREROUTING ACCEPT [0:0]
|
||||||
|
-A PREROUTING -i eth0 -p tcp --dport 80 -j REDIRECT --to-ports 3129
|
||||||
|
-A PREROUTING -i eth0 -p tcp --dport 443 -j REDIRECT --to-ports 3143
|
||||||
|
COMMIT
|
||||||
|
</pre>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
В Linux 3proxy спрашивает у ядра, куда шло соединение. В BSD он спрашивает у pf,
|
||||||
|
который хранит исходный адрес назначения в таблице состояний, через
|
||||||
|
<b>/dev/pf</b> - поэтому работают правила <b>rdr</b>, и 3proxy должен иметь
|
||||||
|
доступ к этому устройству. Если перенаправление оставляет адрес на самом сокете,
|
||||||
|
используется он: так делают OpenBSD <b>divert-to</b> и FreeBSD <b>ipfw fwd</b>.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Механизм выбирается автоматически, а команда <b>transparent</b> принимает
|
||||||
|
аргумент для случаев, когда его надо зафиксировать: <b>auto</b> (по умолчанию),
|
||||||
|
<b>netfilter</b>, <b>pf</b> или <b>socket</b> для чтения адреса с сокета. Режим,
|
||||||
|
которого нет в сборке, отвергается, поэтому конфигурация, написанная для другой
|
||||||
|
платформы, не запустится вместо того, чтобы молча делать что-то другое.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p><b>FreeBSD, NetBSD, OpenBSD, pf</b>. Перенаправление в <b>/etc/pf.conf</b> с
|
||||||
|
исключением адреса, с которого соединяется прокси:
|
||||||
|
</p><pre>
|
||||||
|
rdr pass on em0 inet proto tcp from ! 192.0.2.10 to any port 80 -> 127.0.0.1 port 3129
|
||||||
|
rdr pass on em0 inet proto tcp from ! 192.0.2.10 to any port 443 -> 127.0.0.1 port 3143
|
||||||
|
</pre>
|
||||||
|
<p>
|
||||||
|
Загружается через <b>pfctl -f /etc/pf.conf</b>. 3proxy ищет адрес назначения в
|
||||||
|
таблице состояний pf, поэтому ему нужен доступ на чтение к <b>/dev/pf</b>: либо
|
||||||
|
запуск от root, либо права на устройство для его учётной записи.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p><b>OpenBSD, divert-to</b> - альтернатива, оставляющая адрес на сокете, доступ
|
||||||
|
к <b>/dev/pf</b> при этом не нужен:
|
||||||
|
</p><pre>
|
||||||
|
pass in on em0 inet proto tcp to any port 80 divert-to 127.0.0.1 port 3129
|
||||||
|
pass in on em0 inet proto tcp to any port 443 divert-to 127.0.0.1 port 3143
|
||||||
|
</pre>
|
||||||
|
|
||||||
|
<p><b>FreeBSD, ipfw</b>. <b>fwd</b> доставляет соединение локально, не переписывая
|
||||||
|
его, и адрес тоже остаётся на сокете:
|
||||||
|
</p><pre>
|
||||||
|
ipfw add fwd 127.0.0.1,3129 tcp from any to any 80 in recv em0
|
||||||
|
ipfw add fwd 127.0.0.1,3143 tcp from any to any 443 in recv em0
|
||||||
|
</pre>
|
||||||
|
|
||||||
|
<p><b>macOS</b>: <b>/dev/pf</b> есть, но заголовочных файлов для него нет,
|
||||||
|
поэтому в сборке под macOS нет обращения к pf, а ни <b>divert-to</b>, ни ipfw в
|
||||||
|
macOS нет. Транспарентное проксирование там неприменимо, хотя команды в сборке
|
||||||
|
присутствуют.
|
||||||
|
</p>
|
||||||
|
|
||||||
<li><a name="PCRE"><i>Как использовать PCRE-фильтрацию (регулярные выражения)</i></a>
|
<li><a name="PCRE"><i>Как использовать PCRE-фильтрацию (регулярные выражения)</i></a>
|
||||||
<p>
|
<p>
|
||||||
Начиная с версии 0.9.7 фильтрация PCRE встроена в 3proxy при компиляции с поддержкой
|
Начиная с версии 0.9.7 фильтрация PCRE встроена в 3proxy при компиляции с поддержкой
|
||||||
|
|||||||
@ -1,31 +1,56 @@
|
|||||||
<h3>3proxy TransparentPlugin (Linux/BSD only)</h3>
|
<h3>3proxy transparent proxying (Linux/BSD only)</h3>
|
||||||
|
|
||||||
This plugin can turn 3proxy into a transparent proxy for virtually any TCP-based protocol
|
Transparent proxying is part of 3proxy itself since 1.0.1. It was the separate
|
||||||
and use all 3proxy features - redirections, parent proxies, ACLs, traffic limitations,
|
TransparentPlugin before that, and the <b>plugin</b> line that used to load it
|
||||||
etc. The TransparentPlugin takes the destination IP:port from Linux and uses this
|
is no longer needed: the <b>transparent</b> and <b>notransparent</b> commands
|
||||||
information as the target IP in the proxy. An example usage:
|
are always available on the platforms that can redirect a connection.
|
||||||
|
|
||||||
|
<p>
|
||||||
|
It turns 3proxy into a transparent proxy for virtually any TCP-based protocol,
|
||||||
|
with the rest of 3proxy applying as usual - redirections, parent proxies, ACLs,
|
||||||
|
traffic limitations and logging. The destination IP and port come from the
|
||||||
|
packet filter that redirected the connection, and are used as the target of the
|
||||||
|
proxied connection.
|
||||||
|
</p>
|
||||||
|
|
||||||
<pre>
|
<pre>
|
||||||
plugin /path/to/TransparentPlugin.ld.so transparent_plugin
|
|
||||||
log /path/to/log
|
log /path/to/log
|
||||||
auth iponly
|
auth iponly
|
||||||
allow * * * 80
|
allow * * * 80
|
||||||
parent 1000 http 0.0.0.0 0
|
parent 1000 http 0.0.0.0 0
|
||||||
allow *
|
allow *
|
||||||
parent 1000 socks5 SOCKS5_IP SOCKS5_PORT USER PASSWORD
|
parent 1000 socks5 SOCKS5_IP SOCKS5_PORT USER PASSWORD
|
||||||
|
|
||||||
transparent
|
transparent
|
||||||
tcppm -iLOCAL_IP 12345 127.0.0.1 11111
|
tcppm -eLOCAL_IP 12345 127.0.0.1 11111
|
||||||
notransparent
|
notransparent
|
||||||
proxy
|
proxy
|
||||||
</pre>
|
</pre>
|
||||||
Now, any TCP traffic transparently redirected to port 12345 will be routed via
|
|
||||||
the parent SOCKSv5 proxy and logged; all URLs for web requests are visible in logs.
|
|
||||||
The parameters '127.0.0.1 11111' in this case are not used and are overwritten by
|
|
||||||
the destination IP:port for each transparent connection.
|
|
||||||
|
|
||||||
<h4>Download:</h4>
|
<p>
|
||||||
<ul>
|
Any TCP traffic redirected to port 12345 is routed through the parent SOCKSv5
|
||||||
<li>Plugin is included in 3proxy 0.8
|
proxy and logged, with the URLs of web requests visible in the log. The
|
||||||
</li></ul>
|
'127.0.0.1 11111' arguments are not used in that case: they are replaced by the
|
||||||
|
destination the client was trying to reach.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
The destination is looked up in pf on the BSDs, through <b>/dev/pf</b>, and
|
||||||
|
asked of the kernel on Linux; a redirection that leaves the address on the
|
||||||
|
socket is used where there is one. <b>transparent</b> takes an optional
|
||||||
|
<b>auto</b>, <b>netfilter</b>, <b>pf</b> or <b>socket</b> to pin that choice.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
The redirection rules must not match the connections 3proxy itself makes, or
|
||||||
|
the traffic returns to the proxy and loops. Give the service an address to
|
||||||
|
connect from with <b>-e</b> and exclude it in the rules, or run 3proxy as its
|
||||||
|
own account and exclude that account.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
Redirection rules for iptables, nftables, firewalld, ufw and pf are in
|
||||||
|
<a href="../howtoe.html#TRANSPARENT">How to proxy transparently</a>, and the
|
||||||
|
commands are described in 3proxy.cfg(5).
|
||||||
|
</p>
|
||||||
|
|
||||||
© Vladimir Dubrovin, License: BSD style
|
© Vladimir Dubrovin, License: BSD style
|
||||||
|
|||||||
@ -1,33 +1,56 @@
|
|||||||
<h3>Плагин TransparentPlugin 3proxy (только для Linux/BSD)</h3>
|
<h3>Транспарентное проксирование 3proxy (только для Linux/BSD)</h3>
|
||||||
|
|
||||||
Плагин превращает 3proxy в транспарентный прокси для практически любых TCP-соединений
|
Начиная с 1.0.1 транспарентное проксирование встроено в 3proxy. Раньше это был
|
||||||
и позволяет прозрачно для клиентов использовать весь фунционал прокси - редиректоры,
|
отдельный TransparentPlugin, и строка <b>plugin</b>, которой он загружался,
|
||||||
родительские прокси, ACLи, ограничения трафика. TransparentPlugin получает IP:port
|
больше не нужна: команды <b>transparent</b> и <b>notransparent</b> доступны
|
||||||
назначения от Linux и использует эту информацию в качестве конечного адреса назначения.
|
всегда на тех платформах, где соединение можно перенаправить.
|
||||||
<br>
|
|
||||||
Пример использования:
|
<p>
|
||||||
|
3proxy становится транспарентным прокси практически для любых TCP-соединений,
|
||||||
|
причём весь остальной функционал работает как обычно - редиректоры,
|
||||||
|
родительские прокси, ACLи, ограничения трафика и логирование. IP и порт
|
||||||
|
назначения берутся у пакетного фильтра, перенаправившего соединение, и
|
||||||
|
используются как адрес назначения проксируемого соединения.
|
||||||
|
</p>
|
||||||
|
|
||||||
<pre>
|
<pre>
|
||||||
plugin /path/to/TransparentPlugin.ld.so transparent_plugin
|
|
||||||
log /path/to/log
|
log /path/to/log
|
||||||
auth iponly
|
auth iponly
|
||||||
allow * * * 80
|
allow * * * 80
|
||||||
parent 1000 http 0.0.0.0 0
|
parent 1000 http 0.0.0.0 0
|
||||||
allow *
|
allow *
|
||||||
parent 1000 socks5 SOCKS5_IP SOCKS5_PORT USER PASSWORD
|
parent 1000 socks5 SOCKS5_IP SOCKS5_PORT USER PASSWORD
|
||||||
|
|
||||||
transparent
|
transparent
|
||||||
tcppm -iLOCAL_IP 12345 127.0.0.1 11111
|
tcppm -eLOCAL_IP 12345 127.0.0.1 11111
|
||||||
notransparent
|
notransparent
|
||||||
proxy
|
proxy
|
||||||
</pre>
|
</pre>
|
||||||
Теперь любые TCP-соединения транспарентно перенаправленные в локальный порт 12345
|
|
||||||
будут прологгированы и перенаправлены в родительский SOCKSv5 proxy, при этом для
|
<p>
|
||||||
HTTP-запросов по порту TCP/80 будут видны параметры HTTP-запроса.
|
Любой TCP-трафик, перенаправленный на порт 12345, пойдёт через родительский
|
||||||
Параметры '127.0.0.1 11111' в данном случае не оказывают влияния, т.к.
|
SOCKSv5 прокси и будет залогирован, URL веб-запросов видны в логе. Аргументы
|
||||||
будут перезаписываться IP и портом назначения для каждого TCP-соединения соответственно.
|
'127.0.0.1 11111' в этом случае не используются: они заменяются адресом, к
|
||||||
<h4>Загрузить:</h4>
|
которому обращался клиент.
|
||||||
<ul>
|
</p>
|
||||||
<li>Плагин включен в дистрибутив 3proxy 0.8
|
|
||||||
</li></ul>
|
<p>
|
||||||
|
В BSD адрес назначения ищется в pf через <b>/dev/pf</b>, в Linux запрашивается у
|
||||||
|
ядра; если перенаправление оставляет адрес на сокете, используется он. Команда
|
||||||
|
<b>transparent</b> принимает необязательный аргумент <b>auto</b>,
|
||||||
|
<b>netfilter</b>, <b>pf</b> или <b>socket</b>, чтобы зафиксировать выбор.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Правила перенаправления не должны попадать на соединения, которые устанавливает
|
||||||
|
сам 3proxy, иначе трафик возвращается в прокси и зацикливается. Задайте сервису
|
||||||
|
адрес для исходящих соединений через <b>-e</b> и исключите его в правилах, либо
|
||||||
|
запускайте 3proxy под отдельной учётной записью и исключайте её.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
Правила перенаправления для iptables, nftables, firewalld, ufw и pf приведены в
|
||||||
|
<a href="../howtor.html#TRANSPARENT">описании транспарентного проксирования</a>,
|
||||||
|
команды описаны в 3proxy.cfg(5).
|
||||||
|
</p>
|
||||||
|
|
||||||
© Vladimir Dubrovin, License: BSD style
|
© Vladimir Dubrovin, License: BSD style
|
||||||
|
|||||||
@ -1192,6 +1192,54 @@ the format:
|
|||||||
Note: double quotes are required because the password contains a $ sign.
|
Note: double quotes are required because the password contains a $ sign.
|
||||||
|
|
||||||
.br
|
.br
|
||||||
|
.BR transparent
|
||||||
|
\fI[auto|netfilter|pf|socket]\fR
|
||||||
|
.br
|
||||||
|
Take the destination of a connection, both address and port, from the packet
|
||||||
|
filter that redirected it, instead of from the request. It applies to services declared after it, and
|
||||||
|
\fBnotransparent\fR turns it off again for the services after that. Built into the
|
||||||
|
binary since 1.0.1, and previously the separate TransparentPlugin.
|
||||||
|
.br
|
||||||
|
On Linux the kernel is asked, so \fBiptables\fR or \fBnftables\fR
|
||||||
|
redirection is enough. On the BSDs pf is asked through \fB/dev/pf\fR, which
|
||||||
|
3proxy must be able to read, so \fBrdr\fR rules work; a redirection that
|
||||||
|
leaves the destination on the socket is used where there is one, as
|
||||||
|
\fBdivert-to\fR on OpenBSD and \fBipfw fwd\fR on FreeBSD do. macOS ships no
|
||||||
|
header for pf and has neither of those, so the commands exist in a macOS build
|
||||||
|
but cannot be used.
|
||||||
|
.br
|
||||||
|
The mechanism is chosen automatically. The optional argument pins it for an
|
||||||
|
installation that has more than one: \fBauto\fR is the default,
|
||||||
|
\fBnetfilter\fR asks the Linux kernel, \fBpf\fR looks the connection up in
|
||||||
|
the packet filter, and \fBsocket\fR reads the address off the socket. A mode
|
||||||
|
the build has no code for is refused rather than ignored.
|
||||||
|
.br
|
||||||
|
A connection that reaches a \fBsocket\fR mode service without having been
|
||||||
|
redirected is refused: its destination is the address the service listens on,
|
||||||
|
and using that would send the service to itself.
|
||||||
|
.br
|
||||||
|
A redirected connection carries no destination of its own, so without this the
|
||||||
|
service uses whatever it would use otherwise: the \fBHost\fR header for an
|
||||||
|
HTTP request, or the address a port mapper was configured with. \fBtlspr\fR
|
||||||
|
receives nothing at all unless traffic is redirected to it or the clients
|
||||||
|
resolve names to it, and with a redirection it has the address as well as the
|
||||||
|
name from the handshake, which is what allows access rules to be written with
|
||||||
|
host names. With it, every service reaches the address the
|
||||||
|
client was trying to reach, and access rules, parents, limits and logging apply
|
||||||
|
to it as usual.
|
||||||
|
.br
|
||||||
|
The redirection rules must not match the connections the proxy itself makes to
|
||||||
|
those destinations, or the traffic returns to the proxy and loops. Give the
|
||||||
|
service an outgoing address with \fB-e\fR and exclude that address in the rules,
|
||||||
|
or run 3proxy as its own user and exclude that user. See the
|
||||||
|
.B TRANSPARENT PROXYING
|
||||||
|
section of the documentation for rules per platform.
|
||||||
|
.br
|
||||||
|
.BR notransparent
|
||||||
|
.br
|
||||||
|
Stop taking the destination from the packet filter for the services declared
|
||||||
|
after it.
|
||||||
|
|
||||||
.B flush
|
.B flush
|
||||||
.br
|
.br
|
||||||
empty the active access list. The access list must be flushed every time you create a
|
empty the active access list. The access list must be flushed every time you create a
|
||||||
|
|||||||
@ -13,6 +13,9 @@ void ssl_install(void);
|
|||||||
#ifdef WITH_PCRE
|
#ifdef WITH_PCRE
|
||||||
void pcre_install(void);
|
void pcre_install(void);
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef WITH_TRANSPARENT
|
||||||
|
void transparent_install(void);
|
||||||
|
#endif
|
||||||
#ifndef _WIN32
|
#ifndef _WIN32
|
||||||
#include <sys/resource.h>
|
#include <sys/resource.h>
|
||||||
#ifndef NOPLUGINS
|
#ifndef NOPLUGINS
|
||||||
@ -529,6 +532,9 @@ int WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpCmdLine, int
|
|||||||
#ifdef WITH_PCRE
|
#ifdef WITH_PCRE
|
||||||
pcre_install();
|
pcre_install();
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef WITH_TRANSPARENT
|
||||||
|
transparent_install();
|
||||||
|
#endif
|
||||||
|
|
||||||
freeconf(&conf);
|
freeconf(&conf);
|
||||||
initcommands();
|
initcommands();
|
||||||
|
|||||||
@ -116,6 +116,9 @@ srvsocks$(OBJSUFFICS): socks.c proxy.h structures.h
|
|||||||
srvwebadmin$(OBJSUFFICS): webadmin.c proxy.h structures.h
|
srvwebadmin$(OBJSUFFICS): webadmin.c proxy.h structures.h
|
||||||
$(CC) $(COUT)srvwebadmin$(OBJSUFFICS) $(CFLAGS) webadmin.c
|
$(CC) $(COUT)srvwebadmin$(OBJSUFFICS) $(CFLAGS) webadmin.c
|
||||||
|
|
||||||
|
transparent$(OBJSUFFICS): transparent.c proxy.h structures.h
|
||||||
|
$(CC) $(COUT)transparent$(OBJSUFFICS) $(CFLAGS) transparent.c
|
||||||
|
|
||||||
srvhttpsrv$(OBJSUFFICS): httpsrv.c proxy.h structures.h
|
srvhttpsrv$(OBJSUFFICS): httpsrv.c proxy.h structures.h
|
||||||
$(CC) $(COUT)srvhttpsrv$(OBJSUFFICS) $(CFLAGS) httpsrv.c
|
$(CC) $(COUT)srvhttpsrv$(OBJSUFFICS) $(CFLAGS) httpsrv.c
|
||||||
|
|
||||||
@ -191,6 +194,6 @@ ssl$(OBJSUFFICS): ssl.c structures.h proxy.h ssl.h
|
|||||||
pcre$(OBJSUFFICS): pcre.c structures.h
|
pcre$(OBJSUFFICS): pcre.c structures.h
|
||||||
$(CC) $(COUT)pcre$(OBJSUFFICS) $(CFLAGS) $(DEFINEOPTION)WITH_PCRE pcre.c
|
$(CC) $(COUT)pcre$(OBJSUFFICS) $(CFLAGS) $(DEFINEOPTION)WITH_PCRE pcre.c
|
||||||
|
|
||||||
$(BUILDDIR)3proxy$(EXESUFFICS): 3proxy$(OBJSUFFICS) mainfunc$(OBJSUFFICS) srvproxy$(OBJSUFFICS) srvpop3p$(OBJSUFFICS) srvimapp$(OBJSUFFICS) srvsmtpp$(OBJSUFFICS) srvftppr$(OBJSUFFICS) srvsocks$(OBJSUFFICS) srvtcppm$(OBJSUFFICS) srvtlspr$(OBJSUFFICS) srvauto$(OBJSUFFICS) srvudppm$(OBJSUFFICS) sockmap$(OBJSUFFICS) udpsockmap$(OBJSUFFICS) sockgetchar$(OBJSUFFICS) common$(OBJSUFFICS) auth$(OBJSUFFICS) acl$(OBJSUFFICS) limiter$(OBJSUFFICS) redirect$(OBJSUFFICS) authradius$(OBJSUFFICS) hash$(OBJSUFFICS) hashtables$(OBJSUFFICS) resolve$(OBJSUFFICS) sql$(OBJSUFFICS) conf$(OBJSUFFICS) log$(OBJSUFFICS) datatypes$(OBJSUFFICS) md4$(OBJSUFFICS) md5$(OBJSUFFICS) blake2$(OBJSUFFICS) 3proxy_crypt$(OBJSUFFICS) base64$(OBJSUFFICS) ftp$(OBJSUFFICS) stringtable$(OBJSUFFICS) $(HTTPSRV_OBJS) srvdnspr$(OBJSUFFICS) plugins$(OBJSUFFICS) mdhash$(OBJSUFFICS) $(SSL_OBJS) $(PCRE_OBJS) $(COMPATLIBS) $(VERSIONDEP)
|
$(BUILDDIR)3proxy$(EXESUFFICS): 3proxy$(OBJSUFFICS) mainfunc$(OBJSUFFICS) srvproxy$(OBJSUFFICS) srvpop3p$(OBJSUFFICS) srvimapp$(OBJSUFFICS) srvsmtpp$(OBJSUFFICS) srvftppr$(OBJSUFFICS) srvsocks$(OBJSUFFICS) srvtcppm$(OBJSUFFICS) srvtlspr$(OBJSUFFICS) srvauto$(OBJSUFFICS) srvudppm$(OBJSUFFICS) sockmap$(OBJSUFFICS) udpsockmap$(OBJSUFFICS) sockgetchar$(OBJSUFFICS) common$(OBJSUFFICS) auth$(OBJSUFFICS) acl$(OBJSUFFICS) limiter$(OBJSUFFICS) redirect$(OBJSUFFICS) authradius$(OBJSUFFICS) hash$(OBJSUFFICS) hashtables$(OBJSUFFICS) resolve$(OBJSUFFICS) sql$(OBJSUFFICS) conf$(OBJSUFFICS) log$(OBJSUFFICS) datatypes$(OBJSUFFICS) md4$(OBJSUFFICS) md5$(OBJSUFFICS) blake2$(OBJSUFFICS) 3proxy_crypt$(OBJSUFFICS) base64$(OBJSUFFICS) ftp$(OBJSUFFICS) stringtable$(OBJSUFFICS) $(HTTPSRV_OBJS) srvdnspr$(OBJSUFFICS) plugins$(OBJSUFFICS) mdhash$(OBJSUFFICS) $(SSL_OBJS) $(PCRE_OBJS) $(TRANSPARENT_OBJS) $(COMPATLIBS) $(VERSIONDEP)
|
||||||
$(LN) $(LNOUT)$(BUILDDIR)3proxy$(EXESUFFICS) $(LDFLAGS) $(VERFILE) 3proxy$(OBJSUFFICS) mainfunc$(OBJSUFFICS) auth$(OBJSUFFICS) acl$(OBJSUFFICS) limiter$(OBJSUFFICS) redirect$(OBJSUFFICS) authradius$(OBJSUFFICS) hash$(OBJSUFFICS) hashtables$(OBJSUFFICS) resolve$(OBJSUFFICS) sql$(OBJSUFFICS) conf$(OBJSUFFICS) datatypes$(OBJSUFFICS) srvauto$(OBJSUFFICS) srvproxy$(OBJSUFFICS) srvpop3p$(OBJSUFFICS) srvimapp$(OBJSUFFICS) srvsmtpp$(OBJSUFFICS) srvftppr$(OBJSUFFICS) srvsocks$(OBJSUFFICS) srvtcppm$(OBJSUFFICS) srvtlspr$(OBJSUFFICS) srvudppm$(OBJSUFFICS) sockmap$(OBJSUFFICS) udpsockmap$(OBJSUFFICS) sockgetchar$(OBJSUFFICS) common$(OBJSUFFICS) log$(OBJSUFFICS) 3proxy_crypt$(OBJSUFFICS) md4$(OBJSUFFICS) md5$(OBJSUFFICS) blake2$(OBJSUFFICS) base64$(OBJSUFFICS) ftp$(OBJSUFFICS) stringtable$(OBJSUFFICS) $(HTTPSRV_OBJS) srvdnspr$(OBJSUFFICS) plugins$(OBJSUFFICS) mdhash$(OBJSUFFICS) $(SSL_OBJS) $(PCRE_OBJS) $(COMPATLIBS) $(LIBS) $(PCRE_LIBS)
|
$(LN) $(LNOUT)$(BUILDDIR)3proxy$(EXESUFFICS) $(LDFLAGS) $(VERFILE) 3proxy$(OBJSUFFICS) mainfunc$(OBJSUFFICS) auth$(OBJSUFFICS) acl$(OBJSUFFICS) limiter$(OBJSUFFICS) redirect$(OBJSUFFICS) authradius$(OBJSUFFICS) hash$(OBJSUFFICS) hashtables$(OBJSUFFICS) resolve$(OBJSUFFICS) sql$(OBJSUFFICS) conf$(OBJSUFFICS) datatypes$(OBJSUFFICS) srvauto$(OBJSUFFICS) srvproxy$(OBJSUFFICS) srvpop3p$(OBJSUFFICS) srvimapp$(OBJSUFFICS) srvsmtpp$(OBJSUFFICS) srvftppr$(OBJSUFFICS) srvsocks$(OBJSUFFICS) srvtcppm$(OBJSUFFICS) srvtlspr$(OBJSUFFICS) srvudppm$(OBJSUFFICS) sockmap$(OBJSUFFICS) udpsockmap$(OBJSUFFICS) sockgetchar$(OBJSUFFICS) common$(OBJSUFFICS) log$(OBJSUFFICS) 3proxy_crypt$(OBJSUFFICS) md4$(OBJSUFFICS) md5$(OBJSUFFICS) blake2$(OBJSUFFICS) base64$(OBJSUFFICS) ftp$(OBJSUFFICS) stringtable$(OBJSUFFICS) $(HTTPSRV_OBJS) srvdnspr$(OBJSUFFICS) plugins$(OBJSUFFICS) mdhash$(OBJSUFFICS) $(SSL_OBJS) $(PCRE_OBJS) $(TRANSPARENT_OBJS) $(COMPATLIBS) $(LIBS) $(PCRE_LIBS)
|
||||||
|
|
||||||
|
|||||||
14
src/conf.c
14
src/conf.c
@ -18,6 +18,9 @@ void ssl_install(void);
|
|||||||
#ifdef WITH_PCRE
|
#ifdef WITH_PCRE
|
||||||
void pcre_install(void);
|
void pcre_install(void);
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef WITH_TRANSPARENT
|
||||||
|
void transparent_install(void);
|
||||||
|
#endif
|
||||||
#ifndef _WIN32
|
#ifndef _WIN32
|
||||||
#include <sys/resource.h>
|
#include <sys/resource.h>
|
||||||
#include <pwd.h>
|
#include <pwd.h>
|
||||||
@ -1793,6 +1796,10 @@ int h_server_verify(int argc, unsigned char **argv);
|
|||||||
int h_no_server_verify(int argc, unsigned char **argv);
|
int h_no_server_verify(int argc, unsigned char **argv);
|
||||||
int h_client_mode(int argc, unsigned char **argv);
|
int h_client_mode(int argc, unsigned char **argv);
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef WITH_TRANSPARENT
|
||||||
|
int h_transparent(int argc, unsigned char **argv);
|
||||||
|
int h_notransparent(int argc, unsigned char **argv);
|
||||||
|
#endif
|
||||||
#ifdef WITH_PCRE
|
#ifdef WITH_PCRE
|
||||||
int h_pcre(int argc, unsigned char **argv);
|
int h_pcre(int argc, unsigned char **argv);
|
||||||
int h_pcre_rewrite(int argc, unsigned char **argv);
|
int h_pcre_rewrite(int argc, unsigned char **argv);
|
||||||
@ -1922,6 +1929,10 @@ struct commands commandhandlers[]={
|
|||||||
{NULL, "ssl_client_mode", h_client_mode, 1, 2},
|
{NULL, "ssl_client_mode", h_client_mode, 1, 2},
|
||||||
{NULL, "ssl_certcache", h_certcache, 2, 2},
|
{NULL, "ssl_certcache", h_certcache, 2, 2},
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef WITH_TRANSPARENT
|
||||||
|
{NULL, "transparent", h_transparent, 1, 2},
|
||||||
|
{NULL, "notransparent", h_notransparent, 1, 1},
|
||||||
|
#endif
|
||||||
#ifdef WITH_PCRE
|
#ifdef WITH_PCRE
|
||||||
{NULL, "pcre", h_pcre, 4, 0},
|
{NULL, "pcre", h_pcre, 4, 0},
|
||||||
{NULL, "pcre_rewrite", h_pcre_rewrite, 5, 0},
|
{NULL, "pcre_rewrite", h_pcre_rewrite, 5, 0},
|
||||||
@ -2212,6 +2223,9 @@ int reload (void){
|
|||||||
#endif
|
#endif
|
||||||
#ifdef WITH_PCRE
|
#ifdef WITH_PCRE
|
||||||
pcre_install();
|
pcre_install();
|
||||||
|
#endif
|
||||||
|
#ifdef WITH_TRANSPARENT
|
||||||
|
transparent_install();
|
||||||
#endif
|
#endif
|
||||||
conf.paused++;
|
conf.paused++;
|
||||||
freeconf(&conf);
|
freeconf(&conf);
|
||||||
|
|||||||
@ -1,6 +0,0 @@
|
|||||||
# TransparentPlugin
|
|
||||||
# Works on Linux (with netfilter), BSD and macOS (without netfilter support)
|
|
||||||
|
|
||||||
add_3proxy_plugin(TransparentPlugin
|
|
||||||
SOURCES transparent_plugin.c
|
|
||||||
)
|
|
||||||
@ -1 +0,0 @@
|
|||||||
include Makefile.var
|
|
||||||
@ -1,10 +0,0 @@
|
|||||||
all: $(BUILDDIR)TransparentPlugin$(DLSUFFICS)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
transparent_plugin$(OBJSUFFICS): transparent_plugin.c
|
|
||||||
$(CC) $(CFLAGS) $(DCFLAGS) transparent_plugin.c
|
|
||||||
|
|
||||||
|
|
||||||
$(BUILDDIR)TransparentPlugin$(DLSUFFICS): transparent_plugin$(OBJSUFFICS)
|
|
||||||
$(LN) $(LNOUT)../../$(BUILDDIR)TransparentPlugin$(DLSUFFICS) $(LDFLAGS) $(DLFLAGS) transparent_plugin$(OBJSUFFICS)
|
|
||||||
@ -1,128 +0,0 @@
|
|||||||
/*
|
|
||||||
3APA3A simplest proxy server
|
|
||||||
(c) 2002-2026 by Vladimir Dubrovin <vlad@3proxy.org>
|
|
||||||
|
|
||||||
please read License Agreement
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
#ifdef WITH_NETFILTER
|
|
||||||
#include <sys/utsname.h>
|
|
||||||
#endif
|
|
||||||
#include "../../structures.h"
|
|
||||||
#include "../../proxy.h"
|
|
||||||
#ifdef WITH_NETFILTER
|
|
||||||
#include <sys/types.h>
|
|
||||||
#include <sys/socket.h>
|
|
||||||
#include <limits.h>
|
|
||||||
#include <linux/netfilter_ipv4.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
extern "C" {
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
static struct pluginlink * pl;
|
|
||||||
|
|
||||||
static int transparent_loaded = 0;
|
|
||||||
|
|
||||||
static void* transparent_filter_open(void * idata, struct srvparam * param){
|
|
||||||
return idata;
|
|
||||||
}
|
|
||||||
|
|
||||||
static FILTER_ACTION transparent_filter_client(void *fo, struct clientparam * param, void** fc){
|
|
||||||
|
|
||||||
char addrbuf[64];
|
|
||||||
|
|
||||||
#ifdef WITH_NETFILTER
|
|
||||||
socklen_t len;
|
|
||||||
|
|
||||||
len = sizeof(param->req);
|
|
||||||
#ifdef SO_ORIGINAL_DST
|
|
||||||
|
|
||||||
if(getsockopt(param->clisock,
|
|
||||||
#ifndef NOIPV6
|
|
||||||
#ifdef SOL_IPV6
|
|
||||||
*SAFAMILY(¶m->sincr) == AF_INET6?SOL_IPV6:
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
SOL_IP, SO_ORIGINAL_DST,(struct sockaddr *) ¶m->req, &len) || !memcmp((char *)SAADDR(¶m->req), "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0", SAADDRLEN(¶m->req))){
|
|
||||||
return PASS;
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
#error No SO_ORIGINAL_DST defined
|
|
||||||
param->srv->logfunc(param, (unsigned char *)"transparent_plugin: No SO_ORIGINAL_DST defined");
|
|
||||||
return REJECT;
|
|
||||||
#endif
|
|
||||||
#else
|
|
||||||
if(*SAFAMILY(¶m->sincl) == AF_INET || *SAFAMILY(¶m->sincl) == AF_INET6){
|
|
||||||
param->req = param->sincl;
|
|
||||||
param->sincl = param->srv->intsa;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
pl->myinet_ntop(*SAFAMILY(¶m->req), SAADDR(¶m->req), (char *)addrbuf, sizeof(addrbuf));
|
|
||||||
if(param->hostname) pl->freefunc(param->hostname);
|
|
||||||
param->hostname = (unsigned char *)pl->strdupfunc(addrbuf);
|
|
||||||
param->sinsr = param->req;
|
|
||||||
return PASS;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static void transparent_filter_clear(void *fo){
|
|
||||||
}
|
|
||||||
|
|
||||||
static void transparent_filter_close(void *fo){
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct filter transparent_filter = {
|
|
||||||
NULL,
|
|
||||||
"Transparent filter",
|
|
||||||
"Transparent filter",
|
|
||||||
transparent_filter_open,
|
|
||||||
transparent_filter_client,
|
|
||||||
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
|
|
||||||
transparent_filter_clear,
|
|
||||||
transparent_filter_close
|
|
||||||
};
|
|
||||||
|
|
||||||
static int h_transparent(int argc, unsigned char **argv){
|
|
||||||
transparent_filter.filter_open = transparent_filter_open;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int h_notransparent(int argc, unsigned char **argv){
|
|
||||||
transparent_filter.filter_open = NULL;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct commands transparent_commandhandlers[] = {
|
|
||||||
{transparent_commandhandlers+1, "transparent", h_transparent, 1, 1},
|
|
||||||
{NULL, "notransparent", h_notransparent, 1, 1}
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
#ifdef WATCOM
|
|
||||||
#pragma aux transparent_plugin "*" parm caller [ ] value struct float struct routine [eax] modify [eax ecx edx]
|
|
||||||
#undef PLUGINCALL
|
|
||||||
#define PLUGINCALL
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
PLUGINAPI int PLUGINCALL transparent_plugin (struct pluginlink * pluginlink,
|
|
||||||
int argc, char** argv){
|
|
||||||
pl = pluginlink;
|
|
||||||
if(!transparent_loaded){
|
|
||||||
transparent_loaded = 1;
|
|
||||||
transparent_filter.next = pl->conf->filters;
|
|
||||||
pl->conf->filters = &transparent_filter;
|
|
||||||
transparent_commandhandlers[1].next = pl->commandhandlers->next;
|
|
||||||
pl->commandhandlers->next = transparent_commandhandlers;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
}
|
|
||||||
#ifdef __cplusplus
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
240
src/transparent.c
Normal file
240
src/transparent.c
Normal file
@ -0,0 +1,240 @@
|
|||||||
|
/*
|
||||||
|
3APA3A simplest proxy server
|
||||||
|
(c) 2002-2026 by Vladimir Dubrovin <vlad@3proxy.org>
|
||||||
|
|
||||||
|
please read License Agreement
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "structures.h"
|
||||||
|
#include "proxy.h"
|
||||||
|
|
||||||
|
#ifdef WITH_TRANSPARENT
|
||||||
|
|
||||||
|
#ifdef WITH_NETFILTER
|
||||||
|
#include <sys/utsname.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <limits.h>
|
||||||
|
#include <linux/netfilter_ipv4.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef WITH_PF
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <sys/ioctl.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <net/if.h>
|
||||||
|
#include <net/pfvar.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Where the address the client was trying to reach is read from.
|
||||||
|
|
||||||
|
AUTO uses what the platform offers, which is the only thing an
|
||||||
|
installation usually needs. The rest name one mechanism, for a machine
|
||||||
|
that has more than one and redirects with a particular one.
|
||||||
|
*/
|
||||||
|
#define TRANSPARENT_AUTO 0
|
||||||
|
#define TRANSPARENT_NETFILTER 1
|
||||||
|
#define TRANSPARENT_PF 2
|
||||||
|
#define TRANSPARENT_SOCKET 3
|
||||||
|
|
||||||
|
static struct pluginlink * pl;
|
||||||
|
|
||||||
|
static int transparent_loaded = 0;
|
||||||
|
static int transparent_mode = TRANSPARENT_AUTO;
|
||||||
|
|
||||||
|
#ifdef WITH_PF
|
||||||
|
static int pf_device = -1;
|
||||||
|
|
||||||
|
/* Ask the packet filter what the connection was addressed to before it was
|
||||||
|
redirected. pf keeps that in its state table rather than on the socket,
|
||||||
|
so it has to be looked up with the addresses of both ends.
|
||||||
|
*/
|
||||||
|
static int transparent_pf(struct clientparam *param)
|
||||||
|
{
|
||||||
|
struct pfioc_natlook nl;
|
||||||
|
|
||||||
|
if(pf_device < 0){
|
||||||
|
pf_device = open("/dev/pf", O_RDONLY);
|
||||||
|
if(pf_device < 0) return 1;
|
||||||
|
}
|
||||||
|
memset(&nl, 0, sizeof(nl));
|
||||||
|
nl.proto = IPPROTO_TCP;
|
||||||
|
nl.direction = PF_OUT;
|
||||||
|
#ifndef NOIPV6
|
||||||
|
if(*SAFAMILY(¶m->sincr) == AF_INET6){
|
||||||
|
nl.af = AF_INET6;
|
||||||
|
memcpy(&nl.saddr.v6, SAADDR(¶m->sincr), 16);
|
||||||
|
memcpy(&nl.daddr.v6, SAADDR(¶m->sincl), 16);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
nl.af = AF_INET;
|
||||||
|
memcpy(&nl.saddr.v4, SAADDR(¶m->sincr), 4);
|
||||||
|
memcpy(&nl.daddr.v4, SAADDR(¶m->sincl), 4);
|
||||||
|
}
|
||||||
|
nl.sport = *SAPORT(¶m->sincr);
|
||||||
|
nl.dport = *SAPORT(¶m->sincl);
|
||||||
|
|
||||||
|
if(ioctl(pf_device, DIOCNATLOOK, &nl)) return 1;
|
||||||
|
|
||||||
|
memset(¶m->req, 0, sizeof(param->req));
|
||||||
|
*SAFAMILY(¶m->req) = nl.af;
|
||||||
|
#ifndef NOIPV6
|
||||||
|
if(nl.af == AF_INET6) memcpy(SAADDR(¶m->req), &nl.rdaddr.v6, 16);
|
||||||
|
else
|
||||||
|
#endif
|
||||||
|
memcpy(SAADDR(¶m->req), &nl.rdaddr.v4, 4);
|
||||||
|
*SAPORT(¶m->req) = nl.rdport;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef WITH_NETFILTER
|
||||||
|
/* Linux keeps the original address for the connection it redirected. */
|
||||||
|
static int transparent_netfilter(struct clientparam *param)
|
||||||
|
{
|
||||||
|
socklen_t len = sizeof(param->req);
|
||||||
|
|
||||||
|
#ifdef SO_ORIGINAL_DST
|
||||||
|
if(getsockopt(param->clisock,
|
||||||
|
#ifndef NOIPV6
|
||||||
|
#ifdef SOL_IPV6
|
||||||
|
*SAFAMILY(¶m->sincr) == AF_INET6?SOL_IPV6:
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
SOL_IP, SO_ORIGINAL_DST, (struct sockaddr *) ¶m->req, &len)
|
||||||
|
|| !memcmp((char *)SAADDR(¶m->req), "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0", SAADDRLEN(¶m->req))){
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
#else
|
||||||
|
#error No SO_ORIGINAL_DST defined
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Some redirections leave the original address on the socket itself, so the
|
||||||
|
local address of the accepted connection is what the client asked for.
|
||||||
|
|
||||||
|
A connection which was not redirected at all arrives at the address the
|
||||||
|
service listens on, and taking that as the destination would send the
|
||||||
|
service to itself. Refuse instead of making the connection.
|
||||||
|
*/
|
||||||
|
static int transparent_socket(struct clientparam *param)
|
||||||
|
{
|
||||||
|
if(*SAFAMILY(¶m->sincl) != AF_INET && *SAFAMILY(¶m->sincl) != AF_INET6)
|
||||||
|
return 1;
|
||||||
|
if(*SAPORT(¶m->sincl) == *SAPORT(¶m->srv->intsa)
|
||||||
|
&& (SAISNULL(¶m->srv->intsa)
|
||||||
|
|| !memcmp(SAADDR(¶m->sincl), SAADDR(¶m->srv->intsa), SAADDRLEN(¶m->sincl))))
|
||||||
|
return 2;
|
||||||
|
param->req = param->sincl;
|
||||||
|
param->sincl = param->srv->intsa;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void* transparent_filter_open(void * idata, struct srvparam * param){
|
||||||
|
return idata;
|
||||||
|
}
|
||||||
|
|
||||||
|
static FILTER_ACTION transparent_filter_client(void *fo, struct clientparam * param, void** fc){
|
||||||
|
|
||||||
|
char addrbuf[64];
|
||||||
|
int res = 1;
|
||||||
|
|
||||||
|
#ifdef WITH_NETFILTER
|
||||||
|
if(transparent_mode == TRANSPARENT_AUTO || transparent_mode == TRANSPARENT_NETFILTER)
|
||||||
|
res = transparent_netfilter(param);
|
||||||
|
#endif
|
||||||
|
#ifdef WITH_PF
|
||||||
|
if(res && (transparent_mode == TRANSPARENT_AUTO || transparent_mode == TRANSPARENT_PF))
|
||||||
|
res = transparent_pf(param);
|
||||||
|
#endif
|
||||||
|
if(res && (transparent_mode == TRANSPARENT_AUTO || transparent_mode == TRANSPARENT_SOCKET)){
|
||||||
|
res = transparent_socket(param);
|
||||||
|
if(res == 2){
|
||||||
|
param->srv->logfunc(param, (unsigned char *)"transparent: connection was not redirected");
|
||||||
|
return REJECT;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* Nothing knows where this was going: leave the request alone, so the
|
||||||
|
service decides as it would without the command. */
|
||||||
|
if(res) return PASS;
|
||||||
|
|
||||||
|
pl->myinet_ntop(*SAFAMILY(¶m->req), SAADDR(¶m->req), (char *)addrbuf, sizeof(addrbuf));
|
||||||
|
if(param->hostname) pl->freefunc(param->hostname);
|
||||||
|
param->hostname = (unsigned char *)pl->strdupfunc(addrbuf);
|
||||||
|
param->sinsr = param->req;
|
||||||
|
return PASS;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void transparent_filter_clear(void *fo){
|
||||||
|
}
|
||||||
|
|
||||||
|
static void transparent_filter_close(void *fo){
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct filter transparent_filter = {
|
||||||
|
NULL,
|
||||||
|
"Transparent filter",
|
||||||
|
"Transparent filter",
|
||||||
|
transparent_filter_open,
|
||||||
|
transparent_filter_client,
|
||||||
|
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
|
||||||
|
transparent_filter_clear,
|
||||||
|
transparent_filter_close
|
||||||
|
};
|
||||||
|
|
||||||
|
int h_transparent(int argc, unsigned char **argv){
|
||||||
|
transparent_mode = TRANSPARENT_AUTO;
|
||||||
|
if(argc > 1){
|
||||||
|
if(!strcmp((char *)argv[1], "auto")) transparent_mode = TRANSPARENT_AUTO;
|
||||||
|
else if(!strcmp((char *)argv[1], "netfilter")){
|
||||||
|
#ifndef WITH_NETFILTER
|
||||||
|
fprintf(stderr, "transparent: netfilter is not available in this build\n");
|
||||||
|
return 1;
|
||||||
|
#else
|
||||||
|
transparent_mode = TRANSPARENT_NETFILTER;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
else if(!strcmp((char *)argv[1], "pf")){
|
||||||
|
#ifndef WITH_PF
|
||||||
|
fprintf(stderr, "transparent: pf is not available in this build\n");
|
||||||
|
return 1;
|
||||||
|
#else
|
||||||
|
transparent_mode = TRANSPARENT_PF;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
else if(!strcmp((char *)argv[1], "socket")) transparent_mode = TRANSPARENT_SOCKET;
|
||||||
|
else {
|
||||||
|
fprintf(stderr, "transparent: unknown mode %s, expected auto, netfilter, pf or socket\n", argv[1]);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
transparent_filter.filter_open = transparent_filter_open;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int h_notransparent(int argc, unsigned char **argv){
|
||||||
|
transparent_filter.filter_open = NULL;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void transparent_install(void){
|
||||||
|
pl = &pluginlink;
|
||||||
|
/* A reload runs this again: the filter is a single static entry, so it
|
||||||
|
is only linked in once, and the commands decide whether it acts. */
|
||||||
|
if(!transparent_loaded){
|
||||||
|
transparent_loaded = 1;
|
||||||
|
transparent_filter.next = pl->conf->filters;
|
||||||
|
pl->conf->filters = &transparent_filter;
|
||||||
|
}
|
||||||
|
transparent_filter.filter_open = NULL;
|
||||||
|
transparent_mode = TRANSPARENT_AUTO;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
155
tests/cases/transparent.py
Normal file
155
tests/cases/transparent.py
Normal file
@ -0,0 +1,155 @@
|
|||||||
|
"""Transparent proxying: the destination comes from the redirection.
|
||||||
|
|
||||||
|
A redirected connection no longer says where it was going, so the proxy has
|
||||||
|
to ask the packet filter. That means a real redirection rule, which needs
|
||||||
|
privilege, so the case skips unless it can install one and remove it again.
|
||||||
|
|
||||||
|
The rule must not catch the proxy's own connection to the origin, or the
|
||||||
|
traffic goes round for ever. Here the proxy is given an outgoing address of
|
||||||
|
its own and the rule excludes it, which is the arrangement the documentation
|
||||||
|
recommends.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import os
|
||||||
|
import platform
|
||||||
|
import shutil
|
||||||
|
import subprocess
|
||||||
|
|
||||||
|
ORIGIN_ADDR = "127.0.0.9" # where the client believes it is going
|
||||||
|
PROXY_ADDR = "127.0.0.8" # the source the proxy connects from
|
||||||
|
DECOY_ADDR = "127.0.0.7" # a second server, to show where traffic went
|
||||||
|
|
||||||
|
|
||||||
|
def _run(command):
|
||||||
|
done = subprocess.run(command, stdout=subprocess.PIPE,
|
||||||
|
stderr=subprocess.STDOUT, timeout=30)
|
||||||
|
return done.returncode, done.stdout.decode("utf-8", "replace").strip()
|
||||||
|
|
||||||
|
|
||||||
|
def _iptables_rule(action, origin_port, proxy_port):
|
||||||
|
return ["iptables", "-t", "nat", action, "OUTPUT",
|
||||||
|
"-p", "tcp", "-d", ORIGIN_ADDR, "--dport", str(origin_port),
|
||||||
|
"!", "-s", PROXY_ADDR,
|
||||||
|
"-j", "REDIRECT", "--to-ports", str(proxy_port)]
|
||||||
|
|
||||||
|
|
||||||
|
def _config(t, name, body):
|
||||||
|
"""Run a configuration that has no service, and return what it said."""
|
||||||
|
return t.run_config(name, body + "\nnot_a_command\n")
|
||||||
|
|
||||||
|
|
||||||
|
def run(t):
|
||||||
|
# --- the command and its modes ------------------------------------
|
||||||
|
# These need no redirection, so they run wherever the feature is built.
|
||||||
|
out = _config(t, "transparent_probe", "log\ntransparent")
|
||||||
|
if "'transparent'" in out:
|
||||||
|
t.skip("transparent proxying (not built in this configuration)")
|
||||||
|
return
|
||||||
|
|
||||||
|
for mode in ("auto", "socket"):
|
||||||
|
t.not_contains(_config(t, "mode_" + mode, f"log\ntransparent {mode}"),
|
||||||
|
"transparent:", f"the {mode} mode is accepted")
|
||||||
|
|
||||||
|
t.contains(_config(t, "mode_bogus", "log\ntransparent bogus"),
|
||||||
|
"unknown mode", "an unknown mode is refused")
|
||||||
|
|
||||||
|
# A mode the build has no code for is refused rather than ignored, so a
|
||||||
|
# configuration written for another platform fails where it is wrong
|
||||||
|
# instead of quietly doing something else.
|
||||||
|
for mode, built in (("netfilter", platform.system() == "Linux"), ("pf", False)):
|
||||||
|
out = _config(t, "mode_" + mode, f"log\ntransparent {mode}")
|
||||||
|
if built:
|
||||||
|
t.not_contains(out, "not available", f"the {mode} mode is accepted where it exists")
|
||||||
|
elif "not available" in out:
|
||||||
|
t.ok(f"the {mode} mode is refused where it does not exist")
|
||||||
|
else:
|
||||||
|
t.skip(f"the {mode} mode (built here, nothing to check)")
|
||||||
|
|
||||||
|
t.not_contains(_config(t, "notransparent", "log\ntransparent\nnotransparent"),
|
||||||
|
"'notransparent'", "notransparent is accepted")
|
||||||
|
|
||||||
|
# --- and the redirection itself ------------------------------------
|
||||||
|
if platform.system() != "Linux":
|
||||||
|
# The BSDs need a redirection that leaves the original destination on
|
||||||
|
# the socket - divert-to on OpenBSD, ipfw fwd on FreeBSD - and macOS
|
||||||
|
# has neither, so there is nothing to set up here.
|
||||||
|
t.skip(f"transparent proxying (no redirection to set up on {platform.system()})")
|
||||||
|
return
|
||||||
|
if os.geteuid() != 0 or not shutil.which("iptables"):
|
||||||
|
t.skip("transparent proxying (needs root and iptables to redirect)")
|
||||||
|
return
|
||||||
|
|
||||||
|
origin_port = t.free_port()
|
||||||
|
decoy_port = t.free_port()
|
||||||
|
mapper_port = t.free_port()
|
||||||
|
plain_port = t.free_port()
|
||||||
|
|
||||||
|
t.start("transparent", f"""
|
||||||
|
log
|
||||||
|
auth iponly
|
||||||
|
allow *
|
||||||
|
http * /echo* echo
|
||||||
|
httpsrv -p{origin_port} -i{ORIGIN_ADDR}
|
||||||
|
|
||||||
|
# a second server, to tell apart where a connection actually went
|
||||||
|
flush
|
||||||
|
auth iponly
|
||||||
|
allow *
|
||||||
|
http * * data size=13
|
||||||
|
httpsrv -p{decoy_port} -i{DECOY_ADDR}
|
||||||
|
|
||||||
|
# a port mapper aimed at the decoy: with the destination taken from
|
||||||
|
# the redirection instead, it goes to the origin
|
||||||
|
flush
|
||||||
|
auth iponly
|
||||||
|
allow *
|
||||||
|
transparent
|
||||||
|
tcppm -e{PROXY_ADDR} {mapper_port} {DECOY_ADDR} {decoy_port}
|
||||||
|
notransparent
|
||||||
|
|
||||||
|
# the same mapper without it, which keeps going to the decoy
|
||||||
|
flush
|
||||||
|
auth iponly
|
||||||
|
allow *
|
||||||
|
tcppm -e{PROXY_ADDR} {plain_port} {DECOY_ADDR} {decoy_port}
|
||||||
|
""", ports=[(ORIGIN_ADDR, origin_port), (DECOY_ADDR, decoy_port),
|
||||||
|
mapper_port, plain_port])
|
||||||
|
|
||||||
|
code, out = _run(_iptables_rule("-A", origin_port, mapper_port))
|
||||||
|
if code:
|
||||||
|
t.skip(f"transparent proxying (could not add a redirect rule: {out})")
|
||||||
|
return
|
||||||
|
|
||||||
|
try:
|
||||||
|
# The client asks for the address it wants; the rule sends the
|
||||||
|
# connection to the mapper instead, and the mapper has to work out
|
||||||
|
# where it was headed.
|
||||||
|
r = t.http(f"http://{ORIGIN_ADDR}:{origin_port}/echo")
|
||||||
|
t.eq(200, r.status, "a redirected connection reaches its destination")
|
||||||
|
t.contains(r, "path=/echo", "the request arrives unchanged")
|
||||||
|
t.contains(r, f"host={ORIGIN_ADDR}:{origin_port}",
|
||||||
|
"the client still believes it is talking to the origin")
|
||||||
|
t.contains(r, f"peer.addr={PROXY_ADDR}",
|
||||||
|
"the origin is reached from the proxy's own address")
|
||||||
|
|
||||||
|
# That address is what the rule excludes, which is what stops the
|
||||||
|
# proxy's own connection from being redirected back into itself.
|
||||||
|
t.not_contains(r, "size=13", "the connection did not go to the decoy")
|
||||||
|
|
||||||
|
# Without the command the mapper has no reason to look, and goes
|
||||||
|
# where it was configured to go.
|
||||||
|
_run(_iptables_rule("-D", origin_port, mapper_port))
|
||||||
|
code, out = _run(_iptables_rule("-A", origin_port, plain_port))
|
||||||
|
if code:
|
||||||
|
t.skip("the mapper without the command (could not move the rule)")
|
||||||
|
else:
|
||||||
|
r = t.http(f"http://{ORIGIN_ADDR}:{origin_port}/echo")
|
||||||
|
t.eq(13, r.length,
|
||||||
|
"without the command the connection goes to the configured target")
|
||||||
|
t.not_contains(r, "path=/echo",
|
||||||
|
"and never reaches the address the client asked for")
|
||||||
|
_run(_iptables_rule("-D", origin_port, plain_port))
|
||||||
|
finally:
|
||||||
|
# leave the machine as it was found, whatever happened above
|
||||||
|
_run(_iptables_rule("-D", origin_port, mapper_port))
|
||||||
|
_run(_iptables_rule("-D", origin_port, plain_port))
|
||||||
Loading…
Reference in New Issue
Block a user