At the moment, Windows 10’s implementation of the OpenSSH client does not have the ssh-copy-id
command available. However, a PowerShell one-line command can mimic the ssh-copy-id
command and allow you to copy an SSH public key generated by the ssh-keygen command to a remote Linux device for passwordless login.
Generate an SSH Key
Note: If you have already generated an SSH keypair that you would like to use, skip this section and proceed to the Copy SSH Key to Remote Linux Device section.
First, open a new PowerShell window (not a Command Prompt window!) and generate a new SSH keypair with the ssh-keygen
command. By default, the public and private keys will be placed in the %USERPROFILE%/.ssh/
directory. The public key file we are interested in is named id_rsa.pub
.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
PS C:\Users\Christopher> ssh-keygen Generating public/private rsa key pair. Enter file in which to save the key (C:\Users\Christopher/.ssh/id_rsa): Created directory 'C:\Users\Christopher/.ssh'. Enter passphrase (empty for no passphrase): Enter same passphrase again: Your identification has been saved in C:\Users\Christopher/.ssh/id_rsa. Your public key has been saved in C:\Users\Christopher/.ssh/id_rsa.pub. The key fingerprint is: SHA256:/mjkrJOQbRzCAwlSPYVBNcuxntm/Ms5/MMC15dCRrMc christopher@Christopher-Win10-VM-01 The key's randomart image is: +---[RSA 2048]----+ |oo.+o== o.o | |. o +. = o = | | o .+. . B | | +..+o o E | | *+.S. . | | o +...o | | o =. .o | | o.*o .. | | .=+++. | +----[SHA256]-----+ PS C:\Users\Christopher> |
Copy SSH Key to Remote Linux Device
Next, we use the below PowerShell one-line command to copy the contents of the id_rsa.pub
public key to a remote Linux device. Replace the {IP-ADDRESS-OR-FQDN}
with the IP address or FQDN (Fully Qualified Domain Name) of the remote Linux device you would like to copy the public key to.
1 |
type $env:USERPROFILE\.ssh\id_rsa.pub | ssh {IP-ADDRESS-OR-FQDN} "cat >> .ssh/authorized_keys" |
An example of this command is shown below. In this example, I am copying the contents of the id_rsa.pub
public key to a remote Linux device at IP address 192.168.30.31.
1 2 3 4 5 6 7 |
PS C:\Users\Christopher> type $env:USERPROFILE\.ssh\id_rsa.pub | ssh 192.168.30.31 "cat >> .ssh/authorized_keys" The authenticity of host '192.168.30.31 (192.168.30.31)' can't be established. ECDSA key fingerprint is SHA256:mTD0/WNCVZ/p/PFSkNDmLJtzIGb5eD7qj6erOQkomjM. Are you sure you want to continue connecting (yes/no)? yes Warning: Permanently added '192.168.30.31' (ECDSA) to the list of known hosts. [email protected]'s password: PS C:\Users\Christopher> |
Test Passwordless SSH Connectivity to Remote Linux Device
Finally, verify that you can SSH to the remote Linux device with the ssh
command. An example to a remote Linux device at IP address 192.168.30.31 is shown below. Note how a password did not need to be entered in order for us to establish SSH connectivity to the remote Linux device.
1 2 3 4 |
PS C:\Users\Christopher> ssh 192.168.30.31 Last login: Sat May 23 12:44:51 2020 from 192.168.10.139 [christopher@linux ~]$ who christopher pts/0 2020-05-24 19:35 (192.168.10.113) |
References
The instructions for this blog post were heavily inspired by Scott Hanselman’s blog post on the subject.
This post is licensed under CC BY-NC-SA by the author.
Windows OpenSSH equivalent of ssh-copy-id
ssh-copy-id is a awesome command to copy a ssh-key to a remote server.
It copies the public key of your ssh key to the remote server, making you able to login without using a password.
On linux normally you would use the ssh-copy-id command, but this command doesn’t work for Windows. Therefore you should use the following command for windows.
ONLY copies the public key to the remote host
1 2 # Copy id_rsa.pub to the remote aiuthorized server get-content $env:USERPROFILE\.ssh\id_rsa.pub | ssh USER@IP-ADDRESS "cat >> .ssh/authorized_keys"
And to copy from linux to linux machine use this command:
It only copies the public key to the remote host
1ssh-copy-id -i ~/.ssh/id_rsa USER@IP-ADDRESS
This post is licensed under CC BY 4.0 by the author.
The `ssh-copy-id` command is used to install your SSH public key on a remote server, allowing for passwordless authentication when connecting via SSH.
ssh-copy-id user@remote_host
Understanding SSH Key Authentication
What are SSH Keys?
SSH keys are a pair of cryptographic keys used to authenticate a secure connection between a client and a server. Each pair consists of a public key and a private key. The public key is stored on the server in a special file, while the private key remains with the client. This method enhances security by allowing users to connect to remote servers without needing to transmit passwords over the network.
Benefits of Key-Based Authentication
Using SSH keys provides numerous advantages:
- Enhanced Security: The encryption provided by SSH keys makes unauthorized access considerably more difficult compared to traditional password authentication.
- Convenience: Once set up, key-based authentication allows for seamless logins without entering passwords. This is particularly beneficial for automated scripts and routines.
- Prevention of Brute Force Attacks: Since SSH keys are virtually impossible to guess or crack, they effectively fend off brute force attacks aimed at password-protected logins.
Mastering PowerShell SSH Commands: A Quick Guide
Setting Up PowerShell for SSH
Installing OpenSSH
To utilize the `powershell ssh-copy-id` functionality, you need to ensure that OpenSSH is installed on your Windows system. You can do this through the Settings app:
- Open Settings > Apps > Optional features.
- Look for OpenSSH Client. If it’s not listed, click on Add a feature, search for OpenSSH Client, and install it.
After installation, verify it by executing the following command in PowerShell:
ssh -V
This command will display the version of SSH installed, confirming a successful installation.
Generating SSH Keys
The first step in setting up key-based authentication is to generate SSH keys. You can do this by using the `ssh-keygen` command in PowerShell. Follow these steps:
- Open PowerShell.
- Enter the following command to generate a new SSH key pair:
ssh-keygen -t rsa -b 4096 -C "your_email@example.com"
This command will generate two files in the default location (`%USERPROFILE%\.ssh\`):
- `id_rsa`: Your private key (keep this secure and do not share it).
- `id_rsa.pub`: Your public key (this is what you’ll send to servers for authentication).
Where to Find SSH Keys
You can locate your SSH keys in the `.ssh` directory within your user profile:
C:\Users\YourUsername\.ssh\
Effortless File Transfers: PowerShell Copy-Item -Exclude
Introduction to `ssh-copy-id`
What is `ssh-copy-id`?
The `ssh-copy-id` command is a convenient utility to install your public key onto a remote server’s authorized keys. By doing so, you can establish a password-less SSH connection to that server.
Limitations of `ssh-copy-id` in PowerShell
While `ssh-copy-id` is a native command in Unix-like systems, PowerShell does not support it directly. However, you can simulate its functionality through a manual approach in PowerShell, which we’ll explore next.
Mastering PowerShell SSH -i for Seamless Connections
Using `ssh-copy-id` in PowerShell
Simulating `ssh-copy-id` Functionality
To implement the functionality of `ssh-copy-id` in PowerShell, you need to manually copy the public key to the remote server. The command below demonstrates this process:
cat ~/.ssh/id_rsa.pub | ssh user@remote_host "mkdir -p ~/.ssh && cat >> ~/.ssh/authorized_keys"
Explanation of Each Command in the Snippet:
- `cat ~/.ssh/id_rsa.pub`: Reads your public key file.
- `|`: Pipes the output of the previous command into the next one.
- `ssh user@remote_host`: Initiates an SSH session to the specified user and host.
- `»mkdir -p ~/.ssh»`: Creates the `.ssh` directory on the remote server (if it does not exist).
- `cat >> ~/.ssh/authorized_keys`: Appends the public key to the `authorized_keys` file, allowing for key-based authentication.
Verifying Key-Based Authentication
After copying the SSH key, test the connection to ensure that it works without a password prompt. You can do this by executing:
ssh user@remote_host
If everything is set up correctly, you should be logged in without requiring a password. If you encounter issues, check for common mistakes such as incorrect permissions or misconfiguration.
PowerShell Copy-Item: Create Folder Made Easy
Practical Examples
Example Scenario: Copying Keys to a Remote Server
Assuming you have a remote server (`remote_host`) and a user (`user`), use the provided command to transfer your key:
cat ~/.ssh/id_rsa.pub | ssh user@remote_host "mkdir -p ~/.ssh && cat >> ~/.ssh/authorized_keys"
Once executed, you’ll be prompted for your password for the last time. Upon successful connection, verifying key-based authentication should grant you password-less access.
Example Scenario: Automating Key Copy with PowerShell Scripts
To further streamline the process, you can automate the key copying with a PowerShell script. Below is a sample script:
# Save as Copy-SSHKeys.ps1
param (
[string]$User,
[string]$Host
)
$keyPath = "$HOME\.ssh\id_rsa.pub"
if (Test-Path $keyPath) {
$key = Get-Content $keyPath
ssh $User@$Host "mkdir -p ~/.ssh && echo $key >> ~/.ssh/authorized_keys"
} else {
Write-Error "SSH public key not found!"
}
Explanation of the Script:
- `param`: Accepts parameters for the username and host to which the key is being copied.
- `Test-Path`: Checks if the public key file exists.
- `Get-Content`: Reads the public key from the specified file.
- `ssh $User@$Host`: Executes the SSH command to append the key.
Mastering PowerShell Split: A Quick Guide to Strings
Troubleshooting Common Issues
Common Errors with SSH Key Authentication
While setting up SSH key authentication, you may encounter various errors, such as:
- Permission denied errors: Often occurs due to incorrect permissions on the `.ssh` directory or the `authorized_keys` file.
- SSH key not recognized: This can happen if the public key isn’t properly added or has incorrect formatting.
Fixing Permissions of `.ssh` Directory
To ensure proper permissions, you can set the correct permissions directly on the remote server using:
ssh user@remote_host "chmod 700 ~/.ssh; chmod 600 ~/.ssh/authorized_keys"
This command makes the `.ssh` directory accessible only to the user and secures the `authorized_keys` file.
Mastering PowerShell Select-Object in a Nutshell
Tips and Best Practices
Protecting Your SSH Keys
Managing your SSH keys securely is crucial. Consider implementing the following practices:
- Use a Passphrase: Protect your private key with a strong passphrase to add an extra security layer.
- Backup Your Keys: Always keep a secure backup of your SSH keys in a safe location.
Rotating SSH Keys Regularly
Regularly updating and rotating your SSH keys is essential to maintaining a robust security posture. Implement this practice as part of your routine system maintenance.
Using SSH Config File for Convenience
You can simplify your SSH connection methods by using an SSH config file (`~/.ssh/config`). Here’s an example entry:
Host myserver
HostName remote_host
User user
IdentityFile ~/.ssh/id_rsa
This configuration allows you to connect using a shorthand command (`ssh myserver`), improving efficiency.
Mastering PowerShell Get-Credential: A Quick Guide
Conclusion
In this article, we explored how to implement `powershell ssh-copy-id` functionality. By leveraging the PowerShell commands and understanding SSH key authentication mechanisms, you can enhance your network operations and secure your connections effectively. Start applying these principles today for a more secure SSH experience!
PowerShell Shutdown: Quick Commands for Instant Reboot
Resources
For further reading, consider checking the official PowerShell and OpenSSH documentation. Engaging with community forums can also provide additional support and insights as you deepen your understanding and skills in using PowerShell and SSH.
<#
.SYNOPSIS
Copy ssh public key to remote account
.DESCRIPTION
Copies public key to target machine
Default is .ssh\id_rsa.pub
See copyright notice at end of script
If this script won’t run, then run this command once
Set-ExecutionPolicy RemoteSigned CurrentUser
refer to https://go.microsoft.com/fwlink/?LinkID=135170
#>
[CmdletBinding()]
param(
[Parameter(Position = 0, Mandatory = $True)]
[string]$Target <# SSH compatible Name of Target machine #>,
[Parameter( Position = 1, Mandatory = $False)]
[string]$IdentityFile = «id_rsa« <#Identity File, must be in .ssh directory #>,
[switch]$Save = $False <# Save script used #>
)
$IdentityPath = Join-Path (Join-Path $env:USERPROFILE .ssh) $IdentityFile
if (-not (Test-Path —Path $IdentityPath —PathType Leaf)) {
Write-Host Key file $IdentityFile does not exist, use ssh—keygen
exit(1)
}
$PUBLIC_KEY = get-content «${IdentityPath}.pub«
$SCRIPT = @»
#!/bin/bash
#
PUBLIC_KEY=»$PUBLIC_KEY«
umask 0077
[ -d ~/.ssh ]||mkdir -p ~/.ssh
[ -f ~/.ssh/authorized_keys ]|| touch ~/.ssh/authorized_keys
grep —quiet —fixed-strings «`$PUBLIC_KEY» ~/.ssh/authorized_keys
if [ `$? -ne 0 ]
then
echo «`$PUBLIC_KEY» >> ~/.ssh/authorized_keys
else
echo Key already exists, no update performed.
fi
«@
if ($Save) {
Set-Content .\ssh—copy-id.sh —Value $SCRIPT
}
# The sed step is to convert crlf to lf for bash — it’s fussy
$SCRIPT | ssh $Target —o StrictHostKeyChecking=no «sed ‘s/\r//’ | bash«
if ($?) {
Write-Output «You should be able to login to $Target without a password using $IdentityFile key«
}
else {
Write-Host «Could not connect to $Target — Terminating« —ForegroundColor Red
}
<#
This is free and unencumbered software released into the public domain.
Anyone is free to copy, modify, publish, use, compile, sell, or
distribute this software, either in source code form or as a compiled
binary, for any purpose, commercial or non-commercial, and by any
means.
In jurisdictions that recognize copyright laws, the author or authors
of this software dedicate any and all copyright interest in the
software to the public domain. We make this dedication for the benefit
of the public at large and to the detriment of our heirs and
successors. We intend this dedication to be an overt act of
relinquishment in perpetuity of all present and future rights to this
software under copyright law.
THE SOFTWARE IS PROVIDED «AS IS», WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.
For more information, please refer to <https://unlicense.org>
#>
Авторизация по RSA-ключам уже давно стала стандартом — стандартом хорошим, удобным, логичным и правильным. Это надежно и безопасно. Однако, в то время пока юниксы уверенно идут по пути повсеместной интеграции SSH-управления, пока появляется множество утилит управления и наблюдения за инфраструктурой средствами SSH, Windows стоит в сторонке от этого прогресса. Здесь напряженка с клиентами, тяжело найти достойную консоль и терминал, есть вопросы к агентам — но всё это тема для отдельной статьи. Здесь же речь пойдет о том, как упростить первый этап в настройке нового сервера — заливку своего RSA-ключа.
Пользователи *nix-систем должны знать о существовании команды ssh-copy-id: это просто bash-скрипт следующего содержания:
#!/bin/sh # Shell script to install your public key on a remote machine # Takes the remote machine name as an argument. # Obviously, the remote machine must accept password authentication, # or one of the other keys in your ssh-agent, for this to work. ID_FILE="${HOME}/.ssh/id_rsa.pub" if [ "-i" = "$1" ]; then shift # check if we have 2 parameters left, if so the first is the new ID file if [ -n "$2" ]; then if expr "$1" : ".*\.pub" > /dev/null ; then ID_FILE="$1" else ID_FILE="$1.pub" fi shift # and this should leave $1 as the target name fi else if [ x$SSH_AUTH_SOCK != x ] && ssh-add -L >/dev/null 2>&1; then GET_ID="$GET_ID ssh-add -L" fi fi if [ -z "`eval $GET_ID`" ] && [ -r "${ID_FILE}" ] ; then GET_ID="cat \"${ID_FILE}\"" fi if [ -z "`eval $GET_ID`" ]; then echo "$0: ERROR: No identities found" >&2 exit 1 fi if [ "$#" -lt 1 ] || [ "$1" = "-h" ] || [ "$1" = "--help" ]; then echo "Usage: $0 [-i [identity_file]] [user@]machine" >&2 exit 1 fi # strip any trailing colon host=`echo $1 | sed 's/:$//'` { eval "$GET_ID" ; } | ssh $host "umask 077; test -d ~/.ssh || mkdir ~/.ssh ; cat >> ~/.ssh/authorized_keys && (test -x /sbin/restorecon && /sbin/restorecon ~/.ssh ~/.ssh/authorized_keys >/dev/null 2>&1 || true)" || exit 1 cat <<EOF Now try logging into the machine, with "ssh '$host'", and check in: ~/.ssh/authorized_keys to make sure we haven't added extra keys that you weren't expecting. EOF
Занимается этот скрипт тем, что заливает пользовательский публичный ключ (по-умолчанию ~/.ssh/id_rsa.pub
на указанный сервер для указанного пользователя. Да, пара строк кода, но они упрощают жизнь. Что же делать, если под рукой только Windows? Как обычно, писать свой костыль.
Я не первый кто этим озадачился. Здесь, например, предлагается однострочный батник, делающий всё то же самое:
type public_id | plink.exe username@hostname "umask 077; test -d .ssh || mkdir .ssh ; cat >> .ssh/authorized_keys"
Есть и более допиленное решение, уже небольшой скрипт, на вход которому необходимо передать адрес сервера, пароль и ключевой файл:
:: from http://serverfault.com/questions/224810/is-there-an-equivalent-to-ssh-copy-id-for-windows :: expected parameters :: [email protected] password [id_ras.pub] ::usage: ssh-copy-id [email protected] password [identity file] ::@echo off IF "%~3"=="" GOTO setdefault set /p id=<%3 GOTO checkparams :setdefault set /p id=<id_rsa.pub :checkparams IF "%~1"=="" GOTO promptp IF "%~2"=="" GOTO promptp2 :exec :: To accept the signature the first time echo y | plink.exe %1 -pw %2 "exit" :: now to actually copy the key echo %id% | plink.exe %1 -pw %2 "umask 077; test -d .ssh || mkdir .ssh ; cat >> .ssh/authorized_keys" GOTO end :promptp set /p 1="Enter [email protected]: " :promptp2 set /p 2="Enter password: " GOTO exec :end pause
Я же пошёл немного дальше и написал реализацию на PowerShell. Главное отличие: при запуске мы получаем диалоговое окно для выбора файла, содержащего публичный ключ, после чего следует небольшая проверка, что файл действительно подходит по формату и может быть использован для авторизации. Далее — всё то же самое: авторизуемся на сервере и копируем содержимое файла.
# ssh-copy-id analog for Windows systems # Script to install your public key on a remote machine # The remote machine must accept password authentication, # or one of the other keys in your Putty Agent, for this to work. # # (c) soar - http://soar.name # Dialog for public key selection Function Select-File() { [void][System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms"); $OpenFileDialog = New-Object System.Windows.Forms.OpenFileDialog; $OpenFileDialog.Filter = "Public Keys (*.pub) | *.pub"; $OpenFileDialog.ShowHelp = $true; # Without this line - dialog not appears.. I don't understand why. [void] $OpenFileDialog.ShowDialog(); $OpenFileDialog.filename; } Function ShowMessage($title, $content, $type) { [void][System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms"); [void][Windows.Forms.Messagebox]::show($content, $title, $type); } # Returns current script directory, plink.exe must be in same place Function Get-ScriptDirectory { $Invocation = (Get-Variable MyInvocation -Scope 1).Value; Split-Path $Invocation.MyCommand.Path; } $pubKeyFile = Select-File if (! $pubKeyFile) { ShowMessage 'Need to select file' 'Please, select public key file for upload to remote host' 'OK'; Exit; } $pubKey = Get-Content -LiteralPath $pubKeyFile; if (! ($pubKey -is [string]) -or ! $pubKey.StartsWith("ssh-")) { ShowMessage "Wrong file?" "Selected file not looks like valid SSH public key. It must starts with `"ssh-`" and contain one line." "OK" Exit; } $plinkExecutable = Join-Path (Get-ScriptDirectory) "plink.exe"; if ($args.Length) { foreach ($arg in $args) { & $plinkExecutable -ssh $arg "umask 077; test -d ~/.ssh || mkdir ~/.ssh ; echo `"$pubKey`" >> ~/.ssh/authorized_keys" } } else { $hostname = Read-Host "Please enter target hostname (user@hostname):"; if ($hostname) { & $plinkExecutable -ssh $hostname "umask 077; test -d ~/.ssh || mkdir ~/.ssh ; echo `"$pubKey`" >> ~/.ssh/authorized_keys" } } Write-Host "Press any key to continue ..." $x = $host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown")
Данный скрипт использует plink, поэтому plink.exe
должен лежать рядом, в той же директории, что и скрипт.