Azure DevOps
오늘날 애플리케이션 개발에서 순수하게 자체 코드로만 서비스를 구축하는 경우는 찾아보기 어렵습니다. 조직에서 개발하는 애플리케이션 중 상당수는 수십에서 수백 개의 오픈소스 라이브러리와 서드파티 구성 요소로 이루어져 있습니다. 비중으로 보자면 전체 코드베이스의 70~90%가 외부 컴포넌트로 구성되는 것이 일반적입니다. 특히 오픈소스 비중이 높은데, OpenSSF에 따르면 상용 소프트웨어 코드베이스의 97%가 어떤 형태로든 오픈소스를 포함합니다. 이처럼 오픈소스에 대한 높은 의존도는 개발 생산성과 혁신을 높이지만 동시에 소프트웨어 공급망의 복잡성을 키우고 보안 통제를 어렵게 만드는 요인이 됩니다.
외부 라이브러리와 서드파티 구성 요소에 크게 의존하는 개발 관행은 소프트웨어 공급망 보안 측면에서 새로운 위험을 낳습니다. 조직이 개발한 애플리케이션에 포함된 서드파티 컴포넌트 중 하나라도 취약하거나 악의적으로 손상되면, 공격자는 이를 발판으로 여러 시스템을 한꺼번에 공격할 수 있습니다. 대표적인 예가 Log4j 라이브러리의 심각한 취약점(Log4Shell)인데, 이는 뒤에서 시나리오를 통해 자세히 다루겠습니다.
이처럼 공급망 전반에 걸친 보안 취약성은 현재 사이버 보안 분야에서 최우선으로 고려해야 할 문제로 부상했습니다. 이는 막연한 걱정이 아닙니다. 사이버 위협 행위자들이 소프트웨어 공급망을 노린 공격은 계속해서 늘어나고 있습니다.
취약점 파악 및 패치 적용의 어려움 – 사례 시나리오
외부 컴포넌트 의존도가 높아진 상황에서 우리 시스템이 어떤 취약한 구성 요소에 노출되어 있는지 신속히 파악하는 것은 쉽지 않습니다. 실제로 2021년 말 치명적인 Log4j 취약점이 공개되었을 때 많은 조직은 자사 애플리케이션 어디에 Log4j가 사용되는지 찾기 위해 우왕좌왕해야 했습니다.
규모가 큰 조직의 경우 수만 대 서버에서 실행 중인 수백 개 서비스 전반에 걸쳐 Log4j 라이브러리를 찾는 일은 SBOM 없이는 수작업으로 불가능에 가까운 작업이었습니다. 게다가 취약점이 발견된 구성 요소가 직접적인 의존성인지, 여러 단계를 거친 간접 의존성인지에 따라 파급 범위를 정확히 알기 어려워 패치 적용이 지연되는 일도 발생했습니다. 이처럼 주요 구성 요소의 취약점을 제때 파악하고 조치하기 어려운 근본적인 이유는 가시성 부족 때문입니다. 우리가 만든 소프트웨어에 어떤 구성 요소가 포함되었는지 명확히 목록화하지 않다면, 새로운 보안 이슈가 발생할 때마다 일일이 추적하고 대응하는 데 많은 시간과 노력이 들 수밖에 없습니다.
SBOM 개념과 활용 방안
이러한 문제를 해결할 도구로 SBOM(Software Bill of Materials)이 주목받고 있습니다. 미국 사이버 보안 및 인프라 보안국(CISA)과 통신정보관리청(NTIA)은 SBOM을 "소프트웨어 구성 요소와 의존성, 해당 구성 요소에 대한 정보, 그리고 그들 간의 계층적 관계를 포함하는, 공식적이며 기계로 판독 가능한 목록"이라고 정의합니다. 제조업에서 제품을 만들 때 부품 명세서를 작성하듯이 SBOM은 하나의 소프트웨어를 빌드하는 데 사용된 모든 라이브러리, 패키지, 모듈 등의 이름, 버전, 공급처, 라이선스, 해시, 의존 관계 등 정보를 망라하는 구성 요소 명세서라고 할 수 있습니다.
SBOM의 중요성은 미국 정부의 적극적인 움직임으로 더욱 부각되었습니다. 2021년 바이든 행정부는 '국가 사이버 보안 개선에 관한 행정명령(Executive Order 14028)'을 발표하며 연방 정부에 소프트웨어를 공급하는 모든 기업에 SBOM 제출을 의무화했습니다. 이처럼 SBOM은 소프트웨어 공급망의 가시성을 높이는 핵심 수단으로 여겨지고 있습니다.
SBOM을 도입하면 여러 이점을 얻을 수 있습니다. 먼저 보안 취약점 대응 속도를 높일 수 있습니다. SBOM을 통해 우리 소프트웨어에 어떤 구성 요소가 포함되어 있는지 한눈에 파악할 수 있습니다. 따라서 새로운 CVE 취약점이 발표되었을 때 해당 컴포넌트의 사용 여부를 즉시 확인할 수 있습니다. 예를 들어 SBOM 목록을 검색하여 "Log4j 2.x 버전을 사용하는 서비스가 있는가?"를 바로 알아내고 대응 조치를 할 수 있습니다. SBOM이 없다면 이러한 작업은 소스 코드를 검색하거나 담당자에게 일일이 문의해야 하는 번거로운 일이었을 것입니다.
다음으로 오픈소스 라이선스 관리가 용이해집니다. SBOM에는 각 오픈소스 구성 요소의 라이선스 정보도 포함되므로, 사용 중인 모든 오픈소스의 라이선스를 체계적으로 관리할 수 있습니다. 이를 통해 라이선스 충돌이나 위반 소지를 사전에 파악하여 법적 위험을 줄이고, 기업 내부의 오픈소스 사용 정책을 준수하는 데에도 도움이 됩니다.
세 번째로 소프트웨어 공급망의 투명성을 강화합니다. SBOM은 소프트웨어가 어떤 외부/내부 구성 요소로 이루어졌는지 투명하게 보여주어 전체 공급망에 대한 보안 가시성을 높입니다. 이를 통해 의존성 체인 상의 잠재적 위험 요소를 식별하고, 서드파티 공급업체의 취약점이나 악성코드 삽입 시도를 조기에 발견할 수 있습니다. 이러한 투명성 때문에 유럽연합의 Cyber Resilience Act 등 각국 규제에서도 SBOM 공개를 요구하는 방향으로 나아가고 있습니다.
마지막으로 운영 효율성과 유지보수 편의성이 증대됩니다. SBOM을 유지하면 구성 요소 업그레이드나 패치 작업의 범위를 쉽게 파악할 수 있어 업데이트 관리가 효율적입니다. 예를 들어 새 버전이 출시된 라이브러리를 SBOM을 통해 한눈에 식별하고 그 영향을 받는 제품을 찾아 신속히 업데이트 일정을 잡을 수 있습니다. 또한, SBOM은 다양한 자동화 도구와 연계되어 구성 요소 변경 추적이나 취약점 스캔 자동화를 지원하므로, DevSecOps 파이프라인에서 유용하게 활용됩니다.
이처럼 SBOM은 소프트웨어 구성 요소의 투명한 장부로서 보안, 컴플라이언스, 운영 측면에서 여러 이점을 제공합니다. 다만 SBOM을 작성하는 것만으로는 충분하지 않으며, 이를 활용한 자동화된 보안 관리 체계를 구축해야 실질적인 가치가 극대화됩니다. SBOM이 있다면 자동화 도구를 통해 취약점 점검을 일상화하고, 문제가 발견될 경우 빌드 파이프라인 단계에서 자동으로 차단하는 등 능동적인 대응 프로세스를 구현할 수 있습니다. 이제 안랩클라우드메이트가 Azure DevOps 환경에 적용한 SBOM 기반 취약점 알림 시스템 구축 경험을 공유하겠습니다.
우리 팀은 SBOM 기반 취약점 관리 시스템을 구상하면서 Azure DevOps 파이프라인에서 제공하는 기본 알림 기능을 활용할 수 있는지 검토해보았습니다. Azure DevOps에는 파이프라인 실패 시 이메일 알림 등을 보내는 기능이 기본 제공되지만, 우리가 확인한 바로는 여러 가지 제약 사항이 있었습니다
제한적인 커스터마이징
SBOM 기반 취약점 관리 시스템을 구상하면서 Azure DevOps 파이프라인의 기본 알림 기능 활용 가능성을 검토했습니다. 그러나 Azure DevOps의 기본 기능에는 몇 가지 명확한 한계가 있었습니다.
가장 큰 문제는 커스터마이징의 제약이었습니다. 기본 알림 시스템은 사전에 정의된 템플릿 구조를 따르므로 보안 취약점의 상세 정보, 영향을 받는 모듈 목록, 위험도 수준 등 핵심 정보를 체계적으로 구성하기 어렵습니다. 특히 SBOM 스캔을 통해 도출된 복합적인 취약점 데이터를 구조화된 형태로 전달하는 것은 현실적으로 불가능합니다. Stack Overflow의 Q&A에서도 Azure DevOps 알림 템플릿은 사용자 수정이 불가능하며, 더 풍부한 내용을 보내려면 Microsoft Power Automate나 IFTTT 등을 활용한 우회 방법이 필요하다는 조언을 찾아볼 수 있습니다.
또한, 확장성과 중앙 관리의 부재도 문제였습니다. 만약 Azure DevOps 파이프라인 YAML 내부에 스크립트를 삽입해 알림을 구현한다면, 이를 모든 프로젝트의 모든 파이프라인에 일일이 적용해야 합니다. 다수의 프로젝트를 운영하는 조직에서 알림 로직이나 수신자를 변경할 때마다 모든 파이프라인 정의를 개별적으로 업데이트하는 것은 매우 비효율적이고 오류 발생 가능성이 높습니다. 이렇게 분산된 방식으로는 모든 프로젝트에 일관된 알림 기준과 포맷을 유지하기도 어렵습니다.
이러한 이유로 우리 팀은 Azure DevOps 자체 기능만으로는 만족스러운 SBOM 취약점 알림 체계를 구축하기 어렵다고 판단하고, 대안으로 Microsoft Power Automate 활용을 결정했습니다. 우리 팀은 기존 Microsoft 365 구독 환경에서 추가 라이선스 비용 없이 Power Automate의 핵심 기능을 활용할 수 있었습니다. 또한, 코드 기반 개발 없이 직관적인 드래그 앤 드롭 인터페이스를 통해 복잡한 알림 로직을 구현할 수 있어 개발 및 유지보수 리소스를 최적화할 수 있었습니다.
Power Automate 기반 커스텀 알림 시스템 구현
Power Automate는 다양한 트리거와 워크플로 자동화를 지원하는 플랫폼으로 Azure DevOps와 효과적으로 연계할 수 있습니다. 특히 HTTP 요청을 수신하여 흐름을 시작하는 트리거와 이메일 전송 액션을 조합하면, Azure DevOps 파이프라인에서 발생한 이벤트를 받아 가공한 뒤 커스텀 이메일로 전송하는 시나리오를 쉽게 구현할 수 있습니다.
이러한 접근 방식은 알림 관련 운영 효율성을 크게 높일 수 있습니다. Power Automate를 통해 알림 관련 설정들을 중앙에서 관리할 수 있기 때문입니다. 단일 Power Automate 플로우 변경만으로 알림 수신자 목록 변경, 이메일 템플릿 업데이트, Teams 채널 등 추가 알림 채널 확장과 같은 변경 사항을 전체 프로젝트에 즉시 반영할 수 있습니다.
Power Automate만으로 알림 관리의 중앙화는 가능했지만 SBOM 검사 로직까지 포함한 완전한 중앙 집중식 관리를 위해서는 추가적인 구조가 필요했습니다. 그래서 전체 시스템을 이중 중앙화 구조로 설계했습니다. 참고로 설계 방향은 다음과 같이 잡았습니다.
• Azure DevOps 파이프라인 템플릿화: 각 프로젝트 파이프라인에 SBOM 생성 및 취약점 스캔 로직을 개별적으로 구현하는 대신 공용 YAML 템플릿(sbom-syft.yml)으로 작성하고자 했습니다. 이 템플릿에는 SBOM 생성부터 취약점 검사, 결과 판단 및 처리까지 모든 단계가 포함되어 개별 프로젝트 파이프라인에서는 해당 템플릿을 불러오기만 하면 동일한 로직을 적용할 수 있습니다. 이를 통해 SBOM 기반 검사 로직의 중앙화와 재사용성을 확보하고자 했습니다.
• Power Automate로 알림 흐름 중앙화: 알림 전송 로직은 Azure DevOps에서 완전히 분리하여 Power Automate 상의 단일 클라우드 플로우(Flow)로 구축하고자 했습니다. 각 파이프라인은 취약점 데이터가 있을 경우 이 중앙 플로우의 HTTP 엔드포인트를 호출하고 플로우가 이를 받아 이메일 구성 및 전송을 수행합니다. 이렇게 하면 알림 양식이나 수신자 변경 등의 조치를 Power Automate에서 한 번만 수정하면 모든 프로젝트에 적용되어 운영 관리가 일원화됩니다.
• 파이프라인 단계를 제어하여 배포 차단: 파이프라인을 단계(Stage)별로 구성해 SBOM 스캔 단계에서 취약점이 발견되면 이후 빌드 및 배포 단계가 실행되지 않도록 설계하고자 했습니다. 즉, SBOM 검사 단계가 성공해야만 다음 단계로 진행하는 조건을 설정해 취약한 구성 요소가 검출된 빌드는 자동으로 실패 처리되도록 하고자 했습니다. 이런 식으로 취약한 코드의 프로덕션 배포를 사전에 봉쇄하고 개발 단계에서 문제를 해결하도록 유도합니다.
이제 구체적인 구현 단계를 살펴보겠습니다. 참고로 전체 시스템을 구축하기 위해 다음과 같이 4단계로 작업을 진행했습니다:
1단계: Power Automate 플로우 설정
첫 단계는 파이프라인으로부터 취약점 정보를 받아 구조화된 이메일 알림을 발송하는 Power Automate 플로우를 구성하는 것입니다. 전체 플로우는 HTTP 트리거, Compose, 이메일 전송, 응답(Optional)의 네 가지 컴포넌트로 이루어집니다.
1) HTTP 트리거 설정
Instant cloud flow를 생성하여 외부 시스템의 HTTP 요청 시 자동으로 실행되도록 구성했습니다. 참고로 Instant cloud flow는 버튼 클릭, 예약된 시간, 또는 외부 시스템의 HTTP 요청과 같은 특정 이벤트가 발생할 때 자동으로 실행되는 흐름입니다.
요청 본문의 스키마는 다음과 같이 JSON 형식으로 정의하여 파이프라인에서 전달할 데이터 구조를 명시했습니다.
{
"type": "object",
"properties": {
"projectName": {"type": "string"},
"buildNumber": {"type": "string"},
"requestEmail": {"type": "string"},
"confluenceUrl": {"type": "string"},
"htmlContent": {"type": "string"}
}
}
2) Compose 설정
이메일 본문에 사용할 HTML 템플릿을 구성합니다. 동적 콘텐츠(Dynamic content)를 활용하여 파이프라인에서 전달받은 변수들을 템플릿에 삽입할 수 있습니다. 다음은 HTML 템플릿 예시입니다.
<div style="font-family: Arial, sans-serif; max-width: 800px; margin: 0 auto;">
<div style="padding: 20px; background-color: #f9f9f9;">
<h1>SBOM for @{triggerBody()?['projectName']} (Version - @{triggerBody()?['buildNumber']})</h1>
<div style="background-color: white; padding: 15px;">
<h4 style="color: #d83b01;">빌드 보안 점검 결과 알림</h4>
@{triggerBody()?['htmlContent']}
</div>
</div>
<div style="text-align: center; margin-top: 20px;">
<a href="@{triggerBody()?['confluenceUrl']}" style="background-color: #0078d4; color: white; padding: 10px 20px;">
Confluence 페이지 이동
</a>
</div>
</div>
3) Send Email 설정
To 필드에 수신자 이메일 주소를 입력하고, Subject에 이메일 제목을 작성합니다. Body에는 동적 콘텐츠에서 Compose 단계의 출력값을 선택하여 위에서 정의한 HTML 템플릿을 적용합니다.
4) Response 설정(선택 사항)
파이프라인에 처리 완료를 알리는 HTTP 응답을 반환합니다.
최종적으로 우측 상단의 ‘Save’ 버튼을 누르면 Manual 에 HTTP URL이 생성됩니다. 플로우 저장 완료 후 생성되는 HTTP URL은 다음 단계에서 Azure DevOps 환경 변수로 등록하여 사용합니다.
2단계: Azure DevOps 환경 변수 구성
1단계에서 생성된 Power Automate HTTP 웹훅 URL을 Azure DevOps 파이프라인에서 사용할 수 있도록 환경 변수로 등록했습니다. 보안을 위해 이 변수는 반드시 Secret 변수로 설정하여 파이프라인 로그에서 URL이 노출되는 것을 방지해야 합니다. 변수 이름은 프로젝트 전체에서 일관되게 사용하여 관리 효율성을 높였습니다.
variables:
POWER_AUTOMATE_URL: $(POWER_AUTOMATE_URL)# Secret 변수로 설정
3단계: SBOM 템플릿 로직 구현
SBOM 템플릿(sbom-syft.yml)에는 취약점 존재 여부를 판단하고 결과에 따라 Power Automate를 호출하며 파이프라인을 중단시키는 핵심 로직을 구현했습니다.
먼저 SBOM 스캔 결과를 분석하여 취약점 존재 여부를 판단하고 그 결과를 vulnerabilities_found.txt 파일에 저장합니다.
# 취약점 존재 여부 판단 및 결과 파일 작성
has_vulnerabilities = len(vulnerabilities) > 0
with open("$(Build.ArtifactStagingDirectory)/build/vulnerabilities_found.txt", "w") as f:
f.write("true" if has_vulnerabilities else "false")
동시에 발견된 취약점 정보를 Power Automate가 처리할 수 있는 JSON 형태로 구성하여 email-data.json 파일로 저장합니다.
# 요청 데이터를 파일로 저장
with open("$(Build.ArtifactStagingDirectory)/build/email-data.json", "w") as f:
json.dump(request_data, f)
마지막으로 취약점이 발견된 경우에만 Power Automate를 호출하여 알림을 발송하고 동시에 파이프라인을 오류 상태로 중단시키는 스크립트를 실행합니다.
# 취약점이 있는 경우에만 이메일 전송
if [ -f "$VULNERABILITIES_FILE" ] && [ "$(cat $VULNERABILITIES_FILE)" = "true" ]; then
curl -X POST "$(POWER_AUTOMATE_URL)" \\
-H "Content-Type: application/json" \\
-d "$EMAIL_DATA"
echo "##[error]취약점이 발견되어 빌드를 중단합니다."
exit 1
fi
4단계: 파이프라인 구조 개편
파이프라인의 실행 순서를 ‘버전 관리 → SBOM 검사 → 빌드 → 배포’ 순으로 개편했습니다. SBOM 생성 시 정확한 버전 정보가 필요하고, 버전이 확정된 후 빌드가 진행되어야 일관된 버전 관리가 가능하기 때문입니다. 개선한 구조는 다음과 같습니다.
stages:
# 1단계: 버전관리 및 SBOM 검사
- stage: VersionAndSBOM
displayName: Version and SBOM Check
jobs:
- job: version
displayName: Version
steps:
- template: version.yml@templates
- job: SBOM
displayName: SBOM
steps:
- template: sbom-syft.yml@templates
# 2단계: SBOM 검사 통과 시에만 빌드 및 배포
- stage: BuildAndDeploy
condition: succeeded()
dependsOn: VersionAndSBOM
jobs:
- job: BuildAndDeploy
steps:
- script: echo 'Building Docker image...'
이러한 구조를 통해 VersionAndSBOM 단계에서 취약점이 발견되어 실패하면 succeeded() 조건에 따라 BuildAndDeploy 단계는 실행되지 않아 프로덕션 배포를 사전에 차단할 수 있습니다.
테스트 과정 및 적용 결과
시스템이 의도한 대로 작동하는지 확인하기 위해 현재 운영 중인 프로젝트의 라이브러리 버전을 의도적으로 다운그레이드하여 취약점 시나리오를 만들고 테스트를 진행했습니다.
테스트 결과 취약점이 발견되었을 때는 파이프라인이 SBOM 검사 단계에서 중단되었고 담당자에게 상세한 정보가 담긴 알림 이메일이 발송되었습니다.
취약점이 없을 때는 파이프라인이 정상적으로 빌드 및 배포를 완료했으며 알림은 발송되지 않았습니다.
우리 팀은 설계, 구현, 테스트 과정에서 시스템을 실제 운영 환경에 도입할 때는 몇 가지 사항을 추가로 고려해야 한다는 것을 알 수 있었습니다. 이를 간단히 정리하면 다음과 같습니다.
• 파이프라인 구조를 신중하게 설계: SBOM 검사 단계는 반드시 빌드 이전에 위치해야 하며, 그 결과에 따라 후속 단계 실행 여부를 제어해야 합니다. Stage/Job 간 의존성(dependsOn)과 조건(condition)을 적절히 활용하여 취약점 스캔 실패 시 나머지 단계가 실행되지 않도록 설정하는 것이 중요합니다. 또한, SBOM 리포트에 표시될 버전 정보가 정확해야 하므로 빌드 버전 결정 로직은 SBOM 스캔보다 앞서 실행되도록 순서를 조정해야 합니다.
• 민감 정보를 안전하게 관리: Power Automate 웹훅 URL과 같은 민감 정보는 소스 코드에 노출하지 말고 Azure DevOps의 Secret 파이프라인 변수로 관리해야 합니다. 이렇게 하면 저장소 YAML에는 변수 참조만 포함되므로 비밀 값이 노출되지 않고, 실수로 로그에 출력될 위험도 최소화할 수 있습니다.
• 성능 및 리소스 고려: SBOM 생성과 취약점 스캔은 빌드 프로세스에 추가 시간을 소요시킬 수 있습니다. 따라서 모든 브랜치에 대해 매번 실행하기보다는, main, dev 등 주요 브랜치에 대해서만 조건부로 SBOM 단계를 실행하도록 구성하면 효율적입니다. 우리 팀은 개발 편의와 보안 간의 균형을 위해 메인 브랜치와 릴리즈 브랜치 빌드에 SBOM 체크를 의무화하고, 기타 임시 브랜치에서는 필요시 수동으로 파이프라인을 실행할 수 있도록 했습니다.
이번에 소개한 Azure DevOps와 Power Automate 기반 SBOM 취약점 자동 알림 시스템을 통해 우리 팀은 기존에 수작업으로 흩어져 이루어지던 보안 점검 과정을 표준화하고 자동화할 수 있었습니다. 특히 SBOM 생성/검사 로직은 Azure DevOps 템플릿으로 그리고 알림 전송 로직은 Power Automate로 중앙화하는 이중 중앙화 구조는 각 역할에 맞게 시스템을 효율적으로 관리하는 데 주효했습니다. 이제 새로운 취약점이 발표되면 SBOM을 통해 영향을 즉시 파악하고, 문제가 되는 빌드는 자동으로 차단함과 동시에 담당자에게 상세 정보가 전달되어 선제적인 대응이 가능해졌습니다.
이 포스팅이 Azure DevOps와 Power Platform을 사용하는 팀에게는 실질적인 적용 사례가 되고, 다른 CI/CD 환경을 사용하는 팀에게도 ‘SBOM + CI 파이프라인 + 자동 알림’이라는 흐름을 통해 공급망 보안을 강화하는 유효한 접근법으로 참고가 되기를 바랍니다.
소프트웨어 공급망 보안의 중요성이 날로 커짐에 따라 SBOM 활용은 선택이 아닌 필수가 되어가고 있습니다. 이번 포스팅이 효과적인 SBOM 기반 자동화 보안 관리 방안을 모색하는 계기가 되기를 바랍니다.
참고
Mastering HTTP Triggers in Power Automate
How to Run an Instant Flow in Power Automate