728x90
osquery와 wazuh agent 패키지를 로컬 시스템에 미러링하고, 내부 시스템에서 yum을 통해 설치/업데이트할 수 있는 도커 기반 환경을 구축하는 방법입니다.
패키지 미러링 시스템 아키텍처
디렉토리 구조
repo-mirror/
├── docker-compose.yml
├── nginx/
│ ├── Dockerfile
│ └── nginx.conf
├── sync/
│ ├── Dockerfile
│ ├── sync-repos.sh
│ └── crontab
├── data/
│ ├── repos/
│ │ ├── wazuh/
│ │ └── osquery/
│ └── gpg-keys/
└── scripts/
└── init-repos.sh
300x250
Docker Compose 구성
docker-compose.yml - 전체 서비스 구성
version: '3.8'
services:
# Nginx 웹서버 - YUM 저장소 제공
nginx:
build: ./nginx
container_name: repo-nginx
ports:
- "8080:80"
volumes:
- ./data/repos:/usr/share/nginx/html/repos:ro
- ./data/gpg-keys:/usr/share/nginx/html/gpg-keys:ro
restart: unless-stopped
networks:
- repo-network
# 동기화 스케줄러
sync-scheduler:
build: ./sync
container_name: repo-sync
volumes:
- ./data/repos:/data/repos
- ./data/gpg-keys:/data/gpg-keys
- ./sync/sync-repos.sh:/scripts/sync-repos.sh:ro
environment:
- TZ=Asia/Seoul
restart: unless-stopped
networks:
- repo-network
# 초기 설정 및 메타데이터 생성
init-repos:
image: centos:7
container_name: repo-init
volumes:
- ./data/repos:/data/repos
- ./scripts/init-repos.sh:/scripts/init-repos.sh:ro
command: /scripts/init-repos.sh
networks:
- repo-network
networks:
repo-network:
driver: bridge
Nginx 설정
nginx/Dockerfile - Nginx 컨테이너 설정
FROM nginx:alpine
# 필요한 패키지 설치
RUN apk add --no-cache tzdata
# 시간대 설정
ENV TZ=Asia/Seoul
RUN cp /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
# Nginx 설정 복사
COPY nginx.conf /etc/nginx/nginx.conf
# 디렉토리 생성
RUN mkdir -p /usr/share/nginx/html/repos/wazuh \
/usr/share/nginx/html/repos/osquery \
/usr/share/nginx/html/gpg-keys
EXPOSE 80
nginx/nginx.conf - Nginx 웹서버 설정
user nginx;
worker_processes auto;
error_log /var/log/nginx/error.log warn;
pid /var/run/nginx.pid;
events {
worker_connections 1024;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
access_log /var/log/nginx/access.log main;
sendfile on;
tcp_nopush on;
keepalive_timeout 65;
autoindex on;
autoindex_exact_size off;
autoindex_localtime on;
server {
listen 80;
server_name _;
root /usr/share/nginx/html;
# Wazuh 저장소
location /repos/wazuh/ {
alias /usr/share/nginx/html/repos/wazuh/;
autoindex on;
}
# osquery 저장소
location /repos/osquery/ {
alias /usr/share/nginx/html/repos/osquery/;
autoindex on;
}
# GPG 키
location /gpg-keys/ {
alias /usr/share/nginx/html/gpg-keys/;
autoindex on;
}
# 보안 헤더 추가
add_header X-Content-Type-Options nosniff;
add_header X-Frame-Options DENY;
add_header X-XSS-Protection "1; mode=block";
}
}
동기화 스크립트
sync/Dockerfile - 동기화 컨테이너 설정
FROM centos:7
# 필요한 패키지 설치
RUN yum install -y epel-release && \
yum install -y \
wget \
rsync \
createrepo \
yum-utils \
cronie \
curl \
&& yum clean all
# 시간대 설정
ENV TZ=Asia/Seoul
RUN ln -sf /usr/share/zoneinfo/$TZ /etc/localtime
# 스크립트 디렉토리 생성
RUN mkdir -p /scripts /data/repos /data/gpg-keys
# crontab 설정 복사
COPY crontab /etc/cron.d/sync-repos
RUN chmod 0644 /etc/cron.d/sync-repos && \
crontab /etc/cron.d/sync-repos
# 동기화 스크립트 복사
COPY sync-repos.sh /scripts/sync-repos.sh
RUN chmod +x /scripts/sync-repos.sh
# 초기 동기화 실행
RUN /scripts/sync-repos.sh
# cron 실행
CMD ["crond", "-n"]
sync/sync-repos.sh - 패키지 동기화 스크립트
#!/bin/bash
# 로그 설정
LOG_DIR="/var/log/repo-sync"
mkdir -p "$LOG_DIR"
LOG_FILE="$LOG_DIR/sync-$(date +%Y%m%d).log"
# 함수: 로그 기록
log() {
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" | tee -a "$LOG_FILE"
}
# 함수: 에러 처리
error_exit() {
log "ERROR: $1"
exit 1
}
# 디렉토리 설정
REPO_BASE="/data/repos"
GPG_KEY_DIR="/data/gpg-keys"
# Wazuh 설정
WAZUH_REPO_URL="https://packages.wazuh.com/4.x/yum/"
WAZUH_GPG_URL="https://packages.wazuh.com/key/GPG-KEY-WAZUH"
WAZUH_LOCAL_DIR="$REPO_BASE/wazuh"
# osquery 설정
OSQUERY_REPO_URL="https://pkg.osquery.io/rpm/"
OSQUERY_GPG_URL="https://pkg.osquery.io/rpm/GPG"
OSQUERY_LOCAL_DIR="$REPO_BASE/osquery"
# 디렉토리 생성
mkdir -p "$WAZUH_LOCAL_DIR" "$OSQUERY_LOCAL_DIR" "$GPG_KEY_DIR"
log "========== 저장소 동기화 시작 =========="
# Wazuh 동기화
log "Wazuh 패키지 동기화 시작..."
cd "$WAZUH_LOCAL_DIR" || error_exit "Wazuh 디렉토리 접근 실패"
# Wazuh GPG 키 다운로드
log "Wazuh GPG 키 다운로드..."
curl -sS -o "$GPG_KEY_DIR/GPG-KEY-WAZUH" "$WAZUH_GPG_URL" || error_exit "Wazuh GPG 키 다운로드 실패"
# Wazuh 패키지 동기화
log "Wazuh 패키지 미러링..."
wget -r -np -nH --cut-dirs=3 \
-R "index.html*" \
--reject-regex ".*\?.*" \
--timeout=30 \
--tries=3 \
"$WAZUH_REPO_URL" 2>&1 | tee -a "$LOG_FILE"
# repodata 업데이트
log "Wazuh repodata 생성..."
createrepo --update "$WAZUH_LOCAL_DIR" || error_exit "Wazuh createrepo 실패"
# osquery 동기화
log "osquery 패키지 동기화 시작..."
cd "$OSQUERY_LOCAL_DIR" || error_exit "osquery 디렉토리 접근 실패"
# osquery GPG 키 다운로드
log "osquery GPG 키 다운로드..."
curl -sS -o "$GPG_KEY_DIR/GPG-KEY-OSQUERY" "$OSQUERY_GPG_URL" || error_exit "osquery GPG 키 다운로드 실패"
# osquery 패키지 동기화
log "osquery 패키지 미러링..."
wget -r -np -nH --cut-dirs=1 \
-R "index.html*" \
--reject-regex ".*\?.*" \
--timeout=30 \
--tries=3 \
"$OSQUERY_REPO_URL" 2>&1 | tee -a "$LOG_FILE"
# repodata 업데이트
log "osquery repodata 생성..."
createrepo --update "$OSQUERY_LOCAL_DIR" || error_exit "osquery createrepo 실패"
# 디스크 사용량 확인
log "디스크 사용량 확인:"
df -h "$REPO_BASE" | tee -a "$LOG_FILE"
du -sh "$WAZUH_LOCAL_DIR" "$OSQUERY_LOCAL_DIR" | tee -a "$LOG_FILE"
# 오래된 로그 파일 정리 (30일 이상)
find "$LOG_DIR" -name "sync-*.log" -mtime +30 -delete
log "========== 저장소 동기화 완료 =========="
sync/crontab - 크론 스케쥴 설정
# 매일 새벽 2시에 저장소 동기화 실행
0 2 * * * /scripts/sync-repos.sh >> /var/log/repo-sync/cron.log 2>&1
# 매 6시간마다 간단한 메타데이터 업데이트
0 */6 * * * cd /data/repos/wazuh && createrepo --update . >> /var/log/repo-sync/cron.log 2>&1
0 */6 * * * cd /data/repos/osquery && createrepo --update . >> /var/log/repo-sync/cron.log 2>&1
초기화 스크립트
scripts/init-repos.sh - 초기 설정 스크립트
#!/bin/bash
echo "저장소 초기화 시작..."
# 디렉토리 생성
mkdir -p /data/repos/wazuh /data/repos/osquery /data/gpg-keys
# 권한 설정
chmod -R 755 /data/repos
chmod -R 755 /data/gpg-keys
echo "초기화 완료!"
클라이언트 설정 파일
클라이언트 YUM 저장소 설정 파일
# /etc/yum.repos.d/wazuh-internal.repo
[wazuh-internal]
name=Internal Wazuh Repository
baseurl=http://your-internal-server:8080/repos/wazuh/
enabled=1
gpgcheck=1
gpgkey=http://your-internal-server:8080/gpg-keys/GPG-KEY-WAZUH
protect=1
# /etc/yum.repos.d/osquery-internal.repo
[osquery-internal]
name=Internal osquery Repository
baseurl=http://your-internal-server:8080/repos/osquery/
enabled=1
gpgcheck=1
gpgkey=http://your-internal-server:8080/gpg-keys/GPG-KEY-OSQUERY
protect=1
운영 관리 스크립트
운영 관리를 위한 유틸리티 스크립트
#!/bin/bash
# manage-repo.sh - 저장소 관리 스크립트
COMPOSE_FILE="docker-compose.yml"
# 함수: 도움말 표시
show_help() {
cat << EOF
사용법: $0 [명령]
명령:
start - 모든 서비스 시작
stop - 모든 서비스 중지
restart - 모든 서비스 재시작
sync - 수동으로 저장소 동기화
status - 서비스 상태 확인
logs - 로그 확인
clean - 오래된 패키지 정리
backup - 저장소 백업
restore - 저장소 복원
EOF
}
# 서비스 시작
start_services() {
echo "서비스 시작중..."
docker-compose -f $COMPOSE_FILE up -d
}
# 서비스 중지
stop_services() {
echo "서비스 중지중..."
docker-compose -f $COMPOSE_FILE down
}
# 서비스 재시작
restart_services() {
stop_services
start_services
}
# 수동 동기화
manual_sync() {
echo "수동 동기화 시작..."
docker-compose -f $COMPOSE_FILE exec sync-scheduler /scripts/sync-repos.sh
}
# 상태 확인
check_status() {
echo "=== 서비스 상태 ==="
docker-compose -f $COMPOSE_FILE ps
echo -e "\n=== 디스크 사용량 ==="
du -sh data/repos/*
echo -e "\n=== 최근 동기화 로그 ==="
docker-compose -f $COMPOSE_FILE exec sync-scheduler tail -n 20 /var/log/repo-sync/sync-$(date +%Y%m%d).log
}
# 로그 확인
show_logs() {
docker-compose -f $COMPOSE_FILE logs -f --tail=100
}
# 오래된 패키지 정리
clean_old_packages() {
echo "30일 이상 된 패키지 정리중..."
find data/repos -name "*.rpm" -mtime +30 -delete
# repodata 재생성
docker-compose -f $COMPOSE_FILE exec sync-scheduler bash -c "
createrepo --update /data/repos/wazuh
createrepo --update /data/repos/osquery
"
}
# 백업
backup_repos() {
BACKUP_DIR="backups/$(date +%Y%m%d_%H%M%S)"
mkdir -p "$BACKUP_DIR"
echo "백업 시작: $BACKUP_DIR"
tar czf "$BACKUP_DIR/repos.tar.gz" data/repos/
tar czf "$BACKUP_DIR/gpg-keys.tar.gz" data/gpg-keys/
echo "백업 완료!"
}
# 복원
restore_repos() {
if [ -z "$1" ]; then
echo "사용법: $0 restore [백업 디렉토리]"
exit 1
fi
BACKUP_DIR="$1"
if [ ! -d "$BACKUP_DIR" ]; then
echo "백업 디렉토리를 찾을 수 없습니다: $BACKUP_DIR"
exit 1
fi
echo "복원 시작: $BACKUP_DIR"
tar xzf "$BACKUP_DIR/repos.tar.gz"
tar xzf "$BACKUP_DIR/gpg-keys.tar.gz"
echo "복원 완료!"
}
# 메인 로직
case "$1" in
start)
start_services
;;
stop)
stop_services
;;
restart)
restart_services
;;
sync)
manual_sync
;;
status)
check_status
;;
logs)
show_logs
;;
clean)
clean_old_packages
;;
backup)
backup_repos
;;
restore)
restore_repos "$2"
;;
*)
show_help
;;
esac
보안 강화 설정
보안 강화를 위한 추가 설정
# nginx/nginx-secure.conf - IP 기반 접근 제어 추가
# ... (기존 설정)
http {
# ... (기존 설정)
# IP 접근 제어를 위한 geo 블록
geo $allowed_ips {
default 0;
10.0.0.0/8 1; # 내부 네트워크
172.16.0.0/12 1; # 내부 네트워크
192.168.0.0/16 1; # 내부 네트워크
}
# Rate limiting
limit_req_zone $binary_remote_addr zone=repo_limit:10m rate=30r/s;
server {
listen 80;
server_name _;
root /usr/share/nginx/html;
# IP 기반 접근 제어
if ($allowed_ips = 0) {
return 403;
}
# Rate limiting 적용
limit_req zone=repo_limit burst=20 nodelay;
# Basic Authentication (선택사항)
# auth_basic "Internal Repository";
# auth_basic_user_file /etc/nginx/.htpasswd;
# ... (나머지 설정)
}
}
---
# docker-compose-secure.yml - 보안 강화 버전
version: '3.8'
services:
nginx:
build: ./nginx
container_name: repo-nginx
ports:
- "127.0.0.1:8080:80" # 로컬호스트에서만 접근
volumes:
- ./data/repos:/usr/share/nginx/html/repos:ro
- ./data/gpg-keys:/usr/share/nginx/html/gpg-keys:ro
- ./nginx/.htpasswd:/etc/nginx/.htpasswd:ro # Basic Auth
restart: unless-stopped
networks:
- repo-network
security_opt:
- no-new-privileges:true
read_only: true
tmpfs:
- /var/cache/nginx
- /var/run
sync-scheduler:
build: ./sync
container_name: repo-sync
volumes:
- ./data/repos:/data/repos
- ./data/gpg-keys:/data/gpg-keys
- ./sync/sync-repos.sh:/scripts/sync-repos.sh:ro
environment:
- TZ=Asia/Seoul
restart: unless-stopped
networks:
- repo-network
security_opt:
- no-new-privileges:true
cap_drop:
- ALL
cap_add:
- DAC_OVERRIDE
- CHOWN
networks:
repo-network:
driver: bridge
ipam:
config:
- subnet: 172.20.0.0/16
모니터링 및 알림
monitor-repo.sh - 저장소 상태 모니터링
#!/bin/bash
# 설정
REPO_URL="http://localhost:8080"
SLACK_WEBHOOK="YOUR_SLACK_WEBHOOK_URL"
EMAIL_TO="admin@example.com"
DISK_THRESHOLD=80 # 디스크 사용률 임계값 (%)
# 함수: Slack 알림
send_slack() {
local message="$1"
curl -X POST $SLACK_WEBHOOK \
-H 'Content-type: application/json' \
--data "{\"text\":\"🚨 Repo Mirror Alert: $message\"}"
}
# 함수: 이메일 알림
send_email() {
local subject="$1"
local body="$2"
echo "$body" | mail -s "$subject" $EMAIL_TO
}
# 1. 웹서버 상태 확인
check_webserver() {
if ! curl -s -o /dev/null -w "%{http_code}" "$REPO_URL" | grep -q "200"; then
send_slack "웹서버가 응답하지 않습니다!"
return 1
fi
echo "✅ 웹서버 정상"
}
# 2. 저장소 접근성 확인
check_repo_access() {
for repo in wazuh osquery; do
if ! curl -s "$REPO_URL/repos/$repo/repodata/repomd.xml" > /dev/null; then
send_slack "$repo 저장소 메타데이터에 접근할 수 없습니다!"
return 1
fi
done
echo "✅ 저장소 접근성 정상"
}
# 3. 디스크 사용량 확인
check_disk_usage() {
local usage=$(df -h /data/repos | awk 'NR==2 {print $5}' | sed 's/%//')
if [ "$usage" -gt "$DISK_THRESHOLD" ]; then
send_slack "디스크 사용량 경고: ${usage}% (임계값: ${DISK_THRESHOLD}%)"
return 1
fi
echo "✅ 디스크 사용량 정상: ${usage}%"
}
# 4. 동기화 상태 확인
check_sync_status() {
local last_sync=$(find /var/log/repo-sync -name "sync-*.log" -mtime -1 | wc -l)
if [ "$last_sync" -eq 0 ]; then
send_slack "24시간 내 동기화 로그가 없습니다!"
return 1
fi
echo "✅ 동기화 상태 정상"
}
# 5. 패키지 무결성 확인
check_package_integrity() {
# GPG 키 확인
for key in GPG-KEY-WAZUH GPG-KEY-OSQUERY; do
if [ ! -f "/data/gpg-keys/$key" ]; then
send_slack "GPG 키가 없습니다: $key"
return 1
fi
done
# 최신 패키지 확인
local wazuh_count=$(find /data/repos/wazuh -name "*.rpm" -mtime -7 | wc -l)
local osquery_count=$(find /data/repos/osquery -name "*.rpm" -mtime -7 | wc -l)
echo "✅ 패키지 무결성 정상 (최근 7일 패키지: Wazuh=$wazuh_count, osquery=$osquery_count)"
}
# 메인 실행
echo "========== 저장소 모니터링 시작: $(date) =========="
check_webserver
check_repo_access
check_disk_usage
check_sync_status
check_package_integrity
echo "========== 모니터링 완료 =========="
실행 가이드
1. 초기 설정 및 실행
# 1. 프로젝트 디렉토리 생성
mkdir -p repo-mirror && cd repo-mirror
# 2. 위의 파일들을 각각 생성
# (docker-compose.yml, Dockerfiles, 스크립트 등)
# 3. 실행 권한 부여
chmod +x sync/sync-repos.sh
chmod +x scripts/init-repos.sh
chmod +x manage-repo.sh
chmod +x monitor-repo.sh
# 4. 서비스 시작
./manage-repo.sh start
# 5. 초기 동기화 확인
./manage-repo.sh status
2. 클라이언트 설정
# 클라이언트 서버에서
# 1. 기존 저장소 비활성화
yum-config-manager --disable wazuh osquery
# 2. 내부 저장소 설정 추가
cat > /etc/yum.repos.d/internal-repos.repo << EOF
[wazuh-internal]
name=Internal Wazuh Repository
baseurl=http://your-repo-server:8080/repos/wazuh/
enabled=1
gpgcheck=1
gpgkey=http://your-repo-server:8080/gpg-keys/GPG-KEY-WAZUH
[osquery-internal]
name=Internal osquery Repository
baseurl=http://your-repo-server:8080/repos/osquery/
enabled=1
gpgcheck=1
gpgkey=http://your-repo-server:8080/gpg-keys/GPG-KEY-OSQUERY
EOF
# 3. 패키지 설치
yum clean all
yum makecache
yum install wazuh-agent osquery
3. 운영 관리
# 수동 동기화
./manage-repo.sh sync
# 로그 확인
./manage-repo.sh logs
# 상태 모니터링
./monitor-repo.sh
# 백업
./manage-repo.sh backup
# 오래된 패키지 정리
./manage-repo.sh clean
운영 체크리스트
항목 | 주기 | 명령/확인사항 |
서비스 상태 확인 | 일일 | docker-compose ps |
동기화 로그 확인 | 일일 | ./manage-repo.sh logs |
디스크 사용량 | 주간 | df -h data/ |
패키지 무결성 | 주간 | ./monitor-repo.sh |
백업 | 주간 | ./manage-repo.sh backup |
오래된 패키지 정리 | 월간 | ./manage-repo.sh clean |
보안 패치 확인 | 월간 | 컨테이너 이미지 업데이트 |
문제 해결
1. 동기화 실패 시
# 로그 확인
docker-compose logs sync-scheduler
# 수동 동기화 시도
docker exec -it repo-sync /scripts/sync-repos.sh
# 네트워크 연결 확인
docker exec -it repo-sync curl -I https://packages.wazuh.com
2. 클라이언트 연결 실패 시
# 방화벽 확인
firewall-cmd --list-ports
# SELinux 확인
getenforce
# 저장소 메타데이터 확인
curl http://your-repo-server:8080/repos/wazuh/repodata/repomd.xml
3. 디스크 공간 부족 시
# 오래된 패키지 정리
./manage-repo.sh clean
# 로그 정리
find data/logs -name "*.log" -mtime +30 -delete
이 구성을 통해 osquery와 wazuh agent 패키지를 안정적으로 미러링하고 내부 시스템에서 쉽게 설치/업데이트할 수 있습니다.
728x90
그리드형(광고전용)
댓글