2016년 7월 24일 일요일

VIM 사용 설명서(secure 로그 분석)

이제 VIM을 이용한 secure 로그 분석을 진행해보자. 특히 오늘은 문자열 데이터를 다양한 관점에서 해체하고 조립하는 과정을 통해 전체적인 구조와 의미를 (대충) 파악하는 시간을 가져볼 것이다.

'헬로 데이터 과학'편에서 이미 살펴봤듯이 secure 로그는 일정 길이나 ':' 등의 특정 기호를 이용해서 완벽하진 않지만 나름의 일관성을 부여해서 데이터의 성격별 영역 구분이 가능한 반정형 데이터이다.


정확한 통계 및 관계 분석을 위해서는 적당히 나열된 로그를, 고정된 필드에 필요한 데이터가 적재된 정형 데이터, 즉 열과 행의 구조를 갖는 테이블 구조로 만들어야 하는데, 그러기 위해서는 먼저 로그를 잘 알 필요가 있다. 그러나 엄마 뱃속에서부터 로그 공부하다 온 사람은 없다.

로그를 잘 알아야 비정형 또는 반정형 데이터를 정형 데이터로 바꾸는 과정, 즉 ETL(추출, 변환, 적재) 과정을 잘 수행할 수 있는 건 맞지만, 반대로 ETL 과정에서 시행착오를 반복하다 보면 자연스럽게 로그 전문가가 될 수 있다는 것.

삽질이 필요하다는 뜻이다. (삽질 횟수와 전문성은 비례) 그리고 VIM은 조금은 편한 삽질을 가능하게 해준다.

개인적으로 처음 접하는 데이터베이스 테이블 구조를 파악하고 싶을 때는 각 필드별 통계를 구해보곤 했다. 필드별로 어떤 데이터가 어떤 분포로 저장되어 있는지 파악하면서 해당 테이블의 성격과 분석에 필요한 데이터 및 방향을 결정했던 것.

로그도 마찬가지다. secure 로그의 다섯 개 필드는 어떤 데이터로 구성되어 있을까? 다행히 해당 로그는 구조가 단순해서 처음 세 개 필드(날짜, 시간, 호스트명)는 파악이 쉽다. 그러나 네 번째 필드는 '프로세스' 정보인 것 같긴 한데 분포는 잘 모르겠다.

'VIM 사용 설명서 - 3rd' 편에서 소개한 'sort' 명령을 이용해서 발생 프로세스의 분포를 파악해보자. 정확한 분포 파악을 위해서는 '프로세스'를 제외한 나머지 문자열을 삭제할 필요가 있다.

먼저 '프로세스' 정보를 기준으로 왼쪽에 위치한 문자열을 삭제해보자. 해당 로그는 단일 호스트에서 발생한 로그이기 때문에 세 번째 호스트명 정보까지는 일정한 길이로 구분된다. 다른 호스트명이 섞여 있을지도 모른다구? 확인해보자.


글로벌 명령어를 이용해서 호스트명 문자열이 포함된 줄을 삭제했더니 전부 지워졌다. 단일 호스트에서 발생한 로그 맞다. 그런데 로그가 다 지워졌네? 당황하지 말고 읽기 모드에서 'u(ndo)' 키를 입력하자. VIM의 'undo' 기능은 한계가 없으며, 실행했던 모든 작업을 취소할 수 있다. 'redo' 기능은 'CTRL+R'.

날짜, 시간, 호스트명 문자열의 길이가 같으니 이 특징을 이용하면 해당 위치의 문자열을 쉽게 삭제할 수 있다. 그리고 이때 VIM의 문자열 치환 기능이 빛을 발한다. 당연히 '검색'할 수 있어야 '치환'할 수 있으니 검색을 먼저 해보자. 사용된 검색어 '^.\{23}'은,
  1. 각 줄에서 첫 번째 시작하는(^) 문자부터 검색을 시작하며, 
  2. 검색 시작 위치에서 임의 문자(.)를 23개(\{23})만 검색하라는 의미의 정규표현식이다.

