firewall-cmd 를 쉽게 사용하기 위해 bash의 자동 완성 기능을 참고하세요.


firewalld 란


Linux 커널 2.2 까지는 ipchains 이라는 패킷 필터링/방화벽 프레임워크가 구현되어 있었고 2.4부터는 더 유연하고 다양한 기능을 가진 netfilter 로 프레임워크가 교체 되었습니다.


iptables 은 netfilter 프레임워크의 최상단에 위치하는 사용자 레벨의 프로그램으로 시스템 관리자는 iptables 명령어로 리눅스 서버로 들어오고 나가는 패킷을 필터링하거나 포트 포워딩을 설정할 수 있으며 방화벽으로도 사용할 수 있습니다.


iptables 는 숙련된 관리자가 아니면 사용이 어려운 단점이 있었는데 이런 문제를 해결하고자 RHEL/CentOS 7 부터는 방화벽을 firewalld 라는 데몬으로 교체하였고 이에 따라 사용자 레벨의 프로그램은 iptables 명령어 대신 명령행에서는 firewall-cmd , GUI 환경에서는 firewall-config 를 사용하게 되었습니다.


이 절에서는 firewall-cmd 명령을 사용하여 firewalld 데몬을 통해 방화벽 정책을 구성하고 수정하는 것을 학습하게 됩니다.


영구적 규칙과 정책 재구동

기본적으로 firewall-cmd 로 방화벽 정책을 변경했을 경우 현재 구동되고 있는 firewalld 에 즉시 적용되지만 정책은 지속성이 없이 임시로 적용되며 정책을 재구동하는 명령어인 firewall-cmd --reload 를 실행하거나 시스템을 재부팅하면 예전 정책으로 다시 초기화 되며 이로 인해 서비스의 장애가 발생할 수 있습니다.


이때문에 방화벽 정책을 영구적으로 유지하기 위해서는 --permanent 옵션을 추가해서 실행하면 되지만 이는 즉시 적용되지 않고 firewall-cmd --reload 명령어로 방화벽 정책을 재구동하거나 재부팅을 하기 전에는 변경한 방화벽 설정이 적용되지 않습니다.


firewalld 를 처음에 사용할 때 이 때문에 혼란을 겪고 정책 설정을 잘못한 걸로 오해하는 사용자가 많으므로 아래 표를 보고 방화벽 정책의 즉시 적용 여부와 지속성 여부를 꼭 익혀 두어야 합니다.



예로 fireall-cmd 로 정책을 추가했을 경우 즉시 반영되지만 만약 잘못된 설정이었을 경우에 firewall-cmd --reload 명령어를 실행하면 예전 정책으로 복구됩니다.


하지만 firewall-cmd --permanent  로 정책을 추가했을 경우 firewall-cmd --reload 명령어를 실행해야 변경한 정책이 적용되며 예전 정책으로 복구하려면 새로 추가한 정책을 삭제하고 다시 방화벽을 구동해야 합니다.

만약 방화벽 정책 변경시 즉시 반영하고 재부팅시에도 유지되도록 하고 싶으면 firewall-cmd 를 두 번 호출하면 되며 한 번은 --permanet 옵션을 주고 한 번은 옵션을 빼면 됩니다.


Network Zone

firewalld 의 특징중 하나는 이 장의 서두에서 설명한 네트워크 구성과 영역을 기본 설정으로 포함시켰다는 것입니다. 이에 따라 서버가 어떤 zone에 포함될 지만 정의하면 상대적으로 설정해야 하는 부분이 대폭 간소화 되었습니다.


먼저 기본 설정된 zone 의 목록을 확인하려면 firewall-cmd 명령에 --get-zones 옵션을 주고 실행하면 됩니다.

표시된 목록은 사전 정의된  zone 으로 이제 서버의 용도에 맞게 zone 을 할당하면 되며 해당 zone 이 어떤 정책을 갖고 있는지 확인하려면 --list-all-zones 옵션으로 사전 정의된 zone 에 대한 자세한 정보를 볼 수 있습니다.

