From fa601c0fa75f2eba37f0f9886722364a262b7aee Mon Sep 17 00:00:00 2001 From: Jan Rafaj Date: Tue, 1 Sep 2009 19:52:48 +0200 Subject: [PATCH 1/5] pknock: add manpage for pknock --- doc/changelog.txt | 1 + extensions/libxt_pknock.man | 112 ++++++++++++++++++++++++++++++++++++ 2 files changed, 113 insertions(+) create mode 100644 extensions/libxt_pknock.man diff --git a/doc/changelog.txt b/doc/changelog.txt index e541322..e408e1a 100644 --- a/doc/changelog.txt +++ b/doc/changelog.txt @@ -14,6 +14,7 @@ - pknock: check interknock time only for !ST_ALLOWED peers - pknock: preserve time/autoclose values for rules added in reverse/arbitrary order + - pknock: add a manpage Xtables-addons 1.18 (September 09 2009) diff --git a/extensions/libxt_pknock.man b/extensions/libxt_pknock.man new file mode 100644 index 0000000..1552a35 --- /dev/null +++ b/extensions/libxt_pknock.man @@ -0,0 +1,112 @@ +Pknock match implements so-called Port-Knocking, a stealthy system +for network authentication: client sends packets to selected +ports in a specific sequence (= simple mode, see Example 1 below), or HMAC +payload to a single port (= complex mode, see Example 2 below), +to target machine that has pknock rule(s) installed. The target machine +then decides whether to unblock or block (again) pknock-protected port with +listening service. This can be, for instance, used to avoid brute force +attacks on ssh or ftp services. +.PP +Example prerequisites: +.IP +modprobe cn +.IP +modprobe xt_pknock +.PP +Example 1 (TCP mode, manual closing of opened port not possible): +.IP +iptables -P INPUT DROP +.IP +iptables -A INPUT -p tcp -m pknock --knockports 4002,4001,4004 --strict +--name SSH --time 10 --autoclose 60 --dport 22 -j ACCEPT +.PP +The rule will open tcp port 22 after successfull reception of TCP SYN packets +to ports 4002, 4001, 4004 in this sequence (a.k.a port-knocking), for the IP +that sent them. Port numbers in connect sequence must follow each other, no +other ports may be "knocked" inbetween. The rule is named '\fBSSH\fP' - file of +the same name for tracking port knocking states will be created in +\fB/proc/net/xt_pknock\fP . +Port knocks must follow each other with delay <= 10 seconds. The port 22 will +be auto-closed in 60 minutes since its opening. +.PP +Example 2 (UDP mode - nonreplayable and nonspoofable, manual closing +of opened port possible, secure, also called "SPA" = Secure Port +Authorization): +.IP +iptables -A INPUT -p udp -m pknock --knockports 4000 --name FTP +--opensecret foo --closesecret bar --autoclose 240 -j DROP +.IP +iptables -A INPUT -p tcp -m pknock --checkip --name FTP --dport 21 -j ACCEPT +.PP +First rule will create 'ALLOWED' record in /proc/net/xt_pknock/FTP after +successfull reception of UDP packet to port 4000. The packet payload must be +constructed as HMAC256 using 'foo' as a key, and clients IP in network byteorder +unsigned long format, concatenated with minutes since epoch in the same format, +as HMAC'd content (a.k.a Simple Packet Authorization, also called "SPA"). +In such case, any subsequent attempt to connect to port 21 from clients IP +will trigger ACCEPT in the second rule. +.PP +Similarly, upon reception of UDP packet, constructed the same way, but with +key 'bar', the first rule will remove previously installed 'ALLOWED' state +record from /proc/net/xt_pknock/FTP, which means that the second rule will +stop matching for subsequent attempts to connect to port 21. +In case no close-secret packet is received within 4 hours, the first rule +will remove 'ALLOWED' record from /proc/net/xt_pknock/FTP itself. +.PP +Things worth noting: +.PP +\fBGeneral\fP: +.PP +Specifying \fB--autoclose 0\fP means no autoclose will be performed at all. +.PP +xt_pknock is capable of sending information about successful match +via netlink socket to userspace, should you need to implement your own +way of received portknock handling. +Be sure to read documentation in 'doc/xtables-addons/pknock' directory, +or visit the original site - http://portknocko.berlios.de/ . +.PP +\fBTCP mode\fP: +.PP +This mode is not immune against eavesdropping, spoofing and +replaying of the port knock sequence by someone else (but its use may still +be sufficient for scenarios, where these factors are not necessarilly +so important, such as bare shielding of SSH port from brute-force attacks). +If you need these features, you should use UDP mode. +.PP +It is always wise to specify 3 or more ports, that dont subsequently +follow each other in ascending or descending sequence, to avoid triggering +the rule by a portscan. +.PP +Specifying inter-knock timeout with \fB--time\fP is mandatory in TCP mode, +to avoid permanent DoS by clogging up peer knock-state tracking table +that xt_pknock internally keeps, forever, should there be a DDoS on the +first-in-row knock port from more hostile IPs than what is the actual size +of this table (16, can be changed via 'peer_hasht_ents' module parameter). +It is also wise to use as short \fB--time\fP as possible (1 second) +for this very reason, too. You may also consider increasing the size +of peer knock-state tracking table. Using \fB--strict\fP also helps, +as it requires the knock sequence to be exact. This means that if the +hostile IP client sends more knocks to the same port, xt_pknock will +mark such attempt as failed knock sequence and will forget it immediately. +To completely thwart this kind of DDoS, knock-ports would need to have +an additional rate-limit protection. Or you may consider using UDP mode. +.PP +\fBUDP mode\fP: +.PP +This mode is immune against eavesdropping, replaying and spoofing attacks. +It is also immune against DDoS attack on the knockport. +.PP +For this mode to work, the clock difference on the client and on the server +must be below 1 minute. Synchronizing time on both ends by means +of NTP or rdate is strongly suggested. +.PP +There is a rate limiter built into xt_pknock, that blocks any subsequent +opening attempt in UDP mode, should it arrive in period shorter than 1 +minute since the first successfull opening. This is intentional; +it thwarts eventual spoofing attacks. +.PP +Becouse the payload value of UDP knock packet is influenced by client's IP, +UDP mode cannot be used across NAT. +.PP +For sending UDP "SPA" packets, you may use either \fBknock.sh\fP or +\fBknock-orig.sh\fP. You'll find these in 'doc/xtables-addons/pknock/util'. From 20365cf762c043599c7fbf177565af92f387de98 Mon Sep 17 00:00:00 2001 From: Jan Engelhardt Date: Thu, 1 Oct 2009 02:02:52 +0200 Subject: [PATCH 2/5] pknock: manpage writing style updates --- extensions/libxt_pknock.man | 101 ++++++++++++++++++------------------ 1 file changed, 51 insertions(+), 50 deletions(-) diff --git a/extensions/libxt_pknock.man b/extensions/libxt_pknock.man index 1552a35..c57d1c9 100644 --- a/extensions/libxt_pknock.man +++ b/extensions/libxt_pknock.man @@ -1,10 +1,10 @@ -Pknock match implements so-called Port-Knocking, a stealthy system -for network authentication: client sends packets to selected -ports in a specific sequence (= simple mode, see Example 1 below), or HMAC -payload to a single port (= complex mode, see Example 2 below), -to target machine that has pknock rule(s) installed. The target machine -then decides whether to unblock or block (again) pknock-protected port with -listening service. This can be, for instance, used to avoid brute force +Pknock match implements so-called "port knocking", a stealthy system +for network authentication: a client sends packets to selected +ports in a specific sequence (= simple mode, see example 1 below), or a HMAC +payload to a single port (= complex mode, see example 2 below), +to a target machine that has pknock rule(s) installed. The target machine +then decides whether to unblock or block (again) the pknock-protected port(s). +This can be used, for instance, to avoid brute force attacks on ssh or ftp services. .PP Example prerequisites: @@ -20,16 +20,16 @@ iptables -P INPUT DROP iptables -A INPUT -p tcp -m pknock --knockports 4002,4001,4004 --strict --name SSH --time 10 --autoclose 60 --dport 22 -j ACCEPT .PP -The rule will open tcp port 22 after successfull reception of TCP SYN packets -to ports 4002, 4001, 4004 in this sequence (a.k.a port-knocking), for the IP -that sent them. Port numbers in connect sequence must follow each other, no -other ports may be "knocked" inbetween. The rule is named '\fBSSH\fP' - file of +The rule will allow tcp port 22 for the attempting IP address after the successful reception of TCP SYN packets +to ports 4002, 4001 and 4004, in this order (a.k.a. port-knocking). +Port numbers in the connect sequence must follow the exact specification, no +other ports may be "knocked" inbetween. The rule is named '\fBSSH\fP' \(em a file of the same name for tracking port knocking states will be created in \fB/proc/net/xt_pknock\fP . -Port knocks must follow each other with delay <= 10 seconds. The port 22 will -be auto-closed in 60 minutes since its opening. +Successive port knocks must occur with delay of at most 10 seconds. Port 22 (from the example) will +be automatiaclly dropped after 60 minutes after it was previously allowed. .PP -Example 2 (UDP mode - nonreplayable and nonspoofable, manual closing +Example 2 (UDP mode \(em non-replayable and non-spoofable, manual closing of opened port possible, secure, also called "SPA" = Secure Port Authorization): .IP @@ -38,55 +38,56 @@ iptables -A INPUT -p udp -m pknock --knockports 4000 --name FTP .IP iptables -A INPUT -p tcp -m pknock --checkip --name FTP --dport 21 -j ACCEPT .PP -First rule will create 'ALLOWED' record in /proc/net/xt_pknock/FTP after -successfull reception of UDP packet to port 4000. The packet payload must be -constructed as HMAC256 using 'foo' as a key, and clients IP in network byteorder -unsigned long format, concatenated with minutes since epoch in the same format, -as HMAC'd content (a.k.a Simple Packet Authorization, also called "SPA"). -In such case, any subsequent attempt to connect to port 21 from clients IP -will trigger ACCEPT in the second rule. +The first rule will create an "ALLOWED" record in /proc/net/xt_pknock/FTP after +the successful reception of an UDP packet to port 4000. The packet payload must be +constructed as a HMAC256 using "foo" as a key. The HMAC content is the particular client's IP address as a 32-bit network byteorder quantity, +plus the number of minutes since the Unix epoch, also as a 32-bit value. +(This is known as Simple Packet Authorization, also called "SPA".) +In such case, any subsequent attempt to connect to port 21 from the client's IP +address will cause such packets to be accepted in the second rule. .PP -Similarly, upon reception of UDP packet, constructed the same way, but with -key 'bar', the first rule will remove previously installed 'ALLOWED' state +Similarly, upon reception of an UDP packet constructed the same way, but with +the key "bar", the first rule will remove a previously installed "ALLOWED" state record from /proc/net/xt_pknock/FTP, which means that the second rule will -stop matching for subsequent attempts to connect to port 21. +stop matching for subsequent connection attempts to port 21. In case no close-secret packet is received within 4 hours, the first rule -will remove 'ALLOWED' record from /proc/net/xt_pknock/FTP itself. +will remove "ALLOWED" record from /proc/net/xt_pknock/FTP itself. .PP Things worth noting: .PP \fBGeneral\fP: .PP -Specifying \fB--autoclose 0\fP means no autoclose will be performed at all. +Specifying \fB--autoclose 0\fP means that no automatic close will be performed at all. .PP -xt_pknock is capable of sending information about successful match -via netlink socket to userspace, should you need to implement your own -way of received portknock handling. -Be sure to read documentation in 'doc/xtables-addons/pknock' directory, -or visit the original site - http://portknocko.berlios.de/ . +xt_pknock is capable of sending information about successful matches +via a netlink socket to userspace, should you need to implement your own +way of receiving and handling portknock notifications. +Be sure to read the documentation in the doc/pknock/ directory, +or visit the original site \(em http://portknocko.berlios.de/ . .PP \fBTCP mode\fP: .PP This mode is not immune against eavesdropping, spoofing and replaying of the port knock sequence by someone else (but its use may still -be sufficient for scenarios, where these factors are not necessarilly -so important, such as bare shielding of SSH port from brute-force attacks). -If you need these features, you should use UDP mode. +be sufficient for scenarios where these factors are not necessarily +this important, such as bare shielding of the SSH port from brute-force attacks). +However, if you need these features, you should use UDP mode. .PP -It is always wise to specify 3 or more ports, that dont subsequently -follow each other in ascending or descending sequence, to avoid triggering +It is always wise to specify three or more ports that are not monotonically +increasing or decreasing with a small stepsize (e.g. 1024,1025,1026) +to avoid accidentally triggering the rule by a portscan. .PP -Specifying inter-knock timeout with \fB--time\fP is mandatory in TCP mode, -to avoid permanent DoS by clogging up peer knock-state tracking table -that xt_pknock internally keeps, forever, should there be a DDoS on the -first-in-row knock port from more hostile IPs than what is the actual size -of this table (16, can be changed via 'peer_hasht_ents' module parameter). -It is also wise to use as short \fB--time\fP as possible (1 second) -for this very reason, too. You may also consider increasing the size -of peer knock-state tracking table. Using \fB--strict\fP also helps, +Specifying the inter-knock timeout with \fB--time\fP is mandatory in TCP mode, +to avoid permanent denial of services by clogging up the peer knock-state tracking table +that xt_pknock internally keeps, should there be a DDoS on the +first-in-row knock port from more hostile IP addresses than what the actual size +of this table is (defaults to 16, can be changed via the "peer_hasht_ents" module parameter). +It is also wise to use as short a time as possible (1 second) for \fB--time\fP +for this very reason. You may also consider increasing the size +of the peer knock-state tracking table. Using \fB--strict\fP also helps, as it requires the knock sequence to be exact. This means that if the -hostile IP client sends more knocks to the same port, xt_pknock will +hostile client sends more knocks to the same port, xt_pknock will mark such attempt as failed knock sequence and will forget it immediately. To completely thwart this kind of DDoS, knock-ports would need to have an additional rate-limit protection. Or you may consider using UDP mode. @@ -100,13 +101,13 @@ For this mode to work, the clock difference on the client and on the server must be below 1 minute. Synchronizing time on both ends by means of NTP or rdate is strongly suggested. .PP -There is a rate limiter built into xt_pknock, that blocks any subsequent -opening attempt in UDP mode, should it arrive in period shorter than 1 -minute since the first successfull opening. This is intentional; +There is a rate limiter built into xt_pknock which blocks any subsequent +open attempt in UDP mode should the request arrive within less than one +minute since the first successful open. This is intentional; it thwarts eventual spoofing attacks. .PP -Becouse the payload value of UDP knock packet is influenced by client's IP, +Because the payload value of an UDP knock packet is influenced by client's IP address, UDP mode cannot be used across NAT. .PP For sending UDP "SPA" packets, you may use either \fBknock.sh\fP or -\fBknock-orig.sh\fP. You'll find these in 'doc/xtables-addons/pknock/util'. +\fBknock-orig.sh\fP. These may be found in doc/pknock/util. From 9568747d94e29288d0e567e6f1861fd12b0c8fae Mon Sep 17 00:00:00 2001 From: Jan Rafaj Date: Tue, 1 Sep 2009 19:52:48 +0200 Subject: [PATCH 3/5] pknock: import userspace netlink listener program --- extensions/pknock/.gitignore | 1 + extensions/pknock/Makefile.am | 2 + extensions/pknock/pknlusr.c | 91 +++++++++++++++++++++++++++++++++++ 3 files changed, 94 insertions(+) create mode 100644 extensions/pknock/.gitignore create mode 100644 extensions/pknock/pknlusr.c diff --git a/extensions/pknock/.gitignore b/extensions/pknock/.gitignore new file mode 100644 index 0000000..122e07c --- /dev/null +++ b/extensions/pknock/.gitignore @@ -0,0 +1 @@ +/pknlusr diff --git a/extensions/pknock/Makefile.am b/extensions/pknock/Makefile.am index af3f625..18342eb 100644 --- a/extensions/pknock/Makefile.am +++ b/extensions/pknock/Makefile.am @@ -1,3 +1,5 @@ # -*- Makefile -*- include ../../Makefile.extra + +noinst_PROGRAMS = pknlusr diff --git a/extensions/pknock/pknlusr.c b/extensions/pknock/pknlusr.c new file mode 100644 index 0000000..1b99166 --- /dev/null +++ b/extensions/pknock/pknlusr.c @@ -0,0 +1,91 @@ +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "xt_pknock.h" + +#define GROUP 1 + +struct sockaddr_nl src_addr, dest_addr; +struct msghdr msg; +int sock_fd; + +unsigned char *buf = NULL; + +struct xt_pknock_nl_msg *nlmsg; + +int main() { + socklen_t addrlen; + int status; + int group = GROUP; + struct cn_msg *cnmsg; + + int i, buf_size; + + char *ip; + + sock_fd = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_CONNECTOR); + + if (sock_fd == -1) { + perror("socket()"); + return 1; + } + + memset(&src_addr, 0, sizeof(src_addr)); + src_addr.nl_family = AF_NETLINK; + src_addr.nl_pid = getpid(); + src_addr.nl_groups = group; + + status = bind(sock_fd, (struct sockaddr*)&src_addr, sizeof(src_addr)); + + if (status == -1) { + close(sock_fd); + perror("bind()"); + return 1; + } + + memset(&dest_addr, 0, sizeof(dest_addr)); + dest_addr.nl_family = AF_NETLINK; + dest_addr.nl_pid = 0; + dest_addr.nl_groups = group; + + buf_size = sizeof(struct xt_pknock_nl_msg) + sizeof(struct cn_msg) + sizeof(struct nlmsghdr); + buf = (unsigned char *) malloc(buf_size); + + if (!buf) { + perror("malloc()"); + return 1; + } + + addrlen = sizeof(dest_addr); + + while(1) { + + memset(buf, 0, buf_size); + + status = recvfrom(sock_fd, buf, buf_size, 0, (struct sockaddr *)&dest_addr, &addrlen); + + if (status <= 0) { + perror("recvfrom()"); + return 1; + } + + nlmsg = (struct xt_pknock_nl_msg *) (buf + sizeof(struct cn_msg) + sizeof(struct nlmsghdr)); + + ip = (char *)inet_ntoa((struct in_addr *) htonl(nlmsg->peer_ip)); + printf("rule_name: %s - ip %s\n", nlmsg->rule_name, ip); + + } + + close(sock_fd); + + free(buf); + + return 0; +} From 439dd913f16cfd9b4682dd6311d98d33f0308f35 Mon Sep 17 00:00:00 2001 From: Jan Engelhardt Date: Mon, 12 Oct 2009 18:52:02 +0200 Subject: [PATCH 4/5] pknock: fix pknlusr compile warnings pknlusr.c: In function "main": pknlusr.c:81:25: warning: cast to pointer from integer of different size pknlusr.c:81:7: warning: cast to pointer from integer of different size --- extensions/pknock/pknlusr.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/extensions/pknock/pknlusr.c b/extensions/pknock/pknlusr.c index 1b99166..01ddcdc 100644 --- a/extensions/pknock/pknlusr.c +++ b/extensions/pknock/pknlusr.c @@ -4,7 +4,7 @@ #include #include #include - +#include #include #include @@ -28,7 +28,8 @@ int main() { int i, buf_size; - char *ip; + const char *ip; + char ipbuf[48]; sock_fd = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_CONNECTOR); @@ -78,7 +79,7 @@ int main() { nlmsg = (struct xt_pknock_nl_msg *) (buf + sizeof(struct cn_msg) + sizeof(struct nlmsghdr)); - ip = (char *)inet_ntoa((struct in_addr *) htonl(nlmsg->peer_ip)); + ip = inet_ntop(AF_INET, &nlmsg->peer_ip, ipbuf, sizeof(ipbuf)); printf("rule_name: %s - ip %s\n", nlmsg->rule_name, ip); } From de4f6e8994117103d6049717e3b7f5a2dc0ae98b Mon Sep 17 00:00:00 2001 From: Jan Engelhardt Date: Mon, 12 Oct 2009 18:53:03 +0200 Subject: [PATCH 5/5] pknlusr: fix up standard errors in pknlusr --- extensions/pknock/pknlusr.c | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/extensions/pknock/pknlusr.c b/extensions/pknock/pknlusr.c index 01ddcdc..c1d0ff2 100644 --- a/extensions/pknock/pknlusr.c +++ b/extensions/pknock/pknlusr.c @@ -12,15 +12,16 @@ #define GROUP 1 -struct sockaddr_nl src_addr, dest_addr; -struct msghdr msg; -int sock_fd; +static struct sockaddr_nl src_addr, dest_addr; +static struct msghdr msg; +static int sock_fd; -unsigned char *buf = NULL; +static unsigned char *buf; -struct xt_pknock_nl_msg *nlmsg; +static struct xt_pknock_nl_msg *nlmsg; -int main() { +int main(void) +{ socklen_t addrlen; int status; int group = GROUP; @@ -57,7 +58,7 @@ int main() { dest_addr.nl_groups = group; buf_size = sizeof(struct xt_pknock_nl_msg) + sizeof(struct cn_msg) + sizeof(struct nlmsghdr); - buf = (unsigned char *) malloc(buf_size); + buf = malloc(buf_size); if (!buf) { perror("malloc()");