{"id":3273,"date":"2017-11-24T09:09:34","date_gmt":"2017-11-24T15:09:34","guid":{"rendered":"http:\/\/dev.iachieved.it\/iachievedit\/?p=3273"},"modified":"2019-01-12T17:40:35","modified_gmt":"2019-01-12T23:40:35","slug":"hardening-your-ssh-servers","status":"publish","type":"post","link":"https:\/\/dev.iachieved.it\/iachievedit\/hardening-your-ssh-servers\/","title":{"rendered":"SSH Hardening"},"content":{"rendered":"<p><i>DevOps ToolChain, WikiPedia, CC BY-SA 4.0<\/i><\/p>\n<h2>Why Hardening<\/h2>\n<p><a href=\"https:\/\/en.wikipedia.org\/wiki\/Hardening_(computing)\"><i>Hardening<\/i><\/a>, as I define it, is the process of applying best practices to make it <i>harder<\/i> for others to obtain unauthorized access to a given service or to the data transmitted by the service. In this post we&#8217;ll take a look at hardening <a href=\"https:\/\/en.wikipedia.org\/wiki\/Secure_Shell\">SSH<\/a> access to our server, as well as making it more difficult for others to potentially snoop our SSH traffic.<\/p>\n<p>We&#8217;ll be using a fresh AWS EC2 instance running Ubuntu 16.04 for our examples. If you&#8217;re running a virtual server in Azure, Digital Ocean, or some other hosting provider, you&#8217;ll want to check out how the equivalent of AWS security groups are configured.  And of course, these techniques can also be applied to non-virtual systems.<\/p>\n<h3>AWS Security Groups<\/h3>\n<p>The first step in hardening your SSH server is applying a more restrictive security group to your instance. Think of AWS security groups as custom firewalls you can apply to your instance. Even better, these custom firewalls can apply source-based filtering rules that only allow traffic from subnets or hosts you specify.<\/p>\n<p>Subnet-based rules provides for rules like &#8220;Only allow SSH traffic from my development team&#8217;s network 10.90.4.0\/24&#8221; If your internal network is segregated and configured such that developers must authenticate to receive a 10.90.4.0\/24 IP address, an additional safeguard is added.<\/p>\n<p>A host-based rule will only allow traffic from a given host (strictly speaking, a given IP address; if a host is behind a NAT then any hosts also behind that NAT will be allowed). This is what we&#8217;ll use.<\/p>\n<p><a href=\"http:\/\/dev.iachieved.it\/iachievedit\/wp-content\/uploads\/2017\/11\/private-ssh-sg.png\" rel=\"attachment wp-att-3278\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-3278\" src=\"http:\/\/dev.iachieved.it\/iachievedit\/wp-content\/uploads\/2017\/11\/private-ssh-sg.png\" alt=\"private-ssh-sg\" width=\"837\" height=\"314\" srcset=\"https:\/\/dev.iachieved.it\/iachievedit\/wp-content\/uploads\/2017\/11\/private-ssh-sg.png 837w, https:\/\/dev.iachieved.it\/iachievedit\/wp-content\/uploads\/2017\/11\/private-ssh-sg-300x113.png 300w, https:\/\/dev.iachieved.it\/iachievedit\/wp-content\/uploads\/2017\/11\/private-ssh-sg-768x288.png 768w\" sizes=\"(max-width: 837px) 100vw, 837px\" \/><\/a><\/p>\n<p>In the above example, we&#8217;ve created a security group <code>private-ssh-sg<\/code> and added a single Inbound rule that allows traffic on port 22 from a specific IP address. This will effectively only allow packets whose source IP is specified in that rule to reach port 22 of the instance.<\/p>\n<h2>SSH Cipher Strength<\/h2>\n<p>Another technique you can use to harden your SSH server is ensuring that the latest strong <a href=\"https:\/\/en.wikipedia.org\/wiki\/Key_exchange\">key exchange<\/a> protocols, <a href=\"https:\/\/en.wikipedia.org\/wiki\/Cipher\">ciphers<\/a>, and <a href=\"https:\/\/en.wikipedia.org\/wiki\/Message_authentication_code\">message authentication code<\/a> (MAC) algorithms are utilized.<\/p>\n<p>We&#8217;ve used several references as a guide to hardening SSH, including <a href=\"https:\/\/wiki.mozilla.org\/Security\/Guidelines\/OpenSSH\">Mozilla&#8217;s OpenSSH Guidelines<\/a> as well as <a href=\"https:\/\/github.com\/arthepsy\/ssh-audit\">ssh-audit<\/a>, a nice tool designed specifically for this task. Using <code>ssh-audit<\/code> is as easy as<\/p>\n<p>[code lang=text]<br \/>\n$ git clone https:\/\/github.com\/arthepsy\/ssh-audit<br \/>\n$ cd ssh-audit<br \/>\n$ .\/ssh-audit.py your.ip.address.here<br \/>\n[\/code]<\/p>\n<p><a href=\"http:\/\/dev.iachieved.it\/iachievedit\/wp-content\/uploads\/2017\/11\/sshaudit.png\" rel=\"attachment wp-att-3284\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-3284\" src=\"http:\/\/dev.iachieved.it\/iachievedit\/wp-content\/uploads\/2017\/11\/sshaudit.png\" alt=\"sshaudit\" width=\"659\" height=\"1036\" srcset=\"https:\/\/dev.iachieved.it\/iachievedit\/wp-content\/uploads\/2017\/11\/sshaudit.png 659w, https:\/\/dev.iachieved.it\/iachievedit\/wp-content\/uploads\/2017\/11\/sshaudit-191x300.png 191w, https:\/\/dev.iachieved.it\/iachievedit\/wp-content\/uploads\/2017\/11\/sshaudit-651x1024.png 651w\" sizes=\"(max-width: 659px) 100vw, 659px\" \/><\/a><\/p>\n<p>Our first pass uncovers a number of issues:<\/p>\n<ul>\n<li>use of weak elliptic curves in the key exchange algorithms supported<\/li>\n<li>use of weak elliptic curves in the host-key algorithms supported<\/li>\n<\/ul>\n<p><code>ssh-audit<\/code> goes on to recommend the key exchange, host key, and MAC algorithms to remove.<\/p>\n<p>Let&#8217;s look at changes we want to make to our <code>\/etc\/ssh\/sshd_config<\/code> file:<\/p>\n<pre class=\"theme:dark-terminal lang:default decode:true \" >Protocol 2\nHostKey \/etc\/ssh\/ssh_host_ed25519_key\n\nKexAlgorithms curve25519-sha256@libssh.org\nCiphers chacha20-poly1305@openssh.com,aes256-gcm@openssh.com,aes128-gcm@openssh.com,aes256-ctr,aes192-ctr,aes128-ctr\nMACs hmac-sha2-512-etm@openssh.com,hmac-sha2-256-etm@openssh.com,umac-128-etm@openssh.com\n<\/pre>\n<p>Restart your SSH daemon with <code>sudo systemctl restart ssh<\/code>. <em>Note<\/em>: It&#8217;s a good idea to have a second terminal logged in if you bork your SSH configuration and lock yourself out of your instance.<\/p>\n<p>Once we&#8217;ve updated our <code>sshd_config<\/code> configuration, it&#8217;s time to run an audit against it.<\/p>\n<p><a href=\"http:\/\/dev.iachieved.it\/iachievedit\/wp-content\/uploads\/2017\/11\/Shell.png\" rel=\"attachment wp-att-3286\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-3286\" src=\"http:\/\/dev.iachieved.it\/iachievedit\/wp-content\/uploads\/2017\/11\/Shell.png\" alt=\"Shell\" width=\"590\" height=\"436\" srcset=\"https:\/\/dev.iachieved.it\/iachievedit\/wp-content\/uploads\/2017\/11\/Shell.png 590w, https:\/\/dev.iachieved.it\/iachievedit\/wp-content\/uploads\/2017\/11\/Shell-300x222.png 300w\" sizes=\"(max-width: 590px) 100vw, 590px\" \/><\/a><\/p>\n<p>Nice! Strong key exchange, encryption, and MAC algorithms all around.<\/p>\n<p>If you&#8217;re looking for a simple SSH daemon configuration, look no further:<\/p>\n<pre class=\"theme:dark-terminal lang:default decode:true \" title=\"A Solid SSHD Config File\" >HostKey \/etc\/ssh\/ssh_host_ed25519_key\nPasswordAuthentication no\nChallengeResponseAuthentication no\nUsePAM yes\nX11Forwarding yes\nPrintMotd no\nAcceptEnv LANG LC_*\nKexAlgorithms curve25519-sha256@libssh.org\nCiphers chacha20-poly1305@openssh.com,aes256-gcm@openssh.com,aes128-gcm@openssh.com,aes256-ctr,aes192-ctr,aes128-ctr\nMACs hmac-sha2-512-etm@openssh.com,hmac-sha2-256-etm@openssh.com,umac-128-etm@openssh.com<\/pre>\n<h2>Two-Factor Authentication<\/h2>\n<p>There are different interpretations as to what constitutes two-factor or multi-factor authentication. Many believe that two-factor authentication implies an additional authentication code delivered via text message or provided by a <a href=\"https:\/\/en.wikipedia.org\/wiki\/RSA_SecurID\">key fob<\/a>. Others may consider the steps taken to obtain access to a given computing resource as a part of the authentication steps (e.g., to obtain access to a given server you must get past the security guard, provide a retinal scan, and so on). In this example, we&#8217;re going to use the former interpretation.<\/p>\n<h3>Authy<\/h3>\n<p>We&#8217;ve chosen to use <a href=\"https:\/\/authy.com\/\">Authy<\/a> in this example for two-factor authentication using a <a href=\"https:\/\/en.wikipedia.org\/wiki\/One-time_password\">time-based one time password<\/a>. To get started, install the Authy application on your phone (iOS or Android) and follow the quick-start prompts.<\/p>\n<p>After you&#8217;ve successfully set up the application on your phone, you can download the app to your desktop or add it to Google Chrome.<\/p>\n<h3>Getting Your EC2 Instance Ready<\/h3>\n<p>To use the authentication code provided by Authy to add an additional authentication step for SSH logins requires installing the <code>libpam-google-authenticator<\/code> module and configuring both SSH and PAM.<\/p>\n<p>Install the module with <code>sudo apt-get install libpam-google-authenticator<\/code>.<\/p>\n<p>Now, as a user that needs to use two-factor authentication, run <code>google-authenticator<\/code> to get set up. The application will generate several prompts, the first of which is <code>Do you want authentication tokens to be time-based<\/code> to which you&#8217;ll answer &#8220;yes&#8221;<\/p>\n<p><code>google-authenticator<\/code> will then generate QR-code that you can scan with the Authy phone application, as well as a secret key that can be used with the phone, desktop, or browser application. When using the desktop application I prefer just copy-paste of the secret key.<\/p>\n<p>There are additional prompts from the application to follow:<\/p>\n<p>[code lang=text]<br \/>\nDo you want me to update your &quot;\/home\/ubuntu\/.google_authenticator&quot; file (y\/n) y<\/p>\n<p>Do you want to disallow multiple uses of the same authentication<br \/>\ntoken? This restricts you to one login about every 30s, but it increases<br \/>\nyour chances to notice or even prevent man-in-the-middle attacks (y\/n) y<\/p>\n<p>By default, tokens are good for 30 seconds and in order to compensate for<br \/>\npossible time-skew between the client and the server, we allow an extra<br \/>\ntoken before and after the current time. If you experience problems with poor<br \/>\ntime synchronization, you can increase the window from its default<br \/>\nsize of 1:30min to about 4min. Do you want to do so (y\/n) n<\/p>\n<p>If the computer that you are logging into isn&#039;t hardened against brute-force<br \/>\nlogin attempts, you can enable rate-limiting for the authentication module.<br \/>\nBy default, this limits attackers to no more than 3 login attempts every 30s.<br \/>\nDo you want to enable rate-limiting (y\/n) y<br \/>\n[\/code]<\/p>\n<p><em>NB<\/em>: It is a <em>good<\/em> idea to save your emergency scratch codes in the event you lose access to the devices that are generating your OTPs.<\/p>\n<p>Now that you&#8217;ve configured the authenticator, it&#8217;s time to update <code>sshd_config<\/code> to consult the PAM Google Authenticator module when a user attempts to log in.<\/p>\n<p>Open <code>\/etc\/ssh\/sshd_config<\/code> as root, and set the following:<\/p>\n<p>[code lang=text]<br \/>\nChallengeResponseAuthentication yes<br \/>\nPasswordAuthentication no<br \/>\nAuthenticationMethods publickey,keyboard-interactive<br \/>\n[\/code]<\/p>\n<p>Restart <code>ssh<\/code> with <code>sudo systemctl restart ssh<\/code>.<\/p>\n<p>Now, in <code>\/etc\/pam.d\/sshd<\/code> replace the line <code>@include common-auth<\/code> with <code>auth required pam_google_authenticator.so<\/code>.<\/p>\n<p>Once the <code>sshd<\/code> PAM module has been configured in this manner users will be challenged for a two-factor authentication code, so it&#8217;s important that every user on the system be configured with the <code>google-authenticator<\/code> application.<\/p>\n<h3>Test your login!<\/h3>\n<p>Try logging in via <code>ssh<\/code> with the user you&#8217;ve just configured for two-factor authentication. You should receive a prompt requesting a verification code (after your key is authenticated).<\/p>\n<p><a href=\"http:\/\/dev.iachieved.it\/iachievedit\/wp-content\/uploads\/2017\/11\/enter_code.png\" rel=\"attachment wp-att-3292\"><img loading=\"lazy\" decoding=\"async\" src=\"http:\/\/dev.iachieved.it\/iachievedit\/wp-content\/uploads\/2017\/11\/enter_code.png\" alt=\"enter_code\" width=\"506\" height=\"366\" class=\"aligncenter size-full wp-image-3292\" srcset=\"https:\/\/dev.iachieved.it\/iachievedit\/wp-content\/uploads\/2017\/11\/enter_code.png 506w, https:\/\/dev.iachieved.it\/iachievedit\/wp-content\/uploads\/2017\/11\/enter_code-300x217.png 300w\" sizes=\"(max-width: 506px) 100vw, 506px\" \/><\/a><\/p>\n<p>Enter the code displayed on your Authy app (note that all of your Authy apps will display the same code for the same application configuration) to login.<\/p>\n<p><a href=\"http:\/\/dev.iachieved.it\/iachievedit\/wp-content\/uploads\/2017\/11\/code_entered.png\" rel=\"attachment wp-att-3293\"><img loading=\"lazy\" decoding=\"async\" src=\"http:\/\/dev.iachieved.it\/iachievedit\/wp-content\/uploads\/2017\/11\/code_entered.png\" alt=\"code_entered\" width=\"504\" height=\"363\" class=\"aligncenter size-full wp-image-3293\" srcset=\"https:\/\/dev.iachieved.it\/iachievedit\/wp-content\/uploads\/2017\/11\/code_entered.png 504w, https:\/\/dev.iachieved.it\/iachievedit\/wp-content\/uploads\/2017\/11\/code_entered-300x216.png 300w\" sizes=\"(max-width: 504px) 100vw, 504px\" \/><\/a><\/p>\n<h3>One Last Thought on Authy<\/h3>\n<p>While writing this post I was doing additional research on two-factor authentication implementations; while Authy supports time-based one time passwords, it supports additional methods that require access to their infrastructure.  If you don&#8217;t care for providing your cellphone number (and a lot of people don&#8217;t), try out <a href=\"https:\/\/chrome.google.com\/webstore\/detail\/authenticator\/bhghoamapcdpbohphigoooaddinpkbai?hl=en\">Authenticator<\/a>, a Chrome plugin that doesn&#8217;t require any account setup.<\/p>\n<p>You&#8217;ll notice if you try out different applications that you can use the same secret key and each application will generate the same code at the same time, hence the <i>time-based<\/i> one time password.<\/p>\n<h2>Recap<\/h2>\n<p>There is no such thing as perfect security save turning off the computer, disconnecting all of its cables, putting it in a trunk, filling that with cement, and tossing it into the Pacific. Even that might not be perfect. In the absence of perfection we can put as many barriers in place between our server and others that shouldn&#8217;t have access to it. In this post we&#8217;ve taken a look at several of those barriers:<\/p>\n<ul>\n<li>source-based firewall rules that only allow access on port 22 from a specific IP or subnet<\/li>\n<li>hardened key exchange algorithms, ciphers, and MACs for SSH<\/li>\n<li>two-factor authentication that requires both public key authentication as well as an OTP code<\/li>\n<\/ul>\n<p>We did not cover additional techniques such as configuring SSH to listen on a different port; I&#8217;ve found that despite explaining that this is done primarily to minimize port-scanning chatter (who wants to sift through <code>auth<\/code> or <code>fail2ban<\/code> logs with script kiddie traffic) it never fails to incite the crowd of folks who just learned the phrase <i>security through obscurity<\/i> to gather up their pitchforks.<\/p>\n<p>If you have any additional recommendations regarding SSH security hardening, please leave a comment!<\/p>\n","protected":false},"excerpt":{"rendered":"<p>DevOps ToolChain, WikiPedia, CC BY-SA 4.0 Why Hardening Hardening, as I define it, is the process of applying best practices to make it harder for others to obtain unauthorized access to a given service or to the data transmitted by the service. In this post we&#8217;ll take a look at hardening SSH access to our [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":3318,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[21,19],"tags":[39],"class_list":["post-3273","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-devops","category-linux","tag-security"],"_links":{"self":[{"href":"https:\/\/dev.iachieved.it\/iachievedit\/wp-json\/wp\/v2\/posts\/3273"}],"collection":[{"href":"https:\/\/dev.iachieved.it\/iachievedit\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/dev.iachieved.it\/iachievedit\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/dev.iachieved.it\/iachievedit\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/dev.iachieved.it\/iachievedit\/wp-json\/wp\/v2\/comments?post=3273"}],"version-history":[{"count":24,"href":"https:\/\/dev.iachieved.it\/iachievedit\/wp-json\/wp\/v2\/posts\/3273\/revisions"}],"predecessor-version":[{"id":3647,"href":"https:\/\/dev.iachieved.it\/iachievedit\/wp-json\/wp\/v2\/posts\/3273\/revisions\/3647"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/dev.iachieved.it\/iachievedit\/wp-json\/wp\/v2\/media\/3318"}],"wp:attachment":[{"href":"https:\/\/dev.iachieved.it\/iachievedit\/wp-json\/wp\/v2\/media?parent=3273"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/dev.iachieved.it\/iachievedit\/wp-json\/wp\/v2\/categories?post=3273"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/dev.iachieved.it\/iachievedit\/wp-json\/wp\/v2\/tags?post=3273"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}