개발자 / 클라우드

스팀파이프로 코드형 KPI를 구현하는 방식

Jon Udell | InfoWorld 2023.01.02
시아란 피네건은 CMD 솔루션스(CMD Solutions) 오스트레일리아의 사이버 보안 실무 책임자, 필 매신은 같은 회사의 선임 보안 컨설턴트다. 이 두 사람은 약 1년 전 스팀파이프(Steampipe)크라우드스트라이크(CrowdStrike) 플러그인을 사용해 고객의 AWS 환경을 스캔하기 시작했다.
 
ⓒ PublicDomainPictures (CC0)

현재 피네건과 매신은 이른바 “지속적 컨트롤 보장(continuous controls assurance)”, 다른 표현으로 “코드형 KPI”를 위한 내부 시스템을 구축 중이다. KPI(핵심 성과 지표)의 예를 들면 다음과 같다.
 

치명적이거나 심각도 높음 수준의 취약점은 조직의 정책 기간 내에 교정된다.


이 목표를 코드로 어떻게 변환할 수 있을까? 스팀파이프를 사용하면 소프트웨어 스택이 노출하는 다양한 API에 걸쳐 조인이 가능한 SQL 쿼리를 작성하는 방법으로 가능하다. 즉, 엔드포인트 관리 시스템인 크라우드스트라이크에 쿼리한 다음 인력 관리 시스템인 세일즈포스(Salesforce)의 정보와 결합해서(둘 중 하나 또는 둘 모두 변경될 수 있음을 인식함) 취약점에서 디바이스, 그리고 사람으로 매핑되는 쿼리 결과를 생성한다는 의미다.
 
다음은 쿼리다.
 
SELECT    
ZTA.system_serial_number || ' (' || salesforce_krow__project_resources__c.name || ')' as resource,
   CASE
      WHEN ZTA.assessment ->> 'os' = '100' THEN 'ok'
        ELSE 'alarm'
    END AS status,
    ZTA.system_serial_number || ' (' || salesforce_krow__project_resources__c.name || ' has a score of ' || (ZTA.assessment ->> 'os') as reason,     jsonb_path_query_array(ZTA.assessment_items['os_signals'], '$[*] ? (@.meets_criteria != "yes").criteria') #>> '{}' as detail
FROM
        crowdstrike_zta_assessment ZTA -- Link the serial number to the Salesforce data, so we can find the owner -- LEFT JOIN is important, in case there isn't a link, we still want to see the data
LEFT JOIN salesforce_fixed_asset__c
    ON ZTA.system_serial_number = serial_number__c
-- Here an INNER JOIN is necessary.  If the serial number exists in Krow, but no owner, that could indicate a
-- a data inconsistency in Krow, which will break the query.  We want anINNER JOIN, because both entries must exist
INNER JOIN salesforce_krow__project_resources__c
 
여기 사용되는 테이블은 크라우드스트라이크와 세일즈포스 플러그인에서 제공한다. 사전 정의된 세일즈포스 테이블 중에서는 이 요건을 충족하는 테이블이 없었지만 CMD 솔루션스는 자체 맞춤형 세일즈포스 객체를 사용하고 세일즈포스 플러그인은 맞춤형 객체를 동적으로 획득할 수 있으므로 문제될 부분은 없다.
 
쿼리 실행은 스팀파이프 쿼리 실행 방법 중 아무거나 사용하면 된다. 스팀파이프 CLI, psql(또는 임의의 포스트그레스 CLI), 메타베이스(또는 임의의 포스트그레스 호환 BI 툴), 파이썬(또는 임의의 프로그래밍 언어)을 사용할 수 있다. 아니면 CMD 솔루션스가 했던 방법처럼 steampipe check를 사용해서 명령줄에서 실행되는 벤치마크 일부를 형성하는 스팀파이프 컨트롤에 쿼리를 래핑하거나 steampipe dashboard를 사용해서 대시보드로 래핑할 수 있다.
 

쿼리에서 컨트롤, 벤치마크까지

다음은 쿼리를 패키징하는 컨트롤이다. KPI 이름을 지정하고 정의하는 가벼운 래퍼다.
 
