지난 글에는 단일 오펜스만 닫는 perl script를 작성하였는데

속도가 굉장히 느려서 perl의 ForkManager 모듈을 이용한 멀티 프로세스의 offense closure를 새로 코딩했습니다.

 

10 pid로 10 items 를 해결하는데 27초 걸립니다.

 

테스트 환경은 QRadar 7.4.2 2020.7.0.20201113144954 버젼 입니다.

 

use strict;
use warnings;
use Parallel::ForkManager;
my $truecount = 0;
my @offlist;
my @listcmd = "curl -S -X GET -u admin:password1! -k -H 'Range: items=0-9' -H 'Version: 15.0' -H 'Accept: application/json' 'https://<console_ip>/api/siem/offenses?fields=id&filter=status%20%3D%20%22OPEN%22'";
my $pm = Parallel::ForkManager->new(10);

$pm->run_on_start(
    sub {
        my ( $pid ) = @_; # $pid
        print "### START pid: $pid\n";
    }
);

$pm->run_on_finish(
    sub {    # must be declared before first 'start'
        my (
            $pid,
            $exit_code,
            $ident,
            $exit_signal,
            $core_dump,
            #$data,
        ) = @_; # $data exit return value
        print "## END pid: $pid\n";
        #$out{ $data->[0] } = $data->[1];
    }
);

while($truecount == 0){
	my $list = qx(@listcmd); #[{"id":85562},{"id":85561}] string
	$list =~ s/\{\[?"id":(\d+)\]?\}/$1/g;
	@offlist = split(/,/, $list); # create array
	
	foreach my $shfoff (@offlist)
	{
		$pm->start() and next; # start pid

		$shfoff =~ s/\[?(\d+)]?/$1/;
		my @closecmd = ("curl -S -X POST -u admin:password1! -k-H 'Version: 15.0' -H 'Accept: application/json' 'https://<console_ip>/api/siem/offenses/$shfoff?closing_reason_id=1&status=CLOSED'");
		system(@closecmd);

		$pm->finish(0); # stop pid
	};
	
	$pm->wait_all_children;
};

이전 스크립트 보다 훨씬 깔끔해졌습니다.

username : password 와 <console_ip>를 채워주면 되고

my $list와 $shfoff를 보면 알겠지만 단순한 id값만 뽑아내서 열려있는 모든 오펜스를 닫는 스크립트입니다.

그래서 원하는 오펜스만 자동으로 닫으시려면 @listcmd 의 filter를 바꾸고 정규표현식만 약간 바꿔주면 작동이 잘 될겁니다.

use strict;
use warnings;
my $truecount = 0;  #루프문 변수
my @offlist;  #오펜스 배열 변수
#Range는 리스트 갯수 조정
my @listcmd = "curl -S -X GET -u admin:password12! -k -H 'Range: items=0-100' -H 'Version: 15.0' -H 'Accept: application/json' 'https://<콘솔아이피>/api/siem/offenses?fields=id&filter=status%20%3D%20%22OPEN%22'";
my $arrcount;  #오펜스 배열 자리 변수
my $lenoff = 0;  #오펜스 배열 크기 변수

while($truecount == 0){
	if($lenoff == 0){
    	#[{"id":85562},{"id":85561}] => listcmd는 배열이 아닌 문자열로 저장됨
		my $list = qx(@listcmd);  #qx: 실행한 결과값 변수로 저장하는 함수
		$list =~ s/\{\[?"id":(\d+)\]?\}/$1/g;  #list내에 모든 {"id":숫자}를 숫자만으로 치환
		@offlist = split(/,/, $list);  #쉼표로 구분해서 배열로 새롭게 저장
		$lenoff = @offlist;  #배열 크기 저장
		$arrcount = 0;
	} else {
		for (my $i = 0; $i < $lenoff+1; $i++){  #1을 더해야 배열 크기가 정확함
			my $shfoff = @offlist[$arrcount];  #오펜스 배열의 숫자 불러오기
			$shfoff =~ s/\[?(\d+)]?/$1/;  #맨 처음, 뒤에 들어가는 [ 와 ] 제거
			if($shfoff =~ /\d+/ ){  #가끔 null 값을 피하기 위한 검사
				my @closecmd = ("curl -S -X POST -u admin:password12! -k -H 'Version: 15.0' -H 'Accept: application/json' 'https://<콘솔아이피>/api/siem/offenses/$shfoff?closing_reason_id=1&status=CLOSED'");
				system(@closecmd);
				$arrcount++;  #다음 배열을 가져오기 위함
			};
		};
		$lenoff = 0;  #초기화 - 새로운 리스트 받아오기
	};
};

꼬박 밤을 샜지만 재밌었다 ㅋㅋ

단점은 조금 느리다.
100개를 closing하는데 생각보다 오래 걸리는 느낌이든다.
나중에 python이나 Bash로 만들어서 API 테스트를 해봐야겠다.

