Skip to content

Latest commit

 

History

History
executable file
·
763 lines (669 loc) · 34.2 KB

LinuxSrvCfg.md

File metadata and controls

executable file
·
763 lines (669 loc) · 34.2 KB

Background:

Below are a set of instructions I have compiled for provisioning and securing
a bare-bones Linux VPS (virtual private server) as this has become a common
requirement of late.

Some vendors now provide bootstrap scripts with dashboards/configuration tools
for basic server setup steps but I felt it was important to understand the
underlying steps involved without the overlay of these tools.

The following key assumptions apply:

1). Desired Target Server & Image: Linux/Ubuntu
(Chosen among a plethora of available distributions)

2). Vendor: Linode
  (Note: I happen to be a happy AWS user and recommend them highly but
  I'm choosing another provider to start the process from scratch and
  to avoid the otherwise very convenient steps such as use of my existing
  rsa keys for authentication, etc. Of course, I have heard heaps of
  praise for Linode, as well, not least because of their straightforward
  approach).

3). Client (Local Machine) Operating System: Ubuntu
  The instructions below assume they are being performed on some
  flavor of a *nix distribution on the client machine. To be specific, the OS
  commands referenced here are for Linux/Ubuntu. Users on other *nix
  distributions should be mindful of substituting their OS-equivalent commands,
  as a result.

  Windows users have multiple virtualization options at their disposal to install
  a virtual Linux guest on their machines (see: Vagrant, VirtualBox, VMware ) or
  windows applications providing such environments (see: Cygwin, MinGW).

4). Text Editor Commands: vim
  In the file editing steps below I reference vim commands, as this is
  my editor of choice. For these steps, if you do not have vim installed,
  simply substitute your favorite text editor (e.g. 'nano', 'gedit', etc. )
  mindful of its own commands, obviously.

CAVEAT EMPTOR: Requirements vary with every use-case scenario.
The instructions listed here are meant to be an illustrative example of
provisioning and securing a server for one particular base-case scenario.
You should take care to note your own particular requirements for server type,
security, and software installation and be mindful where they differ from
the instructions presented here.

I. Provision A Server

( Note there are many options for provisioning a Linux server. Here is a random source of cloud providers and here is a source of comparison articles. Per our stated objective, we are going with Linode)

A). SIGN UP WITH VENDOR

 Visit:

https://www.linode.com/

 and create an account if you don't already have one.

   Note: many vendors have free trial periods.  At  the time of this writing,  
   the option of trying Linode risk free for 7-days was available and this was  
   the option I chose.  

 Follow the remaining instructions to create your account (e.g., if creating a
 Linode account for the first time, click on the link in email to activate your
 account, etc.).

B). SELECT AN INSTANCE TYPE

 Next, select an instance type from the menu of available ones you are
 presented with. I chose the simplest/cheapest:

Linode 1024

 (Note: it's conceivable that options and/or names may change over time).

C). SELECT REGION:

 I chose:

Newark, NJ

 At this point, you should be presented with a 'Linode Manager' page indicating
 that your machine is being created.

Being Created

 Wait until it's created - (watch for the message 'Brand New' after refreshing).

Brand New

D). SETUP MACHINE IMAGE:

 1). Install Operating System
  a). From the Linode Manager page, click on the 'Dashboard' and
   under Options to the right click the 'Deploy an Image' link.

   Select the 64-bit Ubuntu 16.04 LTS distribution

   Configs

 2). Choose a strong root password

  From Linode themselves:

