Just security.
Port knocking: a stealthy system for network authentication across closed ports
Port Knocking has not been seen on TV
port knocking > documentation > knockdaemon

Documentation

Perl prototype: v0.30

  • pcaplib support added; daemon no longer requires firewall log file

2004-Nov-14 18:59 | ...more

new Net::Pcap support added to sniff packets directly ...more

The manpages for the Perl implementation of port knocking are available here.


NAME

knockdaemon - a port knocking server responsible for monitoring and responding to incoming knocks generated by knockclient


SYNOPSIS

  # use a given configuration file (default knockdaemon.conf)
  > knockdaemon -conf mydaemonknock.conf

  # produce diagnostic messages
  > knockdaemon -debug

  # silent operation
  > knockdaemon -quiet

  # do everything, but do not respond to knocks in any way
  > knockdaemon -test

  # brief usage
  > knockdaemon -h

  # full man page
  > knockdaemon -man


DESCRIPTION

This is a prototype port knocking server. It is not designed for a production environment but as a sandbox for playing with the port knocking method.

The daemon has two modes of operation. The mode determines how the daemon detects knocks. At all times, the server maintains a port queue for each remote IP address and attempts to interpret (decode, decrypt) the port numbers into as information. Upon successful receipt of a knock, the server carries out actions, defined in an external configuration file. The server may respond immediately, or delay its response.


File Mode

The daemon monitors the firewall log file for signs of knocks.

  runmode = file


Network Mode

The daemon uses pcaplib to monitor the network interface directly. When this mode is activated, the daemon does not use a firewall log file. This means that you do not need to log connections to the ports in the knock span.

  runmode = network


OPTIONS


Configuration File

The server will try to read configuration files from these locations: ~/.knockdaemon.conf, BIN/knockdaemon.conf, BIN/etc/knockdaemon.conf, BIN/../etc/knockdaemon.conf, where BIN is the directory in which this script resides. The server reads only the first configuration file that it finds.

The configuration file is in an Apache-like format, as understood by the Config::General module. You must have this module installed.

Refer to the configuration file to learn about the supported parameters.


File Mode Operation

The server continually monitors the local firewall log file using File::Tail. Specify the log file using

  logfile = /var/log/firewall
  fileinterval = 0.5

Each new line in the log file is parsed for certain key fields which are used by the server. The maximum amount of time in seconds between checking the file is specified by fileinterval. For example, the server needs to know what the port is that is being knocked on. Specify the format of the log file using

  uselogformat = FORMAT

and define the format in a <logformat> block.

  <logformat FORMAT>
  required  = REGEXP
  interface = REGEXP
  protocol  = REGEXP
  ipserver  = REGEXP
  ipclient  = REGEXP
  port      = REGEXP
  </logformat>

The block contains a list of named regular expressions which are applied to each line. If you specify a regular expression with a 'required' field, then this regex must match the line before the line is processed. If the required regex does not match, the line will not be processed by the server. The 'required' field can be useful in reducing the parsing effort expended by the server when combined with --log-prefix option in IPTABLES (see RECIPES).

For all other regexps, if matched, their first capture field is associated with the regexp name. Thus, you may have

  port = DPT=([0-9]+)

and this would match the line

  ... DPT=883 ...

and assign 883 to the key 'port'.

You can define multiple log formats - use separate blocks - and then only adjust the 'uselogformat' parameter. For example,

  <logformat iptables>
  ...
  </logformat>

  <logformat ipchains>
  ...
  </logformat>

  <logformat mydebugformat>
  ...
  </logformat>

  #uselogformat = iptables
  #uselogformat = ipchains
  uselogformat  = mydebugformat

Consider using the network mode, described below. The file mode is less secure because the knocks may be reconstructed by a third party from the firewall log file.


Network Mode Operation

When operating in network mode, the daemon listens to the network interface specified by

  interface = lo

Packets are captured using Net::Pcap up to

  snapshotsize = 1500

bytes each. Each packet is tested using a specific filter set by

  useethfilter = tcplo

which is subsequently defined in a <ethfilter> block. For example,

  <ethfilter tcplo>
  proto   = IP_PROTO_TCP
  flags   = SYN
  dest_ip = 127.0.0.1
  </ethfilter>

This filter will accept only TCP packets with their SYN flag set destined to 127.0.0.1 (the loopback address). You can have as many filters defined as you wish, and select the one to use with ``useethfilter''. Refer to man page of the NetPacket modules for a full list of packet properties you can use in the filter. In general, a property is desribed either by a binary value (e.g. flags) or by a string (e.g. dest_ip). These two types are tested by ANDing the value of a symbol (e.g. flags & SYN) or testing with a regular expression (e.g. dest_ip =~ /xxx.xxx.xxx.xxx/), respectively.