control "SEC_002" {
    title = "SEC-002 - % of in-scope personnel compute devices with a Crowdstrike Agent Zero Trust Score for OS of 100"
    sql = <<EOT
    -- SQL as above
    EOT
    }
 
이 컨트롤이 벤치마크가 된다.
 
benchmark "sec" {
    title = "Security"
    children = [
        ...    
    control.SEC_002    
     ...
    ]
}
 
즉, SEC_002를 개별적으로 실행하거나(steampipe check control.SEC_002) 벤치마크의 모든 컨트롤을 실행할 수 있다(steampipe check benchmark.sec). 결과는 다운스트림 분석을 위해 다양한 형식으로 생성이 가능하다.
 
우선 steampipe check를 예약된 방식으로 실행하는 위치와 방법부터 알아보자. 설명서에는 다음과 같이 나와 있다.
 
ECS 파게이트(Fargate)를 사용해서 AWS에서 안전하고 저렴하게 예약된 스팀파이프 벤치마크 검사를 실행한다. 우리는 AWS 코파일럿(Copilot)을 사용해서 계단 함수(Step Function)를 정의하고 AWS ECS 파게이트 예약 작업을 사용해서 스팀파이프 검사를 도커에서 실행한다. 깃옵스(GitOps) 워크플로우를 지원하기 위해 런타임에 깃 리포지토리에서 스팀파이프 벤치마크와 컨트롤을 불러온다.

이 작업은 매일 밤 실행되면서 리포지토리에서 쿼리를 가져와 대상에 대해 실행하고, 그 출력을 아마존 S3로 마크다운으로, 그리고 맞춤형 템플릿에 의해 압축되는 JSON으로 내보낸다.
 

DMARC 구성 확인

또 다른 KPI를 보자.
 

모든 조직 이메일 도메인은 DMARC에 맞춰 구성된다.


여기에 상응하는 쿼리는 다음과 같으며 마찬가지로 컨트롤에 래핑된다.

  
control "INF_001" {
    title = "INF-001 - Organisational email domains without DMARC configured"
    description = "Protect against spoofing & phishing, and help prevent messages from being marked as spam. See https://support.google.com/a/answer/2466563?hl=en for more details."
    sql = <<EOT
        WITH ASSET_LIST as (
            SELECT
                D.domain,
                concat('_dmarc.',D.domain) as dmarc,
                COUNT(N.*) as MXCount
            FROM
                csv.domains D
            LEFT JOIN net_dns_record N on  N.domain = D.domain and N.type = 'MX'
            GROUP BY
                D.domain,
                concat('_dmarc.',D.domain)
        )
        SELECT
            A.domain as resource,
            CASE
                WHEN A.MXCount = 0 then 'skip'
                WHEN N.value LIKE '%p=reject;%' THEN 'ok'
                WHEN N.value LIKE '%p=quarantine;%' THEN 'ok'
                ELSE 'alarm'
            END as status,
            CASE
                WHEN A.MXCount = 0 then 'No MX record for domain ' || A.domain
                WHEN N.value LIKE '%p=reject;%' THEN 'Domain ' || A.domain || ' has a reject policy.'
                WHEN N.value LIKE '%p=quarantine;%' THEN 'Domain ' || A.domain || ' has a quarantine policy.  Consider making it reject.'
                WHEN N.value IS NULL THEN 'Domain ' || A.domain || ' has no DMARC policy defined.'
                WHEN N.value LIKE '%p=none;%' THEN 'Domain ' || A.domain || ' has a dmarc policy of none.'                 ELSE 'Domain ' || A.domain || ' has no DMARC policy'
            END as reason,
            A.domain as domain
        FROM
            ASSET_LIST A
        LEFT JOIN net_dns_record N on N.domain = A.dmarc and N.type = 'TXT' and N.value like 'v=DMARC1%'     EOT
}

여기서 테이블의 출처는 CSVNet 플러그인이다. 세일즈포스와 마찬가지로 CSV 플러그인은 동적으로 테이블을 획득한다. 확인할 도메인 목록은 도메인 이름 시스템 관리 API에서 가져오는 domains.csv라는 파일에 있다. 도메인 이름은 MX 레코드에서 DMARC용으로 구성된 이름을 알아내기 위해 net_dns_record 테이블과의 조인을 유도한다.
 