Enter a root password for your Linode in the Root Password field. This password must be provided when you log in to your Linode via SSH and must be at least 6 characters long and contain characters from two of the following categories: lowercase and uppercase case letters numbers punctuation characters

 3). Configure remaining details as needed (disk swap sizes, etc.)

  For this exercise, I left default disk sizes for swapping, etc.
  (Note: these are important settings which you should consider
   carefully given your particular requirements).

  Once configuration settings have been set, you should then be met with a
  somewhat more detailed screen listing your configuration details that you
  have chosen.

  Of particular importance here are the items in the Host Job Queue which
  confirm Ubuntu 16.04 LTS as our operating system image on the machine
  and that the root password has been set (we'll need that to login to our
  server from the beginning).

  Confirm Configs

  The convenient 'Remote Access' tab lists the public ip for our machine
  as well as ssh access. In my case these are:

  ipaddress

  SSH Access:

  ssh root@12.345.67.890

  Next, let us Boot our machine - as we can see from the Server Status
  section in the top right of our Dashboard, the status currently reads:

  "Powered Off"

  dashboard

  Click Boot
  and 'OK' to confirm.

  Helpful Guide: https://www.linode.com/docs/getting-started

  (Note: StackScripts is Linode's collection of helpful scripts for server
  deployment, which we've purposefully avoided using here so as to get to
  the underlying concepts).

  For the steps that follow, we'll need to note our newly provisioned server's
  public ip address:

  IP address: 12.345.67.890

At this point, we have successfully provisioned for ourselves a basic server
with an Ubuntu Linux image installed. In the next section, we walkthrough the
very basic steps of securing the server we have just provisioned.

II. Secure the Server

(Note: in the steps below we prioritize securing our freshly-minted server over software updates/upgrades which are performed at the end of this process, once basic security measures have been put into place. One could choose to perform software updates before following these security protocols. The trade off is an exposed server during update time (which might be negligible) versus the (small) chance of using an outdated security package before updates.)

A). REMOVE ATTACK VECTOR: root login

 This means creating a user profile whose name only we know which has root
 privileges but is not the actual role: 'root' (known to everyone).
   1). Start by logging in to the remote server as root (default to begin with):
    ssh root@12.345.67.890

  You will get a message similar to the following:

The authenticity of host 12.345.67.890 (12.345.67.890) cant be 
established. ECDSA key fingerprint is SHA256:i1rDpCRgVqjE5BJl0Xhk0...
Are you sure you want to continue connecting (yes/no)?

  Answer yes to recognize our remote server and to add it to the list of
  known hosts.

  You will then see:
  Warning: Permanently added '12.345.67.890' (ECDSA) to the list of known hosts.

  which means that the remote server has been added to the file:
  ~/.ssh/known_hosts

  on your local machine.

  Immediately after this, you'll be prompted for the root password you setup
  during deployment with Linode.
     Enter that password at the prompt to log in.

  If you remembered your password correctly, you should now be logged into
  your newly provisioned server and should see a prompt similar to the
  following:

Welcome to Ubuntu 16.04.2 LTS (GNU/Linux 4.9.7-x86_64-linode80 x86_64)

 * Documentation:  https://help.ubuntu.com
 * Management:     https://landscape.canonical.com
 * Support:        https://ubuntu.com/advantage
Last login: Tue Mar  7 20:18:09 2017
root@localhost:~#


  This welcome message will vary over time and with the particular
  distribution you have chosen to install. The important thing to note is
  the prompt:
     root@localhost:~#

  Our objective is to change this so we never have to login as 'root' again.

2). To do this, we add a new user account whose profile has root privileges:

  The following commands are Ubuntu-specific (modify them as required,
  particularly if working on a non-Debian derived distribution):
  sudo adduser <user>

  Use a strong password and store it safely (or memorize it)!

  You will then be prompted to enter information for the .
  You can fill these in as you like or just press Enter to skip filling in.

root@localhost:~# sudo adduser <user>
Adding user <user> ...
Adding new group <user> (1000) ...
Adding new user <user> (1000) with group <user> ...
Creating home directory /home/<user> ...
Copying files from /etc/skel ...
Enter new UNIX password:
Retype new UNIX password:
passwd: password updated successfully
Changing the user information for <user>
Enter the new value, or press ENTER for the default
  Full Name []: <user>
  Room Number []:
  Work Phone []:
  Home Phone []:
  Other []:
