심플하게 crontab,python,telegram을 이용하여 서버의 cpu와 memory를 특정 주기로 검사하여 기준값 초과시 텔레그램으로 알림을 주도록 합니다. python 은 설치되어 있고 아래 내용들을 따라가면서 발생하는 trouble shooting이 어느 정도 가능한 분들을 대상으로 작성합니다.

 

1. 텔레그램 봇 생성

1) 텔레그램 클라이언트에서 @BotFather 사용자 추가후 스타트 버튼 클릭

2) /newbot 입력

3) 생성할 봇의 이름 입력(ex) NOTIFICATION_BOT 또는 알림_로봇 -> 반드시 BOT 또는 로봇이 들어가 있어야 생성됨)

4) 이렇게 생성된 봇을 알림을 받고 싶은 채널에 사용자 추가를 한다

* chat_id를 알아내는 방법

@BotFather에서 /token 을 누르고 생성한 봇을 선택하면 HTTP API 토큰값을 얻을 수 있다

https://api.telegram.org/bot토큰/getUpdates 으로 나온 응답값의 chat_id로 메세지 전송이 가능하다

https://api.telegram.org/bot토큰/sendMessage?chat_id=&text=Hello%20World%21

 

2. python telepot 모듈을 이용한 메세지 전송

1) pip 설치

- sudo yum install python-pip

2) telepot 설치 ( api 명세 : http://telepot.readthedocs.io/en/latest/ )

- pip install telepot

3) 테스트 코드 (send_msg.py)

 

#!/usr/bin/env python
import telepot
import os
import socket
import commands
import string
import sys

#Telegram bot
NOTI_API_TOKEN = '위방법으로 알아낸 토큰값 설정하면 됩니다'
NOTI_CHAT_ID = 위방법으로알아낸chat_id
bot = telepot.Bot(NOTI_API_TOKEN)

def send(chat):
 bot.sendMessage(NOTI_CHAT_ID, chat, parse_mode='HTML')

if len(sys.argv) == 1:
    print "# need more parameter"
    exit(1)

msg=''
for idx, value in enumerate(sys.argv):
    if idx > 0:
        msg += value + "\n"
send(msg)

테스트 -> python send_msg.py 'hello, world'

 

3. cpu와 memory 체크 및 알림 코드 (noti_monitoring.py, 본인에 맞게 커스터마이징 하세요)

 

#!/usr/bin/env python
# -*- coding: utf-8 -*-
import telepot
import os
import socket
import commands
import string
import sys
import psutil
import time
 
# 기준 및 초기값 설정
cpuLimit = 50 # 서버 환경에 맞게 수정하세요
memLimit = 50 # 서버 환경에 맞게 수정하세요
serverName = '서버이름'
title = '[' + serverName + ' 서버]\n'
msg = title
 
#Telegram bot
NOTI_API_TOKEN = '알아서넣으세요'
NOTI_CHAT_ID = 알아서넣으세요
bot = telepot.Bot(NOTI_API_TOKEN)
 
# 텔레그램 메세지 전송
def send(chat):
        bot.sendMessage(NOTI_CHAT_ID, chat, parse_mode='HTML')
 
# cpu 사용율 계산
def getCpuUsage():
        cpu = 0
        for x in range(2):
            cpu += psutil.cpu_percent(interval=1)
        return round(float(cpu)/3,2)
 
# memory 사용량 계산
def getMemUsage():
        mem=str(os.popen('free -t -m').readlines())
        T_ind=mem.index('T')
        mem=mem[T_ind+6:]
        mem_T=mem[:13]
        mem_sub=mem[14:]
        mem_U=mem_sub[:13]
        return round(float(mem_U)/float(mem_T)*100,2)
 
avgCpu = getCpuUsage()
memUsage = getMemUsage()
 
if avgCpu > cpuLimit:
        msg += 'cpu 사용량이 기준값(' + str(cpuLimit) + '%)을 초과하였습니다.\n'
        msg += 'cpu usage : ' + str(avgCpu) + '%\n'
 
if memUsage > memLimit:
        msg += 'memory 사용량이 기준값(' + str(memLimit) + '%)을 초과하였습니다.\n'
        msg += 'memory usage : ' + str(memUsage) + '%\n'
 
