취약점 진단 스크립트 DevOps 구축

취약점 진단 스크립트 DevOps 구축

in

컨설팅 사업을 위한 인프라 취약점 진단 스크립트 구축에 관한 개요를 제공합니다. 복잡한 유지보수 문제를 해결하기 위해 개별 진단 항목을 독립적으로 개발하고, 이를 하나의 통합 스크립트 파일로 배포하는 방안을 제안합니다. 또한, Docker를 활용한 환경 설정, GitLab과 GitLab Runner를 이용한 CI/CD 파이프라인 구축, 컨테이너 레지스트리 활성화 및 GitLab 백업 설정 방법을 상세히 설명합니다.

아직 미완성된 프로젝트이라 추후에 내용이 크게 수정될 가능성이 있습니다.

개요

컨설팅 사업(인프라 진단)에 필요한 취약점 진단 스크립트의 경우, 고객사의 편의를 위해 하나의 스크립트 파일로 제작되고 있습니다.

이러한 이유로 하나의 스크립트 파일로 개발이 진행되면서, 인프라 취약점 진단을 위한 스크립트가 유지보수 측면에서 심각한 어려움을 겪고 있다는 점입니다. 이 스크립트는 대상 마다 차이가 있지만, 적게는 8개에서 많게는 100개에 달하는 진단 항목을 포함하고 있으며, 이로 인해 복잡성이 증가해 유지보수가 매우 어려워졌습니다.

이 문제에 대한 해결책으로, 개별 진단 항목을 독립적으로 개발한 후, 이들을 하나의 스크립트 파일로 통합하여 배포하는 방식을 제안합니다. 이 접근법은 개별적인 항목들의 유지보수를 용이하게 하면서도 최종적으로는 고객사에게 간편하게 배포할 수 있는 하나의 통합 스크립트를 제공합니다. 더불어, 많은 진단 대상과 테스트 과정에서의 시간 소모, 그리고 테스트 대상 누락 위험을 고려하여, 자동화된 테스트 방식을 도입하는 것이 필요합니다.

따라서, 개별적인 개발과 통합 배포, 그리고 자동화된 테스트를 포함하는 이러한 방안은 개발 효율성을 높이고, 고객사에게 안정적이고 신뢰할 수 있는 서비스를 제공하는 데 크게 기여할 것으로 판단됩니다.

프로젝트 파일 구조 초안

취약점_진단_프로젝트/
│
├── linux/                 
│   ├── global.sh
│   ├── 취약점A/
│   │   ├── test_cases/
│   │   │   ├── scenario1.sh
│   │   │   ├── scenario2.sh
│   │   │   ├── data/
│   │   ├── expected_results/
│   │   ├── script.sh
│   │   └── .gitlab-ci.yml
│   └── 취약점B/
│       ├── test_cases/
│       ├── script.sh
│       └── .gitlab-ci.yml
│
├── unix/                  
│   └── ... (동일한 구조)
│
├── windows/              
│   └── ... (동일한 구조)
│
├── docs/                  
├── README.md
└── .gitignore

test_cases/ 디렉터리의 주요 용도
①.테스트 시나리오 저장: 각 취약점에 대해 시뮬레이션할 수 있는 다양한 테스트 시나리오를 저장합니다. 이 시나리오들은 진단 스크립트가 예상대로 작동하는지 검증하는 데 사용됩니다.
②.입력 데이터 및 예상 결과: 테스트에 사용될 입력 데이터와 예상되는 결과 값을 저장합니다. 이를 통해 스크립트가 실제 운영 환경과 유사한 조건에서도 정확하게 작동하는지 테스트할 수 있습니다.
③.자동화된 테스트 스크립트: 테스트 케이스를 실행하는 자동화된 스크립트나 도구를 포함할 수 있습니다. 이는 CI/CD 파이프라인과 연동되어 자동 테스트를 수행할 수 있습니다.

data/ 디렉터리는 테스트 입력에 사용될 데이터 파일 저장
expected_results/는 스크립트의 예상 출력 결과를 포함

구축

1. Docker 구축

Ubuntu 서버에 Docker를 설치합니다. Docker는 컨테이너화된 애플리케이션을 실행하는 데 사용됩니다.

