iptables 그리고 포트노킹

woonuk의 이미지

한동안 ssh 포트에 대한 무차별 스캐닝이 많아서 결국 포트를 바꿔서 운영하게 되었습니다.

맘편하게 잊고 지내다가 그동안 개념만 이해하고 있던 포트노킹을 iptables 로 설정하는
샘플을 보았습니니다.

http://www.portknocking.org/ 사이트에 다양한 내용들이 있더군요.

아래는 200, 100, 300 포트에 순서대로 접근 시도하는 경우에 해당 IP에 대하여
22번 포트를 오픈하는 iptables 설정입니다.

*mangle
:PREROUTING ACCEPT [0:0]
:INPUT ACCEPT [0:0]
:FORWARD ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]
:POSTROUTING ACCEPT [0:0]
-A INPUT -p tcp -m tcp --dport 200 -m recent --set --name PHASE1 --rsource -j ACCEPT
-A INPUT -p tcp -m tcp --dport 100 -m recent --rcheck --seconds 10 --name PHASE1 --rsource -j MARK --set-mark 420
-A INPUT -p tcp -m tcp --dport 300 -m recent --rcheck --seconds 10 --name PHASE2 --rsource -j MARK --set-mark 430
-A INPUT -m mark --mark 420 -m recent --remove --name PHASE1 --rsource -j MARK --set-mark 420
-A INPUT -m mark --mark 420 -m recent --set --name PHASE2 --rsource -j ACCEPT
-A INPUT -m mark --mark 430 -m recent --remove --name PHASE2 --rsource -j MARK --set-mark 430
-A INPUT -m mark --mark 430 -m recent --set --name PHASE3 --rsource -j MARK --set-mark 430
-A INPUT -m recent --update --seconds 60 --name PHASE3 --rsource -j MARK --set-mark 47
COMMIT
*filter
:INPUT ACCEPT [43:5330]
:FORWARD DROP [0:0]
:OUTPUT ACCEPT [23:3218]
-A INPUT -p tcp -m tcp --dport 22 -m mark --mark 47 -j ACCEPT
-A INPUT -p tcp -m tcp --dport 22 -j DROP
COMMIT

http://www.gophernet.org/articles/iptables.html 에서 recent 모듈에 해당하는
예제를 참고하였습니다. mark와 recent 모듈에 이해가 부족해서 많은 시행착오를
거쳤습니다 -_-;;

다음은 윈도에서 배치파일로 만들면 편할거 같아서 http://wiki.kldp.org/wiki.php/BeeJNetworkProgramming#s-5.2 client.c를 수정하였습니다.

/*
** Knock.c -- a stream socket client demo for portKnocking
** gcc -o Knock.exe Knock.c -lws2_32
*/
 
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
 
#include <sys/types.h>
#include <winsock.h>
 
int main(int argc, char *argv[])
{
    int port[20], i, count;
    char *errs="ports must be digit number.\n";
    char *ports;
    struct hostent *he;
    struct sockaddr_in their_addr; // connector's address information 
 
    WSADATA wsaData;  //if this doesn't work
    //WSAData wsaData; //then try this instead
 
    if(WSAStartup(MAKEWORD(1,1), &wsaData) != 0){
       fprintf(stderr, "WSAStartup failed.\n");
       exit(1);
    }
 
    if (argc < 5 ) {
        fprintf(stderr,"Usage: Knock.exe hostname [ port1 port2 port3 ... ]\n");
        exit(1);
    }
 
    if ( argc >= 5 ) {
        /* make sure the argument is an integer number */
		count = argc - 2;
		for ( i=2; i<5; i++ ) {
  	        ports=argv[i];
   	        while (*ports!='\0')  
    	        if (!isdigit(*(ports++))) {
                    printf(errs);
                    exit(1);
                }
            sscanf(argv[i],"%d",&port[i-2]);
        }
    }
 
    if ((he=gethostbyname(argv[1])) == NULL) {  // get the host info 
        //herror("gethostbyname");
        exit(1);
    }
 
    their_addr.sin_family = AF_INET;    // host byte order 
    their_addr.sin_addr = *((struct in_addr *)he->h_addr);
    memset(&(their_addr.sin_zero), '\0', 8);  // zero the rest of the struct 
 
    for (i=0; i < count; i++)
    {
		portKnock(port[i], their_addr); _sleep(1000);
    }
 
    //WSAClenaup();
    return 0;
}
 
int portKnock(int dport, struct sockaddr_in their_addr) {
    int sockfd;
    if ((sockfd = socket(PF_INET, SOCK_STREAM, 0)) == -1) {
        perror("socket");
        exit(1);
    }
 
    their_addr.sin_port = htons(dport);  // short, network byte order 
    printf("%d : ",dport); fflush(stdout);
    if (connect(sockfd, (struct sockaddr *)&their_addr, sizeof(struct sockaddr)) == -1) {
        perror("connect");
    }
    closesocket(sockfd);
    return 0;
}

MinGW [ http://sourceforge.net/projects/mingw ] 기본만 설치하고 gcc.exe 로 간단히
컴파일 되더군요.

무단포옹의 이미지

멋지셔요.
잘 지내시죠?!!

hanpedro의 이미지

휴.....

좀 더 쉽게해서 초보들을 위해 강좌로 올려주시면 고맙겠습니다.

자료는 저장하자!
저장해서 정리하지 않은 지식은 쓸모가 없다.