How to escape a # username in ~/.ssh/config file - ssh

I am on macos monterey & have the following in my ~/.ssh/config:
Host foo
HostName cyberark.company.com
User user#user#company.com#target_vm
ssh foo from my terminal has #company.com#target_vm portion of my User value automatically truncated where as ssh user#user#company.com#target_vm#cyberark.company.com works correctly. Seems like everything after # is treated as comments in .ssh/config file. How do I escape # in this file?

From SSH Config documentation the character # is a special character interpreted by ssh:
Empty lines and lines starting with '#' are comments.
You can prevent these behavior from happening by telling explicitly ssh to read litteral characters instead of interpreting them:
using backslash escape character \ special characters such as # and # (more informations here)
flanking the whole string with quotes '
As mentioned in this answer from Martin Prikryl
Here you may want to try the following in your case:
Host foo
HostName cyberark.company.com
User 'user#user#company.com#target_vm'
or using backslash:
Host foo
HostName cyberark.company.com
User user\#user\#company.com\#target_vm

Related

Is the syntax for local port forwarding wrong on Zsh?

I am trying to set up a function on my .zshrc file to create an easy way to do local port forwarding. My goal is starting a Jupyter server on my university's cluster and then using local port forwarding to access it. I have the following on my .zshrc file:
function jptt(){
# Forwards port $1 into port $2 and listens to it
ssh -N -f -L $1:localhost:$2 rgr6291#klc0201.ci.northwestern.edu
}
However, whenever I try to use it, for example with jptt 8888 8888, I get the following error:
Bad local forwarding specification '8888ocalhost:8888'
Am I handling strings or arguments in a wrong way? I can't understand why it isn't able to execute the command. Thanks in advance!
In zsh, you can add history-style expansion operators to the end of a variable expansion. $1:localhost is treated as the expansion $1:l, followed by the text localhost. To prevent this interpretation, use explicit braces to delimit the parameter expansion.
ssh -N -f -L ${1}:localhost:$2 rgr6291#klc0201.ci.northwestern.edu
You can also quote the parameter expansion to separate the : from the expansion syntactically:
ssh -N -f -L "$1":localhost:$2 rgr6291#klc0201.ci.northwestern.edu
:l, by the way, causes the expansion to be converted to lowercase. For example,
% x=FOO
% echo $x
FOO
% echo $x:l
foo
With braces, the operator would go inside the braces, immediately after the parameter name.
% echo ${x}:l
FOO:l
% echo ${x:l}
foo

How to escape # in password when doing ssh to remote server?

I want to do ssh to remote server. So the command is
ssh username:password#ipaddress
But if my password contains # then this command creates a problem. Obviously I can enter password in next step when prompt comes but I want to do in single step as displayed above. How can I escape the # character?
You can use the url encoded version of # sign: %40

How can I remove a Host entry from an ssh config file?

The standard format of the file is:
Host example
HostName example.com
Port 2222
Host example2
Hostname two.example.com
Host three.example.com
Port 4444
Knowing the host's short name, I want to remove an entire entry in order to re-add it with new details.
The closest I have got is this, except I need to keep the following Host declaration and the second search term will capture two many terms (like HostName):
sed '/Host example/,/\nHost/d'
With GNU sed:
host="example"
sed 's/^Host/\n&/' file | sed '/^Host '"$host"'$/,/^$/d;/^$/d'
Output:
Host example2
Hostname two.example.com
Host three.example.com
Port 4444
s/^Host/\n&/: Insert a newline before every line that begins with "Host"
/^Host '"$host"'$/,/^$/d: delete all lines matching "Host $host" to next empty line
/^$/d: clean up: delete every empty line
My final solution (which is somewhat OSX orientated) was this. It's largely based on Cyrus' earlier answer.
sed < $SOURCE "/^$/d;s/Host /$NL&/" | sed '/^Host '"$HOST"'$/,/^$/d;' > $SOURCE
This is more resilient to HostName directives which aren't indented.

How can I get ssh to use a different IdentityFile based on the USER I've chosen?

I've found lots of help showing how to configure .ssh/config to choose an identityFile based on the hostname I'm connecting to, but I connect to a large number of hosts, using one of two usernames. I want ssh to recognize what username I've selected, and select the matching IdentityFile.
I tried this:
Host admin#*
IdentityFile ~/.ssh/admin_id_rsa
Host chintchary#*
IdentityFile ~/.ssh/chintchary_id_rsa
using ssh -v I can see that the config is being ignored.
You have the syntax wrong in few aspects:
There are no colons : between keys and values
User is not part of Host, but it can be matched using Match block
Your config should look like this:
Match user admin
IdentityFile ~/.ssh/admin_id_rsa
Match user chintchary
IdentityFile ~/.ssh/chintchary_id_rsa
All of it can be read from your manual page for ssh_config(5). Afterward make sure that your file is read. With your version, it should yell about bad syntax.
Alternative for older openssh
If your openSSH version doesn't support Match blocks, you need to make some ugly hacks using bash functions, something like this one:
ssh(){
expr match "$#" '.*admin#.*' && I="-i ~/.ssh/admin_id_rsa"
expr match "$#" '.*chintchary#.*' && I="-i ~/.ssh/chintchary_id_rsa"
/usr/bin/ssh $I $#
}