먼저, 패키지 목록을 최신 상태로 업데이트하고 필요한 패키지들을 설치합니다.

sudo apt update
sudo apt install apt-transport-https ca-certificates curl software-properties-common


Docker의 공식 GPG 키를 시스템에 추가합니다. 이는 Docker 패키지의 무결성을 검증하는 데 사용됩니다.

curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -


Docker의 공식 APT 리포지토리를 시스템의 소스 리스트에 추가합니다.

sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable"


패키지 목록을 다시 업데이트하고 Docker CE(Community Edition)를 설치합니다.

sudo apt update
sudo apt install -y docker-ce docker-compose


Docker 서비스를 시작하고, docker 명령어를 sudo 없이 사용할 수 있도록 사용자를 docker 그룹에 추가합니다.

sudo systemctl start docker
sudo systemctl enable docker
sudo usermod -aG docker ${USER}


사용자를 docker 그룹에 추가한 후에는, 변경 사항을 적용하기 위해 로그아웃하고 다시 로그인해야 할 수도 있습니다.

2. Docker Compose 파일 생성

version: '3.8'

services:
  gitlab:
    image: gitlab/gitlab-ce:latest
    container_name: gitlab
    restart: always
    ports:
      - '8080:80'
      - '8081:443'
      - '8082:22'
    volumes:
      - $HOME/gitlab/config:/etc/gitlab
      - $HOME/gitlab/logs:/var/log/gitlab
      - $HOME/gitlab/data:/var/opt/gitlab
    networks:
      - gitlab-network

  registry:
    image: registry:2
    container_name: gitlab-registry
    ports:
      - '5000:5000'
    volumes:
      - $HOME/gitlab/registry:/var/lib/registry
    networks:
      - gitlab-network

networks:
  gitlab-network:
    driver: bridge
  • version: Docker Compose 파일의 버전을 명시합니다. 3.8은 현재 널리 사용되는 버전입니다.

  • services: 실행하려는 각 서비스(컨테이너)를 정의합니다.
    • gitlab: GitLab 컨테이너의 구성입니다.
      • image: 사용할 이미지. 여기서는 GitLab의 최신 커뮤니티 에디션을 사용합니다.
      • ports: 호스트와 컨테이너 간 포트 매핑. GitLab 웹 인터페이스와 SSH 접근을 위해 필요합니다.
      • volumes: 데이터, 로그, 설정 파일을 위한 볼륨 마운트.
    • registry: Docker Registry 컨테이너의 구성입니다.
      • image: Docker Registry 이미지.
      • ports: Docker Registry에 접근하기 위한 포트 매핑.
      • volumes: Docker Registry의 데이터 저장을 위한 볼륨 마운트.
  • networks: 사용할 네트워크를 정의합니다. 여기서는 간단한 브리지 네트워크를 사용합니다.

👉 services의 registry은 컨테이너 레지스트리를 구성하기 위한 컨테이너이다.
👉 만약 컨테이너 레지스트리를 사용 안 할 예정이라면, 선언을 안해도 상관 없음.

3. GitLab 서비스 구성

설정을 적용하고 GitLab 서비스를 시작하기 위해 터미널에서 다음 명령을 실행합니다.

docker-compose up -d


GitLab 서비스를 중지하고 컨테이너를 제거하려면 다음 명령을 사용합니다.

docker-compose down


이러한 설정을 통해 GitLab을 쉽게 배포하고 관리할 수 있습니다.
필요에 따라 GitLab의 설정을 조정하거나 추가 기능을 활성화하기 위해 gitlab.rb 파일을 편집할 수도 있습니다.

4. root 계정 비밀번호

초기 비밀번호를 다음 명령을 사용하여 출력할 수 있습니다. 주의할 점은 해당 방법은 초기 세팅부터 24시간동안만 유지됩니다.

docker exec -it <Gitlab 컨테이너 이름> /bin/bash cat /etc/gitlab/initial_root_password | grep Password:


Docker를 사용하는 경우, GitLab 컨테이너에 접속합니다.

docker exec -it gitlab bash


