mirror of
git://git.code.sf.net/p/xtables-addons/xtables-addons
synced 2025-09-06 12:45:13 +02:00
Merge branch 'pknock'
This commit is contained in:
@@ -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)
|
||||
|
113
extensions/libxt_pknock.man
Normal file
113
extensions/libxt_pknock.man
Normal file
@@ -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.
|
1
extensions/pknock/.gitignore
vendored
Normal file
1
extensions/pknock/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
/pknlusr
|
@@ -1,3 +1,5 @@
|
||||
# -*- Makefile -*-
|
||||
|
||||
include ../../Makefile.extra
|
||||
|
||||
noinst_PROGRAMS = pknlusr
|
||||
|
93
extensions/pknock/pknlusr.c
Normal file
93
extensions/pknock/pknlusr.c
Normal file
@@ -0,0 +1,93 @@
|
||||
#include <sys/socket.h>
|
||||
#include <unistd.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <linux/netlink.h>
|
||||
#include <linux/connector.h>
|
||||
|
||||
#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;
|
||||
}
|
Reference in New Issue
Block a user