
Installing and Configuring SSH in Linux
SSH is how you get a shell on a machine that isn’t in front of you. It replaced telnet and rlogin for the obvious reason: those sent your password across the network in the clear, and SSH doesn’t.
This covers installing the server, the settings worth changing before a box faces the network, key-based logins so you stop typing passwords, and the handful of client tricks that save real time. Everything here is OpenSSH, which is what ships on every mainstream distribution.
Installing the server
On CentOS and Red Hat you want the server and the client tools:
# yum install openssh-server openssh-clients
On Ubuntu and Debian the client is usually already there, so it’s the server you’re adding:
# apt-get install openssh-server
Two different names for the same daemon, which trips people up when they move between families. Red Hat calls the service sshd, Debian and Ubuntu call it ssh. The config file is /etc/ssh/sshd_config on both.
Starting it, and keeping it started
# service sshd start
Starting sshd: [ OK ]
# chkconfig sshd on
service starts it now. chkconfig is the part people forget, and it’s what brings the daemon back after a reboot. Miss it and the server works perfectly until the day it restarts, which is generally the day you’re not in the building.
On Ubuntu the equivalents are service ssh start, and the package enables it at boot for you.
Check it’s actually listening before you trust it:
# netstat -tlnp | grep sshd
tcp 0 0 0.0.0.0:22 0.0.0.0:* LISTEN 1284/sshd
tcp 0 0 :::22 :::* LISTEN 1284/sshd
Your first connection
$ ssh -V
OpenSSH_5.3p1, OpenSSL 1.0.0-fips 29 Mar 2010
Now connect. The first time you reach a machine you get this:
$ ssh asif@test.example.com
The authenticity of host 'test.example.com (192.168.1.50)' can't be established.
RSA key fingerprint is 94:68:3a:3a:bc:f3:9a:9b:01:5d:b3:07:38:e2:11:0c.
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added 'test.example.com' (RSA) to the list of known hosts.
asif@test.example.com's password:
Answer yes and the server’s public key goes into ~/.ssh/known_hosts. From then on SSH compares the key on every connection, which is the point of the whole exercise. Nobody checks that fingerprint against the server’s real one, and everyone should, because that first connection is the one moment a man in the middle would go unnoticed.
You can print the fingerprint on the server itself to compare:
# ssh-keygen -lf /etc/ssh/ssh_host_rsa_key.pub
2048 94:68:3a:3a:bc:f3:9a:9b:01:5d:b3:07:38:e2:11:0c /etc/ssh/ssh_host_rsa_key.pub
If that warning ever changes to a loud block of hashes telling you the host key has changed, don’t just delete the line and carry on. Sometimes it’s a rebuilt server. Sometimes it isn’t.
Editing the config safely
The server config is /etc/ssh/sshd_config. The client config, which is a different file people confuse with it constantly, is /etc/ssh/ssh_config. Server settings go in the one with the d.
Back it up before you touch it:
# cp /etc/ssh/sshd_config /etc/ssh/sshd_config.bak
# vi /etc/ssh/sshd_config
And here’s the habit that matters more than any setting below. Before you restart sshd, open a second terminal and log in again. Keep your existing session open while you do it. If the new config is broken, the old session is still alive and you can undo it. Restart, close your only session, then discover you’ve locked yourself out, and you’re driving to the data centre.
You can also check the file for syntax errors first:
# sshd -t
Silence means it parsed. Any output means fix it before restarting.
Settings worth changing
Stop root logging in directly.
PermitRootLogin no
Every automated attack on the internet tries root first. Turning this off means an attacker has to guess a username as well as a password. Log in as a normal user and use su or sudo.
Limit who can log in at all.
AllowUsers asif deploy
Nobody else gets in, even with a correct password. On a server with a lot of local accounts that exist for services rather than people, this is a bigger win than it looks.
Turn off the old protocol.
Protocol 2
SSH protocol 1 has known weaknesses and no reason to exist. Most distributions default to 2 now, but older configs carry Protocol 2,1 and that comma matters.
Move the port, maybe.
Port 2221
This isn’t security, and anyone who tells you it is has oversold it. A port scan finds it in seconds. What it does do is drop the volume of automated noise in your logs dramatically, which makes real attempts easier to spot.
Two things that bite when you change the port. On Red Hat with SELinux enforcing, the daemon won’t be allowed to bind until you tell SELinux about it:
# semanage port -a -t ssh_port_t -p tcp 2221
And your firewall needs to know:
# iptables -A INPUT -p tcp --dport 2221 -j ACCEPT
# service iptables save
Restart the daemon once you’re done:
# service sshd restart
Logging in with keys instead of passwords
This is the part worth the most and the part most guides leave until last. A key pair is two files: a private key that never leaves your machine, and a public key you install on every server you want to reach.
Generate one on your workstation, not on the server:
$ ssh-keygen -t rsa -b 2048
Generating public/private rsa key pair.
Enter file in which to save the key (/home/asif/.ssh/id_rsa):
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in /home/asif/.ssh/id_rsa.
Your public key has been saved in /home/asif/.ssh/id_rsa.pub.
The key fingerprint is:
3f:1a:9c:44:2b:87:e0:d1:55:6a:cb:19:7f:2e:80:a3 asif@workstation
Use a passphrase. A private key without one is a plain text password sitting in a file, and laptops get stolen. If typing it constantly annoys you, ssh-agent holds the decrypted key for your session:
$ eval $(ssh-agent)
$ ssh-add
Enter passphrase for /home/asif/.ssh/id_rsa:
Identity added: /home/asif/.ssh/id_rsa
Now push the public half to the server:
$ ssh-copy-id asif@test.example.com
That appends your public key to ~/.ssh/authorized_keys on the far end and gets the permissions right, which is why it’s better than doing it by hand. Test it before you change anything else. You should get a shell without a password prompt, or a prompt for your key passphrase rather than your account password.
Once keys work for everyone who needs access, you can turn passwords off entirely:
PasswordAuthentication no
That single line ends password guessing against your server permanently. Do it only after you have confirmed key access works, from a second session, with the first one still open.
When keys silently don’t work
The usual cause is permissions, and SSH is deliberately fussy here. It ignores keys in a directory other people could read, and it does so quietly.
$ chmod 700 ~/.ssh
$ chmod 600 ~/.ssh/authorized_keys
$ chmod 600 ~/.ssh/id_rsa
Your home directory matters too. If it’s group writable, SSH refuses the key and falls back to asking for a password, with nothing obvious in the client output to tell you why. When key auth mysteriously doesn’t take, check permissions before anything else, then read /var/log/secure on the server, which usually says exactly what it objected to.
The verbose client is the other half of the answer:
$ ssh -vvv asif@test.example.com
It’s noisy, but it shows which keys were offered and what the server did with them.
Running one command without a shell
$ ssh asif@test.example.com uptime
14:22:07 up 96 days, 4:11, 2 users, load average: 0.02, 0.06, 0.01
You get the output and your local prompt straight back. This is the foundation of every simple bit of remote automation, and it composes with pipes the way you’d hope:
$ ssh asif@test.example.com "tail -100 /var/log/messages" | grep error
Quote the remote command when it contains anything your local shell would otherwise interpret.
Moving files
scp uses the same authentication and the same port. Copy up:
$ scp localfile.txt asif@test.example.com:/home/asif/
Copy down, note the direction is just the argument order:
$ scp asif@test.example.com:/home/asif/remotefile.txt .
Whole directories need -r, and a non-standard port needs -P, capital P, unlike ssh which uses lowercase. That inconsistency has wasted more time than it should.
$ scp -r -P 2221 mydir/ asif@test.example.com:/home/asif/
For anything you’ll repeat, rsync over SSH is the better tool, because it only sends what changed and picks up where it left off. And if you want all of this without typing a password, that’s covered in setting up passwordless SSH, rsync and scp.
The client config file
This is the most underused file in SSH. Put per-host settings in ~/.ssh/config and stop retyping them:
Host web
HostName web01.example.com
User asif
Port 2221
IdentityFile ~/.ssh/id_rsa
Host *
ServerAliveInterval 60
Now ssh web does the whole thing. The ServerAliveInterval under Host * applies everywhere and sends a keepalive every minute, which stops idle sessions being dropped by a firewall that decides a quiet connection is a dead one.
Tunnels
Local forwarding brings a remote port to your machine. Useful for a database or an admin page that only listens on localhost at the far end:
$ ssh -L 3307:localhost:3306 asif@db.example.com
Connect to port 3307 on your own machine and you’re talking to MySQL on the server, through the encrypted session. The service never has to be exposed to the network at all, which is a much better answer than opening 3306 in the firewall.
Add -N if you only want the tunnel and not a shell.
Stepping away without disconnecting
SSH has escape sequences, and they only work as the first thing on a fresh line. Type ~ then Ctrl-Z to background the session:
remotehost$ ~^Z
[1]+ Stopped ssh asif@remotehost
localhost$ jobs
[1]+ Stopped ssh asif@remotehost
localhost$ fg %1
ssh asif@remotehost
remotehost$
Two more worth knowing. ~. kills a session that has hung, which beats closing the terminal when the network has gone away and the shell won’t respond. And ~? lists the rest.
For work that has to survive the connection dropping entirely, escape sequences aren’t enough. Start it inside screen and reattach later.
Troubleshooting
| What you see | What it usually means |
|---|---|
| Connection refused | Daemon isn’t running, or it’s on another port |
| Connection timed out | Firewall dropping it, no response at all |
| Permission denied (publickey) | Key not accepted and passwords are off |
| Still asks for a password after ssh-copy-id | Permissions on ~/.ssh or the home directory |
| Host key verification failed | Key changed. Find out why before deleting the line. |
| Works locally, not from outside | ListenAddress, firewall, or NAT |
| Daemon won’t start on a new port | SELinux. Use semanage port. |
| Session dies when idle | Set ServerAliveInterval |
The server log is the place to look, and it’s /var/log/secure on Red Hat, /var/log/auth.log on Ubuntu and Debian. Watch it while you make a failing attempt from another machine:
# tail -f /var/log/secure
Worth doing on anything internet facing
Once the basics work, three things cut your exposure considerably. Keys only with PasswordAuthentication no. Root login disabled. And fail2ban, which watches the auth log and firewalls off addresses that keep failing, so the endless background noise stops reaching sshd at all.
If you’re new to the wider system side of this, Linux commands and directory structure covers the ground this assumes.
SSH rewards a bit of setup. Spend twenty minutes on keys and a client config now, and every remote machine you touch afterwards costs you three keystrokes instead of a password and a hostname you can never quite remember.


