nfqws: works without root now

This commit is contained in:
bolvan 2019-05-20 10:53:20 +03:00
parent 0127713829
commit 40b1c7b903
7 changed files with 127 additions and 61 deletions

View File

@ -12,7 +12,7 @@ define Package/nfqws
CATEGORY:=Network CATEGORY:=Network
TITLE:=nfqws TITLE:=nfqws
SUBMENU:=Zapret SUBMENU:=Zapret
DEPENDS:=+libnetfilter-queue DEPENDS:=+libnetfilter-queue +libcap
endef endef
define Build/Prepare define Build/Prepare

View File

@ -136,6 +136,8 @@ It takes the following parameters:
--hostnospace ; remove space after Host: and add it to User-Agent: to preserve packet size --hostnospace ; remove space after Host: and add it to User-Agent: to preserve packet size
--daemon ; daemonize --daemon ; daemonize
--pidfile=<filename> ; write pid to file --pidfile=<filename> ; write pid to file
--user=<username> ; drop root privs
--uid=uid[:gid] ; drop root privs
The manipulation parameters can be combined in any way. The manipulation parameters can be combined in any way.
@ -173,7 +175,7 @@ tpws is transparent proxy.
--daemon ; daemonize --daemon ; daemonize
--pidfile=<filename> ; write pid to file --pidfile=<filename> ; write pid to file
--user=<username> ; drop root privs --user=<username> ; drop root privs
--uid=uid[:gid] ; менять uid процесса --uid=uid[:gid] ; drop root privs
The manipulation parameters can be combined in any way. The manipulation parameters can be combined in any way.
There are exceptions: split-pos replaces split-http-req. hostdot and hosttab are mutually exclusive. There are exceptions: split-pos replaces split-http-req. hostdot and hosttab are mutually exclusive.

View File

@ -137,6 +137,8 @@ nfqws
Она берет следующие параметры : Она берет следующие параметры :
--daemon ; демонизировать прогу --daemon ; демонизировать прогу
--pidfile=<file> ; сохранить PID в файл --pidfile=<file> ; сохранить PID в файл
--user=<username> ; менять uid процесса
--uid=uid[:gid] ; менять uid процесса
--qnum=200 ; номер очереди --qnum=200 ; номер очереди
--wsize=4 ; менять tcp window size на указанный размер --wsize=4 ; менять tcp window size на указанный размер
--hostcase ; менять регистр заголовка "Host:" по умолчанию на "host:". --hostcase ; менять регистр заголовка "Host:" по умолчанию на "host:".

View File

@ -14,8 +14,9 @@ ZAPRET_BASE=/opt/zapret
PIDDIR=/var/run PIDDIR=/var/run
QNUM=200 QNUM=200
NFQWS_USER=daemon
NFQWS=$ZAPRET_BASE/nfq/nfqws NFQWS=$ZAPRET_BASE/nfq/nfqws
NFQWS_OPT_BASE="--qnum=$QNUM" NFQWS_OPT_BASE="--qnum=$QNUM --user=$NFQWS_USER"
TPWS_USER=daemon TPWS_USER=daemon
TPPORT_HTTP=1188 TPPORT_HTTP=1188

View File

