Skip to content
hello tis
Go back

사내 버셀 구축 - 2단계: 보안 피드백 반영

개발 리뷰를 받고 사내 적용을 결정했다

1편에서는 개인 홈 서버에 프로젝트 생성부터 Preview 배포까지 연결한 과정을 정리했다. 이후 개발 리뷰를 받으며 사내 버셀의 목표와 도입 방법, 현재 시스템을 설명했다. 리뷰 후에는 사내 개발에 먼저 적용해보기로 했다.

목표: 검토와 승인을 거쳐 운영까지 연결하기

최종 목표는 GTM 방식으로 프로젝트 검증부터 운영 반영까지 이어지는 파이프라인을 구축하는 것이다. 보안 검토와 서버 개발 검토를 거치면서도 변경 사항을 운영에 빠르게 반영할 수 있는 환경을 만들고 싶다.

보안 관련 공통 설정은 사내 버셀에서 우리가 관리한다. 각 기능에 필요한 세부 보안 검토는 일부 자동화하고, 담당자가 검토하고 승인하기 쉬운 환경을 제공하려 한다. 여기에 서버 개발 관련 검토 과정도 연결해, 개발부터 운영 반영까지 필요한 절차를 이어가는 것이 목표다.

도입 방법: 사내 개발에서 사용성과 활용도 확인하기

첫 적용 대상은 사내 개발로 정했다. 기획자, 마케터, 클라이언트 개발자가 사내 버셀에 어떤 반응을 보이고 어떻게 활용하는지 살펴보며 발전 방향을 정하기로 했다.

먼저 사용하기 편한지, 실제 개발에 얼마나 활용되는지 확인한다. 그 결과를 바탕으로 필요한 기능을 보완하고, 운영까지 반영하는 과정은 이후 구체화하기로 했다.

사내 적용 과정

프로젝트를 자유롭게 만들어보고 검증할 수 있도록 사내망에서 진행했다. 그 과정에서 사내 버셀이 동작하는 데 필요한 통신과 각 프로젝트가 사용하는 서비스에 대한 접근을 정리해야 했다.

1. 방화벽 설정

사용자가 포털에 접속하는 것 외에도 GitHub 로그인과 저장소 조회, 이미지와 패키지 다운로드가 필요하다. 프로젝트마다 연동할 내부 서비스도 달라진다. 사내 버셀이 공통으로 사용하는 통신은 기본 설정으로 관리하고, 개별 프로젝트에 필요한 접근은 목적지와 용도에 따라 추가하는 방식으로 정리하려 한다.

현재 시스템을 기준으로 방화벽 구성하기

먼저 외부 인바운드와 아웃바운드를 정리했다. 아래 그림은 사내 버셀 서버의 통신 흐름으로, 사내 사용자 접근도 포함했다. 인바운드는 왼쪽, 아웃바운드는 오른쪽에 표시했다. 포트는 목적지 포트이며, 실제 방화벽 허용 결과를 나타내는 그림은 아니다.

사내 버셀 서버의 접근 흐름. 왼쪽의 사내 사용자·VPN, 운영 담당자, GitHub Webhook이 TCP 443으로 서버에 접근하고, 서버는 오른쪽 GitHub에 TCP 443으로 인증·저장소 생성 및 조회·소스 다운로드·Poller 조회를 요청한다.

GitHub 관련 IP 대역은 GitHub 메타 API의 최신 응답을 기준으로 설정한다. IP 범위를 표현하는 CIDR 값을 통신 용도에 맞게 선택한다.

사내 인바운드 443의 IP 대역별 허용 검토

사내 버셀 서버의 TCP 443에 접근할 때 IP 대역 단위로 허용할 수 있는지 검토했다. 현재 정책상 IP 대역별로 허용하는 방식은 불가능하다. 따라서 그림의 사내 사용자 접근 경로를 그대로 적용할 수 있는 것은 아니며, 정책에 맞는 접근 방식은 추가로 정해야 한다.

2. 시스템 구축

GitHub App 등록 및 설정 정책안

프로젝트 관리 범위와 사용자 이용 조건은 다음과 같이 정하려 한다.

프로젝트를 시작하는 경로는 두 가지다.

경로사용자 행동사내 버셀 처리
새 프로젝트 생성관리 페이지에서 프로젝트 생성전용 조직에 저장소 생성 → 사내 버셀 등록 → 기본 서비스 배포
기존 저장소 등록전용 조직에 저장소를 만든 뒤 관리 페이지에서 등록해당 저장소를 사내 버셀에 연결 → 서비스 배포