각 zone 별 services 항목을 보면 해당 zone 에서 기본적으로 허용하는 서비스 포트를 확인할 수 있으며 dmz 는 ssh 를, internal 은 dhcpv6-client, mdns 등을 허용하는 것을 알수 있습니다.


현재 활성화된 zone 을 확인하려면 --get-active-zone 옵션을 실행하면 되며 현재 활성화된 zone 은 public 인 것을 알수 있습니다.


존 설정 정보 보기(--list-all)

현재 활성화된 존 정보를 확인하려면 --list-all  옵션을 사용하면 됩니다. 아래는 현재 활성화된 존이 dmz 이고 10022 등의 port 가 열려 있음을 표시합니다.

$ sudo firewall-cmd --list-all 


dmz (active)
  target: default
  icmp-block-inversion: no
  interfaces: enp5s0f0 enp5s0f1
  sources: 
  services: ssh http https
  ports: 10022/tcp 2120-2121/tcp 20/tcp 2120-2142/tcp 10000/tcp
  protocols: 
  masquerade: no
  forward-ports: 
  source-ports: 
  icmp-blocks: 
  rich rules: 
CODE


특정 존의 정보를 볼 경우 --zone 옵션뒤에 존 이름을 기술합니다. 아래는 work 존 설정을 표시합니다.

$ sudo firewall-cmd --list-all --zone work
CODE



기본 존 바꾸기

기본 존을 변경하려면  --set-default-zone=ZONE 옵션을 사용하여 변경해 주면 되며 다음은 dmz 로 변경하는 예제입니다. 


서버의 경우 여러 개의 NIC(Network Interface Card) 가 있을 수 있고 NIC 마다 다른 zone 을 설정해야 할 수 있습니다. NIC 가 eth0eth1 두 개가 있을 경우 --change-interface=[ETH] 명령으로 인터페이스마다 사용할 zone 을 지정할 수 있습니다.


다음은 eth0는 dmz , eth1은 private 로 변경하는 예제입니다.


존 만들기

만약 firewalld 에 내장된 zone 설정이 현재 서비스에 딱 들어 맞지 않는다면 --new-zone 옵션을 사용하여 새로 zone 을 만들면 됩니다. 아래는 webserver 라는 이름의 새로운 존을 만드는 예제입니다.


생성한 존은 바로 반영되지 않으므로 방화벽 정책을 다시 읽어 들여야 합니다. --reload 을 주어서 변경된 내역을 firewalld에 반영할 수 있습니다.



이제 방화벽 정책을 반영했으니 새로 생성한 존이 보이는지 확인을 위해 --get-zones 옵션을 주고 실행하면 목록에 추가된 것을 확인할 수 있습니다.


이제 새로운 존을 기본 존으로 생성합니다.

사전 정의 서비스

사전 정의된 서비스 확인

firewalld 에는 방화벽 정책 변경시 용이하도록 많이 사용하는 서비스를 사전에 정의해 놓고 있으며 --get-services 옵션으로 전체 목록을 확인할 수 있습니다. 

현재 존에 추가된 서비스 확인


--list-services 옵션을 사용하면 현재 존에 추가된 서비스 목록을 확인할 수 있습니다.

$ sudo firewall-cmd --list-services
CODE




서비스 추가

웹 서버는 http(80), https(443) 으로 접근을 허용해야 하므로 --add-service=SERVICENAME 구문으로 서비스를 추가해 줍니다. --add-service 는 여러 개의 서비스를 동시에  기술할 수 없으므로 http와 https 를 따로 기술해 줍니다.


서비스 삭제

서비스 삭제는 --remove-service=SERVICENAME 구문을 사용합니다. 아래는 http/https 서비스를 삭제합니다.




포트 추가

만약 사전 정의된 서비스가 아닌 다른 포트를 사용하는 경우 --add-port=포트번호:프로토콜  형식으로  등록할 수 있습니다. 포트 번호가 범위일 경우 - 를 구분자로 등록할 수 있습니다.