The network mode is preferable to file mode, since the knocks are not stored in any file.


Knock Format

You must teach the server what to expect in a knock. The knock format is specified in the identical way as in the client's configuration file. Namely,

  knock = FIELD1,FIELD2,FIELD3,...

where the fields are the same as in for the client. See knockclient for details.


Responses

The job of the server is to respond to incoming knocks. After a knock has been detected, and successfully interpreted (decoded and possibly decrypted), the server may carry out some combination of (a) immediate response or (b) delayed response.

Responses are defined in the <actions> block in the configuration file. There are two types of subblocks: <action> defines the condition which needs to be satisfied before the action is carried out. The <action> block also defines a possible delay and the name of the action itself. The name of the action is associated with a system call in a <template> block.

For example, if you wanted the server to execute ``/bin/myprog'' when it received a knock on port 22.

  <actions>

  <action>
  condition = PORT == 22
  template  = exec_myprog
  use       = yes
  </action>

  <template exec_myprog>
  system = "/bin/myprog"
  </template>

  </actions>

All action blocks are parsed and blocks with satisfied conditions are evaluated. This means you can have multiple responses the to same knock. In the example below /bin/myprog is executed immediately, and then again after 5 minutes. Scheduling is done using Schedule::At.

  <actions>

  <action>
  condition = PORT == 22
  template  = exec_myprog
  use       = yes
  </action>

  <action>
  condition = PORT == 22
  template  = exec_myprog
  delay     = 5 # minutes
  use       = yes
  </action>

  <template exec_myprog>
  system = "/bin/myprog"
  </template>

  </actions>

To pass values from the data in the knock to the system call, use the field names as appear in the knock format.

  <template exec_myprog>
  system = "/bin/myprog FLAG0 FLAG1"
  </template>

  <template iptables_open>
  system    = "/sbin/iptables -D INPUT -p tcp -s IP/32 -d 0/0 --dport PORT --syn -j ACCEPT"
  </template>

The value for IP and PORT will be substituted into the command before its execution.


Maintaining State

It's possible to maintain state between knock receipts using a state template. The variable that holds the state can be optionally stored to a local cache file, to make the state persistent across instances of the knockdaemon.

The state variable is a hash which is keyed by any combination of knock parameters, such as IP, PORT, FLAG0, etc. Parameters such as TIME, THISYEAR, THISDAY, THISMONTH, etc, are also supported. The state variable is modified by a template defined as

  <template NAME>
  state = OP(state(KEY1,KEY2,...))
  </template>

where OP() is some operation on state, as understood by Perl. For example,

  <template incr_num_visits>
  state = state(IP,visits,num)++
  </template>