How can I write a program (script) to remove obsolete host keys from ~/.ssh/known_hosts?

I use a cluster of about 30 machines that have all recently been reconfigured with new OpenSSH host keys. When I try to log into one, I get this error message (many lines removed for brevity):
# WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED! #
The fingerprint for the RSA key sent by the remote host is
52:bb:71:83:7e:d0:e2:66:92:0e:10:78:cf:a6:41:49.
Add correct host key in /home/nr/.ssh/known_hosts to get rid of this message.
Offending key in /home/nr/.ssh/known_hosts:50
I can go remove the offending line manually, in which case I get a different complaint about the IP addresss, which requires removing another line manually, and I have no desire to repeat this exercise 29 times. I would like to write a program to do it. Unfortunately, the line in the .ssh file no longer contains the host name and IP address in clear text, as it did in earlier versions.
So here's my question:
Given a host name and an IP address, how can I write a program to find out which lines of my ~/.ssh/known_hosts store an SSH host key for that host or IP address?
If I can recover this info, I think I can do the rest myself.
Footnote: I would prefer to code in bash/ksh/sh or C or Lua; my Perl and Python are very rusty.
Clarifications:
I don't want to remove the whole file and repopulate it; it contains over a hundred validated keys that I prefer not to re-validate.
Whether I maintain a single master copy or multiple replicas, the problem of scrubbing away a large group of obsolete host keys remains.
Answer
Here's the Lua script I wrote using ssh-keygen -F:
#!/usr/bin/env lua
require 'osutil'
require 'ioutil'
local known = os.getenv 'HOME' .. '/.ssh/known_hosts'
local function lines(name)
local lines = { }
for l in io.lines(name) do
table.insert(lines, l)
end
return lines
end
local function remove_line(host)
local f = io.popen('ssh-keygen -F ' .. os.quote(host))
for l in f:lines() do
local line = l:match '^# Host %S+ found: line (%d+) type %u+$'
if line then
local thelines = lines(known)
table.remove(thelines, assert(tonumber(line)))
table.insert(thelines, '')
io.set_contents(known, table.concat(thelines, '\n'))
return
end
end
io.stderr:write('Host ', host, ' not found in ', known, '\n')
end
for _, host in ipairs(arg) do
local ip = os.capture('ipaddress ' .. host)
remove_line(host)
remove_line(ip)
end
ssh-keygen -R hostname
ssh-keygen -R ipaddress
personally I scrub the IP addresses with a loop and perl, and remove the conflicts by hand.
$!/usr/bin/perl
for (1..30){
`ssh keygen -R 192.168.0.$_`; #note: backticks arent apostrophies
}
If I want to find out on what line the entry for a host lives,
ssh-keygen -F hostname
The same trick works with IP addresses.
touch and edit "clearkey.sh" or what ever name makes you happy.
#! /bin/bash
# $1 is the first argument supplied after calling the script
sed -i "$1d" ~/.ssh/known_hosts
echo "Deleted line $1 from known_hosts file"
Should be able to do "clearkey.sh 3" and it will delete the offending line!
I usually do the following in bash script checkssh to automatically remove the line:
#!/bin/bash
# Path to "known_hosts" file
KH=~/.ssh/known_hosts
# Find the host in the file, showing line number
L=`grep -i -n $1 $KH`
# If line is not found, exit
[[ $? -ne 0 ]] && exit
# Isolate line number
L=`echo $L | cut -f 1 -d :`
sed -i "${L}d" $KH
You can add ssh $1 exit at the end to automatically re-create an entry in the file, if your ssh is configured to do so.
Call it like checkssh <hostname>.
You might like to try the following when script writing:
declare CHANGED_HOST_NAME="host.yourpublic.work";
declare CHANGED_HOST_IP=$(dig +short ${CHANGED_HOST_NAME});
# Remove old IP address if found
[ -z ${CHANGED_HOST_IP} ] || ssh-keygen -R ${CHANGED_HOST_IP};
# Remove old host key
ssh-keygen -R ${CHANGED_HOST_NAME};
# Add new host key
ssh-keyscan ${CHANGED_HOST_NAME} >> $HOME/.ssh/known_hosts;
Big thanks to #Storm Knight (#289844)