if avgCpu > cpuLimit or memUsage > memLimit:
        msg += serverName + ' 무엇무엇을 합니다'
        send(msg)
        os.system('커멘트라인 명령으로 필요한 작업, 필요없을 경우 주석처리')
        time.sleep(5)
        msg = title + '재시작후 cpu & memory는 아래와 같습니다.\n'
        msg += 'cpu usage : ' + str(getCpuUsage()) + '%\n'
        msg += 'memory usage : ' + str(getMemUsage()) + '%\n'
        send(msg)

4. crontab에 등록할 스크립트 생성 (noti_monitoring.sh, 본인에 맞게 커스터마이징 하세요)

 

 

python /스크립트경로/noti_monitoring.py

 

5. crontab 등록(crontab -e)

 

# 매1분 단위로 실행 (자세한 설정은 구글링하세요)
*/1 * * * * /스크립트경로/noti_monitoring.sh > /dev/null  2>&1

 

예) 127.0.0.0/24 일 경우    127.0.0.0 ~ 127.0.0.255 


설명)

    시작값은 0, 끝값은 0 + (24번째비트부터 31번째비트까지 1로 on 된 10진수값 => 255) = 255






예2) 127.0.0.128/25 일 경우    127.0.0.128 ~ 127.0.0.255

    시작값은 128, 끝값은 128 + (25번째비트부터 31번째비트까지 1로 on 된 10진수값 => 127) = 255

원인 : 쉘스크립트 실행시 유닉스 개행문자와 윈도우 개행문자가 섞여 있을때 ^M 발생


해결

1. 바이너리 편집 모드로 파일 열기 

   # vi -b  파일명

2. 명령 모드에서 아래를 입력하여 ^M 삭제

   %s/^M//g 


주의 : ^M 입력시 ctrl + v, ctrl + M 으로 입력해야 정상적으로 입력된다.


메모리 누수가 의심될때 한가지는 스왑 메모리의 사용량이다. 아래 내용으로 간단히 확인 가능하다.

 

  • 배경
    • 리눅스는 free 메모리를 최대한 cache 메모리로 사용하여 시간이 지날수록 cache 메모리 사용량이 증가하면서 애플리케이션의 로딩 속도가 향상됨.
      • free 메모리가 필요한 순간에는 cache 메모리를 줄이고 free 메모리를 확보함.
  • top 예제
  • 위 그림에서 used는 실제 메모리 사용량이 아니라 빨간색으로 묶은 buffers + cached, 그리고 여기에 표시되지 않은 다른 메모리사용량(실제 메모리 사용량)을 합한 값으로 보여지게 된다.

 

  • free 예제


    • 실제 메모리 사용량을 확인할 수가 있는데, 빨간색으로 보여지는 부분의 used가 실제 메모리 사용량이고 free가 실제 여유 메모리를 나타낸다.
      • 첫번째 라인의 used는 buffers + cached + 실제 메모리사용량 --> 1420 = 167 + 1072 + 180 (mb로 표시하여 1의 오차가 발생, free -k로 보면 값이 정확함)
      • 두번째 라인의 free는 첫번째 라인의 free + buffers + cached --> 1845 = 606 + 167 + 1072 (mb로 표시하여 1의 오차가 발생, free -k로 보면 값이 정확함)

 

게임 포털 웹서비스 조직에서 개발자 지원 업무를 하고 있었을때 발생한 보안 이슈였고 원인과 해결 방법에 대해 정리해본다.


1. 현상

사이트내 갤러리 게시판이고, 이미지를 클릭하면 다운로드하는 형태.

특정 url의 response 데이터에 /etc/passwd 내용이 노출되었다,

ex) http://xxx.xxx.com/xxx/xxx?param=file%3A%2F%2F%2Fetc%2Fpasswd



2. 원인

원래는 http://xxxxxx 식의 이미지 경로가 들어와야 하는 하며, 내부 로직에서는 들어온 파라미터를 이용해 

new Url("http://xxxx") 처리를 하도록 되어 있다. 문제는 파라미터로 들어온 file%3A%2F%2F%2Fetc%2Fpasswd

 url decoding을 하면 file:///etc/passwd 값인데 Url클래스가 로컬 파일 접근이 가능하기 때문에 

password 가 response 에 고스란히 노출되었던 것이었다.


3. 해결

