티스토리 뷰
무언가를 배우는 것도 좋지만 알아야하는 정보도 많다.
이 글은 배우는 것 + 알아야할 정보를 취합했다.
특히 도커를 사용 중이라서 더 복잡한 느낌이 들었다.
firewall보다는 iptables을 사용하자
firewalld는 리눅스 방화벽인 iptables을 쉽게 관리해줄 수 있는 래퍼 패키지이다.
우선 여러가지 특징이 있지만 잊지 말아야할 몇가지 firewalld 특징에 대해 알아보자
firewalld가 실행(start) 중이면 firewalld에 적용된 규칙들이 iptables에 덮어진다.
firewalld가 종료(stop) 상태일 때 iptables -nvL
로 적용된 규칙을 확인해 보면 적용된 규칙이 하나도 없다.
-> iptables는 firewalld가 적용된 규칙에 따라 전체적으로 변한다.
-> firewalld에 규칙이 추가 되면 iptables도 일치된 규칙이 추가된다.
-> iptables 규칙을 따로 추가하는 경우 firewalld에서 적용된 규칙들에게 변화를 줄 수 없다.
firewalld 실행 중에 iptables 규칙을 추가 및 삭제하면 적용은 되지만 재시작할 경우 모두 firewalld 기준으로 적용된 규칙으로 변한다.
firewalld에서 체인을 추가하는 경우 iptables에 nat 테이블에도 변화를 줄 수 있다.
firewalld를 다루면서 가장 크게 느낀건 iptables을 쉽게 관리하는 것이지
디테일하게 관리하려면 결코 iptables을 사용할 수 밖에 없다는 것이다.
firewalld에서 --ipset을 지원하는데 이는 찾아봤더니 iptables에서도 사용할 수 있었다. (ipset --help)
docker-compose를 이용하여 컨테이너를 도커에 올려 사용하는 경우 DOCKER는 FORWARD 규칙에서 관리하여 필터링해야하는데
firewalld를 이용하면 규칙의 우선순위를 바꾸기도 힘들 뿐더러 nat 테이블에 관련된 규칙들도 다루기 힘들어 매우 힘겨웠다.
반대로 iptables는 iptables-restore < /etc/sysconfig/iptables
로 vim으로 규칙의 우선순위를 수정한 뒤 편집한 규칙들을 적용할 수 있다.
/etc/firewalld
에 포함된 파일을 모두 확인해봤지만 firewalld 관련해서 우선순위를 쉽게 제어할 수 있는 룰은 찾아보기 힘들었다.
iptables에 규칙을 추가하더라도 리눅스 머신이 재시작되면 모두 물거품되어 버리기 때문에 더더욱 firewalld를 사용하기 힘들었다.
반드시 DOCKER를 사용하는 경우라면 무조건 iptables
을 사용하도록 하자.
firewalld --direct를 이용한 규칙 추가
firewalld에서 --direct 옵션은 iptables와 유사한 관련 규칙을 추가할 수 있다. (filter 테이블 이용)
규칙을 추가하게 된다면 iptables -nvL에서 확인해보면 <chain이름 또는 zone이름>_direct
체인에 규칙이 추가되는 것을 볼 수 있다.
만약 INPUT 규칙에 다음과 같은 규칙을 추가한다고 하자.
$ firewall-cmd --permanent --direct --add-rule ipv4 filter INPUT 0 -p tcp -m geoip ! --src-cc KR -j DROP
INPUT 체인 안에 INPUT_direct
로 점프하는 규칙이 추가되었고, 패킷이 들어오는 경우 INPUT_direct
체인으로 jump하게 된다.
하지만 INPUT_direct
규칙보다 더 우선순위가 높은 ACCEPT 관련 규칙
이 있다면 INPUT_direct 규칙에서 필터링 작업을 할 수 없다.
이때, INPUT_direct
규칙의 우선순위
를 firewalld에서 변경할 수 있어야 하는데 그럴 수 없었다.
Chain INPUT (policy ACCEPT 0 packets, 0 bytes)
pkts bytes target prot opt in out source destination
0 0 DROP tcp -- * * 0.0.0.0/0 0.0.0.0/0 multiport dports 2052,3306 -m geoip ! --source-country KR
0 0 DROP tcp -- * * 0.0.0.0/0 0.0.0.0/0 tcp dpt:2052 -m geoip ! --source-country KR
4225 861K ACCEPT all -- * * 0.0.0.0/0 0.0.0.0/0 ctstate RELATED,ESTABLISHED
0 0 ACCEPT all -- lo * 0.0.0.0/0 0.0.0.0/0
24 996 INPUT_direct all -- * * 0.0.0.0/0 0.0.0.0/0
24 996 INPUT_ZONES all -- * * 0.0.0.0/0 0.0.0.0/0
18 720 DROP all -- * * 0.0.0.0/0 0.0.0.0/0 ctstate INVALID
0 0 REJECT all -- * * 0.0.0.0/0 0.0.0.0/0 reject-with icmp-host-prohibited
0 0 ACCEPT all -- * * 0.0.0.0/0 0.0.0.0/0 state RELATED,ESTABLISHED
0 0 ACCEPT icmp -- * * 0.0.0.0/0 0.0.0.0/0
0 0 ACCEPT all -- lo * 0.0.0.0/0 0.0.0.0/0
0 0 ACCEPT tcp -- * * 0.0.0.0/0 0.0.0.0/0 state NEW tcp dpt:22
0 0 REJECT all -- * * 0.0.0.0/0 0.0.0.0/0 reject-with icmp-host-prohibited
다음 규칙을 보자.
docker-compose에서 웹서버가 실행 중인 상황이다.
정말 화가 나는 상황이 아니던가 ?
firewalld에서 --direct 옵션으로 규칙을 추가했지만 DOCKER 관련 규칙은 필터링 할 수 없었다.
그 이유는 FORWARD_direct
로 jump되는 규칙에 우선순위가 낮았기 때문이다. (DOCKER-ISOLATION > FORWARD_direct)
PREROUTING을 제어하거나 iptables에서 규칙을 추가(-I)하는 경우 충분히 해결될 문제였지만
firewalld를 재시작 또는 리눅스 머신을 재시작하는 경우 다시 원상복구 된다는 것이다.
(도커에서 사용 중인 브릿지 네트워크 인터페이스는 br-*로 시작된다)
만약 도커에 접근하는 모든 아이피를 차단하고 싶다면 FORWARD chain이나 DOCKER-ISOLATION 최우선순위로 모든 아이피를 차단하는 규칙을 추가해보길 바란다.
Chain FORWARD (policy ACCEPT 0 packets, 0 bytes)
pkts bytes target prot opt in out source destination
0 0 DOCKER-ISOLATION all -- * * 0.0.0.0/0 0.0.0.0/0
0 0 DOCKER all -- * br-c7830344d364 0.0.0.0/0 0.0.0.0/0
0 0 ACCEPT all -- * br-c7830344d364 0.0.0.0/0 0.0.0.0/0 ctstate RELATED,ESTABLISHED
0 0 ACCEPT all -- br-c7830344d364 !br-c7830344d364 0.0.0.0/0 0.0.0.0/0
0 0 ACCEPT all -- br-c7830344d364 br-c7830344d364 0.0.0.0/0 0.0.0.0/0
0 0 DOCKER all -- * docker0 0.0.0.0/0 0.0.0.0/0
0 0 ACCEPT all -- * docker0 0.0.0.0/0 0.0.0.0/0 ctstate RELATED,ESTABLISHED
0 0 ACCEPT all -- docker0 !docker0 0.0.0.0/0 0.0.0.0/0
0 0 ACCEPT all -- docker0 docker0 0.0.0.0/0 0.0.0.0/0
0 0 ACCEPT all -- * * 0.0.0.0/0 0.0.0.0/0 ctstate RELATED,ESTABLISHED
0 0 ACCEPT all -- lo * 0.0.0.0/0 0.0.0.0/0
0 0 DROP all -- * * 0.0.0.0/0 0.0.0.0/0 ctstate INVALID
0 0 REJECT all -- * * 0.0.0.0/0 0.0.0.0/0 reject-with icmp-host-prohibited
0 0 FORWARD_direct all -- * * 0.0.0.0/0 0.0.0.0/0
0 0 FORWARD_IN_ZONES all -- * * 0.0.0.0/0 0.0.0.0/0
0 0 FORWARD_OUT_ZONES all -- * * 0.0.0.0/0 0.0.0.0/0
0 0 DROP all -- * * 0.0.0.0/0 0.0.0.0/0 ctstate INVALID
0 0 REJECT all -- * * 0.0.0.0/0 0.0.0.0/0 reject-with icmp-host-prohibited
Chain DOCKER (2 references)
pkts bytes target prot opt in out source destination
4 176 ACCEPT tcp -- !br-883c785a6a56 br-883c785a6a56 0.0.0.0/0 172.18.0.2 tcp dpt:3306
0 0 ACCEPT tcp -- !br-883c785a6a56 br-883c785a6a56 0.0.0.0/0 172.18.0.4 tcp dpt:80
0 0 ACCEPT tcp -- !br-883c785a6a56 br-883c785a6a56 0.0.0.0/0 172.18.0.5 tcp dpt:443
5 216 ACCEPT tcp -- !br-883c785a6a56 br-883c785a6a56 0.0.0.0/0 172.18.0.5 tcp dpt:80
Chain DOCKER-ISOLATION (1 references)
pkts bytes target prot opt in out source destination
63 2768 RETURN all -- * * 0.0.0.0/0 0.0.0.0/0
이때 2가지 해결 방법
이 존재한다.
1. 재부팅 될때마다 특정 shell 을 실행하게 만들어서 iptables 명령어를 이용하여 규칙을 추가하는 것
2. firewalld를 버리고 nat, filter 테이블을 쉽게 제어할 수 있는 iptables을 사용하는 것
2번을 택했다.
작업 흐름
이번 작업은 모든 접근을 차단하고 허용한 IP 주소를 가진 호스트에 한해서 접근을 허용하게 하는 작업이다.
firewalld에서 Whitelist를 만들고 접속 승인을 처리하려면 차단 작업(block, drop) 보다 접속 승인이 우선적으로 처리되야 한다.
그러기 위해서는 새로운 영역(zone)을 만들고 접속 승인을 처리하게 설정한다.
중요한건 접속 승인이 처리 된 후 차단 작업(block, drop)을 진행하게 만들어야 한다.
여러 개의 IP 주소를 승인하기 위해서는 ipset을 이용한다. 이 ipset이 Whitelist를 의미하는 것이다.
새롭게 만든 접속 승인 규칙들을 처리하는 영역에는 Whitelist4 이름을 가진 ipset을 적용한다.
그외 나머지 접근은 모두 차단한다.
firewalld에서 zone을 생성 및 설정, 규칙을 추가하다보면 iptables에 등록된 라우팅 규칙을 담당하는 테이블(nat)에도 영향을 끼칠 수 있다. 이로 인해 iptables을 따로 작업하지 않고도 firewalld에서 쉽게 필터링할 수 있다.
firewalld zone 우선순위
firewalld에서 영역(zone)의 우선순위는 알파벳 순서이다. ( 0 > 9 > a > z )
0에 가까운 이름을 가진 영역이 가장 우선시 처리되며, a부터 z까지 알파벳 순서로 처리한다.
접근 승인을 담당하는 새로운 Zone만들기
접근 승인이 우선 시 되려면 접근 승인을 처리할 새로운 zone을 생성해야 한다.
이름 앞에 000- 을 붙여 우선순위를 높게 만든다.
새로운 zone의 이름은 000-allow-zone
으로 설정했다.
$ firewall-cmd --new-zone 000-allow-zone --permanent
$ firewall-cmd --set-target=ACCEPT --zone=000-allow-zone --permanent
ipset whitelist 다루기
접속 승인 목록인 whitelist를 만들고 제어한다.
생성
여러 개의 IP 주소를 저장할 수 있는 Whitelist를 생성한다.
Whitelist 이름은 whitelist4로 설정했다. (옵션에서 사용된 inet은 ipv4를 의미한다)
$ firewall-cmd --new-ipset=whitelist4 --type=hash:net --option=family=inet --permanent
$ firewall-cmd --permanent --zone=000-allow-zone --add-source=ipset:whitelist4
ipset 생성 시 사용 가능한 ipset type은 다음 명령어를 통해 확인할 수 있다.
보기 편하게 ,,
를 구분자로 사용하여 출력 내용을 정리했다.
$ firewall-cmd --get-ipset-types
hash:ip ,, hash:ip,mark ,, hash:ip,port ,, hash:ip,port,ip ,, hash:ip,port,net ,, hash:mac
hash:net ,, hash:net,iface ,, hash:net,net ,, hash:net,port ,, hash:net,port,net
목록
생성된 ipset 목록은 다음 명령어를 사용하여 확인할 수 있다.
$ firewall-cmd --get-ipsets
whitelist4
삭제
혹시 ipset을 잘못 만들었다면 다음 명령어를 통해 삭제하고 다시 만들도록 하자.
$ firewall-cmd --delete-ipset=whitelist4 --permanent
정보
생성한 ipset 정보를 확인하고 싶으면 --info-ipset을 이용한다.
$ firewall-cmd --info-ipset=whitelist4
whitelist4
type: hash:ip
options: hashsize=4096 family=inet
entries:
entry 추가하기
접속을 승인할 아이피 또는 ip대역 (127.0.0.1/24)를 추가한다.
ip 대역 관련해서는 cidr에 대하여 알아보자
$firewall-cmd firewall-cmd --ipset=whitelist4 --add-entry=<ip>
entry 삭제하기
접속을 승인 -> 거부로 변경할 아이피를 whitelist4 ipset에서 제거한다.
$firewall-cmd --ipset=whitelist4 --remove-entry=<ip>
drop zone 설정하기
네트워크 인터페이스 추가하기
ifconfig 명령어를 이용하여 현재 사용 중인 네트워크 인터페이스를 확인할 수 있다.
ens3이 리눅스 머신에서 사용되는 인터페이스이므로 ens3에 대한 인터페이스를 drop zone에 설정한다.
이제 drop zone은 인터페이스가 추가되었으므로 활성화(active)된 zone이 되었다.
firewall-cmd --permanent --add-interface=ens3
drop zone, default로 설정하기
default zone을 drop으로 설정하여 모든 접근을 차단하도록 한다.
$ firewall-cmd --permanent --zone=drop --set-default-zone=drop
생성한 000-allow-zone에 whitelist 등록하기
000-allow-zone
에 whitelist4
ipset을 등록하기 위해 다음 명령어를 사용한다.
이제 000-allow-zone의 기본 정책은 ACCEPT이므로 whitelist에 추가된 IP만 접근할 수 있다.
기본 zone이 drop이며, 모든 접근을 차단했기에 ACCEPT로 패킷이 우선적으로 통과되지 못한다면 모두 drop되는 것이다.
$ firewall-cmd --permanent --zone=000-allow-zone --add-source=ipset:whitelist4
결과 확인
whitelist zone 생성 및 설정, default zone 설정을 모두 마쳤으므로
ens3 인터페이스로 들어오는 패킷들은 Whitelist에 등록된 source ip인 경우 ACCEPT하게 된다.
그외 모든 패킷은 DROP한다.
혹시 모르니 다시 한번 iptables -nvL
명령어를 사용하여 규칙의 우선순위를 확인하자 (우선순위가 틀리면 제대로 작동하지 않음)
필터링 통과 후 또 다른 ACCEPT 처리하는 관련 규칙을 추가하고 싶다면
INPUT chain에서 INPUT_ZONES
로 jump되는 규칙보다 우선순위가 낮아야 한다.
$ firewall-cmd --get-active-zones
000-allow-zone
sources: ipset:whitelist4
drop
interfaces: ens3