- Update INSTALLATION_GUIDE.md: * Add Headplane configuration section with proper config.yaml * Update docker-compose.yml example with simplified environment variables * Add Headplane login instructions and API key information * Update troubleshooting section for cookie_secret validation errors - Update CLIENT_CONNECTION_TEST.md: * Add Headplane web UI access information * Include external access URL (192.168.0.151:3000/admin/) * Add login credentials and API key details - Update start.sh: * Include Headplane UI URLs in installation summary * Add external access information for network environments All documentation now reflects the working Headplane UI configuration and provides complete setup instructions for both local and external access. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
370 lines
8.4 KiB
Markdown
370 lines
8.4 KiB
Markdown
# 🚀 Headscale + Headplane 완전 설치 가이드
|
|
|
|
## 📋 프로젝트 개요
|
|
- **목표**: Tailscale을 완전히 대체하는 자체 호스팅 VPN 솔루션 구축
|
|
- **기술 스택**: Docker, Docker Compose, Headscale, Headplane
|
|
- **환경**: Ubuntu 24.04 LTS, Docker 27.2.0
|
|
|
|
## 🛠️ 사전 요구사항
|
|
- Docker 및 Docker Compose 설치
|
|
- 8070, 3000 포트 사용 가능
|
|
- root 권한 또는 sudo 권한
|
|
|
|
## 📁 프로젝트 구조
|
|
```
|
|
headscale-setup/
|
|
├── docker-compose.yml # Docker Compose 설정
|
|
├── .env # 환경변수 (API 키 포함)
|
|
├── .env.example # 환경변수 템플릿
|
|
├── config/
|
|
│ └── config.yaml # Headscale 최신 설정
|
|
├── headplane-config/
|
|
│ └── config.yaml # Headplane 설정
|
|
├── data/ # SQLite 데이터베이스 (자동 생성)
|
|
├── run/ # 런타임 파일 (자동 생성)
|
|
└── start.sh # 자동 설치 스크립트
|
|
```
|
|
|
|
## 🔧 상세 설치 과정
|
|
|
|
### 1단계: 환경 준비
|
|
```bash
|
|
# 작업 디렉토리 생성
|
|
mkdir -p headscale-setup
|
|
cd headscale-setup
|
|
|
|
# 필요한 하위 디렉토리 생성
|
|
mkdir -p config data run headplane-config
|
|
```
|
|
|
|
### 2단계: Docker Compose 설정
|
|
|
|
#### docker-compose.yml 작성
|
|
```yaml
|
|
version: '3.8'
|
|
|
|
services:
|
|
headscale:
|
|
image: headscale/headscale:latest
|
|
container_name: headscale
|
|
restart: unless-stopped
|
|
command: serve
|
|
environment:
|
|
- TZ=Asia/Seoul
|
|
volumes:
|
|
- ./config:/etc/headscale
|
|
- ./data:/var/lib/headscale
|
|
- ./run:/var/run/headscale
|
|
ports:
|
|
- "8070:8080" # 외부:내부 (포트 충돌 방지)
|
|
- "9090:9090" # 메트릭스
|
|
networks:
|
|
- headscale-net
|
|
healthcheck:
|
|
test: ["CMD-SHELL", "nc -z localhost 8080 || exit 1"]
|
|
interval: 30s
|
|
timeout: 10s
|
|
retries: 3
|
|
start_period: 30s
|
|
|
|
headplane:
|
|
image: ghcr.io/tale/headplane:latest
|
|
container_name: headplane
|
|
restart: unless-stopped
|
|
environment:
|
|
- TZ=Asia/Seoul
|
|
- HEADSCALE_URL=http://headscale:8080
|
|
- HEADSCALE_API_KEY=${HEADSCALE_API_KEY}
|
|
volumes:
|
|
- ./headplane-config:/etc/headplane
|
|
ports:
|
|
- "3000:3000"
|
|
depends_on:
|
|
- headscale
|
|
networks:
|
|
- headscale-net
|
|
|
|
networks:
|
|
headscale-net:
|
|
driver: bridge
|
|
```
|
|
|
|
### 3단계: Headscale 설정 파일
|
|
|
|
#### config/config.yaml (최신 형식)
|
|
```yaml
|
|
---
|
|
server_url: http://localhost:8070
|
|
listen_addr: 0.0.0.0:8080
|
|
metrics_listen_addr: 0.0.0.0:9090
|
|
|
|
private_key_path: /var/lib/headscale/private.key
|
|
noise:
|
|
private_key_path: /var/lib/headscale/noise_private.key
|
|
|
|
# 최신 형식: prefixes 사용
|
|
prefixes:
|
|
v4: 100.64.0.0/10
|
|
v6: fd7a:115c:a1e0::/48
|
|
|
|
derp:
|
|
server:
|
|
enabled: false
|
|
urls:
|
|
- https://controlplane.tailscale.com/derpmap/default
|
|
|
|
disable_check_updates: false
|
|
ephemeral_node_inactivity_timeout: 30m
|
|
|
|
database:
|
|
type: sqlite3
|
|
sqlite:
|
|
path: /var/lib/headscale/db.sqlite
|
|
|
|
# 최신 DNS 설정 형식
|
|
dns:
|
|
override_local_dns: true
|
|
nameservers:
|
|
global:
|
|
- 1.1.1.1
|
|
- 8.8.8.8
|
|
search_domains: []
|
|
magic_dns: true
|
|
base_domain: headscale.local
|
|
|
|
# 최신 정책 설정
|
|
policy:
|
|
path: ""
|
|
|
|
log:
|
|
format: text
|
|
level: info
|
|
|
|
unix_socket: /var/run/headscale/headscale.sock
|
|
unix_socket_permission: "0770"
|
|
|
|
logtail:
|
|
enabled: false
|
|
|
|
randomize_client_port: false
|
|
|
|
# 간소화된 OIDC 설정
|
|
oidc:
|
|
only_start_if_oidc_is_available: false
|
|
issuer: ""
|
|
client_id: ""
|
|
client_secret: ""
|
|
scope: ["openid", "profile", "email"]
|
|
extra_params: {}
|
|
allowed_domains: []
|
|
allowed_users: []
|
|
```
|
|
|
|
### 4단계: Headplane 설정 파일
|
|
|
|
#### headplane-config/config.yaml
|
|
```yaml
|
|
headscale:
|
|
url: http://headscale:8080
|
|
api_key: YOUR_API_KEY_HERE # 자동 생성됨
|
|
config_strict: false
|
|
|
|
server:
|
|
host: 0.0.0.0
|
|
port: 3000
|
|
cookie_secret: headscale-ui-secret-32-chars-key # 정확히 32자
|
|
cookie_secure: false
|
|
|
|
settings:
|
|
title: "Headscale 관리 패널"
|
|
favicon_url: ""
|
|
custom_css: ""
|
|
```
|
|
|
|
**중요 설정사항:**
|
|
- `cookie_secret`: 정확히 32자여야 함 (설정 검증 오류 방지)
|
|
- `config_strict: false`: 설정 검증 완화
|
|
- `api_key`: 설치 시 자동 생성되어 교체됨
|
|
- 설정 파일은 환경변수보다 우선순위가 높음
|
|
|
|
### 5단계: 환경변수 설정
|
|
|
|
#### .env.example
|
|
```bash
|
|
# Headscale API Key (설치 후 자동 생성됨)
|
|
HEADSCALE_API_KEY=your_api_key_here
|
|
|
|
# 서버 설정
|
|
SERVER_URL=http://localhost:8070
|
|
LISTEN_ADDR=0.0.0.0:8080
|
|
|
|
# 데이터베이스 (SQLite 기본)
|
|
DB_TYPE=sqlite3
|
|
DB_PATH=/var/lib/headscale/db.sqlite
|
|
|
|
# Magic DNS
|
|
MAGIC_DNS=true
|
|
BASE_DOMAIN=headscale.local
|
|
|
|
# 네트워크 설정
|
|
IP_PREFIXES=100.64.0.0/10
|
|
|
|
# 시간대
|
|
TZ=Asia/Seoul
|
|
```
|
|
|
|
### 5단계: 설치 실행
|
|
|
|
#### 환경변수 파일 복사
|
|
```bash
|
|
cp .env.example .env
|
|
```
|
|
|
|
#### 자동 설치 스크립트 실행
|
|
```bash
|
|
chmod +x start.sh
|
|
./start.sh
|
|
```
|
|
|
|
#### 또는 수동 설치
|
|
```bash
|
|
# 1. Headscale 시작
|
|
docker-compose up -d headscale
|
|
|
|
# 2. API 키 생성 (약 30초 대기 후)
|
|
sleep 30
|
|
API_KEY=$(docker-compose exec -T headscale headscale apikeys create)
|
|
echo "Generated API Key: $API_KEY"
|
|
|
|
# 3. .env 파일에 API 키 입력
|
|
sed -i "s/HEADSCALE_API_KEY=your_api_key_here/HEADSCALE_API_KEY=$API_KEY/" .env
|
|
|
|
# 4. Headplane 시작
|
|
docker-compose up -d headplane
|
|
```
|
|
|
|
## 🎯 중요한 설정 변경사항
|
|
|
|
### 포트 충돌 해결
|
|
- **기존**: 8080:8080 (충돌 발생)
|
|
- **변경**: 8070:8080 (외부 8070 포트 사용)
|
|
|
|
### 최신 Headscale 설정 형식 적용
|
|
- `ip_prefixes` → `prefixes` (v4/v6 분리)
|
|
- `dns_config` → `dns` (구조 변경)
|
|
- `acl_policy_path` → `policy.path`
|
|
- OIDC `strip_email_domain` 제거
|
|
|
|
### Docker 헬스체크 개선
|
|
- `curl` → `nc` (netcat 사용)
|
|
- Headplane 의존성 조건 완화
|
|
|
|
## 🔍 설치 확인 및 검증
|
|
|
|
### 1. 컨테이너 상태 확인
|
|
```bash
|
|
docker-compose ps
|
|
```
|
|
|
|
### 2. Headscale API 테스트
|
|
```bash
|
|
curl -s http://localhost:8070/health
|
|
# 응답: {"status":"pass"}
|
|
```
|
|
|
|
### 3. 로그 확인
|
|
```bash
|
|
docker-compose logs headscale
|
|
docker-compose logs headplane
|
|
```
|
|
|
|
### 4. 사용자 생성
|
|
```bash
|
|
docker-compose exec headscale headscale users create myuser
|
|
```
|
|
|
|
### 5. 사용자 목록 확인
|
|
```bash
|
|
docker-compose exec headscale headscale users list
|
|
```
|
|
|
|
### 6. Pre-auth 키 생성
|
|
```bash
|
|
docker-compose exec headscale headscale preauthkeys create --user 1 --reusable --expiration 24h
|
|
```
|
|
|
|
## 🚨 문제 해결
|
|
|
|
### 포트 충돌 문제
|
|
```bash
|
|
# 8080 포트 사용 중인 프로세스 확인
|
|
lsof -i :8080
|
|
|
|
# 포트를 8070으로 변경하여 해결
|
|
```
|
|
|
|
### Headplane 설정 파일 문제
|
|
```bash
|
|
# cookie_secret 길이 오류 시 (정확히 32자 필요)
|
|
echo "headscale-ui-secret-32-chars-key" | wc -c # 32자 확인
|
|
|
|
# 설정 파일 재검증
|
|
docker-compose logs headplane --tail 10
|
|
|
|
# 컨테이너 재시작으로 설정 재로드
|
|
docker-compose restart headplane
|
|
```
|
|
|
|
### 헬스체크 실패
|
|
```bash
|
|
# wget 대신 netcat 사용
|
|
# CMD-SHELL을 사용하여 호환성 개선
|
|
```
|
|
|
|
## 📊 최종 설치 결과
|
|
|
|
### 접속 정보
|
|
- **Headscale API**: http://localhost:8070
|
|
- **Headplane UI**: http://localhost:3000/admin/ (로그인 페이지)
|
|
- **외부 접속**: http://192.168.0.151:3000/admin/ (네트워크 설정에 따라)
|
|
- **메트릭스**: http://localhost:9090
|
|
|
|
### 생성된 정보
|
|
- **사용자**: myuser (ID: 1)
|
|
- **API 키**: 8qRr1IB.tV95CmA0fLaCiGGIgBfeoN9daHceFkzI (자동 생성됨)
|
|
- **Pre-auth 키**: fc4f2dc55ee00c5352823d156129b9ce2df4db02f1d76a21 (24시간 유효, 재사용 가능)
|
|
|
|
### 🔑 Headplane 로그인
|
|
1. 브라우저에서 http://localhost:3000/admin/ 또는 http://192.168.0.151:3000/admin/ 접속
|
|
2. **API Key** 필드에 입력: `8qRr1IB.tV95CmA0fLaCiGGIgBfeoN9daHceFkzI`
|
|
3. **Sign In** 버튼 클릭
|
|
|
|
### 네트워크 설정
|
|
- **IPv4**: 100.64.0.0/10
|
|
- **IPv6**: fd7a:115c:a1e0::/48
|
|
- **Magic DNS**: headscale.local
|
|
|
|
## 🔄 Git 관리
|
|
|
|
### 브랜치 전략
|
|
```bash
|
|
# 기능 브랜치 생성
|
|
git checkout -b feature/working-headscale-setup
|
|
|
|
# 변경사항 커밋
|
|
git add .
|
|
git commit -m "🎉 Working Headscale Setup Complete"
|
|
|
|
# 원격 저장소 푸시
|
|
git push -u origin feature/working-headscale-setup
|
|
```
|
|
|
|
## 📈 다음 단계
|
|
1. Tailscale 클라이언트 연결 테스트
|
|
2. HTTPS/TLS 인증서 구성
|
|
3. Headplane 한글화 작업
|
|
4. ACL 보안 규칙 설정
|
|
5. 백업 및 모니터링 구성
|
|
|
|
## 🎉 결론
|
|
Headscale과 Headplane을 사용한 완전한 자체 호스팅 VPN 솔루션이 성공적으로 구축되었습니다. 이제 Tailscale을 완전히 대체할 수 있는 환경이 준비되었습니다. |