본문 바로가기

nftables

Port Knocking 활용

source1: https://wiki.nftables.org/wiki-nftables/index.php/Port_knocking_example

 

source2: http://choesin.com/linux%EC%97%90%EC%84%9C-%ED%8F%AC%ED%8A%B8-%EB%85%B8%ED%82%B9%EC%9D%84-%EC%82%AC%EC%9A%A9%ED%95%98%EB%8A%94-%EB%B0%A9%EB%B2%95-%EB%B0%8F-%EC%82%AC%EC%9A%A9%ED%95%98%EC%A7%80-%EC%95%8A%EC%95%84

 

이전 글: nftables examples

 

포트 노킹은 비밀 노크를 말합니다. 특정한 port sequence를 입력하면 출입을 허용하거나 닫는 것을 말합니다. 그런데 port sequence는 어떤 이유로든 노출 가능성이 있으므로, 사용하지 않는 것을 권장합니다. 그러나 특수한 상황에서는 임시로 또는 보완적인 수단으로 이용할 수는 있습니다. 

 

필자는 Ubuntu 18.04 시스템을 이용합니다. 

1. nftables ruleset을 이용한 port knokcing

source1에 있는 Exmaple2를 기반으로 합니다. Example 1은 아래와 같은 error를 맞이합니다. 그래서 error가 없는 Example2만 살펴봅니다. 

port:29:31-33: Error: syntax error, unexpected add, expecting newline or semicolon
		tcp dport 123 add @candidates_ipv4 {ip  saddr . 234 timeout 1s}
                              ^^^

 

아래 ruleset을 파일로 만들어 error 없이 읽어 적용 가능합니다. sourc1에 있는 Exampel2에 1줄을 추가해 port sequence를 정확히 보내고 22번 포트로 ssh 하도록 하였습니다. (ip saddr @Knocked_4 tcp dport 22 accept를 추가)

 

아래 로직을 살펴보면 중요한 내용을 이렇습니다. 

  • port 123, 234, 345, 456을 차례로 30초 이내에 시도한 source address를 허용해 줍니다. 
  • port 123을 시도한 source address를 set Knocked_1에 10초간 저장합니다. Knocked_1에 있는 source address 중에서 10초 이내에 port 234를 시도한 source address를 set Knocked_2에 10초간 저장합니다. Knocked_2에 있는 source address 중에서 10초 이내에 port 345를 시도한 source address를 set Knocked_3에 10초간 저장합니다. Knocked_3에 있는 source address 중에서 10초 이내에 port 456를 시도한 source address를 set Knocked_4에 2분 저장합니다. 
  • set Knocked_4에 있는 source address중에서 ssh(22)를 시도하면 ssh 를 허용합니다. 
  • set Knocked_1에 있는 source address가 다음 sequence port 234를 10초 이내에 시도하지 않으면 set Knocked_1에서 삭제합니다. set Knocked_2에 있는 source address가 다음 sequence port 345를 10초 이내에 시도하지 않으면 set Knocked_2에서 삭제합니다. set Knocked_3에 있는 source address가 다음 sequence port 456를 10초 이내에 시도하지 않으면 set Knocked_3에서 삭제합니다. set Knocked_4에 있는 source address가 다음 sequence port 22를 2분 이내에 시도하지 않으면 set Knocked_4에서 삭제합니다. 이 경우 ssh를 시도해도 성공할 수 없습니다. 
#!/usr/bin/env nft -f
flush ruleset

