Changed --accept-dns=false to --accept-dns=true in both: - farmq-install-en.ps1 - farmq-install.ps1 This allows Windows clients to receive Headscale Magic DNS configuration (100.64.0.1) automatically during installation, enabling proper name resolution for *.headscale.local domains. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
499 lines
17 KiB
PowerShell
499 lines
17 KiB
PowerShell
# FARMQ Headscale Windows One-Click Installation Script
|
|
# Usage: Run in Administrator PowerShell
|
|
# iex ((New-Object System.Net.WebClient).DownloadString('https://git.0bin.in/.../farmq-install.ps1'))
|
|
|
|
# Set UTF-8 encoding for Korean characters
|
|
$OutputEncoding = [Console]::OutputEncoding = [Text.UTF8Encoding]::UTF8
|
|
[Console]::OutputEncoding = [Text.Encoding]::UTF8
|
|
|
|
param(
|
|
[switch]$Force,
|
|
[string]$HeadscaleServer = "https://head.0bin.in",
|
|
[string]$PreAuthKey = "8b3df41d37cb158ea39f41fc32c9af46e761de817ad06038",
|
|
[string]$FarmqNetwork = "100.64.0.0/10"
|
|
)
|
|
|
|
# ================================
|
|
# 색상 출력 함수
|
|
# ================================
|
|
function Write-ColorOutput {
|
|
param(
|
|
[string]$Message,
|
|
[string]$ForegroundColor = "White"
|
|
)
|
|
Write-Host $Message -ForegroundColor $ForegroundColor
|
|
}
|
|
|
|
function Write-Header {
|
|
param([string]$Text)
|
|
Write-Host ""
|
|
Write-Host "============================================" -ForegroundColor Magenta
|
|
Write-Host $Text -ForegroundColor White
|
|
Write-Host "============================================" -ForegroundColor Magenta
|
|
Write-Host ""
|
|
}
|
|
|
|
function Write-Status {
|
|
param([string]$Message)
|
|
Write-Host ""
|
|
Write-Host "[*] $Message" -ForegroundColor Blue
|
|
}
|
|
|
|
function Write-Success {
|
|
param([string]$Message)
|
|
Write-Host ""
|
|
Write-Host "[+] $Message" -ForegroundColor Green
|
|
}
|
|
|
|
function Write-Error {
|
|
param([string]$Message)
|
|
Write-Host ""
|
|
Write-Host "[!] ERROR: $Message" -ForegroundColor Red
|
|
}
|
|
|
|
function Write-Warning {
|
|
param([string]$Message)
|
|
Write-Host ""
|
|
Write-Host "[!] WARNING: $Message" -ForegroundColor Yellow
|
|
}
|
|
|
|
function Write-Info {
|
|
param([string]$Message)
|
|
Write-Host ""
|
|
Write-Host "[i] $Message" -ForegroundColor Cyan
|
|
}
|
|
|
|
# ================================
|
|
# 시스템 요구사항 확인
|
|
# ================================
|
|
function Test-Requirements {
|
|
Write-Status "Checking system requirements..."
|
|
|
|
# Check administrator privileges
|
|
$currentUser = [Security.Principal.WindowsIdentity]::GetCurrent()
|
|
$principal = New-Object Security.Principal.WindowsPrincipal($currentUser)
|
|
|
|
if (-NOT $principal.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)) {
|
|
Write-Error "This script requires administrator privileges."
|
|
Write-Info "Please restart using one of these methods:"
|
|
Write-Info "1. Windows Key + X -> 'Windows PowerShell (Admin)'"
|
|
Write-Info "2. Run the script command again"
|
|
Write-Host ""
|
|
Read-Host "Press any key to exit..."
|
|
exit 1
|
|
}
|
|
|
|
# Check Windows version
|
|
$osVersion = [System.Environment]::OSVersion.Version
|
|
if ($osVersion.Major -lt 10) {
|
|
Write-Warning "Windows 10 or later recommended. Current: Windows $($osVersion.Major).$($osVersion.Minor)"
|
|
}
|
|
|
|
# Check internet connection
|
|
try {
|
|
Test-NetConnection "8.8.8.8" -Port 53 -InformationLevel Quiet | Out-Null
|
|
}
|
|
catch {
|
|
Write-Warning "Please check your internet connection."
|
|
}
|
|
|
|
Write-Success "System requirements check completed"
|
|
}
|
|
|
|
# ================================
|
|
# Tailscale 설치
|
|
# ================================
|
|
function Install-Tailscale {
|
|
Write-Status "Tailscale 클라이언트 확인 중..."
|
|
|
|
# 기존 설치 확인
|
|
$tailscalePath = Get-Command "tailscale" -ErrorAction SilentlyContinue
|
|
if ($tailscalePath) {
|
|
$version = & tailscale version 2>$null | Select-Object -First 1
|
|
Write-Info "Tailscale이 이미 설치되어 있습니다."
|
|
Write-Info "현재 버전: $version"
|
|
return
|
|
}
|
|
|
|
Write-Info "Windows용 Tailscale 설치 중..."
|
|
|
|
# 최신 Tailscale 버전 확인
|
|
try {
|
|
Write-Status "최신 Tailscale 버전 확인 중..."
|
|
$latestRelease = Invoke-RestMethod -Uri "https://api.github.com/repos/tailscale/tailscale/releases/latest" -UseBasicParsing
|
|
$version = $latestRelease.tag_name.TrimStart('v')
|
|
Write-Info "최신 버전: $version"
|
|
}
|
|
catch {
|
|
Write-Warning "최신 버전 확인 실패, 기본 버전 사용"
|
|
$version = "1.86.2"
|
|
}
|
|
|
|
# 임시 다운로드 경로
|
|
$tempPath = "$env:TEMP\tailscale-setup-$version.exe"
|
|
$downloadUrl = "https://pkgs.tailscale.com/stable/tailscale-setup-$version.exe"
|
|
|
|
try {
|
|
Write-Status "Tailscale 다운로드 중: $downloadUrl"
|
|
Invoke-WebRequest -Uri $downloadUrl -OutFile $tempPath -UseBasicParsing
|
|
|
|
Write-Status "Tailscale 설치 중... (잠시 기다려주세요)"
|
|
Start-Process -FilePath $tempPath -ArgumentList "/S" -Wait
|
|
|
|
# PATH 환경변수 새로고침
|
|
$env:Path = [System.Environment]::GetEnvironmentVariable("Path","Machine") + ";" + [System.Environment]::GetEnvironmentVariable("Path","User")
|
|
|
|
# 설치 확인
|
|
Start-Sleep -Seconds 3
|
|
$tailscaleInstalled = Get-Command "tailscale" -ErrorAction SilentlyContinue
|
|
if (-not $tailscaleInstalled) {
|
|
# 직접 경로 시도
|
|
$tailscaleExe = "C:\Program Files\Tailscale\tailscale.exe"
|
|
if (Test-Path $tailscaleExe) {
|
|
# PATH에 Tailscale 경로 추가
|
|
$currentPath = [Environment]::GetEnvironmentVariable("Path", "Machine")
|
|
if ($currentPath -notlike "*Tailscale*") {
|
|
[Environment]::SetEnvironmentVariable("Path", "$currentPath;C:\Program Files\Tailscale", "Machine")
|
|
$env:Path = "$env:Path;C:\Program Files\Tailscale"
|
|
}
|
|
}
|
|
}
|
|
|
|
Remove-Item $tempPath -Force -ErrorAction SilentlyContinue
|
|
Write-Success "Tailscale 설치 완료"
|
|
|
|
}
|
|
catch {
|
|
Write-Error "Tailscale 설치 실패: $($_.Exception.Message)"
|
|
throw
|
|
}
|
|
}
|
|
|
|
# ================================
|
|
# Tailscale 서비스 시작
|
|
# ================================
|
|
function Start-TailscaleService {
|
|
Write-Status "Tailscale 서비스 시작 중..."
|
|
|
|
try {
|
|
# Tailscale 서비스 시작
|
|
$service = Get-Service -Name "Tailscale" -ErrorAction SilentlyContinue
|
|
if ($service) {
|
|
if ($service.Status -ne "Running") {
|
|
Start-Service -Name "Tailscale"
|
|
Start-Sleep -Seconds 3
|
|
}
|
|
Write-Success "Tailscale 서비스가 실행 중입니다."
|
|
} else {
|
|
Write-Warning "Tailscale 서비스를 찾을 수 없습니다. 수동 시작을 시도합니다."
|
|
}
|
|
}
|
|
catch {
|
|
Write-Warning "서비스 시작에 실패했습니다: $($_.Exception.Message)"
|
|
}
|
|
}
|
|
|
|
# ================================
|
|
# Headscale 등록
|
|
# ================================
|
|
function Register-Headscale {
|
|
Write-Status "Headscale 서버에 등록 중..."
|
|
|
|
# Tailscale 실행파일 경로 확인
|
|
$tailscaleCmd = Get-Command "tailscale" -ErrorAction SilentlyContinue
|
|
if (-not $tailscaleCmd) {
|
|
$tailscaleExe = "C:\Program Files\Tailscale\tailscale.exe"
|
|
if (Test-Path $tailscaleExe) {
|
|
$tailscaleCmd = @{Source = $tailscaleExe}
|
|
} else {
|
|
Write-Error "Tailscale 실행파일을 찾을 수 없습니다."
|
|
return $false
|
|
}
|
|
}
|
|
|
|
$tailscalePath = $tailscaleCmd.Source
|
|
|
|
# 기존 연결 확인
|
|
try {
|
|
$status = & $tailscalePath status 2>$null
|
|
if ($LASTEXITCODE -eq 0 -and $status) {
|
|
Write-Warning "이미 Tailscale/Headscale에 연결되어 있습니다."
|
|
|
|
# 현재 연결 상태 표시
|
|
Write-Info "현재 연결 상태:"
|
|
$status | Select-Object -First 5 | ForEach-Object { Write-Host " $_" }
|
|
|
|
# 강제 등록 옵션 확인
|
|
if ($Force) {
|
|
Write-Warning "강제 재등록 옵션이 활성화되었습니다."
|
|
Write-Info "기존 연결을 해제하고 재등록합니다..."
|
|
} else {
|
|
$response = Read-Host "기존 연결을 해제하고 팜큐 Headscale로 등록하시겠습니까? (Y/n)"
|
|
if ($response -eq "" -or $response -match "^[Yy]") {
|
|
Write-Info "기존 연결을 해제합니다..."
|
|
} else {
|
|
Write-Info "등록을 건너뜁니다."
|
|
return $true
|
|
}
|
|
}
|
|
|
|
# 기존 연결 해제
|
|
try {
|
|
& $tailscalePath logout 2>$null
|
|
Start-Sleep -Seconds 3
|
|
Write-Success "기존 연결이 해제되었습니다."
|
|
}
|
|
catch {
|
|
Write-Warning "연결 해제 중 오류가 발생했지만 계속 진행합니다."
|
|
}
|
|
}
|
|
}
|
|
catch {
|
|
# 연결되어 있지 않음 (정상)
|
|
}
|
|
|
|
Write-Info "Headscale 서버: $HeadscaleServer"
|
|
Write-Info "Pre-auth Key: $($PreAuthKey.Substring(0,8))***************"
|
|
|
|
# Headscale 등록 시도
|
|
Write-Status "등록 명령 실행 중..."
|
|
|
|
try {
|
|
$arguments = @(
|
|
"up",
|
|
"--login-server=$HeadscaleServer",
|
|
"--authkey=$PreAuthKey",
|
|
"--accept-routes",
|
|
"--accept-dns=true"
|
|
)
|
|
|
|
& $tailscalePath $arguments
|
|
|
|
if ($LASTEXITCODE -eq 0) {
|
|
Write-Success "Headscale 등록 성공!"
|
|
return $true
|
|
} else {
|
|
Write-Error "자동 등록에 실패했습니다."
|
|
Write-Info "수동 등록 명령어:"
|
|
Write-Host "tailscale up --login-server=`"$HeadscaleServer`" --authkey=`"$PreAuthKey`""
|
|
return $false
|
|
}
|
|
}
|
|
catch {
|
|
Write-Error "등록 중 오류 발생: $($_.Exception.Message)"
|
|
return $false
|
|
}
|
|
}
|
|
|
|
# ================================
|
|
# 방화벽 설정
|
|
# ================================
|
|
function Configure-Firewall {
|
|
Write-Status "방화벽 설정 확인 중..."
|
|
|
|
try {
|
|
# Windows Defender 방화벽 예외 추가
|
|
$ruleName = "Tailscale-FarmQ"
|
|
$existingRule = Get-NetFirewallRule -DisplayName $ruleName -ErrorAction SilentlyContinue
|
|
|
|
if (-not $existingRule) {
|
|
New-NetFirewallRule -DisplayName $ruleName -Direction Inbound -Protocol UDP -LocalPort 41641 -Action Allow -Profile Any | Out-Null
|
|
New-NetFirewallRule -DisplayName "$ruleName-Outbound" -Direction Outbound -Protocol UDP -LocalPort 41641 -Action Allow -Profile Any | Out-Null
|
|
Write-Info "Windows Defender 방화벽 예외를 추가했습니다."
|
|
}
|
|
|
|
Write-Success "방화벽 설정 완료"
|
|
}
|
|
catch {
|
|
Write-Warning "방화벽 설정 중 오류 발생: $($_.Exception.Message)"
|
|
Write-Info "수동으로 방화벽에서 Tailscale을 허용해주세요."
|
|
}
|
|
}
|
|
|
|
# ================================
|
|
# 연결 상태 확인
|
|
# ================================
|
|
function Test-Connection {
|
|
Write-Status "연결 상태 확인 중..."
|
|
|
|
Start-Sleep -Seconds 5
|
|
|
|
# Tailscale 실행파일 경로 확인
|
|
$tailscaleCmd = Get-Command "tailscale" -ErrorAction SilentlyContinue
|
|
if (-not $tailscaleCmd) {
|
|
$tailscaleExe = "C:\Program Files\Tailscale\tailscale.exe"
|
|
if (Test-Path $tailscaleExe) {
|
|
$tailscaleCmd = @{Source = $tailscaleExe}
|
|
} else {
|
|
Write-Error "Tailscale 실행파일을 찾을 수 없습니다."
|
|
return
|
|
}
|
|
}
|
|
|
|
$tailscalePath = $tailscaleCmd.Source
|
|
|
|
try {
|
|
$status = & $tailscalePath status 2>$null
|
|
if ($LASTEXITCODE -ne 0 -or -not $status) {
|
|
Write-Error "Tailscale 연결에 문제가 있습니다."
|
|
return
|
|
}
|
|
|
|
# IP 주소 확인
|
|
$ipv4 = & $tailscalePath ip -4 2>$null
|
|
$ipv6 = & $tailscalePath ip -6 2>$null
|
|
|
|
Write-Success "Headscale 네트워크 연결 완료!"
|
|
Write-Info "할당된 IPv4: $(if($ipv4){$ipv4}else{'N/A'})"
|
|
Write-Info "할당된 IPv6: $(if($ipv6){$ipv6}else{'N/A'})"
|
|
|
|
# 네트워크 테스트
|
|
Write-Status "네트워크 연결 테스트 중..."
|
|
|
|
try {
|
|
Test-Connection "100.64.0.1" -Count 2 -Quiet | Out-Null
|
|
Write-Success "팜큐 네트워크($FarmqNetwork) 연결 정상!"
|
|
}
|
|
catch {
|
|
Write-Warning "네트워크 테스트 실패. 방화벽을 확인해주세요."
|
|
}
|
|
|
|
# 연결된 노드 확인
|
|
Write-Info "네트워크 상태:"
|
|
$status | Select-Object -First 10 | ForEach-Object {
|
|
Write-Host " $_" -ForegroundColor Gray
|
|
}
|
|
}
|
|
catch {
|
|
Write-Error "연결 상태 확인 실패: $($_.Exception.Message)"
|
|
}
|
|
}
|
|
|
|
# ================================
|
|
# 정리 작업
|
|
# ================================
|
|
function Complete-Installation {
|
|
Write-Status "설치 완료 작업 중..."
|
|
|
|
# 임시 파일 정리
|
|
Get-ChildItem "$env:TEMP\tailscale*" -ErrorAction SilentlyContinue | Remove-Item -Force -ErrorAction SilentlyContinue
|
|
|
|
Write-Success "정리 작업 완료"
|
|
}
|
|
|
|
# ================================
|
|
# 최종 정보 출력
|
|
# ================================
|
|
function Show-FinalInfo {
|
|
Write-Header "팜큐 Headscale Windows 설치 완료!"
|
|
|
|
# 시스템 정보
|
|
$computerName = $env:COMPUTERNAME
|
|
$tailscaleCmd = Get-Command "tailscale" -ErrorAction SilentlyContinue
|
|
if (-not $tailscaleCmd) {
|
|
$tailscaleExe = "C:\Program Files\Tailscale\tailscale.exe"
|
|
if (Test-Path $tailscaleExe) {
|
|
$tailscaleCmd = @{Source = $tailscaleExe}
|
|
}
|
|
}
|
|
|
|
if ($tailscaleCmd) {
|
|
$tailscaleIP = & $tailscaleCmd.Source ip -4 2>$null
|
|
}
|
|
|
|
$osVersion = [System.Environment]::OSVersion.Version
|
|
|
|
Write-ColorOutput "🎉 설치가 성공적으로 완료되었습니다!" "Green"
|
|
Write-Host ""
|
|
|
|
Write-ColorOutput "📋 시스템 정보:" "Cyan"
|
|
Write-Host " 컴퓨터명: $computerName"
|
|
Write-Host " Tailscale IP: $(if($tailscaleIP){$tailscaleIP}else{'N/A'})"
|
|
Write-Host " OS: Windows $($osVersion.Major).$($osVersion.Minor)"
|
|
Write-Host " Headscale 서버: $HeadscaleServer"
|
|
|
|
Write-Host ""
|
|
Write-ColorOutput "🔧 유용한 명령어:" "Yellow"
|
|
Write-Host " tailscale status # 연결 상태 확인"
|
|
Write-Host " tailscale ip # 할당된 IP 확인"
|
|
Write-Host " tailscale ping <node> # 다른 노드와 연결 테스트"
|
|
Write-Host " tailscale logout # 네트워크에서 해제"
|
|
|
|
Write-Host ""
|
|
Write-ColorOutput "🌐 팜큐 관리자 페이지:" "Magenta"
|
|
Write-Host " http://192.168.0.151:5002"
|
|
Write-Host " http://192.168.0.151:5002/vms (VM 관리)"
|
|
|
|
Write-Host ""
|
|
Write-ColorOutput "문제가 있을 경우 다음을 확인하세요:" "White"
|
|
Write-Host " 1. Windows 방화벽 설정"
|
|
Write-Host " 2. 바이러스 백신 프로그램 예외 설정"
|
|
Write-Host " 3. 회사 네트워크 정책 확인"
|
|
|
|
Write-Header "설치 완료 - 팜큐 네트워크를 사용할 수 있습니다!"
|
|
}
|
|
|
|
# ================================
|
|
# 메인 함수
|
|
# ================================
|
|
function Main {
|
|
# 에러 발생 시 중단
|
|
$ErrorActionPreference = "Stop"
|
|
|
|
Write-Header "팜큐(FARMQ) Headscale Windows 원클릭 설치"
|
|
|
|
try {
|
|
# 설치 과정
|
|
Test-Requirements
|
|
Install-Tailscale
|
|
Start-TailscaleService
|
|
$registerSuccess = Register-Headscale
|
|
|
|
if ($registerSuccess) {
|
|
Configure-Firewall
|
|
Test-Connection
|
|
Complete-Installation
|
|
Show-FinalInfo
|
|
} else {
|
|
Write-Warning "등록에 실패했지만 Tailscale은 설치되었습니다."
|
|
Write-Info "수동으로 등록을 완료해주세요."
|
|
}
|
|
|
|
}
|
|
catch {
|
|
Write-Error "설치 중 오류가 발생했습니다: $($_.Exception.Message)"
|
|
Write-Info "문제가 지속되면 관리자에게 문의하세요."
|
|
Write-Host ""
|
|
Read-Host "아무 키나 누르세요..."
|
|
exit 1
|
|
}
|
|
}
|
|
|
|
# ================================
|
|
# 스크립트 실행
|
|
# ================================
|
|
|
|
# 파라미터 처리
|
|
if ($args -contains "--help" -or $args -contains "-h") {
|
|
Write-Host "팜큐 Headscale Windows 설치 스크립트"
|
|
Write-Host ""
|
|
Write-Host "사용법:"
|
|
Write-Host " iex ((New-Object System.Net.WebClient).DownloadString('https://git.0bin.in/.../farmq-install.ps1'))"
|
|
Write-Host ""
|
|
Write-Host "옵션:"
|
|
Write-Host " -Force 기존 연결을 강제로 해제하고 재등록"
|
|
Write-Host " -HeadscaleServer 서버 주소 (기본값: https://head.0bin.in)"
|
|
Write-Host ""
|
|
Write-Host "예시:"
|
|
Write-Host " # 강제 재등록"
|
|
Write-Host " iex ((New-Object System.Net.WebClient).DownloadString('https://git.0bin.in/.../farmq-install.ps1?force=1'))"
|
|
exit 0
|
|
}
|
|
|
|
# Force 파라미터 URL에서 처리
|
|
if ($MyInvocation.MyCommand.Path -like "*force=1*") {
|
|
$Force = $true
|
|
}
|
|
|
|
# 메인 함수 실행
|
|
Main |