기능별 구축 및 동작 확인

정책에 맞춰 GitHub App과 인증·저장소 연동을 설정하고, 다음 흐름을 확인한다.

구축 중 확인한 문제와 기능별 동작 확인 결과는 이후 이어서 기록할 예정이다.

3. 보안 피드백 반영

보안 검토에서 다음 항목을 확인하고 대응해야 한다는 피드백을 받았다.

이 피드백을 바탕으로 로컬 Docker 실험 환경에서 격리 설정을 적용했다.

아래 그림은 사용자 접근 경로와 프로젝트별 격리 범위를 요약한 개념도다.

외부 사용자가 HTTPS와 리버스 프록시를 통해 프로젝트 컨테이너에 접근하는 흐름과, 관리 API 및 비밀 정보 보호, 사내망 접근 제한, CPU와 메모리 사용량 제한, 프로젝트별 볼륨 격리.

구성검토 범위
보안 피드백 개요통신 제한, 컨테이너 격리, 로그 감사와 모니터링의 전체 검토 범위
통신 제한IP 제한, TLS 1.2 이상 적용, secret 기반 페이로드 검증
컨테이너 격리 — 담당 작업디스크 격리, 네트워크 격리, 권한 제한, 가용 자원 제한의 목적과 구성 방향
로그 감사와 모니터링 — 담당 작업수집할 이벤트, 기록할 정보, 로그 보관과 조회 방식, 모니터링 대상

네트워크 격리

사용자 코드의 접근 범위를 제한하려면 실행 중인 컨테이너뿐 아니라 빌드 중 실행되는 코드의 통신도 확인해야 했다.

실행 컨테이너에 적용한 제한이 빌드 과정에도 그대로 적용된다고 볼 수는 없었다. 사내 버셀에서 사용하는 빌더의 통신 경로를 확인하고, 그 경로까지 제한 대상에 포함해야 한다는 점을 확인했다.

검토된 이슈적용한 내용적용 결과
사용자 코드의 사내망 접근실행 컨테이너의 사설 대역 접근 차단
  • AS-IS: 사설 주소의 테스트 서버에 HTTP 요청 성공
  • TO-BE: 같은 요청은 타임아웃으로 실패, 정상 통신 경로 유지
사용자 코드의 호스트 서비스 접근실행 컨테이너의 호스트 접근 차단
  • AS-IS: 컨테이너에서 테스트 호스트의 HTTP 서비스에 접속
  • TO-BE: 같은 접속은 타임아웃으로 실패, 다른 서버와의 통신 유지
실행 컨테이너의 제한을 적용해도 남는 빌드 중 사내망 접근빌드 단계의 통신 경로에도 사설 대역 접근 차단 적용
  • AS-IS: 실행 컨테이너만 제한하면 빌드 중 사설 대역 접근 성공
  • TO-BE: 빌드 경로까지 제한하자 같은 요청이 타임아웃으로 실패
내부 연동을 위한 예외 허용 범위의 확대필요한 내부 연동을 위해 지정한 IP와 포트만 허용하는 예외 설정
  • AS-IS: 사설 대역 차단 후 필요한 연동 대상도 접속 실패
  • TO-BE: 지정한 IP와 포트만 접속 성공, 다른 포트와 사설 주소는 차단 유지

디스크 격리

프로젝트마다 읽고 쓸 수 있는 경로를 나누고, 호스트 파일이나 다른 프로젝트의 데이터를 공유하지 않도록 구성했다.

검토된 이슈적용한 내용적용 결과
호스트 파일의 컨테이너 노출테스트 호스트 디렉터리의 bind mount 제거
  • AS-IS: 호스트 디렉터리를 마운트한 컨테이너에서 테스트 파일 열람
  • TO-BE: named volume으로 변경 후 해당 호스트 파일 부재
프로젝트 간 데이터 공유와 노출프로젝트별 named volume 분리
  • AS-IS: 프로젝트 간 볼륨 공유 상태의 파일 접근은 미측정
  • TO-BE: 분리한 프로젝트 B의 볼륨에 프로젝트 A의 파일 부재
컨테이너 루트 파일시스템의 불필요한 변경컨테이너 루트 파일시스템을 읽기 전용으로 설정
  • AS-IS: 컨테이너 루트 파일시스템에 파일을 생성
  • TO-BE: 읽기 전용 설정 후 같은 파일 생성 거부
