본문 바로가기

장애&오류&예외

[VM-Bug] API(libvirt) Packet Drop 주의사항(VM/hypervisors/iptables)

Libvirt에 대해서 간략하게 소개하자면,

KVM, Xen, VMware ESXi, QEMU 및 기타 가상화 플랫폼 관리를 위한 오픈소스 API 데몬/관리도구이다.

보다 상세한 내용은 Libvirt를 참조하면되겠다.

해당 글은 현재, 현업에서 운영중인 사이트 이슈사항에 대해서 언급하겠다.

이슈사항으로 WEB서버 iptables 활성화로 모듈 내, nf_conntrack(Linux OS Kernel Base Packet Trace) 임계값(nf_conntrack_max) 초과에 의한 Packet Drop이 발생되었다.

대상 서비스의 iptables 서비스는 비활성화 상태였으나, 추후 확인된 사항으로 libvirt API의 활성화로 비활성화되어있던 iptables가 활성화 되면서 해당 이슈가 초래되었다.

임계값에 대한 조정으로 대여폭에 대한 조정도 가능하나, 대형사 같은 사이트를 운영하는 입장에서는 되려 문제가 되는 설정에 지나지 않는다.

보안적인 측면으로 나빠지지 않느냐는 이야기도 있겠으나, NAC(Network Access Controller)나, 보안프로그램들(Symantec,V3,ETC) 등등 여러가지 각 구간별로 상상이상의 만반의 준비를 다해둔 보안장비들과 정책... 으로 인해 큰의미를 가지고 있지 않는다.

nf_conntrack_max조정, nf_conntrack 모듈 관련된 사항 관련하여 크게 다루지 않도록하겠다.

 

서비스 상태와 현상은 이러했다.

- iptables 비활성화 상태

- libvirt 활성화 상태

 

결과적으로 libvirt의 활성화에 의해, Hypervisor KVM(Linux OS)내 서비스를 libvirt가 대상 VM들에 Network Interface 설정을 적용하게 되며 문제가 발생하게 된것이다.

 

특정 서버 대상으로 OS가 재기동이 이루어진 대상 VM들에서 iptables가 활성화 되는 이슈가 발생하게된 것이다.

 

해당 이슈 사항 관련하여 Linux OS 명령어를 통하여 확인이 가능하며, 서비스 중단 이슈를 막기위해 검토 조치를 진행한 사항이다.

 

#- Network Interface 확인하기( virbr0 )

ip addr | grep -w inet

ifconfig virbr0

 

#- 서비스 부팅설정 확인( Libvirt / Iptables )

#- OS기동 시, 서비스로딩 등재여부 확인(Libvirt)
→ chkconfig --list | grep libvirt

#- OS기동 시, 서비스로딩 등재여부 확인(Iptables)
→ chkconfig --list | grep iptables

#- IP Access List 확인
→ iptables -nL

 

# 서비스 종료( Libvirt / Iptables )

service libvirt-guests stop
service libvirtd stop
service iptables stop

 

# OS기동 시, 서비스 부팅설정 대상제외( Libvirt / Iptables )

chkconfig --level 35 libvirt-guests off
chkconfig --level 35 libvirtd off
chkconfig --level 35 iptables off

 

# 이슈사항 확인해보기(Try)

#- Libvirt 재기동하기
→ service libvirtd restart

#- 서버 내, IP Access List 정책확인
→ iptables -nL

 

해당 조치 이행 이후, 안정적인 운영을 이루고 있는 중이다.

서버(VM)/접속자수가 많지 않다면 설정값 변경으로 어느정도 조치가 가능하나, 서버를 늘릴수없는 상황 또는 접속자수를 줄일 수(?) 없는 상황이라면 해당조치를 고려하여 진행하면 되겠다.

 

#- 추가주의

- CentOS7이상일 시, firwalld를 사용하기에 대상 내용에 대해서 추가 검토필요.

 

#- 나는 죽어도 Iptables를 사용하고 싶다라는 사람도 있을 것이다.

   따라서, 임계값 조정관련된 사항에 대해서 하기 내용을 기재한다.

#- lunatine님 블로그의 nf_conntrack 임계값 조정 스크립트 커스텀

더보기

- 스크립트 설명 : sysctl.conf 내, netfilter 관련 설정 조정(OS bit/메모리 용량/임계값조정)

- 추가 내용 : 1. 기능추가(backup/자동적용/커널 버전 자동인식(패키지구조변동))

                 2. function 분리 / main func control

#!/bin/bash

# // nf-setup.sh: nf_conntrack configurator
# // author lunatine 
# // modified 롯드

#---------------------------------------
#-- Set Error if Variable is Not Set
#-- Set Exit if Error is Occured
#---------------------------------------

#set -o nounset
set -o errexit

#---------------------------------------
# // Check Parameter & Define Variable
#---------------------------------------

_arch=1
_max=0
_bucket=0
_gto=120
_tcp=54000
_sys_file=/etc/sysctl.conf
_date=`date +%Y%m%d`
_modules=("nf_conntrack" "nf_conntrack_ipv4")
_kernel=`uname -r`
_ver=`echo ${_kernel} | awk -F  "." '{print $1}'`
_sub=`echo ${_kernel} | awk -F  "." '{print $2}'`
_value="${_ver}.${_sub}"
_version="2.6"
_module_dir=/usr/lib/modules
#/sys/module/nf_conntrack/parameters/hashsize
_package=${_module_dir}/${_kernel}/kernel/net/ipv4/netfilter/hashsize