GitLab 컨테이너 내부에서 GitLab 콘솔을 실행합니다.

gitlab-rails console


GitLab 콘솔에서 다음 명령어를 실행하여 관리자 비밀번호를 재설정합니다. 여기서 your-new-password는 원하는 새 비밀번호로 대체합니다.

user = User.where(id: 1).first
user.password = 'your-new-password'
user.password_confirmation = 'your-new-password'
user.save!
exit


변경 사항을 적용하기 위해 GitLab 서비스를 재설정 및 재시작할 수 있습니다.

gitlab-ctl reconfigure
gitlab-ctl restart


내부 설정

1. Custom Git clone URL for HTTP(S)

회사 내에서 사용할 것이기 때문에, 내부망의 IP 주소로 사용할 것이다. 하지만 Gitlab의 경우, 기본값으로 초기 설정된 도메인 주소로 설정되어있다. 이러한 설정을 바꾸지 않으면, 추후 CI의 자동화 서버에서 git clone을 하게되는데 도메인 주소로 clone하게 된다. 자동화 서버 내 Hosts에 Gitlab 서버 IP 주소와 도메인이 매핑되어있지 않기 때문에, 에러가 발생된다.

문제점을 해결하기 위해, 도메인을 사용하지 않고 IP 주소를 사용한다는 설정을 해줘야 한다.

관리자 로그인 > Configure Gitlab > Settigs > General > Visibility and access controls > Custom Git clone URL for HTTP(S) 에서 http://<Gitlab 서버의 IP주소>:<Gitlab 포트>/으로 설정하면 된다.

주의할 점은 root 경로를 기입해줘야 404 상태코드가 발생이 안된다는 것이다.
ex) http://192.168.0.35:8080 - X, http://192.168.0.35:8080/ - O

2. 컨테이너 레지스트리 활성화

나의 목표는 진단 스크립트 환경을 컨테이너로 돌릴 수 있다면, 돌리고 할 수 없다면 서버 구축 후 SSH으로 CI를 구축하는 것이다. 이를 위해 커스텀 컨테이너 이미지를 직접 생성해야하고, 생성한 이미지를 저장소에 저장을 해야한다.
기본 리눅스 컨테이너에 net-tools, service, systemctl 등이 설치가 되어있지 않고, apt 명령어를 통해 설치가 불가능하여 커스텀 이미지를 생성해야 함.

회사 사규에 회사 자산이 외부로 노출되는 것을 방지하고 있기 때문에 서버 내 컨테이너 저장소를 구축하여 저장하려고 한다. 이를 위해 Gitlab의 컨테이너 레지스트리를 구축해야 한다.

GitLab 서비스의 설정 파일(gitlab.rb)에서 Container Registry를 활성화하고 구성합니다.

다음 설정을 gitlab.rb에 추가하거나 수정합니다.

gitlab_rails['registry_enabled'] = true
gitlab_rails['registry_host'] = "<호스트 IP 주소>"
gitlab_rails['registry_port'] = "5000"
gitlab_rails['registry_api_url'] = "http://<호스트 IP 주소>:5000"

Docker compose 파일에 $HOME/gitlab/config:/etc/gitlab하였기 때문에 컨테이터 내부에서 말고 호스트 내부의 $HOME/gitlab/config/gitlab.rb 파일을 수정해도 상관없음.

변경사항을 적용하기 위해 GitLab을 재구성하고 재시작합니다.

gitlab-ctl reconfigure
gitlab-ctl restart

설정이 잘 되었다면, 프로젝트를 생성한 후 사이드 메뉴에서 Project > Deploy > Container Registry가 존재하는지 확인을 해야한다. 만약 메뉴가 존재하지 않는다면, 설정이 먹히지 않았다는 것이다. 이럴 땐, registry 컨테이너가 동작하고 있는지와 에러 로그를 확인하여 문제점을 해결하면 된다.

이미지를 푸시하고 풀하는 방법은 다음과 같습니다.

# 로그인
docker login <호스트 IP 주소>:5000

# 이미지 푸시
docker push <호스트 IP 주소>:5000/your-username/your-project/your-image:tag

