GitLab Docker 설치하고 Apache 리버스 프록시로 HTTPS 적용하기
GitLab Docker 설치하고 Apache 리버스 프록시로 HTTPS 적용하기 (feat. ModSecurity 403 에러 해결)
GitLab 서버를 구축하면서 삽질했던 내용을 정리합니다.
공유기 아래, 서버가 2대 있는데, 서버1에서 기존 웹 서비스를 운영하다가 gitlab을 설치하려고 보니 메모리를 4GB~8GB 먹어서 메모리가 부족해서 서버2로 gitlab 을 설치하려는 상황인데, 서버2에는 이미 80 서비스를 하고 있었습니다. 그래서 gitlab 이 서버2의 8080 포트를 쓰게된 상황입니다.
이때, git.example.com 도메인 인증서가 서버1에서 통합되서 관리되고 있고 80포트가 서버1로 가게 되어 있었기에, 공유기에서 바로 서버2로 오지 않고, 서버1로 간 후 Apache로 Reserver Proxy 를 타고 서버2로 오게된 상황입니다.
이 구성을 설치하는데 하루정도, git clone 할 때 서버1에서 서버2로 Apache로 Reserver Proxy 를 타는 구간에서 403 에러로 거의 꼬박 하루 다해서 이틀 정도 삽질하며 고생했네요. 이 글 보시고 저처럼 삽질하지 마시고 시간 아끼시길 바랍니다.
전체 시스템 구성도
인터넷
↓
[공유기 192.168.0.1]
├─ 80 포트 → 192.168.0.2:80 (HTTP)
├─ 443 포트 → 192.168.0.2:443 (HTTPS)
└─ 2222 포트 → 192.168.0.3:2222 (Git SSH)
[Apache 서버 192.168.0.2]
- SSL 인증서 처리
- 리버스 프록시
- ModSecurity WAF
[GitLab 서버 192.168.0.3]
- Docker GitLab CE
- 8080번: HTTP
- 2222번: SSH
접속 흐름도
1. 웹 GUI 접속 흐름 (관리자)
사용자 브라우저
↓
https://git.example.com (443포트)
↓
[공유기 192.168.0.1]
↓ (443 → 192.168.0.2:443 포워딩)
[Apache 서버 192.168.0.2]
- SSL 인증서 처리
- HTTPS → HTTP 변환
- ModSecurity 검사
↓ (프록시: 192.168.0.3:8080)
[GitLab 서버 192.168.0.3:8080]
- 웹 인터페이스 제공
- 사용자 인증 처리
2. Git HTTPS Clone/Push 흐름
Git 클라이언트
↓
git clone https://git.example.com/repos/myproject.git
↓
[공유기 192.168.0.1]
↓ (443 → 192.168.0.2:443 포워딩)
[Apache 서버 192.168.0.2]
- SSL 처리
- Git HTTP 프로토콜 프록시
- ModSecurity OFF (Git 경로) ← 중요!
↓ (프록시: 192.168.0.3:8080)
[GitLab 서버 192.168.0.3:8080]
- Git 저장소 접근
- 인증 확인
3. Git SSH Clone/Push 흐름 (옵션) - Gitlab 은 SSH 사용시 공개키 인증만 가능해서 실제 사용은 안함
Git 클라이언트
↓
git clone ssh://git@git.example.com:2222/repos/myproject.git
↓
[공유기 192.168.0.1]
↓ (2222 → 192.168.0.3:2222 직접 포워딩)
[GitLab 서버 192.168.0.3:2222]
- SSH 키 인증
- Git 저장소 접근
(Apache 거치지 않음)
서버 환경
- CentOS Stream 9
- Docker 24.0 LTS (2023년 5월 출시)
- Docker Compose v2.20
- Apache 2.4
- 공유기: 192.168.0.1
- Apache 웹서버: 192.168.0.2 (기존에 운영중)
- GitLab 서버: 192.168.0.3 (Docker로 구성)
공유기에서 포트포워딩 설정:
- 80 → 192.168.0.2:80 (HTTP)
- 443 → 192.168.0.2:443 (HTTPS)
- 2222 → 192.168.0.3:2222 (Git SSH - 나중에 쓸수도 있으니 설정만 해둠)
GitLab Docker 설치
GitLab 서버(192.168.0.3)에서 gitlab-ce.yaml 파일을 만듭니다:
# gitlab-ce.yaml
version: '3.8'
services:
gitlab:
image: gitlab/gitlab-ce:latest
container_name: gitlab-ce
restart: always
hostname: 'git.example.com'
environment:
GITLAB_OMNIBUS_CONFIG: |
# 외부 URL (Apache가 SSL 처리)
external_url 'https://git.example.com'
# Nginx는 HTTP만 수신 (Apache가 프록시)
nginx['listen_port'] = 8080
nginx['listen_https'] = false
# SSH 포트 설정
gitlab_rails['gitlab_shell_ssh_port'] = 2222
ports:
- '8080:8080' # HTTP (Apache 프록시 대상)
- '2222:22' # SSH (직접 연결)
volumes:
- nv_gitlab-config:/etc/gitlab
- nv_gitlab-logs:/var/log/gitlab
- nv_gitlab-data:/var/opt/gitlab
volumes:
nv_gitlab-config:
nv_gitlab-logs:
nv_gitlab-data:
실행합니다:
docker compose -f gitlab-ce.yaml up -d
주의: 최신 Docker에서는 docker-compose 대신 docker compose를 사용합니다.
Apache 리버스 프록시 설정
Apache 서버(192.168.0.2)에서 설정합니다. 제가 쓴 설정 파일을 참고해서 만들었습니다.
/etc/httpd/conf.d/git.example.com.conf 파일:
# HTTP to HTTPS Redirect (Port 80)
<VirtualHost *:80>
ServerName git.example.com
# Let's Encrypt 인증서 갱신을 위해 이 경로는 80번에서 처리
RewriteEngine On
RewriteCond %{REQUEST_URI} !^/\.well-known/acme-challenge/
RewriteRule ^(.*)$ https://%{HTTP_HOST}$1 [R=301,L]
<Location /.well-known/acme-challenge/>
Satisfy Any
Allow from all
</Location>
ErrorLog /var/log/httpd/git.example.com-error.log
CustomLog /var/log/httpd/git.example.com-access.log combined
</VirtualHost>
# HTTPS Configuration (Port 443)
<VirtualHost *:443>
ServerName git.example.com
# SSL 설정
SSLEngine on
SSLCertificateFile /etc/letsencrypt/live/git.example.com/cert.pem
SSLCertificateKeyFile /etc/letsencrypt/live/git.example.com/privkey.pem
SSLCertificateChainFile /etc/letsencrypt/live/git.example.com/chain.pem
# Git 관련 경로에 대해 ModSecurity 비활성화
<LocationMatch "^/.*\.git/(info/refs|git-upload-pack|git-receive-pack)">
SecRuleEngine Off
</LocationMatch>
# 프록시 설정
ProxyPreserveHost On
ProxyPass / http://192.168.0.3:8080/
ProxyPassReverse / http://192.168.0.3:8080/
RequestHeader set X-Forwarded-Proto "https"
</VirtualHost>
SELinux 설정도 필요합니다:
setsebool -P httpd_can_network_connect 1
systemctl restart httpd
ModSecurity 403 에러 해결
여기까지 설정하면 웹 브라우저로는 잘 들어가지는데, git clone 하려고 하면 403 에러가 납니다.
$ git clone https://git.example.com/repos/myproject.git
Username for 'https://git.example.com': git_user
Password for 'https://git_user@git.example.com':
error: RPC failed; HTTP 403 curl 22 The requested URL returned error: 403
fatal: expected flush after ref listing
Apache 에러 로그를 확인해보니:
ModSecurity: Warning. Match of "within %{tx.allowed_request_content_type}" against "TX:content_type" required.
[data "|application/x-git-upload-pack-request|"]
[msg "Request content type is not allowed by policy"]
ModSecurity가 Git의 특수한 content-type을 차단하고 있었습니다. 그래서 위 설정에서 Git 경로에 대해서만 ModSecurity를 꺼버렸습니다:
<LocationMatch "^/.*\.git/(info/refs|git-upload-pack|git-receive-pack)">
SecRuleEngine Off
</LocationMatch>
이렇게 하니까 git clone이 정상적으로 됩니다!
동작 확인
모든 설정이 끝났으면 테스트해봅니다:
- 웹 브라우저 접속: https://git.example.com
docker exec -it gitlab-ce grep 'Password:' /etc/gitlab/initial_root_password
2. Git HTTPS clone:
git clone https://git.example.com/repos/myproject.git
SSH 클론 방법은 현재는 사용하지 않으므로 생략함.
유용한 명령어들
GitLab 백업:
docker exec -t gitlab-ce gitlab-backup create
로그 확인:
# GitLab 로그
docker logs -f gitlab-ce
# Apache 로그
tail -f /var/log/httpd/git.example.com-error.log
tail -f /var/log/httpd/error_log
Docker Compose 명령어:
# 상태 확인
docker compose -f gitlab-ce.yaml ps
# 재시작
docker compose -f gitlab-ce.yaml restart
# 중지
docker compose -f gitlab-ce.yaml down
마무리
도움이 되었다면 추천 댓글 부탁 드리고, 시간을 아끼셨다면 비밀 댓글로 커피 한잔 부탁 드립니다 ^^