Is the information correct? [Y/n] Y

  Enter 'Y' when asked if the information is correct to finish adding
  information for our new user.

3). Next, we assign the new user profile root privileges:

  The commands below are specific to the vim text editor (modify them as
  required if vim is unavailable or you prefer another editor like nano, etc.).

  a). Create a sudoer file as follows:
   sudo vim /etc/sudoers.d/<user>

   put this line inside the file:
   <user> ALL=(ALL:ALL) ALL

   This means that for the user we are granting permission to run all
   commands as all users and all groups from all hosts.

   save the file (write and quit):
   :wq

 4). Verify the new user has been created:
   sudo cat /etc/passwd | grep <user>

   this should return an entry.

 5). Switch from our root profile to the new superuser profile:
   su - <user>

   Verify your prompt changes from:
   root@localhost:~#

    to:
    <user>@localhost:~$

 6). Now that we have a new superuser profile, we should disable logins from
  the user 'root'.

  Edit the server-side sshd_config file:
  sudo vim /etc/ssh/sshd_config

  Change the line:
  PermitRootLogin yes

   to:
  PermitRootLogin no

  save and quit:
  :wq

  (Note all work here is being done on the remote server, not your local
  machine. This file is the ssh daemon configuration file which resides
  server-side only. If you're only seeing ssh_config instead of
  sshd_config, it's likely you're on the client rather than server).


 7). Since we changed the sshd_config configurations file, we need the new
  configurations to be read so we must restart the service as follows:
  sudo service ssh restart

 8). Now exit the new superuser profile:
  exit

  which will take you back to the 'root' profile. Let us now exit and
  disconnect from the remote server:
  exit

 9). Let's now confirm that root login to our server has been disabled.

   From the local client, attempt to login as root:
  root@12.345.67.890

  this should fail. Instead, however, you should be able to log in as:
  <user>@12.345.67.890

  (that is, if you haven't forgotten your superuser's password)!