#---------------------------------------
# // Action Scripts 
#---------------------------------------

func_main() {
    # get arguments
    while [[ -n $1 ]]; do
        case "$1" in
            # ${conntrack_max}
            -m|--max) _max=$1; shift 2;;
            -b|--bucket) _bucket=$1; shift 2;;
            -g|--gto) _gto=$1; shift 2;;
            -t|--tcp) _tcp=$1; shift 2;;
            -h|--help) func_help; exit 0;;
        esac
    done
    func_kernel_check
    func_user_check
    func_module_check
    func_define_parmeter
    func_setting_conf
    func_check_result
}

func_kernel_check() {
    if [[ "$(echo ${_value}'>'${_version} | bc -l)" -eq 1 ]]; then
        echo "[kernel-2.6 over] args define]"
        conntrack_count="net.netfilter.nf_conntrack_count"
        conntrack_max="net.netfilter.nf_conntrack_max"
        conntrack_buckets="net.netfilter.nf_conntrack_buckets"
        conntrack_generic_timeout="net.netfilter.nf_conntrack_generic_timeout"
        conntrack_tcp_timeout_established="net.netfilter.nf_conntrack_tcp_timeout_established"
    elif [[ "$(echo ${_value}'<='${_version} | bc -l)" -eq 1 ]]; then
        echo "[kernel-2.6 less] args define]"
        conntrack_count="net.ipv4.netfilter.ip_conntrack_count"
        conntrack_max="net.ipv4.netfilter.ip_conntrack_max"
        conntrack_buckets="net.ipv4.netfilter.ip__conntrack_buckets"
        conntrack_generic_timeout="net.ipv4.netfilter.ip__conntrack_generic_timeout"
        conntrack_tcp_timeout_established="net.ipv4.netfilter.ip__conntrack_tcp_timeout_established"
    fi
}

func_help() {
    cat << EOF
    $ nf-setup.sh [OPTIONS ...]

    Options:
        -m|--max   : ${conntrack_max} (default: memsize / 16384 / arch bit)
        -b|--bucket: ${conntrack_buckets} (default: ${conntrack_max} / 4)
        -g|--gto   : ${conntrack_generic_timeout} (default: 120)
        -t|--tcp   : ${conntrack_tcp_timeout_established} (default: 54000)
        -h|--help  : script function information print.
EOF
}

func_user_check() {
    if [[ "$(id -u)" -ne 0 ]]; then
        echo "[ WARN ] Please Check User Account." 
	echo "[ WARN ] Only Can Start root privilege."
        exit 0;
    fi
}

func_module_check() {
    for mod_name in ${_modules[@]}; do
        if [[ "$(lsmod | grep -c ${mod_name})" -eq 0 ]]; then
            echo "[ INFO ] Can Not Founded [ ${mod_name} ] module."
            exit 0;
        fi
    done
}

func_define_parmeter() {
    # when ${conntrack_max} omitted
    if [[ "$_max" -eq 0 ]]; then
        memtotal=$(grep MemTotal /proc/meminfo | awk '{print $2}')
        [[ "$(uname -m)" == "x86_64" ]] && _arch=2
        _max=$(( memtotal * 1024 / 16384 / _arch ))
    fi

    # when nf_conntrack_buckets omitted
    if [[ "$_bucket" -eq 0 ]]; then
        _bucket=$(( _max / 4 ))
    fi
}

func_setting_conf() {
    # asking for apply
    _conf_values="
    [Applying]
    ---------------------------------------------------
      ${conntrack_max}                    : $_max
      ${conntrack_generic_timeout}        : $_gto
      ${conntrack_tcp_timeout_established}: $_tcp
      ${conntrack_buckets} (hash size)    : $_bucket
    ---------------------------------------------------
     are you sure? (Cancel: Ctrl+C) "
    read -p "$_conf_values" ans

    # Adapt Configuration Setting Before Backup
    cp ${_sys_file} ${_sys_file}_${_date}

    # apply configurations
    sed -i "/^${conntrack_max}/d" ${_sys_file}
    sed -i "/^${conntrack_buckets}/d" ${_sys_file}
    sed -i "/^${conntrack_generic_timeout}/d" ${_sys_file}
    sed -i "/^${conntrack_tcp_timeout_established}/d" ${_sys_file}
    {
        echo "#- [modified] netfilter configuration(${_date})"
        echo "${conntrack_max} = $_max"
	echo "${conntrack_buckets} = $_bucket"
        echo "${conntrack_generic_timeout} = $_gto"
        echo "${conntrack_tcp_timeout_established} = $_tcp"
    } >> ${_sys_file}
    #- input ${conntrack_buckets}
    #echo $_bucket > $_package

    # Adapt Settings
    sysctl -p
}

func_check_result() {
    # Result
    cat << EOF
    [Result]
    ---------------------------------------------------
      max                    : $(sysctl ${conntrack_max})
      hash size (buckets)    : $(sysctl ${conntrack_buckets})
      generic_timeout        : $(sysctl ${conntrack_generic_timeout})
      tcp_timeout_established: $(sysctl ${conntrack_tcp_timeout_established})
    ---------------------------------------------------
EOF
    exit 0
}

func_main

 

#- References

(libvirt)firewall
(bugzilla.redhat)bug_libvirt
(openstack_docs)misc-libvirt
(kernel)nf_conntrack-sysctl
(kernel)nf_flowtable
[네이버] nf_conntrack Packet Drop 대응사례
[lunatine] nf_conntrack과 docker