# Setup SSH key — Administrator@10.100.1.40 (AD LDAP, Windows Server 2012 R2) # SSOT: server-audit/docs/guide/ssh-access.md # Usage: .\server-audit\scripts\setup-ssh-ad-ldap.ps1 # Sekali jalan: password Administrator 1–2x (scp + ssh), lalu key-only. # Wajib PowerShell (bukan CMD). $ErrorActionPreference = 'Stop' $hostIp = '10.100.1.40' $SshUser = 'Administrator' $key = Join-Path $env:USERPROFILE '.ssh\id_rsa_geonet_laptop' $pub = "$key.pub" $aliasName = 'ad-ldap' if (-not (Test-Path $pub)) { Write-Host "Generating key $key ..." ssh-keygen -t rsa -b 4096 -f $key -C 'geonet-laptop' -N '""' } Write-Host "Fixing private key ACL..." icacls $key /inheritance:r 2>$null | Out-Null icacls $key /grant:r "${env:USERNAME}:(R)" 2>$null | Out-Null # Pub key LF-only $tempPub = Join-Path $env:TEMP 'geonet_ad_ldap_key.pub' $keyLine = (Get-Content $pub -Raw).Trim() -replace "`r", "" [System.IO.File]::WriteAllText($tempPub, $keyLine + "`n") # Remote installer (Windows OpenSSH — Administrator) # FORCE overwrite UTF-8 no BOM; tulis ke administrators_authorized_keys + profile fallback $remotePs = @' $ErrorActionPreference = "Stop" $authFile = "C:\ProgramData\ssh\administrators_authorized_keys" $userAuthDir = "C:\Users\Administrator\.ssh" $userAuthFile = Join-Path $userAuthDir "authorized_keys" $sshDir = "C:\ProgramData\ssh" $sshdConfig = "C:\ProgramData\ssh\sshd_config" $pubPath = Join-Path $env:TEMP "geonet_ad_ldap_key.pub" if (-not (Test-Path $pubPath)) { throw "Missing $pubPath — scp failed?" } $keyLine = [System.IO.File]::ReadAllText($pubPath).Trim() -replace "`r","" -replace "`0","" if ($keyLine -notmatch "^ssh-") { throw "Invalid pubkey content" } if (-not (Test-Path $sshDir)) { New-Item -ItemType Directory -Path $sshDir | Out-Null } $utf8 = New-Object System.Text.UTF8Encoding $false # FORCE overwrite — jangan merge file korup UTF-16 lama [System.IO.File]::WriteAllText($authFile, $keyLine + "`n", $utf8) Write-Host "FORCE wrote administrators_authorized_keys (UTF-8 no BOM)" function Fix-AclAdminOnly([string]$path) { $acl = Get-Acl $path $acl.SetAccessRuleProtection($true, $false) @($acl.Access) | ForEach-Object { [void]$acl.RemoveAccessRule($_) } $acl.AddAccessRule((New-Object System.Security.AccessControl.FileSystemAccessRule("SYSTEM","FullControl","Allow"))) $acl.AddAccessRule((New-Object System.Security.AccessControl.FileSystemAccessRule("Administrators","FullControl","Allow"))) Set-Acl -Path $path -AclObject $acl } Fix-AclAdminOnly $authFile Write-Host "ACL ProgramData OK" # Fallback: profile authorized_keys (jika Match Group di-comment) if (-not (Test-Path $userAuthDir)) { New-Item -ItemType Directory -Path $userAuthDir | Out-Null } [System.IO.File]::WriteAllText($userAuthFile, $keyLine + "`n", $utf8) $uacl = Get-Acl $userAuthFile $uacl.SetAccessRuleProtection($true, $false) @($uacl.Access) | ForEach-Object { [void]$uacl.RemoveAccessRule($_) } $uacl.AddAccessRule((New-Object System.Security.AccessControl.FileSystemAccessRule("SYSTEM","FullControl","Allow"))) $uacl.AddAccessRule((New-Object System.Security.AccessControl.FileSystemAccessRule("Administrators","FullControl","Allow"))) $uacl.AddAccessRule((New-Object System.Security.AccessControl.FileSystemAccessRule("Administrator","FullControl","Allow"))) Set-Acl -Path $userAuthFile -AclObject $uacl Write-Host "Wrote profile authorized_keys fallback" if (Test-Path $sshdConfig) { $cfg = Get-Content $sshdConfig -Raw # Uncomment Match Group administrators if commented $cfg = $cfg -replace "(?m)^#\s*Match Group administrators","Match Group administrators" $cfg = $cfg -replace "(?m)^#\s*AuthorizedKeysFile\s+__PROGRAMDATA__/ssh/administrators_authorized_keys"," AuthorizedKeysFile __PROGRAMDATA__/ssh/administrators_authorized_keys" if ($cfg -match "(?m)^#?\s*PubkeyAuthentication\s+no") { $cfg = $cfg -replace "(?m)^#?\s*PubkeyAuthentication\s+no","PubkeyAuthentication yes" Write-Host "Enabled PubkeyAuthentication" } if ($cfg -notmatch "(?m)^\s*PubkeyAuthentication\s+yes") { $cfg = "PubkeyAuthentication yes`r`n" + $cfg Write-Host "Inserted PubkeyAuthentication yes" } # Allow ssh-rsa for older Win32-OpenSSH / mixed client if ($cfg -notmatch "(?m)^\s*PubkeyAccepted") { $cfg = $cfg.TrimEnd() + "`r`nPubkeyAcceptedKeyTypes ssh-ed25519,ecdsa-sha2-nistp256,ecdsa-sha2-nistp384,ecdsa-sha2-nistp521,rsa-sha2-512,rsa-sha2-256,ssh-rsa`r`n" Write-Host "Added PubkeyAcceptedKeyTypes (+ssh-rsa)" } if ($cfg -notmatch "(?m)^\s*Match Group administrators") { $cfg = $cfg.TrimEnd() + "`r`n`r`nMatch Group administrators`r`n AuthorizedKeysFile __PROGRAMDATA__/ssh/administrators_authorized_keys`r`n" Write-Host "Added Match Group administrators block" } [System.IO.File]::WriteAllText($sshdConfig, $cfg, $utf8) Write-Host "sshd_config updated" } Write-Host "--- diagnostics ---" Write-Host ("auth bytes: " + (Get-Item $authFile).Length) $bytes = [System.IO.File]::ReadAllBytes($authFile) Write-Host ("first3: " + $bytes[0] + "," + $bytes[1] + "," + $bytes[2] + " (expect 115,115,104 = ssh)") Select-String -Path $sshdConfig -Pattern "Match Group|AuthorizedKeysFile|PubkeyAuth|PubkeyAccepted" | ForEach-Object { $_.Line } Get-Service sshd | Format-List Status, Name Restart-Service sshd -Force Start-Sleep -Seconds 2 Write-Host "KEY_INSTALLED_OK" '@ $tempRemote = Join-Path $env:TEMP 'setup-ad-ldap-authorized-keys.ps1' # Remote script must be CRLF-friendly for Windows PowerShell; keep as-is [System.IO.File]::WriteAllText($tempRemote, $remotePs) $commonOpts = @( '-o', 'PreferredAuthentications=password', '-o', 'PubkeyAuthentication=no', '-o', 'StrictHostKeyChecking=accept-new', '-o', 'MACs=hmac-sha1' ) # scp: -P (port). ssh: -p (port) + -m hmac-sha1 $scpOpts = $commonOpts + @('-P', '22') $sshOpts = $commonOpts + @('-m', 'hmac-sha1', '-p', '22') Write-Host "Upload pub + installer ke ${SshUser}@${hostIp} (password)..." scp @scpOpts $tempPub "${SshUser}@${hostIp}:C:/Users/Administrator/AppData/Local/Temp/geonet_ad_ldap_key.pub" scp @scpOpts $tempRemote "${SshUser}@${hostIp}:C:/Users/Administrator/AppData/Local/Temp/setup-ad-ldap-authorized-keys.ps1" Write-Host "Run installer on server..." ssh @sshOpts "${SshUser}@${hostIp}" "powershell -NoProfile -ExecutionPolicy Bypass -File C:\Users\Administrator\AppData\Local\Temp\setup-ad-ldap-authorized-keys.ps1" Remove-Item $tempPub, $tempRemote -Force -ErrorAction SilentlyContinue # Ensure local ssh config alias $configPath = Join-Path $env:USERPROFILE '.ssh\config' $block = @" Host $aliasName HostName $hostIp User $SshUser Port 22 IdentityFile ~/.ssh/id_rsa_geonet_laptop IdentitiesOnly yes MACs hmac-sha1 PubkeyAcceptedAlgorithms +ssh-rsa "@ if (-not (Test-Path $configPath)) { Set-Content -Path $configPath -Value $block.TrimStart() -Encoding UTF8 Write-Host "Created ~/.ssh/config with Host $aliasName" } elseif (-not (Select-String -Path $configPath -Pattern "Host\s+$aliasName" -Quiet)) { Add-Content -Path $configPath -Value $block Write-Host "Appended Host $aliasName to ~/.ssh/config" } else { Write-Host "Host $aliasName already in ~/.ssh/config" } Write-Host "" Write-Host "Test key-only..." # Win2012 OpenSSH sering butuh ssh-rsa eksplisit dari client baru ssh -i $key -o BatchMode=yes -o IdentitiesOnly=yes -o PreferredAuthentications=publickey -o PubkeyAcceptedAlgorithms=+ssh-rsa -m hmac-sha1 "${SshUser}@${hostIp}" -p 22 "echo KEY_AUTH_OK & hostname" if ($LASTEXITCODE -ne 0) { Write-Host "" Write-Host "Masih gagal. Cek di server (password sekali):" Write-Host " ssh -m hmac-sha1 ${SshUser}@${hostIp} -p 22 `"powershell -Command Get-Content C:\ProgramData\ssh\administrators_authorized_keys`"" Write-Host " Pastikan OpenSSH Server terpasang & sshd running." Write-Host " Win2012 R2: Match Group administrators -> administrators_authorized_keys" exit 1 } Write-Host "Success. Login: ssh $aliasName"