B). REMOVE ATTACK VECTOR: password-based authentication

 Here, we remove password-based authentication as a means of external
 access to our server due to vulnerabilities with this approach (namely,
 weak passwords). In this step, we will substitute Secure Shell (SSH) login
 and make use of public key encryption as a substitute for password-based
 authentication.

 1). On your LOCAL (client) machine, create an ssh directory (if not there):

  (As stated above, the following commands assume the client operating
  system is some flavor of a ^nix distribution (vary these steps as required
  to match your own operating system requirements. Windows clients can
  refer to this).


  mkdir ~/.ssh

  (note this is in your home directory and prefixed '.' )

 2). Now let's create a file called 'config' in this directory to which we will add
  our remote server's connectivity details. If you already have one, just
  append the information below to the existing contents of your config file.

  vim ~/.ssh/config

  Put the following lines in this file:
  Host <hostalias>
  Hostname=12.345.67.890

  The value next to 'Host' is the name you want to give your remote server.
  The value for 'Hostname' should be the public IP address you were given
  by the vendor for your machine.

  save and quit:
  :wq

  This step is meant to simplify remote logins to our server. Whereas up
  until now we used the public IP address of our server to login, we can now
  simply use the hostname we have given our server to do this, as in:

  ssh <user>@<hostalias>

 3). As a first step towards replacing password-based authentication to our
  server, let us now create a key pair. You can read more on this protocol
  here.

  Note that if you already have a key pair generated, you may skip this step
  and simply make use of the private key you already have. This
  discussion weighs the pros and cons of periodically renewing key pairs.

  a). On the client (your LOCAL MACHINE) run the following command (if you
   have no key pairs generated or wish to generate a new pair):
   ssh-keygen

   1). By default, the encryption used is 'RSA'. This can be changed to
    another format with the '-t' argument as follows:
    ssh-keygen -t <encryption>

    A list of supported formats is available here.

    (Yet another option is to use the -f argument to give a specific
     name to the keys you generate here).


    Assuming the standard RSA encryption was used, this will generate
    two keys in your ~/.ssh directory:
    id_rsa
    id_rsa.pub.

    Copy the contents of your public key id_rsa.pub (the private
    key id_rsa should remain private and on the local client).

 4). Back on the REMOTE SERVER we provisioned, create the .ssh directory
  under the superuser's home directory:
  mkdir ~/.ssh

  a). Create a file called 'authorized_keys' and paste your id_rsa.pub
   content into it as follows:

   vim authorized_keys
   Shift + Insert    [ functions as 'paste' command ]
   :wq      [ save and quit ]

  b). Grant required permissions on this directory (you'll need your new
   superuser password for the first one):

   sudo chmod 700 ~/.ssh

   [ grant user read/write/execute permissions ]

   sudo chmod 644 ~/.ssh/authorized_keys

   [ grant user read/write, group and others read permissions ]

  c). Define localhost in hosts file with server IP address. This file
   establishes static associations between IP addresses and hostnames
   and is read first before any DNS lookups.

   sudo vim /etc/hosts

   if necessary, edit the first entry that it looks like this:

   127.0.0.1.1. localhost.localdomain localhost
   :wq

   Different distributions have different requirements. As a result, should
   host resolution errors crop up downstream - when attempting to service
   a request for a hosted web application, for instance - edit this file to
   include the explicit IP address of your server, as follows:

   127.0.0.1.1 localhost ip-12.345.67.890

 5). Test ssh key pair based login.

  a). Quit the server:
   exit

  b). Back on the client machine, let us ssh back to the remote server.

   The command to do this is as follows:
   ssh <user>@<ipAddress> -i <pathToPrivateKey>

   thus:
   ssh <user>@12.345.67.890 -i ~/.ssh/id_rsa

   1). Note that if you took the extra step to edit your ~/.ssh/config
    file on your local machine, our login command to the remote server
    reduces to simply:
    ssh <user>@hostname

    thus:
    ssh <user>@<hostalias>

    This way, you don't have to remember the IP address every time.

C). REMOVE ATTACK VECTOR: default ssh port log-in

 1). Back on the REMOTE SERVER, open the ssh config file for editing:
  sudo vim /etc/ssh/sshd_config

  Make sure you're on the server editing /etc/ssh/sshd_config
  which is the ssh daemon which runs server-side. If all you see
  is /etc/ssh/ssh_config, you're on your local machine as this is
  the client side OpenSSH config file.
  (More on OpenSSH: https://www.openssh.com/manual.html)

 2). Change the ssh port from default to something else:
  change:
  Port 22

  to:
  Port <port>  [ some other available port of your choosing ]

 3). While we're here, let's also disallow password-based authentication:
  change:
  PasswordAuthentication yes

  to:
  PasswordAuthentication no

  Save and exit
  :wq

 4). Restart the server's ssh service as these updated configurations need
  to be reread for them to take effect:
  sudo service ssh restart

 5). Exit the server once again:
  exit

 6). Re-connect from the client using ssh with non-default port:
  ssh <user>@XX.XX.XXX.XX -i <keyfile> -p <port>
  thus:
  ssh <user>@12.345.67.890 -i ~/.ssh/id_rsa -p <port>

  a). For greater convenience, on your LOCAL machine, add the ssh port to
   our config file:
   vim ~/.ssh/config
   add the non-default port as follows:
   Port=<port>
    thus
    Port=<port>
    save and quit:
    :wq

For the record, the local ~/.ssh/config file entry for our remote server  
should look something like this:  
Host <hostalias>  
Hostname=12.345.67.890  
Port=<port>  

  b). To try out the simpler method, do the following.
   Quit the remote server (yet again):
   exit
   Now, back on the client, with the ~/.ssh/config file edited to include
   the port, simply ssh back in again as:
   ssh <user>@<hostalias>
   

