Windows SSH 원격 로그인

Windows에서 SSH 원격 액세스를 활성화하려면 일반적으로 Windows의 OpenSSH 기능을 사용해야 합니다. 다음은 자세한 절차 설명입니다:

OpenSSH 확인 및 설치

  1. OpenSSH 설치 여부 확인:

    • “설정” > “앱” > “앱 및 기능” > “선택 기능 관리"로 이동합니다.
    • 설치된 목록에서 “OpenSSH 서버"를 찾습니다. 존재한다면 이미 설치되어 있다는 의미입니다.
  2. OpenSSH 설치:

    • OpenSSH 서버를 찾을 수 없다면 “선택 기능 관리” 페이지에서 “기능 추가"를 클릭한 후 목록에서 “OpenSSH 서버"를 찾아 “설치"를 클릭합니다.

OpenSSH 서비스 시작 및 설정

  1. OpenSSH 서비스 시작:

    • 설치完成后, 명령 프롬프트를 관리자 권한으로 실행합니다.
    • net start sshd 명령으로 OpenSSH 서비스를 시작합니다. 시스템 부팅 시 자동으로 시작되도록 하려면 sc config sshd start= auto 명령을 입력합니다.
  2. 방화벽 설정:

    • Windows 방화벽이 SSH 연결을 허용하는지 확인합니다. “제어판” > “시스템 및 보안” > “Windows Defender 방화벽” > “고급 설정"으로 이동하여 TCP 포트 22 연결을 허용하는 새로운 수신 규칙을 만듭니다.

IP 주소 확인 및 연결 테스트

  1. IP 주소 확인:

    • SSH 서비스가 활성화된 Windows PC에 다른 머신에서 연결하려면 IP 주소를 알아야 합니다. 명령 프롬프트에서 ipconfig 명령을 사용하여 로컬 머신의 IP 주소를 확인할 수 있습니다.
  2. 연결 테스트:

    • 다른 컴퓨터나 모바일 장치에서 SSH 클라이언트(PuTTY, Termius 등)를 사용하여 Windows PC에 연결을 시도합니다. 형식은 ssh username@your_ip_address입니다. 여기서 username은 로그인하려는 Windows 계정 이름이며, your_ip_address는 이전에 확인한 IP 주소입니다.

설정 수정

비밀번호 로그인을 피하는 것이 좋습니다. 이는 절대 금기입니다. 반드시 공개키를 사용하여 로그인해야 하며, 비밀번호 로그인을 비활성화하고 공개키 로그인을 허용하도록 설정을 수정해야 합니다.

이 구성 파일은 수정하기에 적합하지 않으며 특별한 권한이 필요하며 디렉토리와 파일의 권한이 특정 값으로 설정되어 있어야 합니다. 여기서는 스크립트를 사용한 수정을 권장합니다.

# 관리자 권한 확인
$elevated = [bool]([System.Security.Principal.WindowsPrincipal]::new(
    [System.Security.Principal.WindowsIdentity]::GetCurrent()
).IsInRole([System.Security.Principal.WindowsBuiltInRole]::Administrator))

if (-not $elevated) {
    Write-Error "이 스크립트를 관리자 권한으로 실행하세요"
    exit 1
}

# 1. OpenSSH 서버 설치 확인
Write-Host "OpenSSH 서버 설치 상태를 확인 중입니다..."
$capability = Get-WindowsCapability -Online -Name OpenSSH.Server~~~~0.0.1.0

if ($capability.State -ne 'Installed') {
    Write-Host "OpenSSH 서버 설치 중..."
    Add-WindowsCapability -Online -Name OpenSSH.Server~~~~0.0.1.0 | Out-Null
}

# 2. SSH 서비스 시작 및 자동 시작 설정
Write-Host "SSH 서비스를 구성 중입니다..."
$service = Get-Service sshd -ErrorAction SilentlyContinue
if (-not $service) {
    Write-Error "OpenSSH 서비스 설치 실패"
    exit 1
}

if ($service.Status -ne 'Running') {
    Start-Service sshd
}
Set-Service sshd -StartupType Automatic

# 3. 구성 파일 수정
$configPath = "C:\ProgramData\ssh\sshd_config"
if (Test-Path $configPath) {
    Write-Host "원본 구성 파일을 백업 중입니다..."
    Copy-Item $configPath "$configPath.bak" -Force
} else {
    Write-Error "구성 파일을 찾을 수 없습니다: $configPath"
    exit 1
}

Write-Host "SSH 구성 수정 중..."
$config = Get-Content -Path $configPath -Raw

# 공개키 인증 활성화 및 비밀번호 로그인 비활성화
$config = $config -replace '^#?PubkeyAuthentication .*$','PubkeyAuthentication yes' `
                  -replace '^#?PasswordAuthentication .*$','PasswordAuthentication no'

# 필요한 구성 포함 확인
if ($config -notmatch 'PubkeyAuthentication') {
    $config += "`nPubkeyAuthentication yes"
}
if ($config -notmatch 'PasswordAuthentication') {
    $config += "`nPasswordAuthentication no"
}