아래는 9090에서 9100 까지 TCP 포트를 허용하는 방화벽 설정 예제입니다.


포트 삭제

포트 삭제는 --remove-port=포트번호:프로토콜 형식으로 사용하면 되며 add 와 마찬가지로 범위일 경우 - 를 구분자로 사용합니다.

아래 명령은 webserver 존에 추가한 9090 에서 9100 포트 오픈 정책을 삭제합니다.





포트 추가/변경, IP 추가/변경은 --reload 옵션으로 firewalld 를 재실행해야 반영됩니다.


특정 source IP 에 대해 포트 접근 허용



특정 IP(예: 1.2.3.4) 만 특정 포트(9090)에 접근 가능하도록 하기 위해 아래와 같은 명령어를 생각할 수 있습니다.


$ firewall-cmd --permanent --zone=dmz --add-source=1.2.3.4/32 --add-port=9090/tcp
CODE

하지만 위 명령어를 실행하면 다음과 같은 에러가 발생합니다.

firewall-cmd: error: argument --add-port: not allowed with argument --add-source
CODE

firewall-cmd 로는 source ip 와 port 를 동시에 지정할 수 없으며 이럴 경우 아래에 설명할 rich rule 를 사용해야 합니다.


인터페이스 변경 및 ssh 서비스 추가

이제 웹 서버 존은 eth0 이더넷을 사용하도록 설정하고 eth1 이더넷은 내부 망에서 ssh로 연결 가능하도록 dmz 존으로 만들기 위해 각 존이 사용하는 NIC를 --change-interface 옵션으로 변경해 봅시다.

internal 이나 home 은 기본적으로 samba 나 printing 서버등이 열려 있으므로 웹 서버같이 DMZ에 위치할 서버에 할당하지 않는 게 좋습니다.



웹 서버이므로 어떤 클라이언트 IP 라도 80, 443 포트에 연결하면 허용하도록 설정했지만 IP 에 따라 접근 제어를 실행해야 하는 서비스도 있습니다.


예로 웹서버를 원격에서 관리할 수 있도록 ssh 서비스를 등록할 경우 일반적으로는 정해진 IP에서만 접근을 허용해야 합니다.


이럴 경우 --add-source=<source range>/netmask 옵션을 사용하여 허용할 IP 를 추가해 줄 수 있습니다. 


이제 dmz 에 ssh 서비스를 추가하고 내부 망(192.168.10.0/24) 에서만 ssh 가 연결 가능하도록 설정하고 webserver 존은 --remove-service 명령어로 ssh 서비스를 삭제합니다.



이제 방화벽 구성이 완료되었으니 firewall-cmd --reload 명령을 사용하여 방화벽 정책을 반영합니다.


rich rule

만약 설정하고자 하는 방화벽 정책이 복잡하고 firewall-cmd 에서 적절한 옵션을 제공하지 않을 경우 rich-rule 을 사용하여 복잡한 규칙을 설정할 수 있습니다.

rich rule 은 별도의 문법을 갖고 있으며 iptables 의 어려운 옵션을 알지 않아도 세밀하게 원본 주소, 목적지 주소에 따른 접근 제어, 로깅, 세밀한 액션 설정등 복잡한 방화벽 규칙을 작성할 수 있는 특성이 있으며 아래와 같은 형식으로 새로운 규칙을 추가해 줄 수 있습니다.

[ ] 안에 있는 옵션은 생략 가능하며 예로 --zone 옵션을 생략하면 기본 존에 반영이 됩니다. --timeout 옵션이 주어질 경우 해당 시간동안만 방화벽 규칙이 적용되고 그 시간이 지나면 자동으로 예전 규칙으로 돌아가며 기본 시간값은 s(초)이나 m(분) , h(시간) 을 붙여서 시간 단위를 지정할 수 있습니다.


규칙 삭제 및 확인

규칙을 삭제할 경우 아래와 같이 --remove-rich-rule 명령으로 삭제하면 됩니다.