D). REMOVE ATTACK VECTOR: unauthorized traffic

 With our server now minimally secured, it is out there, connected to and
 accessible via the internet and as such it is prudent to be selective with any
 incoming traffic from potentially malicious entities.

 Here, we take steps to configure firewall settings so as to permission and
 delegate specific types of traffic/requests to their respective ports using
 Ubuntu's Uncomplicated Firewall UFW
 which is a high-level tool that has made the once tedious tasks of micro-
 managing iptables for firewall configurations a thing of the past.

 While we're likely to want to allow ourselves to be able to reach out to the
 world from our server, incoming traffic to our server is another matter
 altogether and for which our guiding philosophy will be:

Start with no traffic and add on piecemeal

 The UFW does not come enabled by default. This means that with a fresh
 Ubuntu Linux server set up, all traffic is enabled from the get go.

(Note: be sure to process each of the following commands correctly. If
  you make a mistake, you will have a chance to fix it before we enable
  the firewall in the last step. Before it is enabled, however, you need to
  make sure that you haven't setup the firewall to do unintended things -
  key among which would be locking yourself out of the server by disabling
  your chosen ssh port).


 1). Blank Slate: reign in all traffic
  allow ourselves all outgoing traffic:
  sudo ufw default allow outgoing
  deny all incoming traffic:
  sudo ufw default deny incoming

 2). First and foremost, allow incoming ssh requests:
  sudo ufw allow <port>  [ make sure your configured ssh port is here ]

 3). Allow http requests to our server (especially if you're going to host a
  web app on it):
  sudo ufw allow 80

 4). Finally, add any other incoming traffic which might be of value or
  importance to you. A reference for many of these can be found here .

  At a later step, we will be setting our server's timezone for network
  synchronization purposes. The default port for that is 123 so we'll set
  the Network Time Protocol (NTP) for this as follows:
  sudo ufw allow 123

 5). With these commands processed, take a moment to look back in your
  command history to ensure you haven't done anything unintended.
  In particular, make sure you have allowed incoming ssh requests via
  the port you configured in /etc/ssh/sshd_config (originally 22).

  Each rule is updated as you enter the command, hence the return:
  Rules updated

  If you have made a mistake and need to change a port number, simply
  enter the command with the correct port number.

  If you're all good to go, enable the firewall as follows:

  sudo ufw enable

  at which point you'll see the following prompt:

Command may disrupt existing ssh connections.  
Proceed with operation (y|n)?  

  Answer 'y' to confirm. You should then see a confirmation message as
  follows:
  Firewall is active and enabled on system startup

 Another good reference on firewall setups is available here.

To recap, if you have come this far, you have successfully:

1). Removed the 'root' profile from external logins
2). Disabled password-based authentication in favor of the much stronger
 public key encryption method
3). Changed the default ssh port to something other than the default
4). Limited all incoming traffic to only your permissioned activities and only
 to specific ports.

(Be mindful that the steps above comprise a minimum for securing a server.
Plenty of other security issues exist and, of course, security is a hot topic
everywhere. Here is yet another non-exhaustive list of security vulnerabilities
to consider addressing when setting up a server for the first time.)


With these basic steps to securing your newly provisioned server, we're ready
to address any software updates and upgrades your machine may now need.

III. Basic Considerations: Software & Settings


(Now that we've taken the basic steps to secure our freshly-minted server,
we can turn our attention to updates and upgrades of installed software.
This is an opportune time to just go ahead and apply any/all updates as we do
not yet have any tasks (projects/applications) running on the machine where
updates may have unintended consequences in one or more dependencies
in our processes).