use strict;
use warnings;
my $last_name = 1;
my $last_cidr = 1;
my $last_id = 1;
my @content; # json을 저장할 배열 생성
my $count = 0;

for (my $i = 0; $i < 500; $i++) { # 네트워크 대역 500개 생성
	if($i < 255) { # 이중for문 구현 귀찮아서 if문으로 대체
		$content[$count] = "{'id': $last_id,'group': 'Net_500','name': 'TEST_net_$last_name','description': 'test network ','cidr': '192.10.$last_cidr.0/24','domain_id': 0}";
	} else { # 255개 넘어가면 오류로 입력안됨, 마지막에 1 플러스되므로 2씩 줄임.
		$last_cidr -= 2;
		$last_name -= 2;
		$content[$count] = "{'id': $last_id,'group': 'Net_500','name': 'TEST_net_2_$last_name','description': 'test network ','cidr': '192.20.$last_cidr.0/24','domain_id': 0}";
	}
	$last_id++;
	$last_cidr++;
	$last_name++;
	$count++;
};

# 배열을 한 문자열로 합치기
my $total_con = join ", ", @content;
# 모든 홀따옴표를 쌍따옴표로 변경(curl 입력시 안 끊기게)
$total_con =~ s/'/"/g;

# curl 명령어 작성
my @cmd = ("curl -S -X PUT -u admin:password12! -k -H 'Content-Type: application/json' -H 'Version: 15.0' -H 'Accept: application/json' --data-binary '[$total_con]' 'https://콘솔아이피주소/api/config/network_hierarchy/staged_networks'");

# 혹시 몰라 결과물 텍스트로 저장
open (FH, ">network-hierachy.txt") || die ("Can't open file");
print FH @cmd;
#print "Success save!\n";

# 명령어 실행
system(@cmd);

 

큐레이더 팀에서 올려준 파이썬 api 사용하려다가 구문 오류로 헤매서 펄 스크립트로 뚝딱 만드니 잘 되네요.

역시 필요하면 직접 만드는게 빠릅니다.

큐레이더랑 펄은 뗄려고 해도 뗄 수 없는거 같아요 ㅋㅋ

 

if문 안에서 1씩 자동증가하게끔 했어야하는데 나중엔 그렇게 해야죠..

과부하 테스트를 위해서 어제 펄 스크립트를 작성하여 실행하고 잤더니 결과는 대만족입니다.

소스포트를 1씩 늘려가면서 오펜스를 소스포트 기반으로 뜨게 만들었습니다.

 

use strict;
use warnings;
my $srcpt;
my $content;

for(my $i = 0; $i < 500000; $i++) {
	open (TEXT, "+<offense500k.log");
	chomp($content = <TEXT>);
	if($content =~ /srcport=(\d+)/){
		$srcpt = $1 + 1;
	};
	$content =~ s/srcport=(\d+)/srcport=$srcpt/;
	seek(TEXT,0,0);
	print "TEST log: ", $content, "!\n";
	print TEXT $content;
	close TEXT;
	system '/opt/qradar/bin/logrun.pl -u 192.168.0.10 -f offense500k.log 1';
	print "Success number", $i, "!\n";
};

일단 50만개를 만들어줄거라서 for문을 열었습니다.

 

open 함수로 샘플 로그파일을 입출력모드로 열고

샘플 로그파일이 한줄짜리라서 chomp로 변수 $content에 할당했습니다.

로그파일에 있는 소스포트를 받기위해 if문을 작성하여 변수 $srcpt에 1을 더해 저장했습니다.

 

$content 내용에서 srcport를 정규식으로 찾은 후, 아까 if문에서 1을 더한 $srcpt로 치환했습니다.

seek 함수는 입력 포인트를 처음으로 이동하게 하였습니다. (맨 끝에 추가가 아닌 덮어쓰기를 위해서)

로그가 제대로 바뀌었는지 터미널에 바뀐 로그를 출력하고

print TEXT $content; 에서 샘플 로그 파일에 바뀐 로그를 덮어씌우게 됩니다.

close로 파일을 닫고

system으로 QRadar의 logrun.pl 명령어를 작성해주면 끝입니다.

 

펄 스크립트는 처음 써봤는데 요즘은 덜 쓰는 언어이지만 간단한 명령을 실행할때 편합니다.

1. 설치하려는 서버의 RAID 구성

QRadar 호스트 RAID 구성 단계
Flow collector
QRadar Network Insights (QNI)
RAID1 (미러링)
Data node
Event processor
Flow processor
Event and flow processor
All-in-one console
RAID6 (2 parity)
Event collector RAID10 (미러링+스트라이핑)

위 표는 IBM에서 권장하는 RAID 구성이고, 사용자의 니즈에 맞춰 구성하면 됩니다.


RAID설명: https://raisonde.tistory.com/entry/RAID0부터-RAID7까지-모든-RAID구성을-알아보자 [지식잡식]

 

 

2. IBM에서 다운로드 받는 파일이 QRFULL ISO인지 확인

