내가 도와주는 서비스 중에 한 곳이 요즘 디도스로 골머리를 앓고 있다. 이 서비스는 분 단위로 디도스를 받고 있다. 한번 공격이 오면 5분간 지속된다. 그러고는 몇 시간 동안 잠잠하다. 잠잠한 기간이 한시간일 때도, 3시간일 때도, 그 다음날이 될 때도 있다. 서버가 다운됐다는 알림을 받고 조치를 취하려고 보면 이미 물러간 뒤다. 이 문제가 몇일 정도 반복되고 있다.
일단 뭐가 어쨋든, 사용자들이 불편을 겪으면 안된다. 그래서 IP를 새로 발급받고 StackPath나 Cloudflare의 뒤에 숨는것을 하려고 했다. 관리를 편하게 하기 위해서 VNIC를 새로 만들고, 해당 서버에 붙이는 방식으로 구성했다. 즉, 네트워크 카드를 하나 더 붙인 후 과거 인터페이스는 정지하고 새 인터페이스로만 접속을 받으려고 했다. 공격자가 예측하지 못하도록 다른 서브넷의 IP를 사용함과 동시에 기존 접속자들도 DNS TTL(업데이트 기간) 동안 문제없이 접속하게 하기 위해서다.
우리 서버는 오라클 클라우드 안에 있다. 오라클 클라우드 내에서는 가상 게이트웨이를 만들수 있고, 각 서버 인스턴스에 가상 NIC를 붙였다가 땟다가 할 수도 있다. 우리도 이 방식을 통해서 서버에 새 IP를 부여하고자 했다.
오라클에서는 VNIC를 붙일 수 있는 스크립트를 제공한다. (https://docs.oracle.com/en-us/iaas/Content/Network/Tasks/managingVNICs.htm#Linux) 해당 스크립트를 이용하면 VNIC를 손쉽게 붙일 수 있다. 미리 관리 페이지에서 VNIC를 만든 후에 attach를 하고, 해당 스크립트를 돌리면 알아서 IP등을 잡아준다. 그러나 이게 순탄치 못했다. 아래와 같이 네트워크가 구성됐기 때문이다:
[root@linux_centos7 ~]# ifconfig
ens3: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 9000
inet 10.0.0.~~ netmask 255.255.255.0 broadcast 10.0.0.255
inet6 fe80::17ff:fe00:89b8 prefixlen 64 scopeid 0x20<link>
ether ~~:~~:~~:~~:~~:~~ txqueuelen 1000 (Ethernet)
RX packets 45852203794 bytes 4269063429639 (3.8 TiB)
RX errors 0 dropped 68558182 overruns 0 frame 0
TX packets 43595189024 bytes 10049848053900 (9.1 TiB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
ens3.3263: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 9000
inet6 fe80::17ff:fe02:8305 prefixlen 64 scopeid 0x20<link>
ether ~~:~~:~~:~~:~~:~~ txqueuelen 1000 (Ethernet)
RX packets 2 bytes 112 (112.0 B)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 16 bytes 1312 (1.2 KiB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
ens3v3263: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 9000
inet 10.0.0.33 netmask 255.255.255.0 broadcast 0.0.0.0
inet6 fe80::17ff:fe02:8305 prefixlen 64 scopeid 0x20<link>
ether ~~:~~:~~:~~:~~:~~ txqueuelen 1000 (Ethernet)
RX packets 0 bytes 0 (0.0 B)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 8 bytes 656 (656.0 B)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
lo: flags=73<UP,LOOPBACK,RUNNING> mtu 65536
inet 127.0.0.1 netmask 255.0.0.0
inet6 ::1 prefixlen 128 scopeid 0x10<host>
loop txqueuelen 1000 (Local Loopback)
RX packets 20256730095 bytes 4740998121177 (4.3 TiB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 20256730095 bytes 4740998121177 (4.3 TiB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
여기서 ens3가 메인 NIC이다. 그리고 위의 스크립트의 실행 결과로 ens3.3263과 ens3v3263이 생겨났다. 새로운 NIC를 붙였는데 ens3 하위에 네트워크 인터페이스가 생겼다. 만약 NIC가 정상적으로 붙었다면 ens3, ens5 처럼 독립된 네트워크 인터페이스가 생겨야 한다. ens3 하위에 네트워크 인터페이스가 생긴것은 ip link show 명령어를 통해서도 확인 가능하다:
[root@linux_centos7 ~]# ip link
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
2: ens3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 9000 qdisc mq state UP mode DEFAULT group default qlen 1000
link/ether ~~:~~:~~:~~:~~:~~ brd ff:ff:ff:ff:ff:ff
7: ens3.3263@ens3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 9000 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000
link/ether ~~:~~:~~:~~:~~:~~ brd ff:ff:ff:ff:ff:ff
8: [email protected]: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 9000 qdisc noqueue state UP mode DEFAULT group default qlen 1000
link/ether ~~:~~:~~:~~:~~:~~ brd ff:ff:ff:ff:ff:ff
ip link를 실행하면 그 결과가 명확해 지는데, ens3 아래에 ens3.3263가 만들어진다. ens3.3263 아래에 ens3v3263이 만들어진다. 절대 정상적인 모습이 아니다. 우리가 바라는 모습은 아래의 사진과 같아야 한다.
ens3 하위에 인터페이스가 생긴것은 VLAN tagging이라고 하여 하나의 NIC에서 여러개의 가상 인터페이스를 붙일때 사용하는 기법이다. 물리선 하나로 여러개의 IP를 할당받을때나 쓰이는 것이다. 그렇기 때문에 우리와 같이 가상 네트워크를 새로 만든 경우에 서로의 네트워크가 달라서 아예 작동이 안된다. 쉽게 말해서, 하나의 컴퓨터에 KT와 LG 인터넷을 붙였는데 KT측을 통해서 LG에 접속하려는것과 같다.

우선 NIC가 제대로 붙었는지 확인하기로 했다. lspci를 실행했더니 다음과 같은 결과가 나왔다:
[root@sv3 ~]# lspci -vv
00:00.0 Host bridge: Intel Corporation 440FX - 82441FX PMC [Natoma] (rev 02)
~~~
00:01.0 ISA bridge: Intel Corporation 82371SB PIIX3 ISA [Natoma/Triton II]
~~~
00:01.1 IDE interface: Intel Corporation 82371SB PIIX3 IDE [Natoma/Triton II] (prog-if 80 [ISA Compatibility mode-only controller, supports bus mastering])
~~~
00:01.2 USB controller: Intel Corporation 82371SB PIIX3 USB [Natoma/Triton II] (rev 01) (prog-if 00 [UHCI])
~~~
00:01.3 Bridge: Intel Corporation 82371AB/EB/MB PIIX4 ACPI (rev 03)
~~~
00:02.0 VGA compatible controller: Device 1234:1111 (rev 02) (prog-if 00 [VGA controller])
~~~
00:03.0 Ethernet controller: Mellanox Technologies MT28800 Family [ConnectX-5 Ex Virtual Function]
Subsystem: Mellanox Technologies Device 0150
Physical Slot: 3
Control: I/O- Mem+ BusMaster+ SpecCycle- MemWINV- VGASnoop- ParErr- Stepping- SERR- FastB2B- DisINTx+
Status: Cap+ 66MHz- UDF- FastB2B- ParErr- DEVSEL=fast >TAbort- <TAbort- <MAbort- >SERR- <PERR- INTx-
Latency: 0
Region 0: Memory at 2000000000 (64-bit, prefetchable) [size=2M]
Capabilities: [60] Express (v2) Endpoint, MSI 00
DevCap: MaxPayload 512 bytes, PhantFunc 0, Latency L0s unlimited, L1 unlimited
ExtTag+ AttnBtn- AttnInd- PwrInd- RBE+ FLReset+ SlotPowerLimit 0.000W
DevCtl: Report errors: Correctable- Non-Fatal- Fatal- Unsupported-
RlxdOrd- ExtTag- PhantFunc- AuxPwr- NoSnoop- FLReset-
MaxPayload 128 bytes, MaxReadReq 128 bytes
DevSta: CorrErr- UncorrErr- FatalErr- UnsuppReq- AuxPwr- TransPend-
LnkCap: Port #0, Speed 16GT/s, Width x16, ASPM not supported, Exit Latency L0s unlimited, L1 unlimited
ClockPM- Surprise- LLActRep- BwNot- ASPMOptComp+
LnkCtl: ASPM Disabled; RCB 64 bytes Disabled- CommClk-
ExtSynch- ClockPM- AutWidDis- BWInt- AutBWInt-
LnkSta: Speed unknown, Width x0, TrErr- Train- SlotClk- DLActive- BWMgmt- ABWMgmt-
DevCap2: Completion Timeout: Range ABC, TimeoutDis+, LTR-, OBFF Not Supported
DevCtl2: Completion Timeout: 50us to 50ms, TimeoutDis-, LTR-, OBFF Disabled
LnkSta2: Current De-emphasis Level: -6dB, EqualizationComplete-, EqualizationPhase1-
EqualizationPhase2-, EqualizationPhase3-, LinkEqualizationRequest-
Capabilities: [9c] MSI-X: Enable+ Count=6 Masked-
Vector table: BAR=0 offset=00002000
PBA: BAR=0 offset=00003000
Capabilities: [c0] Vendor Specific Information: Len=18 <?>
Kernel driver in use: mlx5_core
Kernel modules: mlx5_core
00:04.0 SCSI storage controller: Red Hat, Inc. Virtio SCSI
~~~
00:05.0 Ethernet controller: Mellanox Technologies MT28800 Family [ConnectX-5 Ex Virtual Function]
Subsystem: Mellanox Technologies Device 0150
Physical Slot: 5
Control: I/O- Mem+ BusMaster- SpecCycle- MemWINV- VGASnoop- ParErr- Stepping- SERR- FastB2B- DisINTx-
Status: Cap+ 66MHz- UDF- FastB2B- ParErr- DEVSEL=fast >TAbort- <TAbort- <MAbort- >SERR- <PERR- INTx-
Region 0: Memory at c1200000 (64-bit, prefetchable) [size=2M]
Capabilities: [60] Express (v2) Endpoint, MSI 00
DevCap: MaxPayload 512 bytes, PhantFunc 0, Latency L0s unlimited, L1 unlimited
ExtTag+ AttnBtn- AttnInd- PwrInd- RBE+ FLReset+ SlotPowerLimit 0.000W
DevCtl: Report errors: Correctable- Non-Fatal- Fatal- Unsupported-
RlxdOrd- ExtTag- PhantFunc- AuxPwr- NoSnoop- FLReset-
MaxPayload 128 bytes, MaxReadReq 128 bytes
DevSta: CorrErr- UncorrErr- FatalErr- UnsuppReq- AuxPwr- TransPend-
LnkCap: Port #0, Speed 16GT/s, Width x16, ASPM not supported, Exit Latency L0s unlimited, L1 unlimited
ClockPM- Surprise- LLActRep- BwNot- ASPMOptComp+
LnkCtl: ASPM Disabled; RCB 64 bytes Disabled- CommClk-
ExtSynch- ClockPM- AutWidDis- BWInt- AutBWInt-
LnkSta: Speed unknown, Width x0, TrErr- Train- SlotClk- DLActive- BWMgmt- ABWMgmt-
DevCap2: Completion Timeout: Range ABC, TimeoutDis+, LTR-, OBFF Not Supported
DevCtl2: Completion Timeout: 50us to 50ms, TimeoutDis-, LTR-, OBFF Disabled
LnkSta2: Current De-emphasis Level: -6dB, EqualizationComplete-, EqualizationPhase1-
EqualizationPhase2-, EqualizationPhase3-, LinkEqualizationRequest-
Capabilities: [9c] MSI-X: Enable- Count=6 Masked-
Vector table: BAR=0 offset=00002000
PBA: BAR=0 offset=00003000
Capabilities: [c0] Vendor Specific Information: Len=18 <?>
Kernel modules: mlx5_core
여기서 00:03.0의 NIC(Mellanox MT28800)는 드라이버가 로딩 됐는데, 00:05.0의 NIC는 드라이버가 로딩이 안됐다는 부분이 중요하다. pci 03번에서는 kernel driver in use가 정확히 잡혀있다. 그러나 pci 05번에서는 kernel module만 있을 뿐이지, 로딩된 드라이버 정보는 없다. kernel modules는 [커널에서 사용할 수 있는 모듈]이지, [커널에서 로딩한·사용중인 모듈(드라이버)] 가 아니다.
혹시나 초기화 중에 오류가 발생했나 싶어서 웹콘솔에서 수차례 뗏다가 붙였다를 반복했다. 해당 장치는 PCI Hot-Plugged 방식으로 붙었기 때문에 커널이 인식하자 마자 (최소한 lspci에서 인식한 시점에서는) 드라이버 로딩을 시도했을 것이다. 그러나 계속해서 드라이버가 붙지 않았다. 03번에서 mlx5_core를 로딩한 것으로 보아, 모듈 자체는 따로 건들 필요 없어 보였다. (insmod 등이 필요없어 보였다) /sys/bus/pci/devices/ 쪽을 통해서 reset 걸어도 반응은 동일했다.
문제가 명확해 졌다. VNIC를 붙였으나 드라이버가 로딩되지 않아서 OS가 사용하지 못하고 있는게 근본 문제였다. 그래서 oracle에서 제공한 쉘 스크립트가 어거지로 네트워크 인터페이스를 붙이려다 보니까 ens3 하위에 vlan 을 생성해 버린 것이다. 그러면 왜 드라이버가 로딩되지 않았을까? 로그를 봐야만 풀 수 있는 부분이다.
리눅스에서는 dmesg 라는것이 있다. 장치, 모듈 또는 드라이버 관련 로그가 이곳에 모인다. /var/log/dmesg 를 열거나 명령어 dmesg -H 를 실행하면 된다. 해당 로그의 맨 아래에서 이 문제와 관련된 정보를 찾을 수 있었다