모든 스팀파이프 컨트롤이 그렇듯이 필수 열resource, status, reason을 보고한다. 플러그인이 제공하는 테이블을 대상으로 모든 종류의 쿼리를 작성할 수 있으므로 이는 온전히 규약일 뿐이지만, 이 규약을 따르면 쿼리는 스팀파이프의 벤치마크 및 대시보드 생태계에서 움직이게 된다.
 

비활성 사용자 계정 확인

여러 API에 걸친 조인이(추론을 위한 일반적인 방법으로 SQL 사용) 스팀파이프의 궁극적인 강점이라는 것은 사실이다. 그러나 여러 API를 꼭 조인할 필요는 없다. 하나의 플러그인이 제공하는 하나 또는 여러 개의 테이블을 쿼리하는 유용한 컨트롤은 많다.
 
KPI를 하나 더 보자.
 

비활성 옥타(Okta) 계정을 조직의 정책 기간 내에 검토한다.


여기에 해당되는 컨트롤은 다음과 같다.
 
 
  control "IAM_001" {
    title = "IAM-001 - Dormant Okta accounts are accounts that have not logged on in the last 30 days"
    sql = <<EOT SELECT
    U.email as resource,
    CASE
        WHEN U.status <> 'ACTIVE' THEN 'skip'
        WHEN date_part('day', CURRENT_TIMESTAMP - U.activated) < 30 OR date_part('day', CURRENT_TIMESTAMP - U.last_login) < 30 THEN 'ok'
        ELSE 'alarm'
    END as status,
    CASE
        WHEN U.status <> 'ACTIVE' THEN 'User ' || u.email || ' is no longer active'
        WHEN U.last_login is null THEN 'User ' || u.email || ' has never logged on' 
       WHEN date_part('day', CURRENT_TIMESTAMP - U.activated) < 30 OR date_part('day', CURRENT_TIMESTAMP - U.last_login) < 30 THEN 'Last logon was on ' || U.last_login
        ELSE 'User ' || u.email || ' last logon on ' || U.last_login
    END as reason,
    U.email,
    U.last_login FROM
    okta_user U EOT
}

이와 같은 컨트롤은 명확하고 읽기 편한 방식으로 비즈니스 로직을 표현하며 약간의 SQL 기술만 있으면 된다.
 

다음 단계

일일 스냅샷이 누적되면서 피네건과 매신은 이를 시각화하고 추세와 핵심 위험 지표(KRI)를 식별할 방법을 연구하고 있다. 파이썬 스크립트는 맞춤화된 steampipe check 출력을 읽고 S3로 흐르는 JSON 및 마크다운 출력을 빌드한다. 두 사람은 쿼리를 시각화하기 위해 프로토타입 스팀파이프 대시보드를 만들었으며, 시각화 툴이 그림을 완성하는데 어떻게 도움이 될지를 고려 중이다.
 
이런 작업을 하는 이유는 무엇일까? 피네건은 “시중에서 파는 제품을 구입해도 된다. 그러나 이러한 제품은 우리의 모든 서비스와 통합되지는 않고 비즈니스 객체에서 SQL 문으로의 세부적인 매핑을 제공하지 않는다. 이것이 스팀파이프가 하는 역할”이라고 말했다.
 
스팀파이프 사용법은 언제나 흥미로운 주제다. 더 자세한 내용은 파게이트 러너에 대한 리포지토리와 두 사람의 지속적 컨트롤 보장 모듈에서 확인할 수 있다. 
editor@itworld.co.kr 

회사명 : 한국IDG | 제호: ITWorld | 주소 : 서울시 중구 세종대로 23, 4층 우)04512
| 등록번호 : 서울 아00743 등록발행일자 : 2009년 01월 19일

발행인 : 박형미 | 편집인 : 박재곤 | 청소년보호책임자 : 한정규
| 사업자 등록번호 : 214-87-22467 Tel : 02-558-6950

Copyright © 2024 International Data Group. All rights reserved.