# 이미지 풀
docker pull <호스트 IP 주소>:5000/your-username/your-project/your-image:tag

docker 로그인은 Gitlab 계정 정보를 입력하면 된다.

Push가 무사히 완료가 되면, 위 사진처럼 컨테이너 이미지가 저장이 되었다는 것을 확인할 수 있다.

3. Gitlab runner 구축

이제 CI/CD를 구축하려면, Gitlab runner을 구축하여 저장소 내부의 .gitlab-ci.yml 파일을 통해 CI/CD를 이용할 수 있다.

아래는 GitLab Runner를 추가한 docker-compose.yml 파일의 예시입니다

version: '3.8'

services:
  gitlab:
    image: gitlab/gitlab-ce:latest
    container_name: gitlab
    restart: always
    ports:
      - '8080:80'
      - '8081:443'
      - '8082:22'
    volumes:
      - $HOME/gitlab/config:/etc/gitlab
      - $HOME/gitlab/logs:/var/log/gitlab
      - $HOME/gitlab/data:/var/opt/gitlab
    networks:
      - gitlab-network

  registry:
    image: registry:2
    container_name: gitlab-registry
    ports:
      - '5000:5000'
    volumes:
      - $HOME/gitlab/registry:/var/lib/registry
    networks:
      - gitlab-network

  gitlab-runner:
    image: gitlab/gitlab-runner:latest
    container_name: gitlab-runner
    restart: always
    volumes:
      - $HOME/gitlab-runner/config:/etc/gitlab-runner
      - /var/run/docker.sock:/var/run/docker.sock
    networks:
      - gitlab-network