# 구성 파일에 다시 쓰기
$config | Set-Content -Path $configPath -Encoding UTF8

authorized_keys 파일 권한 확인

# 일반 사용자
$authKeys = "$env:USERPROFILE\.ssh\authorized_keys"
icacls $authKeys /inheritance:r /grant "$($env:USERNAME):F" /grant "SYSTEM:F"
icacls "$env:USERPROFILE\.ssh" /inheritance:r /grant "$($env:USERNAME):F" /grant "SYSTEM:F"

# 관리자
$adminAuth = "C:\ProgramData\ssh\administrators_authorized_keys"
icacls $adminAuth /inheritance:r /grant "Administrators:F" /grant "SYSTEM:F"

방화벽 규칙 설정

# SSH 포트 허용
New-NetFirewallRule -DisplayName "OpenSSH Server (sshd)" -Direction Inbound -Protocol TCP -Action Allow -LocalPort 22

공개키 추가

일반 사용자

# 일반 사용자
$userProfile = $env:USERPROFILE
$sshDir = Join-Path $userProfile ".ssh"
$authorizedKeysPath = Join-Path $sshDir "authorized_keys"
$PublicKeyPath = "D:\public_keys\id_rsa.pub"

# .ssh 디렉토리 생성
if (-not (Test-Path $sshDir)) {
    New-Item -ItemType Directory -Path $sshDir | Out-Null
}

# .ssh 디렉토리 권한 설정
$currentUser = "$env:USERDOMAIN\$env:USERNAME"
$acl = Get-Acl $sshDir
$rule = New-Object System.Security.AccessControl.FileSystemAccessRule(
    $currentUser, "FullControl", "ContainerInherit,ObjectInherit", "None", "Allow"
)
$acl.AddAccessRule($rule)
Set-Acl $sshDir $acl

# 공개키 추가
if (Test-Path $PublicKeyPath) {
    $pubKey = Get-Content -Path $PublicKeyPath -Raw
    if ($pubKey) {
        # 공개키 끝에 개행 문자가 있는지 확인
        if (-not $pubKey.EndsWith("`n")) {
            $pubKey += "`n"
        }

        # 공개키 추가
        Add-Content -Path $authorizedKeysPath -Value $pubKey -Encoding UTF8

        # 파일 권한 설정
        $acl = Get-Acl $authorizedKeysPath
        $acl.SetSecurityDescriptorRule(
            (New-Object System.Security.AccessControl.FileSystemAccessRule(
                $currentUser, "FullControl", "None", "None", "Allow"
            ))
        )
        Set-Acl $authorizedKeysPath $acl
    }
} else {
    Write-Error "공개키 파일이 존재하지 않습니다: $PublicKeyPath"
    exit 1
}

# SSH 서비스 재시작
Write-Host "SSH 서비스를 재시작 중입니다..."
Restart-Service sshd

관리자 사용자

# 관리자
$adminSshDir = "C:\ProgramData\ssh"
$adminAuthKeysPath = Join-Path $adminSshDir "administrators_authorized_keys"
$adminPublicKeyPath = "D:\public_keys\id_rsa.pub"

# 관리자 SSH 디렉토리 생성
if (-not (Test-Path $adminSshDir)) {
    New-Item -ItemType Directory -Path $adminSshDir | Out-Null
}

# 관리자 SSH 디렉토리 권한 설정
$adminAcl = Get-Acl $adminSshDir
$adminRule = New-Object System.Security.AccessControl.FileSystemAccessRule(
    "Administrators", "FullControl", "ContainerInherit,ObjectInherit", "None", "Allow"
)
$adminAcl.AddAccessRule($adminRule)
Set-Acl $adminSshDir $adminAcl

# 관리자 공개키 추가
if (Test-Path $adminPublicKeyPath) {
    $adminPubKey = Get-Content -Path $adminPublicKeyPath -Raw
    if ($adminPubKey) {
        # 공개키 끝에 개행 문자가 있는지 확인
        if (-not $adminPubKey.EndsWith("`n")) {
            $adminPubKey += "`n"
        }

        # 공개키 추가
        Add-Content -Path $adminAuthKeysPath -Value $adminPubKey -Encoding UTF8

        # 파일 권한 설정
        $adminAcl = Get-Acl $adminAuthKeysPath
        $adminAcl.SetSecurityDescriptorRule(
            (New-Object System.Security.AccessControl.FileSystemAccessRule(
                "Administrators", "FullControl", "None", "None", "Allow"
            ))
        )
        Set-Acl $adminAuthKeysPath $adminAcl
    }
} else {
    Write-Error "관리자 공개키 파일이 존재하지 않습니다: $adminPublicKeyPath"
    exit 1
}

# SSH 서비스 재시작
Write-Host "SSH 서비스를 재시작 중입니다..."
Restart-Service sshd