임시 파일 증가에 따른 자원 소진쓰기가 필요한 임시 경로에만 용량이 제한된 tmpfs 연결
  • AS-IS: 임시 경로의 용량 제한 전 동작은 미측정
  • TO-BE: 64MiB tmpfs에서 쓰기 성공, 상한 초과 시 공간 부족 오류

권한 제한과 비밀 정보 보호

파일 접근을 나누는 것과 함께, 사용자 컨테이너에 전달하는 실행 권한과 비밀 정보의 범위도 줄였다.

검토된 이슈적용한 내용적용 결과
사용자 코드에 부여된 과도한 실행 권한비루트 사용자로 실행하고 Linux capabilities 제거
  • AS-IS: 기본 실행 사용자는 root, capabilities 제거 전 파일 소유자 변경 성공
  • TO-BE: 비루트 실행 시 /root 쓰기 거부, capabilities 제거 후 소유자 변경 거부
Docker socket을 통한 관리 API 접근사용자 컨테이너의 Docker socket 마운트 제거
  • AS-IS: Docker socket을 읽기 전용으로 마운트해도 테스트 API 접근 성공
  • TO-BE: 마운트 제거 후 socket 경로 부재로 접근 실패
환경변수를 통한 사내 버셀의 비밀 정보 노출사내 버셀의 비밀 값을 제외하고 사용자에게 필요한 환경변수만 명시적으로 전달
  • AS-IS: 함께 전달한 사내 버셀의 가짜 비밀 값이 컨테이너 환경변수에 노출
  • TO-BE: 사용자 변수만 전달해 사용자 값 유지, 사내 버셀의 비밀 값 제외
빌드 토큰의 이미지 이력과 설정 잔존빌드에 필요한 가짜 토큰을 ARG와 ENV 대신 BuildKit secret mount로 전달
  • AS-IS: ARG와 ENV로 전달한 가짜 토큰이 이미지 이력과 설정에 잔존
  • TO-BE: secret mount로 변경 후 검사한 이미지 이력, 설정과 파일에서 해당 값 미발견, 빌드 산출물 유지

가용 자원 제한

한 프로젝트가 서버 자원을 계속 점유하지 않도록 컨테이너별 사용량에 상한을 두고 동작을 확인했다.

검토된 이슈적용한 내용적용 결과
특정 프로젝트의 메모리 사용에 따른 다른 서비스 영향메모리 사용량 제한
docker run --memory 512m --memory-swap 512m <image>
  • AS-IS: 768MiB 상한에서 576MiB 메모리 할당과 쓰기 완료
  • TO-BE: 512MiB 상한에서 같은 작업 중 OOM 종료, 별도 테스트 서버 응답 유지
특정 프로젝트의 CPU 점유CPU 사용량 제한
docker run --cpus 1 <image>
  • AS-IS: 2 CPU 상한에서 약 3초간 두 작업자의 CPU 사용 시간 합계 약 6초
  • TO-BE: 1 CPU 상한에서 같은 구간의 CPU 사용 시간 합계 약 3초, throttling 발생
과도한 프로세스 생성에 따른 자원 소진생성할 수 있는 프로세스 수 제한
docker run --pids-limit 256 <image>
  • AS-IS: 프로세스 상한 384개에서 자식 프로세스 300개 생성
  • TO-BE: 상한 256개에서 부모 포함 256개 도달 후 추가 생성 거부

로그 관리와 모니터링

로그가 계속 쌓이는 것을 제한하고, 디스크 사용량에 따라 알림 조건을 판단하는 동작을 확인했다.

검토된 이슈적용한 내용적용 결과
로그 누적에 따른 저장 공간 소진새 컨테이너의 로그에 max-size=10m, max-file=3 로테이션 설정
  • AS-IS: 로테이션 설정 없이 하나의 파일에 테스트 로그 누적
  • TO-BE: 같은 양의 로그 생성 시 파일 회전, 3개 보관
디스크 사용량 증가를 감지할 기준 부재테스트 파일시스템 사용률이 80% 이상이면 알림 조건을 충족하도록 판정
  • AS-IS: 4MiB 테스트 파일시스템 사용률 0%에서 알림 조건 미충족
  • TO-BE: 사용률 85%에서 조건 충족, 파일 제거 후 0%에서 해제

Share this post:

Previous Post
[26.09.16] 챌린지 API의 출석 달력 검증을 위한 회차 경계 테스트 보강하기
Next Post
[26.09.15] 알툴즈 AI API의 OOM kill과 정상 종료 과정 구분하기