diff --git a/doc/changelog.txt b/doc/changelog.txt index cf67d6c..0798e51 100644 --- a/doc/changelog.txt +++ b/doc/changelog.txt @@ -19,6 +19,7 @@ HEAD - 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..c57d1c9 --- /dev/null +++ b/extensions/libxt_pknock.man @@ -0,0 +1,113 @@ +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: +.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 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 . +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 \(em non-replayable and non-spoofable, 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 +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 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 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. +.PP +Things worth noting: +.PP +\fBGeneral\fP: +.PP +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 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 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 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 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 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 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 +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. These may be found in doc/pknock/util. 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..c1d0ff2 --- /dev/null +++ b/extensions/pknock/pknlusr.c @@ -0,0 +1,93 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "xt_pknock.h" + +#define GROUP 1 + +static struct sockaddr_nl src_addr, dest_addr; +static struct msghdr msg; +static int sock_fd; + +static unsigned char *buf; + +static struct xt_pknock_nl_msg *nlmsg; + +int main(void) +{ + socklen_t addrlen; + int status; + int group = GROUP; + struct cn_msg *cnmsg; + + int i, buf_size; + + const char *ip; + char ipbuf[48]; + + 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 = 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 = inet_ntop(AF_INET, &nlmsg->peer_ip, ipbuf, sizeof(ipbuf)); + printf("rule_name: %s - ip %s\n", nlmsg->rule_name, ip); + + } + + close(sock_fd); + + free(buf); + + return 0; +}