table ip Inet4 {
  set Knocked_1 {
    type ipv4_addr
    flags timeout
    timeout 10s
    gc-interval 4s
  }
  set Knocked_2 {
    type ipv4_addr
    flags timeout
    timeout 10s
    gc-interval 4s
  }
  set Knocked_3 {
    type ipv4_addr
    flags timeout
    timeout 10s
    gc-interval 4s
  }
  set Knocked_4 {
    type ipv4_addr
    flags timeout
    timeout 2m
    gc-interval 4s
  }

  chain Knock_1 {
    set add ip saddr @Knocked_1
  }
  chain Unknock_1 {
    set update ip saddr timeout 0s @Knocked_1
  }
  chain Knock_2 {
    set update ip saddr timeout 0s @Knocked_1
    set add ip saddr @Knocked_2
  }
  chain Unknock_2 {
    set update ip saddr timeout 0s @Knocked_2
  }
  chain Knock_3 {
    set update ip saddr timeout 0s @Knocked_2
    set add ip saddr @Knocked_3
  }
  chain Unknock_3 {
    set update ip saddr timeout 0s @Knocked_3
  }
  chain Knock_4 {
    set update ip saddr timeout 0s @Knocked_3
    set add ip saddr @Knocked_4 log prefix "Port-Knock accepted: "
  }

  chain RefreshKnock {
    set update ip saddr timeout 2m @Knocked_4
  }

  chain PortKnock {
    ct state new ip saddr @Knocked_4 goto RefreshKnock
    tcp dport 456 ct state new ip saddr @Knocked_3 goto Knock_4
    tcp dport 345 ct state new ip saddr @Knocked_3 return
    ip saddr @Knocked_3 ct state new goto Unknock_3
    tcp dport 345 ct state new ip saddr @Knocked_2 goto Knock_3
    tcp dport 234 ct state new ip saddr @Knocked_2 return
    ip saddr @Knocked_2 ct state new goto Unknock_2
    tcp dport 234 ct state new ip saddr @Knocked_1 goto Knock_2
    tcp dport 123 ct state new ip saddr @Knocked_1 return
    ip saddr @Knocked_1 ct state new goto Unknock_1
    tcp dport 123 ct state new goto Knock_1
  }

  chain FilterIn {
    type filter hook input priority 0
    policy drop

    # allow established/related connections
    ct state established,related accept

    # early drop of invalid connections
    ct state invalid drop

    # allow from loopback
    meta iif lo accept

    # allow icmp
    ip protocol icmp accept

    # port-knocking
    jump PortKnock

    # misc. filtering
    # ...
    ip saddr @Knocked_4 tcp dport 22 accept

  }

  chain FilterOut {
    type filter hook output priority 0
    policy accept
  }
}

다음은 port sequence를 하나하나 수행하며 ruleset을 dump한 것입니다. set Knocked_1, Knocked_2, Knocked_3, Knocked_4를 살펴봅니다. set Knocked_4에 있는 source address에서 r3로 ssh를 시도하면 접근 가능해집니다. 

 

client r2 192.168.23.2 ------------------ 192.168.23.3 r3 server

 

client r2