기본적으로 XssFilter 적용이 되어 있어서 CRCF injection (http://www.acunetix.com/websitesecurity/crlf-injection/) 

공격과  directory/path traversal 도 되지 않도록 아파치 설정이 되어 있어 노출만 될뿐 추가적인 피해는 없었다.

따라서 아래와 같은 방법으로 해결하도록 가이드하여 적용하였다.

 

1. 파일 확장자 체크 - 정해진 확장자만 허용  (.gif, .jpg, .png )

2. URL 클래스 사용시 새로운 객체 생성에서 url에 HTTP만 허용 (ftp, telnet, file 등은 사용하지 않도록)

 

 추후에라도 파일 다운로드를 하는 웹서비스를 할 경우 까먹지 말고 유의하도록 해야겠다.


연관 이슈

sql injection, xss, parameter manipulation, buffer overflows, directory/path traversal etc.



                                                                    그림. L4 DSR 처리 방식


클라이언트와 먼저 3way handshaking을 한 뒤 정상적이면 2차로 서버와 동일하게 3way handshaking을 하게 된다.

이 과정 후에 클라이언트가 http로 보낸 요청에 대해 서버가 요청을 처리하게 되면 L4로 경유하지 않고 바로 L3 스위치를 통해

클라이언트로 응답을 내려주게 되는데 이때 L4에서는 이 최초 요청에 대한 여러 세션 정보중에 Destination IP가 실제 응답을 준
서버 아이피로 세션 테이블에 정보를 저장하게 되는데, 서버 혹은 클라이언트가 FIN 패킷을 보내어 L4가 받게 되면 세션 테이블에서
해당 세션 정보를 삭제하게 된다. 

만약 아파치에서 keepAlive를 켜지 않았을 경우에는 서버가 응답을 내려주고 FIN 패킷을 보내기 때문에 일반적인 PC환경에서는
클라이언트의 요청이 Round Robin방식으로 요청이 분산이 되지만 KeepAlive를 켤 경우에는 KeepAliveTimeout이 발생하지 않는한

클라이언트와 서버 모두 FIN을 보내지 않기 때문에 L4에 저장된 세션 정보의 Destination Ip를 보고 동일한 서버로 요청이 들어가게 된다.



* 주요 L4 Switch 설명

  1. SLB (Server Load Balancing)

       - 과거에 서비스를 했었던 방법으로 클라이언트에서 서버로 보낼때와 서버에서 리턴될때 모두 L4 Switch를 거치기 때문에 과부하가 걸리는 서비스의 경우 DNS RR방식으로 서비스를 구성하기도 한다.

  2. GSLB (Global Server Load Balancing)

       - 특정 도메인으로 들어온 요청을 분산시키는 방법으로 도메인 네임에 대한 쿼리 요청에 대해 응답을 변형하여 지역적으로 분산시킨다.

  3. DSR (Direct Server Return)

       - 현재 인프라에서 적용하여 사용하는 방법으로 SLB 방식으로 인한 L4 Switch의 과부하를 막기 위해 서버에서의 응답이 바로 클라이언트로 보내도록 한다.  위의 그림과 같이 동작하게 된다.

'Network' 카테고리의 다른 글

아이피 대역 계산 방법  (0) 2015.11.13

내부 사용자들이 간단하게 인증할 수 있는 방법


mod_auth_basic.so
mod_authn_file.so

없다면 설치하고 httpd.conf에서 loadmodule할것

 


설정된 디렉토리 내용 안에 아래 추가.

 httpd.conf 설정은 아래와 같이 한다.

        AllowOverride All
        Order allow,deny
        Allow from all
AuthType Basic
AuthName "test admin"
AuthUserFile "/경로는알아서/apache/conf/.htpasswd"
require valid-user

보안상 .htpasswd 파일은 아파치 conf밑에 두도록 한다.
htpasswd -c .htpasswd 아이디
 


'Apache & Nginx' 카테고리의 다른 글

[Apache] 프로세스 생성 방식에 따른 비교  (0) 2015.09.03

Prefork 방식

  • 1개의 자식 프로세스당 1개의 쓰레드를 가짐(최대 1024개의 프로세스 생성 가능)
    • 1개의 자식 프로세스는 1개의 요청을 처리
  • 프로세스당 독립적인 메모리 사용
    • 메모리 공간이 독립적이기 때문에 안정적이나 메모리를 많이 소모
  • 설정 내용 (샘플)

     <IfModule prefork.c>

        StartServers        20      // 아파치 시작시 생성되는 자식 프로세스 개수

        MinSpareServers      5      // 부하가 적을 경우에 이 수치만큼 유지하려고 함

        MaxSpareServers     10      // 부하가 많을 경우에 이 수치 이하로 줄이려고 함

        MaxClients         256      // 클라이언트의 최대 요청 수 제한

        MaxRequestsPerChild  10000  // 생성된 자식 프로세스가 이 수치만큼 요청을 받을 경우 처리후에 자동으로 종료됨 

                                  //(0은 무한대로 유지 -> 이렇게 값을 주게 되면 특정 모듈의 메모리 누수와 같은 버그로 서비스 불가 상황이 발생할 수 있다!)

    </IfModule>

Worker 방식

  • 1개의 자식 프로세스가 다중 쓰레드를 가지며, 각 쓰레드는 1개의 요청을 처리
  • 쓰레드간 메모리 공유로 Prefork보다 적은 메모리 사용
    • 리소스의 race condition 발생이 생길 가능성이 크므로 조심해야 함. 특히 php는 더 조심해서 사용.
  • 설정 내용 (샘플)

     <IfModule worker.c>

        StartServers         2       // 아파치 시작시 생성되는 자식 프로세스 개수

        ServerLimit          6       // 구성 가능한 자식 프로세스 제한 개수 (MaxClients와 ThreadsPerChild에서 요구한 프로세스 개수보다 높게 설정하면 안됨)

        MaxClients         150       // default는 ServerLimit * ThreadPerChild, 클라이언트의 최대 동시 요청 처리 수

        MinSpareThreads     25       // 서버에 idle 쓰레드가 불충분하다면 자식 프로세스는 idle 쓰레드 > MinSpareThreads 가 될때까지 생성됨

        MaxSpareThreads     75       // 서버에 너무 많은 idle 쓰레드가 존재하면 자식 프로세스는 idle 쓰레드 < MaxSpareThreads 가 될때까지 죽게됨

        ThreadsPerChild     25       // 자식 프로세스당 생성될 수 있는 쓰레드 개수

        MaxRequestsPerChild  0       // 생성된 자식 프로세스가 이 수치만큼 요청을 받을 경우 처리후에 자동으로 종료됨

                                  //(0은 무한대로 유지 -> 이렇게 값을 주게 되면 특정 모듈의 메모리 누수와 같은 버그로 서비스 불가 상황이 발생할 수 있다!)

    </IfModule>

요약

  • 일반적으로는 prefork 방식이 사용되고 요청량이 많은 서버의 경우 worker 방식을 사용하기도 하나, worker방식은 쓰레드간 메모리 공유로 인해
    리소스에 대한 경합이 발생할 가능성이 많기 때문에 애플리케이션 레벨에서 코딩을 thread-safe하게 구현해야 한다. 
    보다 안정적인 서비스를 위해서 prefork + 서버 scale-out으로 많이 사용한다.
  • MaxClient의 튜닝 (prefork 방식 사용시에~)
    • 서버의 메모리 사용량 대비 처리가능한 개수로 역산하여 정하는 것이 좋다.
      • 계산 방법 예제
        • 사용가능한 여유 메모리 산정 : 2G(Total Memory) - OS(500M) - Java(600M) - Swap(Total Memory * 0.8 = 160M) = 약 700M
          • 2G를 TotalMemory로 봤을때 OS 입장에서 80% 이상 사용하게 되면 swap 발생 가능성이 커짐. 따라서 1.6G 만이 가용 가능한 메모리라고 봐야 함.
          • 1.6G에서 예를들어 Tomcat의 Xmx가 600M이고 top 기준으로 RES 700M정도 된다고 하면,  최종 900M 정도가 가용  메모리라고 봐야 한다.
          • 900M에서 OS와 기타 주요 프로세스들이 사용하는 메모리를 200M 정도로 산정하여 추가로 빼주면 700M 정도가 apache가 사용할 수 있는 가용메모리가 됨.
        • 자식 프로세스 하나의 메모리 크기 측정 : 약 10M 라고 가정
        • MaxClients 는 700 / 10 = 70개로 서비스 가능하다.


'Apache & Nginx' 카테고리의 다른 글

[Apache] 로그인 계정 세팅  (0) 2015.09.03

+ Recent posts