diff options
Diffstat (limited to 'blog/2022-03-24-server-hardening.org')
-rw-r--r-- | blog/2022-03-24-server-hardening.org | 638 |
1 files changed, 339 insertions, 299 deletions
diff --git a/blog/2022-03-24-server-hardening.org b/blog/2022-03-24-server-hardening.org index b91ef0e..88fd44e 100644 --- a/blog/2022-03-24-server-hardening.org +++ b/blog/2022-03-24-server-hardening.org @@ -1,23 +1,28 @@ -+++ -date = 2022-03-24 -title = "Hardening a Public-Facing Home Server" -description = "My personal reflection on the steps it took to get a home server ready to be publicly-accessible." -draft = false -+++ - -## Post Updates - -> After reviewing this post today (2022-10-04), I noticed quite a few gaps in my -> write-up and wanted to add a few things, even though this blog is really -> just a retrospective and knowledge dump for myself. -> I left things intact and simply crossed them out (~~like this~~) for -> posterity. - -## Planning Data Flows & Security - -### My Personal Data Flow - -```txt +#+title: Hardening a Public-Facing Home Server +#+date: 2022-03-24 + +** Post Updates +:PROPERTIES: +:CUSTOM_ID: post-updates +:END: + +#+begin_quote +After reviewing this post today (2022-10-04), I noticed quite a few gaps +in my write-up and wanted to add a few things, even though this blog is +really just a retrospective and knowledge dump for myself. I left things +intact and simply crossed them out (+like this+) for posterity. + +#+end_quote + +** Planning Data Flows & Security +:PROPERTIES: +:CUSTOM_ID: planning-data-flows-security +:END: +*** My Personal Data Flow +:PROPERTIES: +:CUSTOM_ID: my-personal-data-flow +:END: +#+begin_src txt ┌───────┐ ┌─────────────────┐ ┌──► VLAN1 ├───► Private Devices │ │ └───────┘ └─────────────────┘ @@ -27,320 +32,355 @@ draft = false │ ┌───────┐ ┌───────────────┐ └──► VLAN2 ├───► Public Server │ └───────┘ └───────────────┘ -``` - -### Thought Process +#+end_src -To serve content from your home server and harden your security posture, you -have to think about the transport of data from `server` to `client`. +*** Thought Process +:PROPERTIES: +:CUSTOM_ID: thought-process +:END: +To serve content from your home server and harden your security posture, +you have to think about the transport of data from =server= to =client=. Let's start with the actual server itself. Think about the following: -- Do I have a firewall enabled? Do I need to update this to allow new ports or - IPs? -- Do I have an IPS/IDS that may prevent outside traffic? -- Do I have any other security software installed? -- Are the services hosted inside Docker containers, behind a reverse proxy, or - virtualized? If so, are they configured to allow outside traffic? - -Once the data leaves the server, where does it go? In my case, it goes to a -managed switch. In this case, I asked the following: - -- What configurations is the switch using? -- Am I using VLANs? - - Yes, I am using 802.1Q VLANs. -- Are the VLANs configured properly? - - Yes, as shown in the [Switch](#switch) section below, I have a separate - VLAN to allow outside traffic to and from the server alone. No other - devices, except for a service port, and in that VLAN. - -At this point, the data has been processed through the switch. Where does it go -next? In my case, it's pretty simple: it goes to the router/modem device. - -- Does my ISP block any ports that I need? - - This is an important step that a lot of people run into when - self-hosting at home. - Use an online port-checker tool for your IP or call your ISP if you - think ports are blocked. -- Is there a router firewall? - - Yes, I checked that it's configured to allow the ports I need to run my - services publicly. Common web servers and reverse proxies require ports - 80 and 443, but other services like media servers or games can require - unique ports, so be sure to check the documentation for your service(s). -- Are there any other settings affecting inbound/outbound traffic? - - Schedules or access blocks - - Static Routing - - QoS - - Port Forwarding - - DMZ Hosting - - Remote Management (this can sometimes mess with services that also - require the use of ports 80 and 443) - -Once the data leaves my router, it goes to the upstream ISP and can be accessed -publicly. - -## Server - -~~The services I run on my server are installed straight into the OS, without -any use of Docker or VMs, so I don't need any extra application configuration to -make them accessible to the outside world.~~ - -As of 2022-10-04, the paragraph above is no longer true as I now run a reverse -proxy with Nginx and host many services inside Docker. However, it doesn't -change anything regarding this post as I still just need to open ports 80 & 443 -and create the necessary website configuration files. - -When creating new services - either installed directly on bare metal or within -something like Docker - I ensure that I read through the documentation -thoroughly to understand a few key things: - - What network activities should this app perform (if any)? Using which ports - and protocols? - - Does this app require any commands/services to be run as `root`? - - Does this app log errors, authentication failures/successes, or anything - else that would be useful for an investigation? - -For extra security, I use limit all incoming connections to SSH connections -through my server firewall (`ufw`) and disable common SSH settings. After all of -that, I use `fail2ban` as a preventative measure against brute-force login -attempts. - -As another piece of security, you can randomize your SSH port to ensure that -random scanners or attackers can't easily try to force their way into your -network. For example, you can edit the port rules in your server to block all -connection requests to port `22` but forward all remote connections from port -`12345` to your server's port `22`. Then you just need to SSH to your network -via your randomized port. - -### `ufw` - -To see how to configure `ufw`, see my other post: -[Secure Your Network with the Uncomplicated Firewall](/blog/secure-your-network-with-the-uncomplicated-firewall/). - -The general notion with an on-device firewall is that you want to deny all -incoming connections by default and then selectively open certain ports for -services or users that you know need access. - -If you know that you will only be logging into this server from a certain set or -list of IPs, you can always set the firewall to only allow connections to port -22 from those IPs. +- Do I have a firewall enabled? Do I need to update this to allow new + ports or IPs? +- Do I have an IPS/IDS that may prevent outside traffic? +- Do I have any other security software installed? +- Are the services hosted inside Docker containers, behind a reverse + proxy, or virtualized? If so, are they configured to allow outside + traffic? + +Once the data leaves the server, where does it go? In my case, it goes +to a managed switch. In this case, I asked the following: + +- What configurations is the switch using? +- Am I using VLANs? + - Yes, I am using 802.1Q VLANs. +- Are the VLANs configured properly? + - Yes, as shown in the [[#switch][Switch]] section below, I have a + separate VLAN to allow outside traffic to and from the server alone. + No other devices, except for a service port, and in that VLAN. + +At this point, the data has been processed through the switch. Where +does it go next? In my case, it's pretty simple: it goes to the +router/modem device. + +- Does my ISP block any ports that I need? + - This is an important step that a lot of people run into when + self-hosting at home. Use an online port-checker tool for your IP or + call your ISP if you think ports are blocked. +- Is there a router firewall? + - Yes, I checked that it's configured to allow the ports I need to run + my services publicly. Common web servers and reverse proxies require + ports 80 and 443, but other services like media servers or games can + require unique ports, so be sure to check the documentation for your + service(s). +- Are there any other settings affecting inbound/outbound traffic? + - Schedules or access blocks + - Static Routing + - QoS + - Port Forwarding + - DMZ Hosting + - Remote Management (this can sometimes mess with services that also + require the use of ports 80 and 443) + +Once the data leaves my router, it goes to the upstream ISP and can be +accessed publicly. + +** Server +:PROPERTIES: +:CUSTOM_ID: server +:END: ++The services I run on my server are installed straight into the OS, +without any use of Docker or VMs, so I don't need any extra application +configuration to make them accessible to the outside world.+ + +As of 2022-10-04, the paragraph above is no longer true as I now run a +reverse proxy with Nginx and host many services inside Docker. However, +it doesn't change anything regarding this post as I still just need to +open ports 80 & 443 and create the necessary website configuration +files. + +When creating new services - either installed directly on bare metal or +within something like Docker - I ensure that I read through the +documentation thoroughly to understand a few key things: - What network +activities should this app perform (if any)? Using which ports and +protocols? - Does this app require any commands/services to be run as +=root=? - Does this app log errors, authentication failures/successes, +or anything else that would be useful for an investigation? + +For extra security, I use limit all incoming connections to SSH +connections through my server firewall (=ufw=) and disable common SSH +settings. After all of that, I use =fail2ban= as a preventative measure +against brute-force login attempts. + +As another piece of security, you can randomize your SSH port to ensure +that random scanners or attackers can't easily try to force their way +into your network. For example, you can edit the port rules in your +server to block all connection requests to port =22= but forward all +remote connections from port =12345= to your server's port =22=. Then +you just need to SSH to your network via your randomized port. + +*** =ufw= +:PROPERTIES: +:CUSTOM_ID: ufw +:END: +To see how to configure =ufw=, see my other post: +[[/blog/secure-your-network-with-the-uncomplicated-firewall/][Secure +Your Network with the Uncomplicated Firewall]]. + +The general notion with an on-device firewall is that you want to deny +all incoming connections by default and then selectively open certain +ports for services or users that you know need access. + +If you know that you will only be logging into this server from a +certain set or list of IPs, you can always set the firewall to only +allow connections to port 22 from those IPs. For a quick start to only allow SSH connections to the server, use this: -```sh +#+begin_src sh sudo ufw default deny incoming sudo ufw default allow outgoing sudo ufw allow 22 sudo ufw enable -``` - - - -### `ssh` - -#### Using SSH Keys - -First, make sure you have an SSH keypair generated on the device(s) that you'll -be using to log in to the server. If you don't have an SSH key, run this -command: - -```sh +#+end_src + +#+caption: ufw +[[https://img.cleberg.net/blog/20220324-hardening-a-public-facing-home-server/ufw.png]] + +*** =ssh= +:PROPERTIES: +:CUSTOM_ID: ssh +:END: +**** Using SSH Keys +:PROPERTIES: +:CUSTOM_ID: using-ssh-keys +:END: +First, make sure you have an SSH keypair generated on the device(s) that +you'll be using to log in to the server. If you don't have an SSH key, +run this command: + +#+begin_src sh ssh-keygen -``` +#+end_src -Now that we have an SSH key, copy it to the server with the following command, -which will ask for the user's password before accepting the key: +Now that we have an SSH key, copy it to the server with the following +command, which will ask for the user's password before accepting the +key: -```sh +#+begin_src sh ssh-copy-id my_user@my_server -``` +#+end_src -If you have multiple keys, you'll need to specify which to use. After it's -complete, `ssh` back into the server as that user and make sure it doesn't ask -for a password. +If you have multiple keys, you'll need to specify which to use. After +it's complete, =ssh= back into the server as that user and make sure it +doesn't ask for a password. -#### Disable Password & Root Authentication +**** Disable Password & Root Authentication +:PROPERTIES: +:CUSTOM_ID: disable-password-root-authentication +:END: +Now that we can access the server without a password, we will disable +password authentication and disable anyone from using =ssh= to login as +=root=. -Now that we can access the server without a password, we will disable password -authentication and disable anyone from using `ssh` to login as `root`. +To do this, open the =sshd_config= file: -To do this, open the `sshd_config` file: - -```sh +#+begin_src sh sudo nano /etc/ssh/sshd_config -``` +#+end_src -You'll need to update the parameters to the values below. If one of these rules -is commented-out or doesn't exist, create the rule at the bottom of the file. +You'll need to update the parameters to the values below. If one of +these rules is commented-out or doesn't exist, create the rule at the +bottom of the file. -```config +#+begin_src config PermitRootLogin no PasswordAuthentication no PubkeyAuthentication yes -``` +#+end_src -Finally, restart the `ssh` service: +Finally, restart the =ssh= service: -```sh +#+begin_src sh sudo systemctl restart sshd.service -``` - -To test that everything’s working so far, open ANOTHER terminal and try logging -in as `root` over SSH. It is very important that you keep your current SSH -session open and test with an additional session, or you will lock yourself out -at some point and will need to use a recovery method (e.g., hooking monitor up -to home server) to get yourself back in. - -#### Enable MFA for `ssh` - -This part is optional, but I highly recommend it. -So far, we've ensured that no one can log into our user on the server -without using our secret key, and we've ensured that no one can log in -remotely as `root`. -Next, you can enable MFA authentication for `ssh` connections. - -This process involves editing a couple files and installing an MFA package, so I -will not include all the details in this post. To see how to configure MFA for -`ssh`, see my other post: -[Enabling MFA for SSH](/blog/enable-totp-mfa-for-ssh/). - - - -### `fail2ban` - -I haven't written a post on how I use `fail2ban`, but it's quite simple. I use -the default `sshd` jail, but you can always create new jails for respective -applications or ports. For example, if you use Nginx as your web server, you can -use the `nginx-http-auth` jail. +#+end_src + +To test that everything's working so far, open ANOTHER terminal and try +logging in as =root= over SSH. It is very important that you keep your +current SSH session open and test with an additional session, or you +will lock yourself out at some point and will need to use a recovery +method (e.g., hooking monitor up to home server) to get yourself back +in. + +**** Enable MFA for =ssh= +:PROPERTIES: +:CUSTOM_ID: enable-mfa-for-ssh +:END: +This part is optional, but I highly recommend it. So far, we've ensured +that no one can log into our user on the server without using our secret +key, and we've ensured that no one can log in remotely as =root=. Next, +you can enable MFA authentication for =ssh= connections. + +This process involves editing a couple files and installing an MFA +package, so I will not include all the details in this post. To see how +to configure MFA for =ssh=, see my other post: +[[/blog/enable-totp-mfa-for-ssh/][Enabling MFA for SSH]]. + +#+caption: SSH MFA +[[https://img.cleberg.net/blog/20220324-hardening-a-public-facing-home-server/ssh_mfa.png]] + +*** =fail2ban= +:PROPERTIES: +:CUSTOM_ID: fail2ban +:END: +I haven't written a post on how I use =fail2ban=, but it's quite simple. +I use the default =sshd= jail, but you can always create new jails for +respective applications or ports. For example, if you use Nginx as your +web server, you can use the =nginx-http-auth= jail. In order to get it up and running, use the following commands: -```sh +#+begin_src sh sudo apt install fail2ban sudo fail2ban-client start sshd sudo fail2ban-client status sshd -``` - -This should be used as a last-resort defense and shouldn't be a replacement for -the security measures mentioned above. - - - -## Switch - -Between the router and any local devices is my managed switch, which is used to -create VLANs. The example below shows how I would isolate the VLANs if I were -starting to host a single service at home. - -### 802.1Q VLAN Configuration - -In this configuration, port 8 is the public server that needs to be accessed -from the outside. Port 23 is my 'dedicated service port' for this server. In -order to SSH to this server, I need to plug my laptop into port 23 or else I -cannot SSH. Otherwise, I'd need to hook up a monitor and keyboard directly to -the server to manage it. +#+end_src + +This should be used as a last-resort defense and shouldn't be a +replacement for the security measures mentioned above. + +#+caption: fail2ban +[[https://img.cleberg.net/blog/20220324-hardening-a-public-facing-home-server/fail2ban.png]] + +** Switch +:PROPERTIES: +:CUSTOM_ID: switch +:END: +Between the router and any local devices is my managed switch, which is +used to create VLANs. The example below shows how I would isolate the +VLANs if I were starting to host a single service at home. + +*** 802.1Q VLAN Configuration +:PROPERTIES: +:CUSTOM_ID: q-vlan-configuration +:END: +In this configuration, port 8 is the public server that needs to be +accessed from the outside. Port 23 is my 'dedicated service port' for +this server. In order to SSH to this server, I need to plug my laptop +into port 23 or else I cannot SSH. Otherwise, I'd need to hook up a +monitor and keyboard directly to the server to manage it. | VLAN ID | VLAN Name | Member Ports | Tagged Ports | Untagged Ports | -|:-------:|:---------:|:------------:|:------------:|:--------------:| -| 1 | Default | 1-24 | | 1-24 | -| 2 | Server | 1,8,23 | | 1,8,23 | - -### 802.1Q VLAN PVID Setting - -Once the VLAN is created, I simply add the `VLAN ID` of `2` as the `PVID` for -any related ports (in this case, see that ports `8` and `23` have a PVID of -`2`). +|---------+-----------+--------------+--------------+----------------| +| 1 | Default | 1-24 | | 1-24 | +| 2 | Server | 1,8,23 | | 1,8,23 | + +*** 802.1Q VLAN PVID Setting +:PROPERTIES: +:CUSTOM_ID: q-vlan-pvid-setting +:END: +Once the VLAN is created, I simply add the =VLAN ID= of =2= as the +=PVID= for any related ports (in this case, see that ports =8= and =23= +have a PVID of =2=). | Port | PVID | -|:----:|:----:| -| 1 | 1 | -| 2 | 1 | -| 3 | 1 | -| 4 | 1 | -| 5 | 1 | -| 6 | 1 | -| 7 | 1 | -| 8 | 2 | -| 9 | 1 | -| 10 | 1 | -| 11 | 1 | -| 12 | 1 | -| 13 | 1 | -| 14 | 1 | -| 15 | 1 | -| 16 | 1 | -| 17 | 1 | -| 18 | 1 | -| 19 | 1 | -| 20 | 1 | -| 21 | 1 | -| 22 | 1 | -| 23 | 2 | -| 24 | 1 | - -## Router - -On my router, the configuration was as easy as opening the firewall settings and -unblocking the ports I needed for my services (e.g., HTTP/S, Plex, SSH, MySQL, -etc.). - -~~Since I'm relying on an ISP-provided modem/router combo for now (not by -choice), I do not use any other advanced settings on my router that would -inhibit any valid traffic to these services.~~ - -The paragraph above regarding the ISP-owned router is no longer accurate as I -now use the Ubiquiti Unifi Dream Machine Pro as my router. -Within this router, I enabled port forwarding/firewall rules, segregate the -network based on the device, and enable traffic restrictions (e.g., silently -drop traffic from certain countries and threat categories). - -If you have the option with your ISP, I recommend using a personal router with -software that you are familiar with so that you can explore all the options -available to you. - -## Physical Security - -One large piece of self-hosting that people generally don't discuss online is -physical security. However, physical security is very important for everyone who -hosts a server like this. Exactly *how* important it is depends on the server -use/purpose. - -If you self-host customer applications that hold protected data (HIPAA, GDPR, -COPPA, etc.), then physical security is extremely important and cannot be -ignored. If you simply host a blog and some hobby sites, then it's a relatively -minor consideration, but one you still need to think about. - -### Location - -The first consideration is quite simple: location. - - Is the server within a property you own or housed on someone else's property? - - Is it nearby (in your house, in your work office, in your neighbor's garage, - in a storage unit, etc.)? - - Do you have 24/7 access to the server? - - Are there climate considerations, such as humidity, fires, tornadoes, - monsoons? - - Do you have emergency equipment nearby in case of emergency? - -### Hardware Ownership - -Secondly, consider the hardware itself: - - Do you own the server in its entirety? - - Are any other users able to access the server, even if your data/space is - segregated? - - If you're utilizing a third party, do they have any documentation to show - responsibility? This could be a SOC 1/2/3 report, ISO compliance report, - internal security/safety documentation. - -### Physical Controls - -Regardless of who owns the hardware, ensure that there are adequate safeguards -in place, if necessary. These usually don't apply to small home servers and are -usually covered already if you're utilizing a third party. - -These can include: - - Server bezel locks - - Server room locks - physical, digital, or biometric authentication - - Security cameras - - Raised floors/lowered ceilings with proper guards/gates in-place within the - floors or ceilings - - Security personnel - - Log sheets and/or guest badges +|------+------| +| 1 | 1 | +| 2 | 1 | +| 3 | 1 | +| 4 | 1 | +| 5 | 1 | +| 6 | 1 | +| 7 | 1 | +| 8 | 2 | +| 9 | 1 | +| 10 | 1 | +| 11 | 1 | +| 12 | 1 | +| 13 | 1 | +| 14 | 1 | +| 15 | 1 | +| 16 | 1 | +| 17 | 1 | +| 18 | 1 | +| 19 | 1 | +| 20 | 1 | +| 21 | 1 | +| 22 | 1 | +| 23 | 2 | +| 24 | 1 | + +** Router +:PROPERTIES: +:CUSTOM_ID: router +:END: +On my router, the configuration was as easy as opening the firewall +settings and unblocking the ports I needed for my services (e.g., +HTTP/S, Plex, SSH, MySQL, etc.). + ++Since I'm relying on an ISP-provided modem/router combo for now (not by +choice), I do not use any other advanced settings on my router that +would inhibit any valid traffic to these services.+ + +The paragraph above regarding the ISP-owned router is no longer accurate +as I now use the Ubiquiti Unifi Dream Machine Pro as my router. Within +this router, I enabled port forwarding/firewall rules, segregate the +network based on the device, and enable traffic restrictions (e.g., +silently drop traffic from certain countries and threat categories). + +If you have the option with your ISP, I recommend using a personal +router with software that you are familiar with so that you can explore +all the options available to you. + +** Physical Security +:PROPERTIES: +:CUSTOM_ID: physical-security +:END: +One large piece of self-hosting that people generally don't discuss +online is physical security. However, physical security is very +important for everyone who hosts a server like this. Exactly /how/ +important it is depends on the server use/purpose. + +If you self-host customer applications that hold protected data (HIPAA, +GDPR, COPPA, etc.), then physical security is extremely important and +cannot be ignored. If you simply host a blog and some hobby sites, then +it's a relatively minor consideration, but one you still need to think +about. + +*** Location +:PROPERTIES: +:CUSTOM_ID: location +:END: +The first consideration is quite simple: location. - Is the server +within a property you own or housed on someone else's property? - Is it +nearby (in your house, in your work office, in your neighbor's garage, +in a storage unit, etc.)? - Do you have 24/7 access to the server? - Are +there climate considerations, such as humidity, fires, tornadoes, +monsoons? - Do you have emergency equipment nearby in case of emergency? + +*** Hardware Ownership +:PROPERTIES: +:CUSTOM_ID: hardware-ownership +:END: +Secondly, consider the hardware itself: - Do you own the server in its +entirety? - Are any other users able to access the server, even if your +data/space is segregated? - If you're utilizing a third party, do they +have any documentation to show responsibility? This could be a SOC 1/2/3 +report, ISO compliance report, internal security/safety documentation. + +*** Physical Controls +:PROPERTIES: +:CUSTOM_ID: physical-controls +:END: +Regardless of who owns the hardware, ensure that there are adequate +safeguards in place, if necessary. These usually don't apply to small +home servers and are usually covered already if you're utilizing a third +party. + +These can include: - Server bezel locks - Server room locks - physical, +digital, or biometric authentication - Security cameras - Raised +floors/lowered ceilings with proper guards/gates in-place within the +floors or ceilings - Security personnel - Log sheets and/or guest badges |