@ -8,20 +8,21 @@ PIDDIR=/var/run
IPSET_CR=$ZAPRET_BASE/ipset/create_ipset.sh IPSET_CR=$ZAPRET_BASE/ipset/create_ipset.sh
WS_USER=tpws
QNUM=200 QNUM=200
NFQWS=$ZAPRET_BASE/nfq/nfqws NFQWS=$ZAPRET_BASE/nfq/nfqws
NFQWS_OPT_BASE="--qnum=$QNUM" NFQWS_OPT_BASE="--qnum=$QNUM --user=$WS_USER"
TPWS_USER=tpws
TPPORT_HTTP=1188 TPPORT_HTTP=1188
TPPORT_HTTPS=1189 TPPORT_HTTPS=1189
TPWS=$ZAPRET_BASE/tpws/tpws TPWS=$ZAPRET_BASE/tpws/tpws
TPWS_HOSTLIST=$ZAPRET_BASE/ipset/zapret-hosts.txt.gz TPWS_HOSTLIST=$ZAPRET_BASE/ipset/zapret-hosts.txt.gz
[ -f "$TPWS_HOSTLIST" ] || TPWS_HOSTLIST=$ZAPRET_BASE/ipset/zapret-hosts-user.txt [ -f "$TPWS_HOSTLIST" ] || TPWS_HOSTLIST=$ZAPRET_BASE/ipset/zapret-hosts-user.txt
TPWS_OPT_BASE="--user=$TPWS_USER --bind-addr=127.0.0.1" TPWS_OPT_BASE="--user=$WS_USER --bind-addr=127.0.0.1"
TPWS_OPT_BASE6="--user=$TPWS_USER --bind-addr=::1" TPWS_OPT_BASE6="--user=$WS_USER --bind-addr=::1"
# first wait for lan to ifup, then wait for bind-wait-ip-linklocal seconds for link local address and bind-wait-ip for any ipv6 as the worst case # first wait for lan to ifup, then wait for bind-wait-ip-linklocal seconds for link local address and bind-wait-ip for any ipv6 as the worst case
TPWS_OPT_BASE6_PRE="--user=$TPWS_USER --bind-linklocal=prefer --bind-wait-ifup=30 --bind-wait-ip=30 --bind-wait-ip-linklocal=3" TPWS_OPT_BASE6_PRE="--user=$WS_USER --bind-linklocal=prefer --bind-wait-ifup=30 --bind-wait-ip=30 --bind-wait-ip-linklocal=3"
TPWS_OPT_BASE_HTTP="--port=$TPPORT_HTTP" TPWS_OPT_BASE_HTTP="--port=$TPPORT_HTTP"
TPWS_OPT_BASE_HTTPS="--port=$TPPORT_HTTPS" TPWS_OPT_BASE_HTTPS="--port=$TPPORT_HTTPS"
@ -169,7 +170,7 @@ fw_tpws()
[ -n "$IFACE_LAN" ] && { [ -n "$IFACE_LAN" ] && {
ipt_add_del $1 PREROUTING -t nat $IPT_ILAN -p tcp $2 -j DNAT --to 127.0.0.1:$4 ipt_add_del $1 PREROUTING -t nat $IPT_ILAN -p tcp $2 -j DNAT --to 127.0.0.1:$4
} }
ipt_add_del $1 OUTPUT -t nat $IPT_OWAN -m owner ! --uid-owner $TPWS_USER -p tcp $2 -j DNAT --to 127.0.0.1:$4 ipt_add_del $1 OUTPUT -t nat $IPT_OWAN -m owner ! --uid-owner $WS_USER -p tcp $2 -j DNAT --to 127.0.0.1:$4
} }
[ "$DISABLE_IPV6" = "1" ] || { [ "$DISABLE_IPV6" = "1" ] || {
print_op $1 "$3" "tpws" 6 print_op $1 "$3" "tpws" 6
@ -177,7 +178,7 @@ fw_tpws()
dnat6_target dnat6_target
[ "$DNAT6_TARGET" != "-" ] && ipt6_add_del $1 PREROUTING -t nat $IPT_ILAN -p tcp $3 -j DNAT --to [$DNAT6_TARGET]:$4 [ "$DNAT6_TARGET" != "-" ] && ipt6_add_del $1 PREROUTING -t nat $IPT_ILAN -p tcp $3 -j DNAT --to [$DNAT6_TARGET]:$4
} }
ipt6_add_del $1 OUTPUT -t nat $IPT_OWAN -m owner ! --uid-owner $TPWS_USER -p tcp $3 -j DNAT --to [::1]:$4 ipt6_add_del $1 OUTPUT -t nat $IPT_OWAN -m owner ! --uid-owner $WS_USER -p tcp $3 -j DNAT --to [::1]:$4
} }
} }
fw_nfqws_pre() fw_nfqws_pre()
@ -266,16 +267,25 @@ do_daemon()
} }
prepare_tpws() prepare_user()
{ {
# $TPWS_USER is required to prevent redirection of the traffic originating from TPWS itself # $WS_USER is required to prevent redirection of the traffic originating from TPWS itself
# otherwise infinite loop will occur # otherwise infinite loop will occur
# also its good idea not to run tpws as root # also its good idea not to run tpws as root
id -u $TPWS_USER >/dev/null 2>/dev/null || useradd --no-create-home --system --shell /bin/false $TPWS_USER id -u $WS_USER >/dev/null 2>/dev/null || useradd --no-create-home --system --shell /bin/false $WS_USER
}
prepare_tpws()
{
prepare_user
# otherwise linux kernel will treat 127.0.0.1 as "martian" ip and refuse routing to it # otherwise linux kernel will treat 127.0.0.1 as "martian" ip and refuse routing to it
# NOTE : kernels <3.6 do not have this feature. consider upgrading or change DNAT to REDIRECT and do not bind to 127.0.0.1 # NOTE : kernels <3.6 do not have this feature. consider upgrading or change DNAT to REDIRECT and do not bind to 127.0.0.1
[ -n "$IFACE_LAN" ] && sysctl -qw net.ipv4.conf.$IFACE_LAN.route_localnet=1 [ -n "$IFACE_LAN" ] && sysctl -qw net.ipv4.conf.$IFACE_LAN.route_localnet=1
} }
prepare_nfqws()
{
prepare_user
}
do_tpws() do_tpws()
{ {
# $1 : 1 - run, 0 - stop # $1 : 1 - run, 0 - stop
@ -289,6 +299,15 @@ do_tpws()
[ -n "$IFACE_LAN" ] && do_daemon $1 $((660+$2)) $TPWS "$TPWS_OPT_BASE6_PRE --bind-iface6=$IFACE_LAN $3" [ -n "$IFACE_LAN" ] && do_daemon $1 $((660+$2)) $TPWS "$TPWS_OPT_BASE6_PRE --bind-iface6=$IFACE_LAN $3"
} }
} }
do_nfqws()
{
# $1 : 1 - run, 0 - stop
# $2 : daemon number
# $3 : daemon args
[ "$1" = "1" ] && prepare_nfqws
do_daemon $1 $2 $NFQWS "$NFQWS_OPT_BASE $3"
}
create_ipset() create_ipset()
@ -385,7 +404,7 @@ zapret_do_daemons()
do_tpws $1 1 "$TPWS_OPT_BASE_HTTP $TPWS_OPT_HTTP" do_tpws $1 1 "$TPWS_OPT_BASE_HTTP $TPWS_OPT_HTTP"
;; ;;
nfqws_ipset|nfqws_ipset_https|nfqws_all|nfqws_all_https) nfqws_ipset|nfqws_ipset_https|nfqws_all|nfqws_all_https)
do_daemon $1 1 $NFQWS "$NFQWS_OPT_BASE $NFQWS_OPT" do_nfqws $1 1 "$NFQWS_OPT"
;; ;;
custom) custom)
# PLACEHOLDER # PLACEHOLDER