will increase the value of the state variable keyed by the IP, ``visits'' and ``num''. Fields like IP will be interpolated to their current value (in this case, the client's IP parsed from the knock).

The value assigned to the ``state'' parameter in the template must be formatted in such a way that after it is parsed, as described below, it can be passed to ``eval''. The string

  state(KEY1,KEY2,KEY3,...)++

is first parsed by replacing all keywords like IP, TIME, etc with their values. For example

  state(IP,visits,num)++ -> state(10.1.1.1,visits,num)++

Next, the string is adjusted so that it looks like a hash reference,

  state(10.1.1.1,visits,num)++ -> $STATE->{10.1.1.1}{visits}{num}++

This expression is passed to eval. The state is modified immediately. Any value of 'delay' defined in the action template does not affect how state is altered.

  • modifying state

    To modify the state, define a template with a ``state'' line. The line defines the expression to evaluate. This expression (presumably) modifies the state in some way.

      <action>
      condition = PORT == 22 && FLAG0 < 255
      template  = iptables_open
      template  = incr_num_visits
      use       = yes
      </action>
    

      <template incr_num_visits>
      state     = state(IP,visits,num)++
      </template>
    

    Each IP will get its own counter. You can combine values in different state variables and assign them to new state variables.

      <template incr_num_visits>
      state     = state(IP,c) = state(IP,a) + state(IP,b)
      </template>
    

    In this example, the following will be eval'ed, if IP is 10.1.1.1

      $STATE->{10.1.1.1}{c} = $STATE->{10.1.1.1}{a} + $STATE->{10.1.1.1}{b}
    
  • testing state

    The values in the state variable can be tested in the 'condition' of an action block.

      <action>
      condition = PORT == 22 && FLAG0 < 255 && state(IP,visits,num) < 1
      template  = iptables_open
      template  = incr_num_visits
      use       = yes
      </action>
    

    This action will be carried out only once, because one of its templates increments state(IP,visits,num) by calling the incr_num_visits template. On subsequent tests of the 'condition', state(IP,visits,num) < 1 will always fail.

  • using state in system calls

    To include values of the state variable in the system call, use the variable in the system value of the template.

      <template exec_myprog>
      system = "/bin/myprog state(IP,visits,num)"
      </template>
    
  • keeping persistent state

    To maintain the same state between invocations of knockdaemon, use

      statecache = /tmp/portknock.cache
      usecache   = yes
    

    If you set 'usecache', then the server will attempt to read from the cache during startup, and update the cache each time the state changes.


State Recipes

  • throttling responses

    Here is a recipe to limit the number of times port 22 is opened to a given IP to once per hour. The prev_visit template modifies the state variable which stores the time of the last knock from a given IP. The difference between this value and the current time is tested. You have to allow for the state variable to not exist, because during the first visit when the condition is being tested the state for this IP has not been initialized.

      <action>
      condition = PORT == 22 && ( ! state(IP,visits,prevtime) || TIME-state(IP,visits,prevtime) > 3600)
      template  = prev_visit
      template  = iptables_open
      use       = yes
      </action>
    

      <template prev_visit>
      state     = state(IP,visits,prevtime) = TIME
      </template>
    
  • limiting connections from an IP

    If you are paranoid, you can limit the number of connections from a remote IP. In this example, port 22 will be opened for the client IP only twice. At the third visit, state(IP,visits,num) < 2 will fail.

      <action>
      condition = PORT == 22 && state(IP,visits,num) < 2
      template  = num_visits
      template  = iptables_open
      use       = yes
      </action>
    

      <template num_visits>
      state     = state(IP,visits,num)++
      </template>
    
  • preventing replay with one time knocks (OTK)

    You can incorporate other values parsed from the knock into the state variable. By using one time knocks (OTK) you can force a client to use a different knock for each connection. In this example, the client uses FLAG0=1 for the first knock. The second time around FLAG0 must be incremented to pass the condition.

      knock   = IP,PORT,FLAG0,CHECKSUM
    

      <action>
      condition = PORT == 22 && state(IP,visits,PORT,num) < FLAG0
      template  = num_visits
      template  = iptables_open
      use       = yes
      </action>
    

      <template num_visits>
      state     = state(IP,visits,PORT,num)++
      </template>
    

    Thus, if the first knock is 10,1,1,1,22,1,36, the same knock will no longer work because FLAG0 must be incremented. Next time you connect, you'd use 10,1,1,1,22,2,37.

    Another way to implement OTKs is to set a key in the state hash corresponding to a knock variable, in this case FLAG0. The next knock must contain a different value for this variable.

      <action>
      condition = PORT == 22 && ! state(IP,uniqueval,FLAG0)
      template  = iptables_open
      template  = keyvisit
      use       = yes
      </action>
    

      <template keyvisit>
      state = state(IP,uniqueval,FLAG0)++
      </template>
    

    To add a expiration time to the OTK,

      # one hour
      condition = PORT == 22 && ! state(IP,uniqueval,THISHOUR,FLAG0)
    

      # one day
      condition = PORT == 22 && ! state(IP,uniqueval,THISDAY,FLAG0)
    


Port Mapping

The server will monitor only those ports which are within the knocking port span.

  portspan = a-b,c,d-e,...

You will need to reserver 2*N ports for the span. Non-contiguous ranges are supported. The encrypted knock sequence (you are encrypting the knock, right?) is mapped onto span ports, therefore the larger the value of N the fewer coding symbols.


Encryption

It is strongly suggested that you encrypt the knock. An encrypted knock will help to protect you from replay attacks, especially if you embed the client IP address in the knock and use one time knocks (see above).

To process an encrypted knock,

  encrypt = yes
  key     = my_secret_phrase
  cipher  = Blowfish | IDEA | DES | ...

You'll need to install the Crypt::XXX module to use cipher = XXX. If the incoming knock has an initialization vector (IV), use

  iv = yes

These settings must be matched by those in the client's configuration file.


RECIPES


Reducing log file parsing effort

IPTABLES support a --log-prefix option which is used to prepend a given string to each log file line triggered by a certain rule. To avoid the daemon's parsing of all log file lines, and limit it to look only at the lines associated with your knock ports (e.g. PORTm-PORTn).

First, set the prefix to some unique string for the range of ports you will be using.

  iptables -A INPUT -p tcp --dport PORTm:PORTn -j LOG --log-prefix "portknock"

Now add the appropriate 'required' regular expression to the logformat block in knockdaemon.conf.

  <logformat FORMAT>
  required  = portknock
  ...
  </logformat>

If your knock ports are not continuous (see knockdaemon.conf) such as

  portspan = 600-699,800-898,900,968-1023

you will need to patch iptables to support the -mport directive. The current release of IPTABLES (1.2.9 02-nov-2003) only supports --dport which requires that the port range be continuous. To perform the patch, install the patch-o-matic base package (www.netfilter.org) and answer ``yes'' to include the ``mport'' patch (not to be confused with the ``multiport'' patch). Once you've patched IPTABLES the command for including a prefix to non-contiguous ports is

  iptables -A INPUT -p tcp -mport --ports 600:699,800:898,900,968:1023 -j LOG --log-prefix "portknock"

This recipe was suggested by Sean (siegex@identityflux.com)


Reducing time between opening and closing a port

Sean (siegex@identityflux.com) suggested this method for ensuring that a port can be closed shortly after it is opened.

With this rule, the time between opening and closing the desired port can be very small, say 30sec to a 1min, just enough time for the person to connect. Because a state has been established once connected, removing the rule that opened up the port will have no effect on the newly established connection, or any established connection for that matter.

  iptables -I INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT


Keeping your knocks short

By allocating 32,768 ports in the port span, your knocks will be as short as possible.

The client maps a sequence of integers in the range 0-255 onto a sequence of ports. The larger the number of ports, the shorter the mapped sequence. If you use a 32,768 port span, such as

  portspan = 1024-5000,6000-25000,26000-35789

then a 16 port knock with a 256 port span will be 9 ports long. The total length of the port span must be a power of 2 and cannot exceed 32,768.


AUTHORS

Martin Krzywinski (martink@bcgsc.ca), Chris Rigby (kai@chaos.gen.nz), Thom Harrison (thom@cox.net), Stephan Muller (smuller@chronox.de)


SEE ALSO

knockclient, www.portknocking.org


HISTORY

  • 14 Nov 2004

    v0.30: The daemon can now scan for packets directly using Net::Pcap and NetPacket. If you choose to go this route, you do not need a firewall log file. Many thanks to Stephan Muller who made the modification to knockdaemon to use the Net* modules.

  • 12 July 2004

    v0.25: Added LOGIP as a field for templates. This field stores the IP as detected by the firewall log file. If you are knocking from a host whose IP (as seen by the listening daemon) cannot be easily obtained, use LOGIP in the templates instead of IP. Avoid using LOGIP unless you need this facility. Always include the IP in your knock, which you encrypt.

  • 9 July 2004

    v0.24.1: Fixed bug in generating concatenated binary representation (Ingo Roessler, ingo.roessler@datext.de).

  • 5 July 2004

    v0.24: Port range expanded to 0-65,535. mod 255 bug fixed (Thom Harrison, thom@cox.net). Port span range extended beyond 256 ports to 2^N, N < 16. Knocks are therefore shorter, since the number of coding symbols is increased.

  • 17 April 2004

    v0.23.1: Fixed IPTABLES rules - chain names are now capitalized. Queue contents were incorrectly being written to STDOUT even when quiet was set. Added support for variable delay between log file checks via 'fileinterval'. Thanks to Sean (siegex@identityflux.com) for reporting these.

  • 16 April 2004

    v0.23: Fixed protocol regex for IPCHAINS and checksum bugs reported by Sean (siegex@identityflux.com) Added lo and ppp[0-9]+ to possible interfaces as suggested by Sean.

  • 11 April 2004

    v0.22: Port span permits non-contiguous knock ports. THISYEAR added. Additional documentation for one time knocks (OTK) and anti-replay measures.

  • 23 February 2004

    v0.21: The daemon now maintains state.

  • 20 February 2004

    v0.20: Parameters now in external configuration files. Added support for custom log file format and custom knock format.

  • 25 February 2003

    v0.10: Initial Perl prototype


COPYRIGHT

(c) 2002-2004 Martin Krzywinski

All rights reserved. This port knocking implementation is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version.

Refer to COPYING in this distribution for the complete GPL license

last updated 2003-Jul-26 00:27
Port Knocking (c) 2002-2014 Martin Krzywinski