by Wayne E Goodrich (Outlaw)
2006-02-11
Many Linux administrators and desktop users need a secure method of shelling into a linux server to do work. So here, I’ll illustrate some tips for secure remote access using OpenSSH.
Secure Shell
Secure shell is the most common remote shell utility in use, since it addresses the shortcomings of the older utilities like telnet and rlogin. But left in its default configuration, as shipped by some of the major Linux vendors, it still can be insecure. Here are some ways to make it more secure. OpenSSH is installed and enabled by default in most, if not all all Linux distributions, so we can get right to configuring it for better security.
Disallow direct root login
If secure shell is configured to allow direct login by root, it makes it easy for a bad guy who either brute forces the password or manages to obtain it outright, to get access. It’s also generally a good idea to force users to log on as themselves and su to root, to preserve an audit trail. Let’s fix that.
Visit the ssh server config file /etc/ssh/sshd_config in your editor of choice and change the line:
toCode:# PermitRootLogin yes
Make sure to remove the ‘#’ to uncomment. So we’ve disabled root login. Now we’ll disable the older ssh protocol 1.Code:PermitRootLogin no
Disable ssh protocol 1
Doing this requires that ssh clients like PuTTY be changed to use protocol 2. Most Linux ssh clients use either, and will be forced to protocol 2 if the server is configured with 2 only. Let’s do that now – change:
toCode:# Protocol 1,2
Code:Protocol 2
Set up public key authentication
If your users have a private key that matches a corresponding public key on the server, you can deny all logins that do not provide such a key. Also, the remote user can use a key agent for some added convenience that will enable them to enter a password once and shell in all day without providing another. The agent handles passing the key that was decrypted using the password. Seeing it in action will clarify the concept.
Let’s do it then, first create the user’s key pairs. We’ll do it with PuTTY and also with the OpenSSH secure shell client in Linux. Grab Pageant and PuTTYgen from here, and fire up PuTTYgen to generate a key pair. Select ssh2 dsa key and the length of key. Then generate the key. Save the private key and copy and paste the public half to notepad. You want to highlight and paste the Public key for pasting into OpenSSH authorized_keys file: The key needs to be one long string when pasted into notepad.
Now we can make pageant load the key when launched, by providing the path to the private key. Right click on the pageant icon and type in a space and then the path to the private key.
In the Target field, we want the executable ant then the path to the pricate key
Test the agentCode:C:\pageant.exe c:\priv.ppk
When you launch pageant, you’ll enter the password to decrypt the key and it will remain in memory until you close pageant. Remember to close it when you’re through to prevent someone from gaining access to your servers from your station.
But first we need to prepare the remote server for the key authentication. First, we’ll work on the account that will be logging in, test it, then configure the ssh server to deny all attempts that do not have a key. In the home directory for the user that will use the key, do the following:
Paste the public key into .ssh/authorized_keysCode:mkdir .ssh; chmod 755 .ssh
If you have’nt already done it, launch the agent and type in your password. Then try to PuTTY into the account that has the keys. If it works without you entering the Linux password, we can move on. If not, there’s a step missing. Double check all the steps thus far.Code:chmod 644 .ssh/authorized_keys
Deny users without keys
Edit /etc/ssh/sshd_config and change the following:
toCode:#PasswordAuthentication yes
toCode:PasswordAuthentication no #ChallengeResponseAuthentication yes
and depending on distributionCode:ChallengeResponseAuthentication no
toCode:#PAMAuthenticationViaKbdInt yes
Now restart the ssh server the Redhat way.Code:PAMAuthenticationViaKbdInt no
Or, the Debian wayCode:service sshd restart
You should now be denied access if you connect and try to login without a key loaded. Try it by closing pageant and attempt login.Code:/etc/init.d/ssh restart
You can use agents in Linux too. To do that, generate an ssh key pair and run the ssh agent with that key. As a user, do the following, entering passwords and accepting default filenames:
Paste id_dsa.pub into the .ssh/authorized_keys on the user’s account on remote server. Copy it over, and:Code:ssh-keygen -t dsa
Now we’ll launch the ssh agent and load the id_dsa key, using the passwordCode:cat id_dsa.pub > .ssh/authorized_keys; chmod 644 .ssh/authorized_keys
You’ll be prompted for your passwordCode:ssh-add
Test it all out
Test logging into the server now. If it works, you now have a more secure ssh setup. A couple things about PuTTY and forwarding agents. In Linux, the site wide client ssh config file, /etc/ssh/ssh_config has an option called ForwardAgent. When set to yes, it allows one more consecutive connection using the loaded agent key. As in – ssh host1 then from there – ssh host2 without password, provided host2 is set up with the proper configuraton for pub key auth and users’ public keys. In PuTTY, check off the forward agent option.
Now we can shell over to as many servers as we set up keys for, with the balance of security and convenience. Make sure to train your users to close the agent when they’re through working. A nice side benefit of this setup is the ability to run commands distributed among a group of similarly configured servers.
My sites: Linux Home Networking – Linux Quick Fix Notebook
Additional OpenSSH Security
Allowed Users
For added security, we can specify users who are allowed to log in using SSH
Edit the /etc/ssh/sshd_config file and add the following two lines right after the Authentication section of the file:
So now we have excluded all other accounts from being valid users. The ssh daemon will throw an error in it’s log file when any other account is tried.Code:# Users allowed access AllowUsers radar avalon
TCP_Wrappers
We will establish a default deny policy for sshd and only allow IP addresses and networks that we specify in the access control list files.
These two files are: /etc/hosts.deny and /etc/hosts.allow. There’s a certain degree of flexibility in how you can use tcp_wrappers including: You can use both files or just one. You can format the rules the way that is best for you.
Here I will show how to use both hosts.deny and hosts.allow, and I will use a rule format that’s easier for me to manage.
The default deny
edit /etc/hosts.deny and add the sshd rule
There should be a carriage return at the end of the fileCode:# /etc/hosts.deny: list of hosts that are _not_ allowed to access the system. # See the manual pages hosts_access(5), hosts_options(5) # and /usr/doc/netbase/portmapper.txt.gz # # Example: ALL: some.host.name, .some.domain # ALL EXCEPT in.fingerd: other.host.name, .other.domain # # If you're going to protect the portmapper use the name "portmap" for the # daemon name. Remember that you can only use the keyword "ALL" and IP # addresses (NOT host or domain names) for the portmapper. See portmap(8) # and /usr/doc/portmap/portmapper.txt.gz for further information. # # The PARANOID wildcard matches any host whose name does not match its # address. # You may wish to enable this to ensure any programs that don't # validate looked up hostnames still leave understandable logs. In past # versions of Debian this has been the default. # ALL: PARANOID sshd : ALL : DENY portmap: ALL :deny
Explicit allows
Edit /etc/hosts.allow
The man page for hosts_access explains the rule structure and formatting options. Generally though, you can verify that sshd is compiled with the proper support for tcp_wrappers this way:Code:# /etc/hosts.allow: list of hosts that are allowed to access the system. # See the manual pages hosts_access(5), hosts_options(5) # and /usr/doc/netbase/portmapper.txt.gz # # Example: ALL: LOCAL @some_netgroup # ALL: .foobar.edu EXCEPT terminalserver.foobar.edu # # If you're going to protect the portmapper use the name "portmap" for the # daemon name. Remember that you can only use the keyword "ALL" and IP # addresses (NOT host or domain names) for the portmapper, as well as for # rpc.mountd (the NFS mount daemon). See portmap(8), rpc.mountd(8) and # /usr/share/doc/portmap/portmapper.txt.gz for further information. # sshd : \ 192.168.0.0/255.255.255.0 \ xx.0.0.0/255.248.0.0 \ xx.8.0.0/255.252.0.0 \ xx.12.0.0/255.254.0.0 \ xx.14.0.0/255.255.0.0 \ : allow portmap: 192.168.0.4,192.168.0.2 :ALLOW
Code:saturn:~# strings -f /usr/sbin/sshd | grep access /usr/sbin/sshd: hosts_access /usr/sbin/sshd: @(#)$OpenBSD: groupaccess.c,v 1.6 2003/04/08 20:21:28 itojun Exp $ /usr/sbin/sshd: userauth_hostbased: access allowed by auth_rhosts2 /usr/sbin/sshd: It is recommended that your private key files are NOT accessible by others.
The line we’re interested in contains hosts_access
Reasoning
Why do we do all this? It is a decent way, IMHO, to thwart brute force attempts against your host accounts. Without this, you probably see logs upon logs of failed password attempts, and we’ll see that LogWatch now contains the failed attempts:
Before
AfterCode:--------------------- SSHD Begin ------------------------ Didn't receive an ident from these IPs: 4h134234.aspadmin.net (216.98.134.234): 1 Time(s) serv-1-0-130.lycos-vds.com (84.244.0.130): 1 Time(s) Illegal users from these: ace/none from ::ffff:216.98.134.234: 1 Time(s) alex/none from ::ffff:84.244.0.130: 1 Time(s) ana/none from ::ffff:84.244.0.130: 1 Time(s) andrea/none from ::ffff:84.244.0.130: 1 Time(s) andrew/none from ::ffff:84.244.0.130: 1 Time(s) angel/none from ::ffff:84.244.0.130: 1 Time(s) anonymous/none from ::ffff:216.98.134.234: 1 Time(s) bank/none from ::ffff:84.244.0.130: 1 Time(s) barbara/none from ::ffff:84.244.0.130: 1 Time(s) betty/none from ::ffff:84.244.0.130: 1 Time(s) billy/none from ::ffff:84.244.0.130: 2 Time(s) bob/none from ::ffff:84.244.0.130: 1 Time(s) brandon/none from ::ffff:84.244.0.130: 1 Time(s) brian/none from ::ffff:84.244.0.130: 1 Time(s) buddy/none from ::ffff:84.244.0.130: 1 Time(s) carmen/none from ::ffff:84.244.0.130: 1 Time(s) charlie/none from ::ffff:84.244.0.130: 1 Time(s) ~ ~ ~
This method also gives you a layered approach to your remote access security:Code:Refused incoming connections: ::ffff:140.136.200.137 (::ffff:140.136.200.137): 2 Time(s) ::ffff:202.181.183.4 (::ffff:202.181.183.4): 2 Time(s) ::ffff:211.136.20.50 (::ffff:211.136.20.50): 1 Time(s) ::ffff:58.159.67.245 (::ffff:58.159.67.245): 1 Time(s)
- We now disallow insecure protocols
- We disallow direct root access to the SSH daemon
- We force the use of secure keys for SSH access
- We have a list of allowed users of SSH
- We have a host-based access list ACL that at least makes it far more difficult to gain an unauthorized connection to the SSH daemon
My sites: Linux Home Networking – Linux Quick Fix Notebook
Bookmarks