salsal@r2:~$ ssh salsal@192.168.23.3 -p123
^C
salsal@r2:~$ ssh salsal@192.168.23.3 -p234
^[[A^C
salsal@r2:~$ ssh salsal@192.168.23.3 -p345
^C
salsal@r2:~$ ssh salsal@192.168.23.3 -p456
^C
salsal@r2:~$ ssh salsal@192.168.23.3
salsal@192.168.23.3's password:

server r3

salsal@r3:~$ sudo nft list ruleset | head -29
table ip Inet4 {
	set Knocked_1 {
		type ipv4_addr
		size 65535
		timeout 10s
		gc-interval 4s
		elements = { 192.168.23.2 timeout 10s expires 7s }
	}

	set Knocked_2 {
		type ipv4_addr
		size 65535
		timeout 10s
		gc-interval 4s
	}

	set Knocked_3 {
		type ipv4_addr
		size 65535
		timeout 10s
		gc-interval 4s
	}

	set Knocked_4 {
		type ipv4_addr
		size 65535
		timeout 2m
		gc-interval 4s
	}
    
salsal@r3:~$ sudo nft list ruleset | head -30
table ip Inet4 {
	set Knocked_1 {
		type ipv4_addr
		size 65535
		timeout 10s
		gc-interval 4s
		elements = { 192.168.23.2 timeout 10s expires 7s }
	}

	set Knocked_2 {
		type ipv4_addr
		size 65535
		timeout 10s
		gc-interval 4s
		elements = { 192.168.23.2 timeout 10s expires 7s }
	}

	set Knocked_3 {
		type ipv4_addr
		size 65535
		timeout 10s
		gc-interval 4s
	}

	set Knocked_4 {
		type ipv4_addr
		size 65535
		timeout 2m
		gc-interval 4s
	}
    
salsal@r3:~$ sudo nft list ruleset | head -30
table ip Inet4 {
	set Knocked_1 {
		type ipv4_addr
		size 65535
		timeout 10s
		gc-interval 4s
		elements = { 192.168.23.2 timeout 10s expires 2s }
	}

	set Knocked_2 {
		type ipv4_addr
		size 65535
		timeout 10s
		gc-interval 4s
		elements = { 192.168.23.2 timeout 10s expires 6s }
	}

	set Knocked_3 {
		type ipv4_addr
		size 65535
		timeout 10s
		gc-interval 4s
		elements = { 192.168.23.2 timeout 10s expires 6s }
	}

	set Knocked_4 {
		type ipv4_addr
		size 65535
		timeout 2m
		gc-interval 4s
    }
    
salsal@r3:~$ sudo nft list ruleset | head -31
table ip Inet4 {
	set Knocked_1 {
		type ipv4_addr
		size 65535
		timeout 10s
		gc-interval 4s
	}

	set Knocked_2 {
		type ipv4_addr
		size 65535
		timeout 10s
		gc-interval 4s
		elements = { 192.168.23.2 timeout 10s expires 1s }
	}

	set Knocked_3 {
		type ipv4_addr
		size 65535
		timeout 10s
		gc-interval 4s
		elements = { 192.168.23.2 timeout 10s expires 4s }
	}

	set Knocked_4 {
		type ipv4_addr
		size 65535
		timeout 2m
		gc-interval 4s
		elements = { 192.168.23.2 timeout 2m expires 1m57s }
	}
    
salsal@r3:~$ sudo nft list ruleset | head -30
table ip Inet4 {
	set Knocked_1 {
		type ipv4_addr
		size 65535
		timeout 10s
		gc-interval 4s
	}

	set Knocked_2 {
		type ipv4_addr
		size 65535
		timeout 10s
		gc-interval 4s
	}

	set Knocked_3 {
		type ipv4_addr
		size 65535
		timeout 10s
		gc-interval 4s
	}

	set Knocked_4 {
		type ipv4_addr
		size 65535
		timeout 2m
		gc-interval 4s
		elements = { 192.168.23.2 timeout 2m expires 1m59s }
	}

 

2. knockd를 이용한 port knocking

2.1. server에서 knockd 설치

salsal@r3:~$ sudo apt policy knockd
knockd:
  Installed: (none)
  Candidate: 0.7-1ubuntu1.18.04.3
  Version table:
     0.7-1ubuntu1.18.04.3 500
        500 http://kr.archive.ubuntu.com/ubuntu bionic-updates/universe amd64 Packages
     0.7-1ubuntu1 500
        500 http://kr.archive.ubuntu.com/ubuntu bionic/universe amd64 Packages

salsal@r3:~$ sudo apt install knockd

salsal@r3:~$ sudo apt policy knockd
knockd:
  Installed: 0.7-1ubuntu1.18.04.3
  Candidate: 0.7-1ubuntu1.18.04.3

 

2.2. server에서 iptables-persistent 설치

salsal@r3:~$ sudo apt policy iptables-persistent
iptables-persistent:
  Installed: (none)
  Candidate: 1.0.4+nmu2ubuntu1
  Version table:
     1.0.4+nmu2ubuntu1 500
        500 http://kr.archive.ubuntu.com/ubuntu bionic-updates/universe amd64 Packages
        500 http://kr.archive.ubuntu.com/ubuntu bionic-updates/universe i386 Packages
     1.0.4+nmu2 500
        500 http://kr.archive.ubuntu.com/ubuntu bionic/universe amd64 Packages
        500 http://kr.archive.ubuntu.com/ubuntu bionic/universe i386 Packages

 

IPv4 rules 구성에 대해서 Yes/No 선택을 해야하는데 Yes를 선택합니다. 

IPv6 rules 구성에 대해서 Yes/No 선택을 해야하는데 Yes 또는 No를 선택합니다. 

설치를 완료합니다. 

salsal@r3:~$ sudo apt policy iptables-persistent
iptables-persistent:
  Installed: 1.0.4+nmu2ubuntu1
  Candidate: 1.0.4+nmu2ubuntu1

설치를 완료하고 나면 netfilter-persistent 서비스를 실행한 것을 알 수 있습니다. 

salsal@r3:~$ sudo systemctl status netfilter-persistent
● netfilter-persistent.service - netfilter persistent configuration
   Loaded: loaded (/lib/systemd/system/netfilter-persistent.service; enabled; vendor preset: enabled)
   Active: active (exited) since Tue 2021-08-10 20:36:50 KST; 3min 5s ago
 Main PID: 3162 (code=exited, status=0/SUCCESS)
    Tasks: 0 (limit: 4664)
   CGroup: /system.slice/netfilter-persistent.service

 8월 10 20:36:50 r3 systemd[1]: Starting netfilter persistent configuration...
 8월 10 20:36:50 r3 netfilter-persistent[3162]: run-parts: executing /usr/share/netfilter-persistent/plugins.d/15-ip4tables start
 8월 10 20:36:50 r3 netfilter-persistent[3162]: Warning: skipping IPv4 (no rules to load)
 8월 10 20:36:50 r3 netfilter-persistent[3162]: run-parts: executing /usr/share/netfilter-persistent/plugins.d/25-ip6tables start
 8월 10 20:36:50 r3 netfilter-persistent[3162]: Warning: skipping IPv6 (no rules to load)
 8월 10 20:36:50 r3 systemd[1]: Started netfilter persistent configuration.

그렇지만 iptables의 변화는 없이 default chain만 있는 것을 알 수 있습니다. 

salsal@r3:~$ sudo iptables -S
-P INPUT ACCEPT
-P FORWARD ACCEPT
-P OUTPUT ACCEPT

 

2.3. server에서 iptables 기본 설정

* iptables 상세내역은 필자의 글  Ubuntu ufw & iptables & nftables를 참고하면 됩니다. 따라서 여기에서는 knockd를 위한 필수 설정만 공유합니다. 

 

 

2.4. server에서 netfilter-persistent 실행 및 설정 저장

* netfilter-persistent에 대해서는 필자의 글  Ubuntu iptables 저장, 리부팅 뒤 자동 복구에서 정리한 내용을 참고하면 됩니다. 따라서 여기에서는 knockd를 위한 필수 설정만 공유합니다. 

 

# 이미 있는 연결이 끊어지지 않도록 아래 룰을 입력합니다. 
salsal@r3:~$ sudo iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT

# ssh를 차단합니다. 
salsal@r3:~$ sudo iptables -A INPUT -p tcp --dport 22 -j REJECT

# 새로 추가한 룰을 확인합니다. 
salsal@r3:~$ sudo iptables -S
-P INPUT ACCEPT
-P FORWARD ACCEPT
-P OUTPUT ACCEPT
-A INPUT -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
-A INPUT -p tcp -m tcp --dport 22 -j REJECT --reject-with icmp-port-unreachable

# 새로 추가한 룰이 /etc/iptables/rules.v4에는 없습니다. 
salsal@r3:~$ more /etc/iptables/rules.v4
# Generated by iptables-save v1.6.1 on Tue Aug 10 20:36:51 2021
*filter
:INPUT ACCEPT [1182:104985]
:FORWARD ACCEPT [0:0]
:OUTPUT ACCEPT [902:88025]
COMMIT
# Completed on Tue Aug 10 20:36:51 2021

# netfilter-persistent save로 설정한 룰을 파일에 저장합니다. 
salsal@r3:~$ sudo netfilter-persistent save
run-parts: executing /usr/share/netfilter-persistent/plugins.d/15-ip4tables save
run-parts: executing /usr/share/netfilter-persistent/plugins.d/25-ip6tables save

# 저장한 룰을 확인합니다. 
salsal@r3:~$ sudo cat /etc/iptables/rules.v4
# Generated by iptables-save v1.6.1 on Tue Aug 10 20:55:12 2021
*filter
:INPUT ACCEPT [18:1252]
:FORWARD ACCEPT [0:0]
:OUTPUT ACCEPT [136:11207]
-A INPUT -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
-A INPUT -p tcp -m tcp --dport 22 -j REJECT --reject-with icmp-port-unreachable
COMMIT
# Completed on Tue Aug 10 20:55:12 2021

netfitler-persistent reload나 systemctl restart netfilter-persistent를 수행하여 저장한 파일에서 룰을 읽어오고 확인합니다. 

salsal@r3:~$ sudo netfilter-persistent reload
run-parts: executing /usr/share/netfilter-persistent/plugins.d/15-ip4tables start
run-parts: executing /usr/share/netfilter-persistent/plugins.d/25-ip6tables start

salsal@r3:~$ sudo iptables -S
-P INPUT ACCEPT
-P FORWARD ACCEPT
-P OUTPUT ACCEPT
-A INPUT -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
-A INPUT -p tcp -m tcp --dport 22 -j REJECT --reject-with icmp-port-unreachable

salsal@r3:~$ sudo systemctl restart netfilter-persistent
salsal@r3:~$ sudo iptables -S
-P INPUT ACCEPT
-P FORWARD ACCEPT
-P OUTPUT ACCEPT
-A INPUT -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
-A INPUT -p tcp -m tcp --dport 22 -j REJECT --reject-with icmp-port-unreachable

 

2.5. server에서 /etc/knockd.conf 수정

"2.1. knockd 설치" 과정을 거치면 /etc/knockd.conf 파일을 생성합니다. 그 내용은 다음과 같습니다. 

의미는 아래와 같습니다. 

  • SSH를 허용하는 부분 [openSSH]
  • SSH를 차단하는 부분 [closeSSH]
  • SSH를 허용하는 port sequence는 7000, 8000, 9000
  • SSH를 차단하는 port sequence는 7000, 8000, 9000
  • port sequence 7000, 8000, 9000에 대해서 timeout은 5초
  • port sequence는 7000, 8000, 9000 즉 access request (syn)에 대해서만 적용
  • 허용하기 위해서 iptables chain INPUT에 허용하는 룰 추가
  • 차단하기 위해서 iptables chain INPUT에 지우는 룰 추가
salsal@r3:~$ more /etc/knockd.conf
[options]
	UseSyslog

[openSSH]
	sequence    = 7000,8000,9000
	seq_timeout = 5
	command     = /sbin/iptables -A INPUT -s %IP% -p tcp --dport 22 -j ACCEPT
	tcpflags    = syn

[closeSSH]
	sequence    = 9000,8000,7000
	seq_timeout = 5
	command     = /sbin/iptables -D INPUT -s %IP% -p tcp --dport 22 -j ACCEPT
	tcpflags    = syn

여기에서는 이 파일 내용을 조금 변경합니다. 수정한 내용은 seq_timeout을 15로 변경하고, "-A INPUT"을 "-I INPUT"으로 변경한 것입니다. "-A" 대신 "-I"로 변경한 이유는 port sequence 7000,8000,9000을 입력한 client에 대해 "-A INPUT -p tcp -m tcp --dport 22 -j REJECT" 이 룰보다 앞에 끼워 넣어 허용해 주기 위해서입니다. 

salsal@r3:~$ cat /etc/knockd.conf
[options]
	UseSyslog

[openSSH]
	sequence    = 7000,8000,9000
	seq_timeout = 15
	command     = /sbin/iptables -I INPUT -s %IP% -p tcp --dport 22 -j ACCEPT
	tcpflags    = syn

[closeSSH]
	sequence    = 9000,8000,7000
	seq_timeout = 15
	command     = /sbin/iptables -D INPUT -s %IP% -p tcp --dport 22 -j ACCEPT
	tcpflags    = syn

 

2.6. server에서 /etc/default/knockd 수정

"2.1. knockd 설치" 과정을 거치면 /etc/default/knockd 파일을 생성합니다. 그 내용은 다음과 같습니다. 

START_KNOCKD=0

  • knockd를 비활성해 놓은 상태입니다. 값을 1로 변경하면 활성화됩니다. 

#KNOCK_OPTS="-i eth1"

  • knockd를 적용하는 interface입니다. comment out을 하지 않으면 모든 interface에 적용합니다. 
salsal@r3:~$ more /etc/default/knockd
# control if we start knockd at init or not
# 1 = start
# anything else = don't start
# PLEASE EDIT /etc/knockd.conf BEFORE ENABLING
START_KNOCKD=0

# command line options
#KNOCKD_OPTS="-i eth1"

그런데 systemctl start knockd를 수행하고 상태를 살펴보면 error가 납니다. 내용을 살펴보면 eth0가 있다고 가정하고 있습니다. 따라서 실제 사용하는 interface를 찾아 /etc/default/knockd에 명시해 줘야 합니다. 

salsal@r3:~$ sudo systemctl status knockd
● knockd.service - Port-Knock Daemon
   Loaded: loaded (/lib/systemd/system/knockd.service; disabled; vendor preset: enabled)
   Active: failed (Result: exit-code) since Tue 2021-08-10 21:24:47 KST; 3s ago
     Docs: man:knockd(1)
  Process: 3759 ExecStart=/usr/sbin/knockd $KNOCKD_OPTS (code=exited, status=1/FAILURE)
 Main PID: 3759 (code=exited, status=1/FAILURE)

 8월 10 21:24:47 r3 systemd[1]: Started Port-Knock Daemon.
 8월 10 21:24:47 r3 knockd[3759]: could not open eth0: eth0: No such device exists (SIOCGIFHWADDR: No such device)
 8월 10 21:24:47 r3 systemd[1]: knockd.service: Main process exited, code=exited, status=1/FAILURE
 8월 10 21:24:47 r3 systemd[1]: knockd.service: Failed with result 'exit-code'.

 

다음은 그 과정과 결과를 보여줍니다. 

salsal@r3:~$ sudo more /etc/default/knockd
# control if we start knockd at init or not
# 1 = start
# anything else = don't start
# PLEASE EDIT /etc/knockd.conf BEFORE ENABLING
START_KNOCKD=1

# command line options
KNOCKD_OPTS="-i enp0s10"

salsal@r3:~$ sudo systemctl restart knockd

salsal@r3:~$ sudo systemctl status knockd
● knockd.service - Port-Knock Daemon
   Loaded: loaded (/lib/systemd/system/knockd.service; disabled; vendor preset: enabled)
   Active: active (running) since Tue 2021-08-10 21:26:46 KST; 2s ago
     Docs: man:knockd(1)
 Main PID: 3773 (knockd)
    Tasks: 1 (limit: 4664)
   CGroup: /system.slice/knockd.service
           └─3773 /usr/sbin/knockd -i enp0s10

 8월 10 21:26:46 r3 systemd[1]: Started Port-Knock Daemon.
 8월 10 21:26:46 r3 knockd[3773]: starting up, listening on enp0s10

 

2.7. server에서 knockd 실행

systemctl start knockd를 수행하고 상태를 살펴보면 error가 납니다. 내용을 살펴보면 eth0가 있다고 가정하고 있습니다. 따라서 실제 사용하는 interface를 찾아 /etc/default/knockd에 명시해 줘야 합니다.

salsal@r3:~$ sudo systemctl status knockd
● knockd.service - Port-Knock Daemon
   Loaded: loaded (/lib/systemd/system/knockd.service; disabled; vendor preset: enabled)
   Active: failed (Result: exit-code) since Tue 2021-08-10 21:24:47 KST; 3s ago
     Docs: man:knockd(1)
  Process: 3759 ExecStart=/usr/sbin/knockd $KNOCKD_OPTS (code=exited, status=1/FAILURE)
 Main PID: 3759 (code=exited, status=1/FAILURE)

 8월 10 21:24:47 r3 systemd[1]: Started Port-Knock Daemon.
 8월 10 21:24:47 r3 knockd[3759]: could not open eth0: eth0: No such device exists (SIOCGIFHWADDR: No such device)
 8월 10 21:24:47 r3 systemd[1]: knockd.service: Main process exited, code=exited, status=1/FAILURE
 8월 10 21:24:47 r3 systemd[1]: knockd.service: Failed with result 'exit-code'.

 

다음은 그 과정과 결과를 보여줍니다. 

salsal@r3:~$ sudo more /etc/default/knockd
# control if we start knockd at init or not
# 1 = start
# anything else = don't start
# PLEASE EDIT /etc/knockd.conf BEFORE ENABLING
START_KNOCKD=1

# command line options
KNOCKD_OPTS="-i enp0s10"

salsal@r3:~$ sudo systemctl restart knockd

salsal@r3:~$ sudo systemctl status knockd
● knockd.service - Port-Knock Daemon
   Loaded: loaded (/lib/systemd/system/knockd.service; disabled; vendor preset: enabled)
   Active: active (running) since Tue 2021-08-10 21:26:46 KST; 2s ago
     Docs: man:knockd(1)
 Main PID: 3773 (knockd)
    Tasks: 1 (limit: 4664)
   CGroup: /system.slice/knockd.service
           └─3773 /usr/sbin/knockd -i enp0s10

 8월 10 21:26:46 r3 systemd[1]: Started Port-Knock Daemon.
 8월 10 21:26:46 r3 knockd[3773]: starting up, listening on enp0s10

 

2.8. client에서 knock 실행 및 ssh 실행

client r2에서도 apt install knockd를 설치합니다. knock client를 얻기 위해서입니다. 

 

그리고 다음과 같이 port sequence 7000, 8000, 9000을 보내고 ssh를 시도하면 ssh login prompt를 만납니다. 

 

salsal@r2:~$ ssh salsal@192.168.23.3
ssh: connect to host 192.168.23.3 port 22: Connection refused

salsal@r2:~$ knock 192.168.23.3 7000 8000 9000 -d 500

salsal@r2:~$ ssh salsal@192.168.23.3
salsal@192.168.23.3's password:

 

server r3의 /var/log/syslog를 살펴보면 openSSH 관련 로그를 볼 수 있습니다. 

Aug 10 21:30:18 r3 knockd: 192.168.23.2: openSSH: Stage 1
Aug 10 21:30:18 r3 knockd: 192.168.23.2: openSSH: Stage 2
Aug 10 21:30:19 r3 knockd: 192.168.23.2: openSSH: Stage 3
Aug 10 21:30:19 r3 knockd: 192.168.23.2: openSSH: OPEN SESAME
Aug 10 21:30:19 r3 knockd: openSSH: running command: /sbin/iptables -I INPUT -s 192.168.23.2 -p tcp --dport 22 -j ACCEPT

 그리고 iptables을 보면 룰이 추가된 것을 확인할 수 있습니다. 

salsal@r3:~$ sudo iptables -S
-P INPUT ACCEPT
-P FORWARD ACCEPT
-P OUTPUT ACCEPT
-A INPUT -s 192.168.23.2/32 -p tcp -m tcp --dport 22 -j ACCEPT
-A INPUT -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
-A INPUT -p tcp -m tcp --dport 22 -j REJECT --reject-with icmp-port-unreachable

 

반대로 client에서 port sequence 9000 8000 7000을 수행하면 iptables에서 해당 룰이 사라진 것을 볼 수 있습니다. 

#client r2

salsal@r2:/var/log$ knock 192.168.23.3 9000 8000 7000 -d 500



# server r3

salsal@r3:~$ sudo iptables -S
-P INPUT ACCEPT
-P FORWARD ACCEPT
-P OUTPUT ACCEPT
-A INPUT -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
-A INPUT -p tcp -m tcp --dport 22 -j REJECT --reject-with icmp-port-unreachable

 

 

이전 글: nftables examples

 

'nftables' 카테고리의 다른 글

nftables examples  (0) 2021.08.06
Output text modifier  (0) 2021.08.06
룰셋 Ruleset debug/tracing  (0) 2021.08.06
Scripting  (0) 2021.08.06
룰셋 갱신 지켜보기 / Monitoring ruleset updates  (0) 2021.08.06