networks:
  gitlab-network:
    driver: bridge
  • image: GitLab Runner 서비스에 사용될 Docker 이미지를 지정합니다. 여기서는 GitLab에서 제공하는 최신 버전의 gitlab/gitlab-runner 이미지를 사용합니다.
  • container_name: 컨테이너의 이름을 gitlab-runner로 설정합니다. 이 이름은 Docker 환경 내에서 해당 컨테이너를 식별하는 데 사용됩니다.
  • restart: 컨테이너의 재시작 정책을 always로 설정합니다. 이는 Docker 호스트가 재부팅되거나 컨테이너가 다른 이유로 중지될 경우 자동으로 재시작되도록 합니다.
  • volumes:
    • $HOME/gitlab-runner/config:/etc/gitlab-runner: GitLab Runner의 설정 파일들을 저장하기 위한 볼륨을 마운트합니다. 이 경로에는 Runner의 구성 파일과 등록 정보가 저장됩니다.
    • /var/run/docker.sock:/var/run/docker.sock: 호스트 시스템의 Docker 소켓을 컨테이너 내부로 마운트합니다. 이를 통해 GitLab Runner 컨테이너가 호스트의 Docker 엔진을 사용하여 다른 Docker 컨테이너를 생성하고 관리할 수 있습니다. 이 방법은 Docker-in-Docker(DinD)를 사용하는 것과는 다른 접근 방식입니다.
  • networks: GitLab Runner가 gitlab-network라는 Docker 네트워크를 사용하도록 설정합니다. 이 네트워크는 GitLab 서비스와 통신하는 데 사용됩니다.
    변경 사항을 적용하기 위해 현재 실행 중인 서비스를 중지합니다.
    docker-compose down
    


    변경된 docker-compose.yml 파일을 사용하여 모든 서비스를 다시 시작합니다.

    docker-compose up -d
    


    Gitlab에 관리자로 로그인 후 Admin Area > CI/CD > Runners에서 Runner 토큰 확인
    Gitlab Runner 컨테이너에 접근하여, Runner를 등록합니다.

    docker exec -it gitlab-runner bash
    gitlab-runner register
    


    등록 시 물어보는 항목은 아래와 같음

  • GitLab 인스턴스 URL (http://<gitlab-host>:<gitlab-port>).
  • 위에서 얻은 Registration Token.
  • Runner 설명 (예: my-runner).
  • Runner 태그 (선택사항).
  • Executor 선택 (Docker를 추천합니다).

Executor 선택은 현재 도커 컨테이너로 구축하는 중이라서 Docker로 선택하였습니다. 만약 VM이라던가, 다른 서버로 연결하여 한다면 SSH로 선택하여 Runner를 추가하면 됩니다.
등록에 성공하면, Runners에 등록된 인스턴스가 보이게 됩니다.

4. Gitlab 백업 설정

GitLab 컨테이너에서 백업 명령을 실행해야 합니다.

docker exec -it gitlab bash
$ gitlab-backup create


GitLab의 모든 중요 데이터(데이터베이스, 업로드된 파일, 리포지토리 등)를 백업합니다. 백업 파일은 /var/opt/gitlab/backups 디렉토리에 저장됩니다.

백업한 데이터를 복구하려면, 백업 파일을 원래의 위치로 복원하고 GitLab을 재시작해야 합니다. GitLab 데이터 복구 명령은 다음과 같습니다.

$ gitlab-backup restore BACKUP=backup_file_name


위와 같이 하면 Gitlab 데이터를 백업할 수 있다.
하지만, 주기적으로 백업을 못한다는 점과 gitlab 설정파일은 별도로 저장을 해줘야 한다는 점이 단점이다.

이를 위해서 shell script 파일을 만들어서 Crontab으로 주기적인 백업과 gitlab 설정파일을 백업하도록 하겠다.

#!/bin/bash
BACKUP_DIR="$HOME/gitlab/backup"
  
# Gitlab 데이터 백업
docker exec -t gitlab gitlab-backup create
  
# 백업 파일 이름 가져오기
BACKUP_FILE=$(docker exec -t gitlab ls -t /var/opt/gitlab/backups | head -n 1 | tr -d '\r')
  
# 백업 파일을 호스트 시스템으로 복사
docker cp "gitlab:/var/opt/gitlab/backups/$BACKUP_FILE" "$BACKUP_DIR/gitlab_data/"
  
# Gitlab 설정 파일 백업
GITLAB_CONFIG="$HOME/gitlab/config/gitlab.rb"
  
# 현재 날짜와 시간을 YYYYMMDD_HHMM 형식으로 가져오기
CURRENT_DATE=$(date +"%Y%m%d_%H%M")

cp "$GITLAB_CONFIG" "$BACKUP_DIR/gitlab_conf/gitlab_$CURRENT_DATE.rb"


계속 저장만 하면 용량이 부족할 수 있기 때문에, 90일이 지난 백업 파일은 삭제하도록 하기 위해 Shell Script를 만들었다.

#!/bin/bash
# 백업 파일이 저장된 디렉토리
BACKUP_DIR="$HOME/gitlab/backup"

# 90일보다 오래된 백업 파일 삭제
find "$BACKUP_DIR/gitlab_conf/" -type f -name "*.rb" -mtime +90 -exec rm {} \;
find "$BACKUP_DIR/gitlab_data/" -type f -name "*.tar" -mtime +90 -exec rm {} \;


이제 해당 스크립트를 일정 주기로 돌릴 Crontab에 설정을 해야한다.
귀찮은 작업이라고 판단되어, 자동 등록 스크립트를 만들었다.

#!/bin/bash
# 백업 및 삭제 스크립트 경로
BACKUP_DIR="$HOME/gitlab/backup"
GITLAB_BACKUP_SCRIPT="$BACKUP_DIR/gitlab_backup.sh"
BACKUP_DEL_SCRIPT="$BACKUP_DIR/backup_del.sh"
  
# crontab에 추가할 내용을 임시 파일에 작성
CRON_FILE="/tmp/crontab-$$"
crontab -l > $CRON_FILE 2>/dev/null || true
echo "0 0 * * * $GITLAB_BACKUP_SCRIPT" >> $CRON_FILE
echo "0 12 * * * $GITLAB_BACKUP_SCRIPT" >> $CRON_FILE
echo "0 1 * * * $BACKUP_DEL_SCRIPT" >> $CRON_FILE
echo "0 13 * * * $BACKUP_DEL_SCRIPT" >> $CRON_FILE
  
# crontab 업데이트
crontab $CRON_FILE
  
# 임시 파일 삭제
rm -f $CRON_FILE


실행 권한을 부여해서 동작시키면 등록이 완료된다.