snmpd 로 인해 발생한 시스템 접속 불가 문제
제가 운영하는 시스템에는 snmpd 가 기본으로 서비스되고 있습니다. 각종 시스템의 상태를 파악하기 위해서 snmpd 를 활용하는 거지요. 그런데, 얼마전에 이 snmpd 때문에 아주 큰 문제가 된 일이 있었습니다.
이 문서는 그것이 무엇이며 왜 발생했고 어떻게 처리하는지에 대한 문서 입니다.
snmpd
snmpd 는 국제표준으로 IT기기에 각종 상태들을 네트워크를 통해서 제공할 수 있도록 설게된 SNMP를 제공하는 데몬이다. 수 많은 서버에 기본으로 탑재되어 있어 별다른 노력없이 사용이 가능하다.
리눅스 플랫폼에서 snmpd 를 사용하는데 있어 현재 인자값 사용에 문제가 있다. 인자값의 잘못된 사용으로 인해서 리눅스 전체 시스템에 미치는 영향은 치명적이다 못해 시스템을 재부팅한다하더라도 콘솔접속이 되지 않는한 문제를 해결될 수 없다.
Problems
대부분의 리눅스는 많은 배포판을 이룬다. 내가 테스트한 배포판은 Ubuntu 12.0.4 LTS 와 CentOS 6, 7 이다. 여기서는 Ubuntu 배포판을 기반으로 내용을 전개할 것이다. snmpd 의 문제만 체크하면 되는 것이기에 기타 하드웨어 스펙과 배포판에 설치된 각종 라이브러리등은 중요하지 않아 기술하지 않는다.
이제 문제에 접근해보자. Ubuntu 에서 snmpd 를 설치하면 다음과 같은 파일이 설치된다.
파일 | 내용 |
---|---|
/etc/default/snmpd | snmpd 의 init 파일에서 사용되는 snmpd 데몬 실행 옵션 |
/etc/init.d/snmpd | snmpd 데몬 init 스크립트 |
주목해야할 파일은 ‘/etc/default/snmpd’ 파일이며 이 파일에서 주목해야할 내용은 다음과 같다.
1 |
SNMPDOPTS='-Lsd -Lf /dev/null -u snmp -g snmp -I -smux -p /var/run/snmpd.pid' |
snmpd 데몬의 기본 운영을 바꾸기 위해서는 ‘/etc/default/snmpd’ 파일에서 위 내용을 편집하면 된다.
그럼, 거두절미하고 문제를 재현해보자. 미리 경고하지만 이는 반드시 콘솔접속이 되는 테스트 서버에서 해야한다. 오직 리모트로만 접속할 수 있는 서버이고 이 문제를 재현했다면 영원히 그 서버에 접속할 방법은 없다.
옵션을 다음과 같이 바꾼다.
1 2 |
- SNMPDOPTS='-Lsd -Lf /dev/null -u snmp -g snmp -I -smux -p /var/run/snmpd.pid' + SNMPDOPTS='-Lsd /dev/null -u snmp -g snmp -I -smux -p /var/run/snmpd.pid' |
자세히 보지 않으면 뭐가 바뀌었는지 알수 없게 된다. ‘-Lf’ 를 빠졌다. 이렇게 바꾸고 snmpd 를 재시작하면 문제가 재현된다.
1 2 |
service snmpd restart * Restarting network management services: |
증상
snmpd 를 재시작한 터미널에서는 별다른 증상을 알 수가 없다. 이제 다른 터미널을 열고 snmpd 를 재시작한 서버에 접속해보자. 아마 다음과 같이 나올 것이다.
1 2 |
]$ ssh root@192.168.96.17 ssh_exchange_identification: read: Connection reset by peer |
root 계정이던 일반 계정이든 아무 상관이 없다. 바로 이게 문제다. 아무것도 접속할 수 없다.
콘솔에서 접속해도 다음과 같은 증상이 나온다.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
ubuntu login: root password: -bash: /dev/null: No such device or address <-- 이게 무수히 나오고 -bash: /dev/null: No such device or address -bash: /dev/null: No such device or address -bash: /dev/null: No such device or address -bash: /dev/null: No such device or address root@ubuntu:~# cd / [탭을 누르면] -bash: /dev/null: No such device or address bash: _upvars: `-a2': invalid number specifier -bash: /dev/null: No such device or address -bash: /dev/null: No such device or address -bash: /dev/null: No such device or address -bash: /dev/null: No such device or address |
원인분석
원인은 ‘/etc/default/snmpd’ 에서 정의한 snmpd 의 시작 옵션에 있다. snmpd 는 다음과 같이 커맨드라인에서 시작할 수 있다.
1 |
snmpd [OPTIONS] [LISTENING ADDRESSES] |
문제는 snmpd 에서 옵션은 반드시 ‘-L’, ‘-a’ 등과 같이 – 로 시작한다. 이것이 없이 그냥 값을 주게되면 그것은 바로 [LISTENING ADDRESSES] 가 된다. 여기서 중요한 것이 [LISTENING ADDRESSES]는 어떤값들이 올수 있느냐인데 이는 snmpd 맨페이지(man)를 통해서 확인할 수 있다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
[<transport-specifier>:]<transport-address> At its simplest, a listening address may consist only of a port number, in which case snmpd listens on that UDP port on all IPv4 interfaces. Oth- erwise, the <transport-address> part of the specification is parsed according to the following table: <transport-specifier> <transport-address> format udp (default) hostname[:port] or IPv4-address[:port] tcp hostname[:port] or IPv4-address[:port] unix pathname ipx [network]:node[/port] aal5pvc or pvc [interface.][VPI.]VCI udp6 or udpv6 or udpipv6 hostname[:port] or IPv6-address[:port] tcp6 or tcpv6 or tcpipv6 hostname[:port] or IPv6-address[:port] Note that <transport-specifier> strings are case-insensitive so that, for example, "tcp" and "TCP" are equivalent. Here are some examples, along with their interpretation: 127.0.0.1:161 listen on UDP port 161, but only on the loopback interface. This prevents snmpd being queried remotely. The port speci- fication ":161" is not strictly necessary since that is the default SNMP port. TCP:1161 listen on TCP port 1161 on all IPv4 interfaces. ipx:/40000 listen on IPX port 40000 on all IPX interfaces. unix:/tmp/local-agent listen on the Unix domain socket /tmp/local-agent. /tmp/local-agent is identical to the previous specification, since the Unix domain is assumed if the first character of the <transport-address> is '/'. |
결론만 말하면 <transport-address> 없이 그냥 문자열을 주게되면 snmpd 는 그것을 Unix Domain Socket 으로 인식하게 된다. 위 맨페이지에서 마지막에 나온 설명이 이것을 말해준다.
문제가 된 옵션을 다시보면
1 |
SNMPDOPTS='-Lsd /dev/null -u snmp -g snmp -I -smux -p /var/run/snmpd.pid' |
‘/dev/null’ 은 snmpd 에서 Unix Domain Socket 으로 인식하게 된다.
/dev/null
/dev/null 은 major 1, Minor 3 값을 가지는 Char device 파일이다. 이 파일은 특수한 장치 파일인데, 실제로는 없는 존재하지 않는 커널에서 제공하는 장치파일로서 Input/Output 을 empty 화시켜준다.
1 2 |
ls -lh /dev/null crw-rw-rw- 1 root root 1, 3 Apr 27 22:43 /dev/null |
이 파일들은 아주 많은 프로그램에서 사용되지는데 대표적인 것으로 sshd 가 있다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
root@ubuntu:~# lsof -p 1014 COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME sshd 1014 root cwd DIR 8,1 4096 2 / sshd 1014 root rtd DIR 8,1 4096 2 / sshd 1014 root txt REG 8,1 517112 2103671 /usr/sbin/sshd sshd 1014 root mem REG 8,1 52120 1835042 /lib/x86_64-linux-gnu/libnss_files-2.15.so sshd 1014 root mem REG 8,1 47680 1835036 /lib/x86_64-linux-gnu/libnss_nis-2.15.so sshd 1014 root mem REG 8,1 35680 1835044 /lib/x86_64-linux-gnu/libnss_compat-2.15.so sshd 1014 root mem REG 8,1 105288 1835043 /lib/x86_64-linux-gnu/libresolv-2.15.so sshd 1014 root mem REG 8,1 14360 1835328 /lib/x86_64-linux-gnu/libkeyutils.so.1.4 sshd 1014 root mem REG 8,1 31200 2098198 /usr/lib/x86_64-linux-gnu/libkrb5support.so.0.1 sshd 1014 root mem REG 8,1 158168 2098187 /usr/lib/x86_64-linux-gnu/libk5crypto.so.3.1 sshd 1014 root mem REG 8,1 14768 1835050 /lib/x86_64-linux-gnu/libdl-2.15.so sshd 1014 root mem REG 8,1 97248 1835041 /lib/x86_64-linux-gnu/libnsl-2.15.so sshd 1014 root mem REG 8,1 1811128 1835037 /lib/x86_64-linux-gnu/libc-2.15.so sshd 1014 root mem REG 8,1 14696 1835113 /lib/x86_64-linux-gnu/libcom_err.so.2.1 sshd 1014 root mem REG 8,1 844648 2098194 /usr/lib/x86_64-linux-gnu/libkrb5.so.3.3 sshd 1014 root mem REG 8,1 252720 2098190 /usr/lib/x86_64-linux-gnu/libgssapi_krb5.so.2.2 sshd 1014 root mem REG 8,1 43288 1835047 /lib/x86_64-linux-gnu/libcrypt-2.15.so sshd 1014 root mem REG 8,1 92720 1835237 /lib/x86_64-linux-gnu/libz.so.1.2.3.4 sshd 1014 root mem REG 8,1 10632 1835035 /lib/x86_64-linux-gnu/libutil-2.15.so sshd 1014 root mem REG 8,1 1930648 1835040 /lib/x86_64-linux-gnu/libcrypto.so.1.0.0 sshd 1014 root mem REG 8,1 135366 1835038 /lib/x86_64-linux-gnu/libpthread-2.15.so sshd 1014 root mem REG 8,1 121936 1835071 /lib/x86_64-linux-gnu/libselinux.so.1 sshd 1014 root mem REG 8,1 55744 1835091 /lib/x86_64-linux-gnu/libpam.so.0.83.0 sshd 1014 root mem REG 8,1 36432 1839207 /lib/x86_64-linux-gnu/libwrap.so.0.7.6 sshd 1014 root mem REG 8,1 149280 1835048 /lib/x86_64-linux-gnu/ld-2.15.so sshd 1014 root 0u CHR 1,3 0t0 5862 /dev/null sshd 1014 root 1u CHR 1,3 0t0 5862 /dev/null sshd 1014 root 2u CHR 1,3 0t0 5862 /dev/null sshd 1014 root 3u IPv6 9762 0t0 TCP *:ssh (LISTEN) sshd 1014 root 4u IPv4 9766 0t0 TCP *:ssh (LISTEN) root@ubuntu:~# |
위에서 아래쪽을 보면 TYPE 이 CHR 이며 DVICE 1,3 나오는 ‘/dev/null’ 이 보인다.
그런데, 이것을 위에 문제처럼 snmpd 의 Unix Domain Socket 으로 하게되면 /dev/null 는 더 이상 장치파일이 아닌 소켓 파일로 변경되어 이 장치를 사용하는 모든 프로그램에 문제가 발생하게 된다. 다음과 같이 이전에 장치파일이 아닌 그냥 일반파일로 변경된것을 알 수 있다.
1 2 |
]# ls -lh /dev/null srwxr-xr-x 1 root root 0 Apr 27 22:57 /dev/null |
그리고 netstat 로 보면 snmpd 가 /dev/null 을 Unix Domain Socket 으로 사용하고 있다는 것이 보인다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
root@ubuntu:~# netstat -lpn Active Internet connections (only servers) Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name tcp 0 0 0.0.0.0:6379 0.0.0.0:* LISTEN 1259/redis-server * tcp 0 0 0.0.0.0:22 0.0.0.0:* LISTEN 1127/sshd tcp6 0 0 :::6379 :::* LISTEN 1259/redis-server * tcp6 0 0 :::22 :::* LISTEN 1127/sshd udp 0 0 0.0.0.0:44581 0.0.0.0:* 1886/snmpd udp 0 0 0.0.0.0:68 0.0.0.0:* 1091/dhclient3 udp 0 0 127.0.0.1:161 0.0.0.0:* 1886/snmpd Active UNIX domain sockets (only servers) Proto RefCnt Flags Type State I-Node PID/Program name Path unix 2 [ ACC ] STREAM LISTENING 7856 1/init @/com/ubuntu/upstart unix 2 [ ACC ] STREAM LISTENING 10843 1270/acpid /var/run/acpid.socket unix 2 [ ACC ] STREAM LISTENING 13934 1886/snmpd /var/agentx/master unix 2 [ ACC ] STREAM LISTENING 13931 1886/snmpd /dev/null unix 2 [ ACC ] SEQPACKET LISTENING 8070 671/udevd /run/udev/control unix 2 [ ACC ] STREAM LISTENING 8189 797/dbus-daemon /var/run/dbus/system_bus_socket |
해결방법
가장 좋은 방법은 아직 연결이 유지되고 있는 터미널에서 ‘/etc/default/snmpd’ 의 옵션을 고쳐주는 것이다. 그리고 반드시 재부팅을 해줘야 한다.
그런데, 재부팅을 해서는 안되는 서버라면 어떻게 해야 할까?
첫째, ‘/etc/default/snmpd’ 에서 옵션을 변경하고 snmpd 를 정지시킨다. 재시작해줘도 /dev/null 파일이 장치로 복구가 안된다.
둘째, /dev/null Unix Domain Socket 파일을 삭제한다. 더이상 장치 파일이 아니기 때문에 과감히 삭제한다.
1 |
sudo rm -f /dev/null |
셋째, /dev/null 파일을 다시 만들어 준다.
1 |
sudo mknod -m 666 /dev/null c 1 3 |
위와같이 장치파일을 생성해주자 마자 바로 sshd 가 복구된다.
문제의 핵심
문제는 /dev/null 를 Domain Unix Socket 이던 무엇이던간에 프로그램에서 파일이 속성을 변경되도록 했다는데 있다. 애초에 snmpd 인자로 /dev/null 을 주었던게 문제였고 그 이전에 오타발생이 문제였다.
비단 snmpd 만 인자값으로 Unix Domain Socket 을 사용한다고 생각하지 않는다. 리눅스에는 수많은 프로그램들이 존재하고 snmpd 와 같이 옵션으로 Unix Domain Socket 을 받는 경우도 분명 존재한다. 그것도 하필이면 /dev/null 를 준다면 똑같은 문제가 발생할 수 있다.
위의 snmpd 문제로 영향받는 프로그램들
위 문제(snmpd 로 발생되는 문제) 로 영향받는 프로그램들은 /dev/null 장치를 사용하는 프로그램이라면 다 영향을 받는다.
- sshd
- nginx ← 동작에는 문제가 없어 보이지만 재시작하면 다음과 같이 나오면서 시작되지 않는다.
1 2 3 4 5 6 |
root@ubuntu:~# service nginx restart Restarting nginx: /etc/init.d/nginx: 30: /etc/init.d/nginx: cannot create /dev/null: No such device or address nginx: the configuration file /etc/nginx/nginx.conf syntax is ok nginx: configuration file /etc/nginx/nginx.conf test is successful nginx. root@ubuntu:~# nginx: [emerg] open("/dev/null") failed (6: No such device or address) |
- proftpd ← 재시작하면 다음과 같이 나오면서 시작이되지만 정상적으로 동작이 되지 않는다.
1 2 3 4 5 6 7 8 9 10 |
root@ubuntu:~# service proftpd restart /etc/init.d/proftpd: 198: /etc/init.d/proftpd: cannot create /dev/null: No such device or address * Stopping ftp server proftpd ...done. /etc/init.d/proftpd: 198: /etc/init.d/proftpd: cannot create /dev/null: No such device or address * Starting ftp server proftpd ubuntu proftpd[5135]: mod_tls/2.4.3: compiled using OpenSSL version 'OpenSSL 1.0.0e 6 Sep 2011' headers, but linked to OpenSSL version 'OpenSSL 1.0.1 14 Mar 2012' library ubuntu proftpd[5135]: mod_sftp/0.9.8: compiled using OpenSSL version 'OpenSSL 1.0.0e 6 Sep 2011' headers, but linked to OpenSSL version 'OpenSSL 1.0.1 14 Mar 2012' library ubuntu proftpd[5135]: mod_tls_memcache/0.1: notice: unable to register 'memcache' SSL session cache: Memcache support not enabled ...done. |
- Tomcat ← 재시작하면 다음과 같이 나오면서 시작되지만 접속하면 아무것도 안된다. 이것은 아마도 bash 이 오류를 내면서 톰캣 시작시 세팅되는 환경변수들이 셋업되지 않았기 때문인것으로 보인다.
1 2 3 4 5 6 7 8 |
[root@localhost instance1]# ./start.sh /opt/tomcat-7/bin/setclasspath.sh: line 35: /dev/null: 허가 거부 Using CATALINA_BASE: /home/instance1 Using CATALINA_HOME: /opt/tomcat-7 Using CATALINA_TMPDIR: /home/instance1/temp Using JRE_HOME: /usr Using CLASSPATH: /opt/tomcat-7/bin/bootstrap.jar:/opt/tomcat-7/bin/tomcat-juli.jar Tomcat started. |
/dev/null 이 없어짐으로 인해서 bash 가 제대로 동작하지 않아 쉘스크립트로 작성된 대부분의 init 스크립트가 제대로 동작하지 못해 대부분의 프로그램들에 문제가 발생한다.
CentOS 6,7
운이 좋게도 CentOS 7 은 배포판 패키징으로 옵션 설정이 없다.
1 2 |
[root@localhost ~]# ps aux | grep snmpd root 1063 0.3 0.1 222992 10660 ? Ss 23:40 0:00 /usr/sbin/snmpd -LS0-6d -f |
CentOS 에서는 ‘/etc/sysconfig/snmpd’ 에 옵션 설정을 할 수 있다.
1 2 3 |
]# vim /etc/sysconfig/snmpd OPTIONS="-Lsd /dev/null" ]# systemctl snmpd restart |
여기서 CentOS 7 에서 상태가 심각했다. CentOS 7 에서는 Init System 이 systemd 로 변경되었다. 사실 systemd 는 Init System 프로그램을 넘어서 CentOS 7 배포판의 중추신경계역활을 하게됨에 따라 지대한 영향을 미친다.
이에 따라서 systemd를 제어하는 systemctl 명령어로 그 어떤 것도 실행되지 않았다. 거기다 /dev/null 를 새로 생성하더라도 systemd 가 복구되지 않았다. 무조건 재부팅이 요구된다.
CentOS 6 에서는 ubuntu 처럼 다음과 같이 옵션이 나온다.
1 2 3 |
[root@localhost ~]# ps aux | grep snmp root 2296 0.0 0.0 199560 4188 ? S 00:31 0:00 /usr/sbin/snmpd -LS0-6d -Lf /dev/null -p /var/run/snmpd.pid root 2299 0.0 0.0 107408 892 pts/0 S+ 00:31 0:00 grep snmp |
역시 옵션은 ‘/etc/sysconfig/snmpd’ 에서 설정할 수 있으며 주석으로 다음과 같이 되어 있다.
1 |
# OPTIONS="-LS0-6d -Lf /dev/null -p /var/run/snmpd.pid" |
CentOS 6과 7의 차이
아마도 개인적인 추측인데, CentOS 7 로 넘어오면서 기본 옵션이 변경된데에는 systemd 의 도입때문으로 보인다. systemd 의 도입으로 각 데몬 프로그램들은 systemd 가 상태, 로깅들을 담당하게 되어 이전 CentOS 6 과는 다르게 각 데몬 프로그램들 각자가 로깅을 할 필요가 없게 되었다.
최종결론
어찌되었든 /dev/null 파일을 삭제하거나 일반파일로 바꾸거나 해서는 안된다. 그렇게되면 sshd 는 접속을 거부할 것이다. 혹시나 다른 방법으로 슈퍼유저인 root 로 커맨드를 날릴 수 있다면 당황하지 말고 다음과 같이하면 적어도 sshd 는 돌아온다.
1 2 |
]$ sudo rm -f /dev/null ]$ sudo mknod -m 666 /dev/null c 1 3 |
그리고 ubuntu 에서 ‘/etc/default/snmpd’ 를 수정할때는 오타 를 항상 조심해야 한다.
좋은 팁이네요, snmpd 의 syslog 옵션 바꾸다가 -Lf 를 지우고 snmpd를 재시작 이후로 상기 증상이 나타나서 /dev/null 삭제 후 재생성, 재부팅으로 문제 해결하였습니다.
감사합니다.