정규표현식 '^.\{23}'의 구조(regexper.com)

검색

검색이 잘 됐으니 치환은 껌이다. 치환 명령어 '%s/.\{23}//'는 검색된 문자열을 깔끔하게 지워준다. 하지만 실제 사용한 명령어는 '%s///'.

치환 명령어에서 첫 번째 '//' 영역(치환 전 문자열)은 빈 칸으로 둬도 VIM이 자동으로 기억해준다는 사실을 잊지 말자. 검색된 문자열의 삭제가 목적이므로 두 번째 '//' 영역(치환 후 문자열)도 당연히 빈 칸.

치환

이제 '프로세스' 정보를 기준으로 오른쪽에 위치한 문자열만 삭제하면 된다. 사용된 검색어 ':.*'는,
  1. 시작하는 문자는 ':'이며
  2. 이후, 임의 문자(.)가 0개 이상(*) 있음을 의미하는 정규표현식이다.


검색

검색된 문자열을 삭제하는 과정은 이전과 같다.

치환

이제 '[]'에 둘러쌓인 프로세스 번호(pid)에 해당하는 문자열만 삭제하면 되는데, 여기서 잠깐! 처음부터 '['로 시작하는 문자열을 찾아서 지웠으면 더 간단하지 않을까? 정규표현식 '[.*'를 이용한 검색 결과는 다음과 같다.

참고로 '['와 ']' 한 쌍은 정규표현식에서 문자열의 검사 범위를 지정하는 메타문자(문자 클래스)로 동작하는데, 쌍이 안 맞을 경우 VIM에서는 일반 문자로 인식하는 반면, PCRE는 무조건 예외처리(\)를 해줘야 일반 문자로 인식한다.

검색

글로벌 명령어를 이용해서 해당 검색 결과를 포함한 줄을 삭제해보자. pid 정보가 없는 프로세스도 있다는 사실을 알 수 있다. 그런데 글로벌 명령어에서는 쌍이 안 맞는 '['도 예외처리(\[)를 해줘야 일반 문자로 인식한다. 노벨상 취소


결국 '['는 프로세스와 그 오른쪽에 위치한 프로세스별 메시지 문자열의 공통 구분 기호가 되지 못하며, ':'으로만 통일된 구분이 가능하다. 몇 번의 삽질끝에 알게 된 정보.

이제 '[]'에 둘러쌓인 pid 정보까지 지워버리면 순수한 프로세스 정보만이 남는다. pid 문자열 검색에 사용한 정규표현식 '\[.*\]'은
  1. 시작 문자 '['와 끝 문자 ']' 사이에
  2. 임의 문자(.)가 0개 이상(*) 있음을 의미한다. 


검색

치환

이제 프로세스 정보만 남았다. 'sort u' 명령을 이용해서 중복을 제거한 후 확인한 프로세스의 종류는 총 7개.

중복 제거 후 정렬

프로세스 종류가 확인됐으니 분포 파악은 쉽다. 글로벌 명령어 'v/ sshd\[/d'는 ' sshd[' 문자열을 포함하지 않는 모든 줄을 삭제해준다. 결과적으로 로그 대부분이 sshd 프로세스에 의해 발생했음을 알 수 있다.


login 프로세스에 의해 발생한 로그는 고작 143개. 이런 식으로 나머지 프로세스의 분포도 확인할 수 있다.


로그의 전체적인 윤곽이 드러나고 있다. 이제 남은 건 프로세스별 메시지 발생 분포. 그런데 너무 덥다. 이런 날엔 동네 수영장이라도 가야 하는데, 의사가 목 디스크 주제에 무슨 수영, 산책이나 하라니 별 수 없음(..) 프로세스별 메시지 발생 분포 확인은 다음에~

관련 글

댓글 없음:

댓글 쓰기

크리에이티브 커먼즈 라이선스