QRFULL 이미지 파일 하나만 다운로드 받아두면 APP host, QNI, collector, processor, data node, aio console 아무거나 설치가 가능하니 QRFULL 이미지 파일과 해쉬 파일에 해당하는 sha256 파일을 꼭 다운로드하시길 권장합니다.

 

3. 서버 자원 할당

QRadar installation Guide를 살펴보면, 리눅스 버젼, 비트버젼, 최소 RAM, 최소 CPU, 최소 Storage 등

다양한 조건을 맞춰야 정상적으로 작동합니다.

7.4부터는 리눅스 버젼 7.7이상, 7.3.3은 리눅스 7.2 64bit

App Host의 최소 RAM은 12GB, All-in-One Console 3199 (5000 EPS and 200,000 FPM)의 최소 RAM은 32GB

App Host의 최소 CPU코어는 4개, All-in-One Console 3199 (5000 EPS and 200,000 FPM)의 최소 CPU코어는 16개

저장공간은 최소 256GB이상 들어가야합니다.

이벤트와 플로우를 저장하는 서버면 테라단위로 있어야 /store 파티션을 넉넉하게 사용할 수 있습니다.

위 정보는 구글에 검색하면 나오기 때문에

자주 설치하다보면 자연스럽게 외워집니다.

https://www.ibm.com/docs/en/SS42VS_7.3.3/com.ibm.qradar.doc/b_siem_inst.pdf

 

4. 설정할 Hostname, IP, DNS 적어놓기

레드햇 리눅스의 설치와 QRadar 설치를 마치면 네트워크 설정으로 넘어가게 됩니다.

QRadar의 구조는 단순합니다.

 

올인원 콘솔이 있고

콘솔에 연결된 이벤트 프로세서 또는 플로우 프로세서

콘솔 또는 프로세서에 연결된 이벤트 콜렉터 또는 플로우 콜렉터(비슷하지만 다른 말로 QNI)

데이터 노드가 각 지점마다 연결될 수 있습니다.

 

콘솔과 프로세서는 HA 고가용성으로 Primary, Secondary로 이중화를 할 수 있습니다.

 

HA를 뺀 구조도는 대충 이렇게 그려집니다.

데이터노드를 프로세서에 붙일 수도, 콘솔에 붙일 수 있습니다.

 

QRadar는 라이센스만 지원한다면 최대 96TB까지 이벤트를 실시간 분석할 수 있습니다.

그리고 CVE 기반의 취약점을 자동으로 진단하고 거의 모든 기능을 UI로만 관리할 수 있습니다.

 

이제 그림에 나온 각 장비의 역할을 알아볼까요?

  • Event Collector: 이벤트 로그 수집, 전달
  • Event Processor: 수집된 이벤트 로그를 받아서 분석, 파싱, 정규화
  • QFlow 또는 QRadar Network Insight: 트래픽 수집, 전달
  • Flow Processor: 수집된 트래픽을 받아서 파싱, 정규화
  • Data Node: 이벤트나 트래픽을 저장만 해두는 공간
  • All-in-one Console: 사용자 인터페이스와 실시간 이벤트 로그 및 트래픽 뷰 제공, Report, Offense, Asset 등 기능제공

 

장비를 알아봤으니 Daemon 서비스도 알아봅시다.

 

  • tomcat: httpd 서비스, GUI에 내용 표시 (웹이 말썽이면 tomcat 재시작)
  • hostcontext: 콘솔 및 관리 대상 호스트에서 실행되는 기본 프로세스, 모든 핵심 QRadar 프로세스 제어(tomcat, postgres, ariel, qflow 등)
  • hostservices: 데몬 동작, 메세지 큐, QRadar 구성요소와 PostgreSQL 사이의 통신 지원
  • ariel: DB 검색, 검색 ID 및 결과 조회
  • ariel_proxy_server: 프로세스 검색
  • ariel_query_server: sql cursor 포맷을 사용해 ariel_proxy_server에 결과 반환, 호스트 요청 수집
  • ecs-ec-ingress: 이벤트 데이터 수집
  • ecs-ec: 이벤트 데이터의 파싱 및 정규화
  • ecs-ep: Rules, Routing, 이벤트 저장 및 분석

 

이 구조를 알아야 OSI 7 layer처럼 엉뚱한 곳에 시간낭비를 하는 일이 줄어듭니다.

 

초보인 제가 더 알아야하는 구조인 것이지요..

 

 

이 게시판에는 제가 배웠던 큐레이더 내용.

 

어려워했던 내용들을 잘 정리해서 간결하게 설명할 것입니다.

 

왜냐하면 미래의 제가 큐레이더에 관련해서 다 잊어버려도 쉽게 따라할 수 있어야하거든요.

 

대신 민감한 정보들은 암호를 걸고 작성할 것입니다. (그 암호는 저를 알면 알 수 있어요.)

 

틀린 내용은 댓글로 수정해주시면 감사하겠습니다.

 

이제 1달차를 지나서 복잡하네요 ㅠㅠ

+ Recent posts