headscale-tailscale-replace.../farmq-install.ps1
시골약사 4123babcea Fix Magic DNS issue: Enable DNS acceptance in Windows installation scripts
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>
2025-09-13 22:34:18 +09:00

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