View File

@ -1,6 +1,6 @@
CC ?= gcc CC ?= gcc
CFLAGS ?= -s -O3 CFLAGS += -s -O3
LIBS = -lnetfilter_queue -lnfnetlink LIBS = -lnetfilter_queue -lnfnetlink -lcap
SRC_FILES = *.c SRC_FILES = *.c
all: nfqws all: nfqws

View File

@ -13,6 +13,8 @@
#include <getopt.h> #include <getopt.h>
#include <fcntl.h> #include <fcntl.h>
#include <pwd.h> #include <pwd.h>
#include <sys/capability.h>
#include <sys/prctl.h>
bool proto_check_ipv4(unsigned char *data,int len) bool proto_check_ipv4(unsigned char *data,int len)
{ {
@ -363,10 +365,40 @@ static int cb(struct nfq_q_handle *qh, struct nfgenmsg *nfmsg,
return nfq_set_verdict(qh, id, NF_ACCEPT, 0, NULL); return nfq_set_verdict(qh, id, NF_ACCEPT, 0, NULL);
} }
bool dropcaps()
{
cap_value_t cap_values[] = {CAP_NET_ADMIN};
cap_t capabilities;
if (!(capabilities = cap_init()))
{
perror("cap_init");
return false;
}
if (cap_set_flag(capabilities, CAP_PERMITTED, sizeof(cap_values)/sizeof(*cap_values), cap_values, CAP_SET) ||
cap_set_flag(capabilities, CAP_EFFECTIVE, sizeof(cap_values)/sizeof(*cap_values), cap_values, CAP_SET))
{
perror("cap_set_flag");
cap_free(capabilities);
return false;
}
if (cap_set_proc(capabilities))
{
perror("cap_set_proc");
cap_free(capabilities);
return false;
}
cap_free(capabilities);
return true;
}
bool droproot(uid_t uid, gid_t gid) bool droproot(uid_t uid, gid_t gid)
{ {
if (uid) if (uid || gid)
{ {
if (prctl(PR_SET_KEEPCAPS, 1L))
{
perror("prctl(PR_SET_KEEPCAPS): ");
return false;
}
if (setgid(gid)) if (setgid(gid))
{ {
perror("setgid: "); perror("setgid: ");
@ -378,7 +410,7 @@ bool droproot(uid_t uid, gid_t gid)
return false; return false;
} }
} }
return true; return dropcaps();
} }
void daemonize() void daemonize()
@ -431,6 +463,8 @@ void exithelp()
" --hostnospace\t\t; remove space after Host: and add it to User-Agent: to preserve packet size\n" " --hostnospace\t\t; remove space after Host: and add it to User-Agent: to preserve packet size\n"
" --daemon\t\t; daemonize\n" " --daemon\t\t; daemonize\n"
" --pidfile=<filename>\t; write pid to file\n" " --pidfile=<filename>\t; write pid to file\n"
" --user=<username>\t; drop root privs\n"
" --uid=uid[:gid]\t; drop root privs\n"
); );
exit(1); exit(1);
} }
@ -447,7 +481,7 @@ int main(int argc, char **argv)
int v; int v;
bool daemon=false; bool daemon=false;
uid_t uid=0; uid_t uid=0;
gid_t gid; gid_t gid=0;
char pidfile[256]; char pidfile[256];
memset(&cbdata,0,sizeof(cbdata)); memset(&cbdata,0,sizeof(cbdata));
@ -461,8 +495,9 @@ int main(int argc, char **argv)
{"hostcase",no_argument,0,0}, // optidx=3 {"hostcase",no_argument,0,0}, // optidx=3
{"hostspell",required_argument,0,0}, // optidx=4 {"hostspell",required_argument,0,0}, // optidx=4
{"hostnospace",no_argument,0,0}, // optidx=5 {"hostnospace",no_argument,0,0}, // optidx=5
{"user",required_argument,0,0}, // optidx=6 {"pidfile",required_argument,0,0}, // optidx=6
{"pidfile",required_argument,0,0}, // optidx=7 {"user",required_argument,0,0 },// optidx=7
{"uid",required_argument,0,0 },// optidx=8
{NULL,0,NULL,0} {NULL,0,NULL,0}
}; };
if (argc<2) exithelp(); if (argc<2) exithelp();
@ -505,7 +540,11 @@ int main(int argc, char **argv)
case 5: /* hostnospace */ case 5: /* hostnospace */
cbdata.hostnospace = true; cbdata.hostnospace = true;
break; break;
case 6: /* user */ case 6: /* pidfile */
strncpy(pidfile,optarg,sizeof(pidfile));
pidfile[sizeof(pidfile)-1]='\0';
break;
case 7: /* user */
{ {
struct passwd *pwd = getpwnam(optarg); struct passwd *pwd = getpwnam(optarg);
if (!pwd) if (!pwd)
@ -517,9 +556,13 @@ int main(int argc, char **argv)
gid = pwd->pw_gid; gid = pwd->pw_gid;
break; break;
} }
case 7: /* pidfile */ case 8: /* uid */
strncpy(pidfile,optarg,sizeof(pidfile)); gid=0x7FFFFFFF; // default git. drop gid=0
pidfile[sizeof(pidfile)-1]='\0'; if (!sscanf(optarg,"%u:%u",&uid,&gid))
{
fprintf(stderr, "--uid should be : uid[:gid]\n");
exit(1);
}
break; break;
} }
} }
@ -567,16 +610,15 @@ int main(int argc, char **argv)
goto exiterr; goto exiterr;
} }
fd = nfq_fd(h); if (!droproot(uid,gid)) goto exiterr;
fprintf(stderr,"Running as UID=%u GID=%u\n",getuid(),getgid());
if (droproot(uid,gid)) fd = nfq_fd(h);
{
while ((rv = recv(fd, buf, sizeof(buf), 0)) && rv >= 0) while ((rv = recv(fd, buf, sizeof(buf), 0)) && rv >= 0)
{ {
int r=nfq_handle_packet(h, buf, rv); int r=nfq_handle_packet(h, buf, rv);
if (r) fprintf(stderr,"nfq_handle_packet error %d\n",r); if (r) fprintf(stderr,"nfq_handle_packet error %d\n",r);
} }
}
printf("unbinding from queue 0\n"); printf("unbinding from queue 0\n");
nfq_destroy_queue(qh); nfq_destroy_queue(qh);