규칙이 있는지 확인하려면 --query-rich-rule 명령을 사용하며 실행 결과로 콘솔에 yes 가 표시되면 해당 규칙이 존재하는 것이고 no 일 경우 규칙이 없는 경우입니다.


rich 규칙 문법

리치 규칙은 아래와 같은 구조를 갖고 있으며 마찬가지로 대괄호([ ]) 안의 옵션은 생략 가능하며 전체 규칙은 한줄에 기술해야 합니다.


family

TCP 프로토콜 패밀리를 지정하며 ipv4 또는 ipv6 를 설정하면 되며 생략할 경우 ipv4, ipv6 를 모두 허용합니다.  만약 규칙에서 source 나 destination 주소를 사용하는 경우 family 가 ipv4  인지 ipv6 인지 명시적으로 기술해야 합니다.


출발지(source)

source [not] address="address[/mask]"|mac="mac-address"|ipset="ipset"
CODE

출발지 주소를 지정해서 IP 기반으로 접근 제어를 실행할 수 있으며 IP 주소 또는 CIDR(Classless Inter-Domain Routing) 방식으로 지정하면 되며 NOT 을 사용하여 반대의 의미로 사용할 수 있습니다.


예로 아래의 예제는 192.168.10.0 의 모든 대역을 지정합니다.


아래의 예제는 192.168.10.5 가 아닌 모든 IP 를 지정합니다.



destination

목적지 주소도 원본 주소와 마찬가지로 아래와 같은 구문으로 사용하면 됩니다.

destition [not] address="address[/mask]"|mac="mac-address"|ipset="ipset"
CODE


element

요소는 service, port, protocol, masquerade, icmp-block, forward-port와 source-port 중에 하나가 될 수 있으며 각 요소의 의미는 다음과 같습니다.


service

서비스 요소의 사용법은 아래와 같이 name=service_name 형식으로 사용하면 됩니다.

service name=https
CODE

service 요소는 firewalld 에서 사전에 정의한 서비스 목록이며 well known 서비스일 경우 포트보다 서비스 이름(예: 443 대신 https)으로 지정하는 게 편리하며 firewall-cmd --get-services 로  전체 서비스 목록을 얻을 수 있습니다.

서비스 요소 사용시 목적지 주소를 제공하면 규칙의 목적지 주소와 충돌하게되고 오류가 발생하므로  멀티 캐스트를 사용하는 서비스외에는 일반적으로는 사용하면 안 됩니다.


port

port port=number_or_range protocol=protocol
CODE

포트는 단일 포트 번호 또는 포트 범위를 지정(예: 8080-8100) 할 수 있으며 tcp/udp 로 프로토콜을 구분해 주어야 합니다.


아래는 tcp 의 8080에서 8100 까지 포트 범위를 지정하는 예제입니다.

port port=8080-8100 protocol=tcp
CODE



protocol

프로토콜 항목은 id 또는 이름으로 지정할 수 있으며 사전에 정의된 프로토콜은 /etc/protocols 에 있으며 아래와 같은 형식으로 사용하면 됩니다.

protocol value=protocol_name_or_ID
CODE

다음은 IP(Internet Protocol) 암호화 버전인 IPSec(Internet Protocol Security) 에서 사용하는 인증 헤더(ah; Authentication Header) 를 지정하는 예제입니다.


icmp-block

하나 이상의 ICMP(Internet Control Message Protocol) 를 차단할 경우 사용하며 아래와 같이 차단하려면 ICMP 유형을 지정하면 됩니다.

icmp-block name=icmptype_name
CODE


전체 ICMP 의 목록을 얻으려면 아래 명령을 사용하면 됩니다.


log

커널 로깅(예 : syslog)을 사용하여 방화벽 규칙에 대한 새로운 연결 시도를 기록할수 있으며 로그의 구분을 위해 로그 메시지에 추가될 접두어 문자열을 정의 할 수 있습니다.

로그 수준은 emerg, alert, crit, error, warning, notice, info 또는 debug 중 하나이며 로그 사용은 선택 사항입니다.

log [prefix=prefix text] [level=log level] limit value=rate/duration
CODE