TIP: When a machine is mature and has been in use, one should not
automatically apply updates (though package source lists may be kept up to
date) for precisely this reason. If at all possible, a redundancy in the form of
a 'dev' or backup production instance of the server should be used to test
updated software packages in parallel to the un-updated server in order to
pinpoint any unexpected behavior as a result of said updates and to prevent
update-related errors from propagating to the production environment.


A). UPGRADE SOFTWARE - in Ubuntu, this is generally a painless process and is
 as simple as issuing the following commands:

 1). update the package source lists
  sudo apt-get update

  This command merely updates the source lists so as to indicate the newest
  available versions of packages. No software is installed in this step.

 2). perform package updates:
  sudo apt-get upgrade

  This step performs the actual download and installation of packages
  with reference to the source list as updated by 'update' command above.

B). (OPTIONAL/RECOMMENDED) ADDITIONAL SECURITY: fail2ban

 Now that we have our updated our server software packages, we can turn
 our attention to non-base install packages which might be helpful. One such
 package is the fail2ban service, which is meant to protect against brute force
 attacks in hacks using passwords after it detects a certain number of
 unsuccessful login attempts.

 While we have disabled passwords and are using an ssh-based authentication
 protocol, the ssh daemon itself is exposed to the internet when it runs, which
 leaves it vulnerable to a potential attack from hackers. It works by
 auto-reconfiguring the iptable's firewall settings without bothering you but
 can also send you reports when suspected attacks happen.

 As a result, while it's optional, it's a good idea to install this service.
 We should also install sendmail along with it, which is the most popular
 Unix-based email implementation and with which we can have fail2ban
 send us reports.

 a). Since this is a non-standard package, we must specifically install it:
  sudo apt-get install sendmail
  sudo apt-get install fail2ban

 b). Configure sendmail:
  sudo sendmailconfig

  ...and answer 'Y' to prompts (for this exercise, at least).

  since this resulted in a configuration change, we need to:
  sudo service sendmail restart

 c). Configuration files for fail2ban reside in the /etc/fail2ban directory
  where the file jail.conf contains standard configurations but can be
  overwritten in the event of an update.

  As a result, we should create a custom configuration file that won't be
  overwritten by updates by copying this file and editing its contents:
  sudo cp /etc/fail2ban/jail.conf /etc/fail2ban/jail.local

  Ideally, we would only change settings which are not dealt with in
  the standard configuration file but to simplify things let us edit our
  custom '.local' file with our email address for fail2ban reports:
  sudo vim /etc/fail2ban/jail.local

  edit the line:
  destemail = root@localhost

  and change it to:
  destemail = <user>@localhost
  :wq

  sudo service fail2ban restart

 Info: fail2ban.

C). SET TIMEZONE (RECOMMENDED):

 It's advisable to set the server timezone to UTC which is impervious to
 changes in Daylight Savings Time (DST) and synchronizes the timing of jobs
 across geographic regions (for larger organizations down the line) and thus
 makes reading logs much easier:
sudo dpkg-reconfigure tzdata

 Select:
None of the Above

 In the second page, select:
UTC

D). DETERMINE YOUR REQUIREMENTS - software requirements and use-cases
 beyond this point tend to diverge greatly but it's likely you'll need to install some
 helpful non-base utilities (some admins like 'finger' and a whole array of other
 packages) as well any application-specific packages for your environment.

E). GOING FORWARD:

 1). PACKAGE UPDATES - with our basic server setup now satisfied, you should
  be aware that software tends to be updated fairly regularly (some
  packages more so than others). As a result, it's a good idea to stay on
  top of these developments and download, install, and test upgrades
  when possible. This is especially important for security updates.

  It is possible to configure automatic updates known as
  unattended-upgrades in Ubuntu. Here is a reference.

  Another source for managing packages and software updates is this.

 2). Remember: upgrade with caution
  As cautioned before, when the server is mature and handling production
  environment tasks, it's always better to first test updates rather than
  immediately rolling them out into a production environment.

Congratulations - you have just completed one base-line provision and setup of
a barebones Linux server.