#!/bin/bash # Proxmox Auto RDP Setup Script - Fixed Version # 자동으로 Proxmox 호스트를 RDP VM에 연결하는 설정을 수행합니다 # 원본 스크립트의 깜빡임 문제를 해결한 개선된 버전 set -euo pipefail # 색상 코드 정의 RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' BLUE='\033[0;34m' PURPLE='\033[0;35m' CYAN='\033[0;36m' NC='\033[0m' # No Color # 로그 함수들 msg_info() { echo -e "${BLUE}[INFO]${NC} $1" } msg_ok() { echo -e "${GREEN}[OK]${NC} $1" } msg_error() { echo -e "${RED}[ERROR]${NC} $1" exit 1 } msg_warn() { echo -e "${YELLOW}[WARN]${NC} $1" } # 헤더 출력 print_header() { clear echo -e "${PURPLE}" echo "═══════════════════════════════════════════════════════════════════" echo " Proxmox Auto RDP Setup Script v2.0 (Fixed)" echo "═══════════════════════════════════════════════════════════════════" echo -e "${NC}" echo "이 스크립트는 Proxmox VE 호스트가 부팅 시 자동으로 RDP 연결하도록 설정합니다." echo "원본 스크립트의 깜빡임 문제를 해결한 개선된 버전입니다." echo "" } # Proxmox 버전 확인 check_proxmox_version() { msg_info "Proxmox VE 버전 확인 중..." if [ ! -f /etc/pve/.version ] && ! command -v pveversion > /dev/null 2>&1; then msg_error "Proxmox VE가 설치되어 있지 않습니다." fi local version_info if command -v pveversion > /dev/null 2>&1; then version_info=$(pveversion 2>/dev/null | head -n1 | awk '{print $1}' | cut -d'/' -f2 2>/dev/null || echo "unknown") else version_info="unknown" fi if [[ "$version_info" =~ ^[0-9]+\.[0-9]+ ]]; then local pve_version=$(echo "$version_info" | cut -d'.' -f1) if [ "$pve_version" -lt 8 ]; then msg_error "지원되지 않는 Proxmox VE 버전입니다. 8.x 이상이 필요합니다." fi msg_ok "Proxmox VE $pve_version.x 버전 확인됨" else msg_warn "Proxmox VE 버전을 확인할 수 없습니다. 계속 진행합니다..." fi } # 루트 권한 확인 check_root() { if [ "$EUID" -ne 0 ]; then msg_error "이 스크립트는 root 권한으로 실행해야 합니다. sudo를 사용하세요." fi } # 입력 검증 함수 validate_rdp_server() { local server="$1" if [[ ! "$server" =~ ^[a-zA-Z0-9.-]+(:([0-9]{1,5}))?$ ]]; then return 1 fi if [[ "$server" =~ :([0-9]+)$ ]]; then local port="${BASH_REMATCH[1]}" if [ "$port" -gt 65535 ]; then return 1 fi fi return 0 } validate_username() { local username="$1" if [[ ! "$username" =~ ^[a-zA-Z0-9._-]+$ ]]; then return 1 fi return 0 } # 사용자 입력 받기 (환경변수 또는 대화형) get_user_input() { echo -e "${CYAN}RDP 연결 정보를 입력해주세요:${NC}" echo "" # 환경변수에서 먼저 확인 if [ -n "${RDP_SERVER:-}" ] && [ -n "${RDP_USER:-}" ] && [ -n "${RDP_PASSWORD:-}" ]; then RDP_USERNAME="$RDP_USER" LOCAL_USER="${LOCAL_USER:-rdpuser}" msg_info "환경변수에서 설정을 읽었습니다." echo " RDP 서버: $RDP_SERVER" echo " RDP 사용자: $RDP_USERNAME" echo " 로컬 사용자: $LOCAL_USER" return 0 fi # RDP 서버 주소 while true; do read -p "RDP 서버 주소 (예: example.com:3389): " RDP_SERVER if [ -z "$RDP_SERVER" ]; then msg_warn "RDP 서버 주소는 필수입니다." elif ! validate_rdp_server "$RDP_SERVER"; then msg_warn "잘못된 서버 주소 형식입니다." else break fi done # RDP 사용자명 while true; do read -p "RDP 사용자명: " RDP_USERNAME if [ -z "$RDP_USERNAME" ]; then msg_warn "RDP 사용자명은 필수입니다." elif ! validate_username "$RDP_USERNAME"; then msg_warn "잘못된 사용자명 형식입니다." else break fi done # 패스워드 (표준 입력으로 받기) while true; do echo -n "RDP 패스워드: " read -s RDP_PASSWORD echo "" if [ -z "$RDP_PASSWORD" ]; then msg_warn "RDP 패스워드는 필수입니다." continue fi echo -n "패스워드 확인: " read -s password_confirm echo "" if [ "$RDP_PASSWORD" != "$password_confirm" ]; then msg_warn "패스워드가 일치하지 않습니다." continue else break fi done # 로컬 사용자명 read -p "로컬 사용자명 [rdpuser]: " LOCAL_USER LOCAL_USER=${LOCAL_USER:-rdpuser} echo "" echo -e "${YELLOW}입력된 정보:${NC}" echo " RDP 서버: $RDP_SERVER" echo " RDP 사용자: $RDP_USERNAME" echo " 로컬 사용자: $LOCAL_USER" echo "" read -p "설정을 계속하시겠습니까? [y/N]: " confirm case $confirm in [yY]|[yY][eE][sS]) return 0 ;; *) msg_error "설정이 취소되었습니다." ;; esac } # 백업 생성 create_backup() { msg_info "기존 설정 백업 중..." local backup_dir="/root/proxmox-rdp-backup-$(date +%Y%m%d-%H%M%S)" mkdir -p "$backup_dir" if [ -f /etc/systemd/system/getty@tty1.service.d/override.conf ]; then cp -r /etc/systemd/system/getty@tty1.service.d "$backup_dir/" 2>/dev/null || true fi if id "$LOCAL_USER" &>/dev/null; then cp -r "/home/$LOCAL_USER" "$backup_dir/home-$LOCAL_USER" 2>/dev/null || true fi msg_ok "백업 완료: $backup_dir" } # 네트워크 연결 확인 check_network() { msg_info "네트워크 연결 확인 중..." if ! ping -c 1 -W 5 8.8.8.8 > /dev/null 2>&1; then msg_warn "인터넷 연결을 확인할 수 없습니다." fi # RDP 서버 연결 가능 여부 확인 local rdp_host=$(echo "$RDP_SERVER" | cut -d: -f1) local rdp_port=$(echo "$RDP_SERVER" | grep -oE ':[0-9]+' | tr -d ':' || echo "3389") msg_info "RDP 서버 연결 테스트 ($rdp_host:$rdp_port)..." if timeout 5 bash -c "echo > /dev/tcp/$rdp_host/$rdp_port" 2>/dev/null; then msg_ok "RDP 서버에 연결 가능합니다." else msg_warn "RDP 서버에 연결할 수 없습니다. 설정 후 연결을 확인하세요." fi } # 필수 패키지 설치 install_packages() { msg_info "필수 패키지 설치 중..." # 패키지 목록 local packages=( "freerdp3-x11" "openbox" "xinit" "xterm" "xorg" "dbus-x11" ) # APT 업데이트 apt-get update > /dev/null 2>&1 # 패키지 설치 for pkg in "${packages[@]}"; do if ! dpkg -l "$pkg" 2>/dev/null | grep -q "^ii"; then msg_info "설치 중: $pkg" DEBIAN_FRONTEND=noninteractive apt-get install -y "$pkg" > /dev/null 2>&1 fi done msg_ok "모든 필수 패키지가 설치되었습니다." } # 사용자 생성 create_user() { msg_info "사용자 설정 중..." if ! id "$LOCAL_USER" &>/dev/null; then msg_info "새 사용자 생성: $LOCAL_USER" useradd -m -s /bin/bash "$LOCAL_USER" echo "$LOCAL_USER:$RDP_PASSWORD" | chpasswd usermod -aG audio,video "$LOCAL_USER" msg_ok "사용자 생성 완료: $LOCAL_USER" else msg_info "기존 사용자 사용: $LOCAL_USER" usermod -aG audio,video "$LOCAL_USER" 2>/dev/null || true fi } # 자동 로그인 설정 (개선된 버전) configure_autologin() { msg_info "자동 로그인 설정 중..." # getty@tty1 서비스 중지 systemctl stop getty@tty1.service 2>/dev/null || true # getty@tty1 서비스 override 디렉토리 생성 mkdir -p /etc/systemd/system/getty@tty1.service.d/ # 자동 로그인 설정 (Type=idle 추가로 재시작 방지) cat > /etc/systemd/system/getty@tty1.service.d/override.conf << EOF [Service] ExecStart= ExecStart=-/sbin/agetty --autologin $LOCAL_USER --noclear %I \$TERM Type=idle RestartSec=0 Restart=no EOF # systemd 재로드 systemctl daemon-reload msg_ok "자동 로그인 설정 완료" } # X 세션 설정 configure_x_session() { msg_info "X 세션 설정 중..." # .bash_profile 설정 (tty1에서만 X 시작) cat > "/home/$LOCAL_USER/.bash_profile" << 'EOF' # Auto-start X on tty1 if [[ -z $DISPLAY ]] && [[ $(tty) = /dev/tty1 ]]; then exec startx 2>/dev/null fi EOF # .xinitrc 설정 (RDP 연결) cat > "/home/$LOCAL_USER/.xinitrc" << EOF #!/bin/sh # D-Bus 세션 버스 시작 if [ -z "\$DBUS_SESSION_BUS_ADDRESS" ]; then eval \$(dbus-launch --sh-syntax --exit-with-session) fi # Openbox 시작 openbox-session & # 화면 해상도 설정 xrandr --output Virtual-1 --mode 1920x1080 2>/dev/null || true # RDP 연결 (재시도 로직 포함) while true; do echo "Connecting to RDP server: $RDP_SERVER" >> /home/$LOCAL_USER/rdp.log xfreerdp3 /v:$RDP_SERVER /u:$RDP_USERNAME /p:'$RDP_PASSWORD' \\ /f /cert:ignore /network:auto /dynamic-resolution \\ /audio-mode:2 +clipboard +home-drive \\ /log-level:ERROR 2>> /home/$LOCAL_USER/rdp-error.log # 연결 실패 시 5초 대기 후 재시도 echo "RDP connection closed. Retrying in 5 seconds..." >> /home/$LOCAL_USER/rdp.log sleep 5 done EOF chmod +x "/home/$LOCAL_USER/.xinitrc" msg_ok "X 세션 설정 완료" } # Openbox 설정 configure_openbox() { msg_info "Openbox 설정 중..." mkdir -p "/home/$LOCAL_USER/.config/openbox" # Openbox 설정 (풀스크린, 장식 제거) cat > "/home/$LOCAL_USER/.config/openbox/rc.xml" << 'EOF' no yes yes 400 0 EOF # autostart 파일 생성 cat > "/home/$LOCAL_USER/.config/openbox/autostart" << 'EOF' # 화면 보호기 비활성화 xset s off xset -dpms xset s noblank EOF chmod +x "/home/$LOCAL_USER/.config/openbox/autostart" msg_ok "Openbox 설정 완료" } # 권한 설정 set_permissions() { msg_info "권한 설정 중..." chown -R "$LOCAL_USER:$LOCAL_USER" "/home/$LOCAL_USER" chmod 755 "/home/$LOCAL_USER" msg_ok "권한 설정 완료" } # 서비스 활성화 enable_services() { msg_info "서비스 활성화 중..." # getty@tty1 서비스 활성화 (마스킹 해제) systemctl unmask getty@tty1.service 2>/dev/null || true systemctl enable getty@tty1.service # 기본 타겟 확인 if [ "$(systemctl get-default)" != "graphical.target" ]; then systemctl set-default graphical.target fi msg_ok "서비스 활성화 완료" } # 설정 검증 verify_configuration() { msg_info "설정 검증 중..." local errors=0 # 설정 파일 존재 확인 local required_files=( "/etc/systemd/system/getty@tty1.service.d/override.conf" "/home/$LOCAL_USER/.bash_profile" "/home/$LOCAL_USER/.xinitrc" "/home/$LOCAL_USER/.config/openbox/rc.xml" ) for file in "${required_files[@]}"; do if [ ! -f "$file" ]; then msg_warn "설정 파일이 없습니다: $file" ((errors++)) fi done # 서비스 상태 확인 if ! systemctl is-enabled getty@tty1.service > /dev/null 2>&1; then msg_warn "getty@tty1 서비스가 활성화되지 않았습니다." ((errors++)) fi if [ $errors -eq 0 ]; then msg_ok "모든 설정이 정상적으로 완료되었습니다." return 0 else msg_warn "일부 설정에 문제가 있을 수 있습니다." return 1 fi } # 완료 메시지 print_completion() { echo "" echo -e "${GREEN}═══════════════════════════════════════════════════════════════════${NC}" echo -e "${GREEN} 설정이 완료되었습니다!${NC}" echo -e "${GREEN}═══════════════════════════════════════════════════════════════════${NC}" echo "" echo "설정 정보:" echo " - RDP 서버: $RDP_SERVER" echo " - RDP 사용자: $RDP_USERNAME" echo " - 로컬 사용자: $LOCAL_USER" echo "" echo "다음 명령으로 즉시 시작할 수 있습니다:" echo -e " ${CYAN}systemctl restart getty@tty1.service${NC}" echo "" echo "또는 시스템을 재부팅하면 자동으로 RDP에 연결됩니다:" echo -e " ${CYAN}reboot${NC}" echo "" echo "문제가 발생하면 다음 로그를 확인하세요:" echo " - /home/$LOCAL_USER/rdp.log" echo " - /home/$LOCAL_USER/rdp-error.log" echo " - journalctl -u getty@tty1.service -f" echo "" echo "다른 터미널로 접속하려면 Ctrl+Alt+F2를 사용하세요." echo "" } # 메인 함수 main() { print_header check_root check_proxmox_version get_user_input check_network create_backup install_packages create_user configure_autologin configure_x_session configure_openbox set_permissions enable_services verify_configuration print_completion } # 환경변수 지원 (비대화형 설치) # 사용법: RDP_SERVER="server:port" RDP_USER="username" RDP_PASSWORD="password" ./script.sh if [ -n "${RDP_SERVER:-}" ] && [ -n "${RDP_USER:-}" ] && [ -n "${RDP_PASSWORD:-}" ]; then export NON_INTERACTIVE=1 fi # 스크립트 실행 main "$@"