로깅 사용은 limit 키워드를 사용하여 제한할 수 있으며 rate 는 1이상의 양의 자연수이며 duration 은 s(second), m(minute), h(hour), d(day) 를 지정할 수 있습니다.

최대 제한은 1/d 이며 이는 하루에 최대 1번만 로깅을 하겠다는 의미입니다.


다음은 ftp 프로토콜을 자세히 로깅하기 위해 로그 파일에 ftp 라는 접두사를 붙이고 debug 레벨로 30초마다 로깅하겠다는 설정입니다.



audit

감사(audit) 은 log 대신 방화벽을 로깅할 수 있는 설정으로 syslog 등의 로그 장치로 보내지 않고 auditd 감사 데몬으로 감사 레코드를 전송합니다.


audit 명령은 파라미터가 없지만 선택적으로 limit 키워드를 사용하여 감사 수집을 제한 할 수 있습니다. audit 사용은 선택 사항입니다.

audit [limit value="rate/duration"]
CODE


action

action 키워드로 규칙에 맞는 패킷에 대한 동작을 정의할 수 있으며 action은 허용(accept), 거부(reject), 파기(drop) 또는 표시(mark) 중 하나를 설정하면 됩니다.

규칙에는 element 또는 출발지만 포함될 수 있으며 규칙에 element가 포함되어 있으면 일치하는 새 연결이 작업과 함께 처리됩니다. 규칙에 원본이 포함되어 있으면 원본 주소의 모든 내용이 지정된 작업으로 처리됩니다.

accept | reject [type=reject type] | drop | mark set="mark[/mask]"
CODE


accept를 사용하면 모든 새로운 연결 시도를 허용하며 reject 를 설정하면 거부되며 출발지  주소에 거부 메시지를 전하며 거부 유형은 다른 값을 사용하도록 설정할 수 있습니다.

drop을 설정하면 모든 패킷이 즉시 파기되고 어떤 정보도 출발지 주소로 전송하지 않으므로 기본 방화벽의 동작으로 권장합니다. mark를 사용하면 모든 패킷에 주어진 표시와 선택 사항 마스크가 표시됩니다.

rich rule 적용 예제

이제 firewall-cmd 의 rich rule 을 사용하여 세밀하게 방화벽 정책을 수립하는 예제를 살펴봅시다.

예제에서는 --zone=dmz 명령으로 적용할 zone 을 지정했지만 하나의 zone 만 있다면 지정할 필요가 없으며 --permanent 옵션을 제외해서 즉시 반영되지만 방화벽 정책 재구동이나 시스템 재부팅시 정책이 유지되지 않으므로 지속해야 할 정책이라면 --permanent 옵션을 주어서 정책을 만들고 --reload 옵션으로 정책을 지속적으로 반영하는 것이 필요합니다.


특정 Source IP 만 특정 Port 접근 허용


source ip 가 1.2.3.4 일 경우 9090 포트에 대해 접속을 허용합니다.

firewall-cmd --permanent --zone=dmz --add-rich-rule='rule family="ipv4" source address="1.2.3.4/32" port protocol="tcp" port="9090" accept'
CODE



IPSec 의 ah 프로토콜 허용

IPSec 의 authentication 헤더를 허용하며 family 를 지정하지 않았으므로 ipv4, ipv6 모두 허용됩니다.

만약 정책을 삭제하려면 위 명령어를 복사한 후에 --add--remove 로 변경하면 되며 아래는 위 rich 규칙을 삭제하는 예제입니다.


ftp 허용

192.168.10.0 대역의 모든 서버에서 ipv4 를 사용하여 ftp 연결을 허용하며 30초마다 로깅을 audit 을 사용하여 기록합니다.


베스천 호스트에서만 SSH 접속 허용

중요한 서비스인 SSH 를 보호하기 위해 베스천 호스트(예: 192.168.58.2) 에서만 ipv4 를 사용하여 SSH 연결을 허용합니다.



white list 허용

화이트 리스트인 192.168.20.2 에서 public 존에 대한 모든 연결을 허용합니다.


black list 연결 거부

블랙 리스트인 192.168.58.10 에서 모든 연결을 거부하고 "icmp-net-prohibited"  ICMP 메시지를 전송합니다.


black list 연결 차단(drop)

블랙 리스트인 192.168.58.11 에서 모든 연결을 drop 합니다.


아래 gist 를 사용하면 편리하게 블랙리스트 IP 를 drop  할수 있습니다.



IP 위장 방어

공격자는 공격지를 들키지 않기 위해 패킷의 출발지 IP 주소를 위장할 수 있으며 특히 사설 IP 주소가 설정된 패킷이 인터넷상의 서버에 도착한다면 주의가 필요합니다. 사설 IP 주소의 범위를 클래스 별로 CIDR(Classless Inter-Domain Routing 방식으로 기술해 보면 다음과 같습니다.

  • 10.0.0.0/8(10.0.0.0 ~ 10.255.255.255)
  • 172.16.0.0/12(172.16.0.0 ~ 172.16.255.255)
  • 192.168.0.0/16(192.168.0.0 ~ 192.168.255.255)


그리고 다음과 같은 특별한 IP 주소도 사용하지 않는다면 서버에서 거부하도록 설정하는 것이 좋습니다.

  • 169.254.0.0/16 - DHCP 가 없는 환경에서 네트워크 구성을 위한 zeroconf route 주소
  • 192.0.2.0/24 - TEST NET
  • 224.0.0.0/4 - Multicast 용
  • 240.0.0.0/5 - 미래를 위해 예약



이중에서 사설 IP 대역과 멀티캐스트를 위해 예약된 224.0.0.0 대역은 회사의 네트워크 구성 여부에 따라 사용할 수 있지만 인터넷 상의 서버에 도착한 패킷이 사설 IP 로 왔을 경우에만 차단해야 하므로 --zone 옵션으로 명시적으로 사설 IP 를 차단할 존을 기술하는 게 좋습니다.

특히 멀티캐스트는 tomcat 의 세션 클러스터링(기본 설정:  228.0.0.4)시 기본 설정이므로 내부 망과 연결된 존에서는 차단하지 말아야 하며 톰캣 클러스터링이 제대로 동작하는지 확인해 보아야 합니다.


Smurf 공격 방어

스머프 공격은 DOS(Denial of Service) 공격의 일종으로서 공격 대상인 서버의 IP 로 위장한 ICMP 패킷을 전체 네트워크에 브로드캐스팅하는 공격으로 다음과 같은 절차에 따라 공격을 수행합니다.


  1. 공격자는 출발지 IP를 공격 대상의 IP로 변조(10.0.0.1)한 ICMP echo request 패킷을 목적지 네트워크의 브로드캐스트 주소(192.168.0.255) 로 전송
  2. 브로드캐스트이므로 네트워크내의 모든 서버는 해당 패킷을 수신한 후에 출발지 IP 주소인 10.0.0.1 에 ICMP Echo Reply 패킷을 전송
  3. 공격 대상 서버(10.0.0.1) 는 ICMP 패킷을 대량으로 수신하여 서비스 불능 상태에 빠짐

이에 대한 대책은 브로드캐스트로 요청된 ICMP 패킷을 파기하도록 방화벽 정책을 설정하면 됩니다.

정상적으로 방화벽 정책이 반영되고 정상 동작 여부를 확인했다면 영구적으로 방화벽 정책을 적용하기 위해  --permanent 옵션을 추가해서 한 번 더 실행해 주면 됩니다.

즉 위의 smurf 방지 대책은 아래와 같이 한 번 더 실행해 주면 됩니다.



위와 같이 CentOS 7 에 포함된  firewalld 를 사용하면 사전에 정의된 규칙에 따라 손쉽게 방화벽 정책을 수립할 수 있으며 고급 정책이 필요할 경우 rich 규칙을 사용하여 더 세밀한 방화벽 정책을 수립할 수 있습니다.


같이 보기