Commit b8bc597ac6
Unsigned
Layout: unified · split
content/blog/#2022-03-24-server-hardening.org# deleted −293
| @@ -1,293 +0,0 @@ | ||
| 1 | #+date: <2022-03-24> | |
| 2 | #+title: Hardening a Public-Facing Home Server | |
| 3 | #+description: | |
| 4 | ||
| 5 | ||
| 6 | * Post Updates | |
| 7 | ||
| 8 | #+begin_quote | |
| 9 | After reviewing this post today (2022-10-04), I noticed quite a few gaps in my | |
| 10 | write-up and wanted to add a few things, even though this blog is really just a | |
| 11 | retrospective and knowledge dump for myself. I left things intact and simply | |
| 12 | crossed them out (+like this+) for posterity. | |
| 13 | #+end_quote | |
| 14 | ||
| 15 | * Planning Data Flows & Security | |
| 16 | ||
| 17 | ** My Personal Data Flow | |
| 18 | ||
| 19 | #+begin_src | |
| 20 | ┌───────┐ ┌─────────────────┐ | |
| 21 | ┌──► VLAN1 ├───► Private Devices │ | |
| 22 | │ └───────┘ └─────────────────┘ | |
| 23 | ┌──────────┐ ┌────────┐ ┌──────────┐ ┌────────┐ │ | |
| 24 | │ Internet ├───► Router ├───► Firewall ├───► Switch ├──┤ | |
| 25 | └──────────┘ └────────┘ └──────────┘ └────────┘ │ | |
| 26 | │ ┌───────┐ ┌───────────────┐ | |
| 27 | └──► VLAN2 ├───► Public Server │ | |
| 28 | └───────┘ └───────────────┘ | |
| 29 | #+end_src | |
| 30 | ||
| 31 | ** Thought Process | |
| 32 | ||
| 33 | To serve content from your home server and harden your security posture, you | |
| 34 | have to think about the transport of data from =server= to =client=. | |
| 35 | ||
| 36 | Let's start with the actual server itself. Think about the following: | |
| 37 | ||
| 38 | - Do I have a firewall enabled? Do I need to update this to allow new ports or | |
| 39 | IPs? | |
| 40 | - Do I have an IPS/IDS that may prevent outside traffic? | |
| 41 | - Do I have any other security software installed? | |
| 42 | - Are the services hosted inside Docker containers, behind a reverse proxy, or | |
| 43 | virtualized? If so, are they configured to allow outside traffic? | |
| 44 | ||
| 45 | Once the data leaves the server, where does it go? In my case, it goes to a | |
| 46 | managed switch. In this case, I asked the following: | |
| 47 | ||
| 48 | - What configurations is the switch using? | |
| 49 | - Am I using VLANs? | |
| 50 | - Yes, I am using 802.1Q VLANs. | |
| 51 | - Are the VLANs configured properly? | |
| 52 | - Yes, as shown in the Switch section below, I have a separate VLAN to allow | |
| 53 | outside traffic to and from the server alone. No other devices, except for a | |
| 54 | service port, and in that VLAN. | |
| 55 | ||
| 56 | At this point, the data has been processed through the switch. Where does it go | |
| 57 | next? In my case, it's pretty simple: it goes to the router/modem device. | |
| 58 | ||
| 59 | - Does my ISP block any ports that I need? | |
| 60 | - This is an important step that a lot of people run into when self-hosting at | |
| 61 | home. Use an online port-checker tool for your IP or call your ISP if you | |
| 62 | think ports are blocked. | |
| 63 | - Is there a router firewall? | |
| 64 | - Yes, I checked that it's configured to allow the ports I need to run my | |
| 65 | services publicly. Common web servers and reverse proxies require ports 80 | |
| 66 | and 443, but other services like media servers or games can require unique | |
| 67 | ports, so be sure to check the documentation for your service(s). | |
| 68 | - Are there any other settings affecting inbound/outbound traffic? | |
| 69 | - Schedules or access blocks | |
| 70 | - Static Routing | |
| 71 | - QoS | |
| 72 | - Port Forwarding | |
| 73 | - DMZ Hosting | |
| 74 | - Remote Management (this can sometimes mess with services that also require | |
| 75 | the use of ports 80 and 443) | |
| 76 | ||
| 77 | Once the data leaves my router, it goes to the upstream ISP and can be accessed | |
| 78 | publicly. | |
| 79 | ||
| 80 | *** Server | |
| 81 | ||
| 82 | The services I run on my server are installed straight into the OS, without any | |
| 83 | use of Docker or VMs, so I don't need any extra application configuration to | |
| 84 | make them accessible to the outside world.+ | |
| 85 | ||
| 86 | As of 2022-10-04, the paragraph above is no longer true as I now run a reverse | |
| 87 | proxy with Nginx and host many services inside Docker. However, it doesn't | |
| 88 | change anything regarding this post as I still just need to open ports 80 & 443 | |
| 89 | and create the necessary website configuration files. | |
| 90 | ||
| 91 | When creating new services - either installed directly on bare metal or within | |
| 92 | something like Docker - I ensure that I read through the documentation | |
| 93 | thoroughly to understand a few key things: - What network activities should this | |
| 94 | app perform (if any)? Using which ports and protocols? - Does this app require | |
| 95 | any commands/services to be run as =root=? - Does this app log errors, | |
| 96 | authentication failures/successes, or anything else that would be useful for an | |
| 97 | investigation? | |
| 98 | ||
| 99 | For extra security, I use limit all incoming connections to SSH connections | |
| 100 | through my server firewall (=ufw=) and disable common SSH settings. After all of | |
| 101 | that, I use =fail2ban= as a preventative measure against brute-force login | |
| 102 | attempts. | |
| 103 | ||
| 104 | As another piece of security, you can randomize your SSH port to ensure that | |
| 105 | random scanners or attackers can't easily try to force their way into your | |
| 106 | network. For example, you can edit the port rules in your server to block all | |
| 107 | connection requests to port =22= but forward all remote connections from port | |
| 108 | =12345= to your server's port =22=. Then you just need to SSH to your network | |
| 109 | via your randomized port. | |
| 110 | ||
| 111 | ** =ufw= | |
| 112 | ||
| 113 | To see how to configure =ufw=, see my other post: [[/blog/ufw.html][Secure Your | |
| 114 | Network with the Uncomplicated Firewall]]. | |
| 115 | ||
| 116 | The general notion with an on-device firewall is that you want to deny all | |
| 117 | incoming connections by default and then selectively open certain ports for | |
| 118 | services or users that you know need access. | |
| 119 | ||
| 120 | If you know that you will only be logging into this server from a certain set or | |
| 121 | list of IPs, you can always set the firewall to only allow connections to port | |
| 122 | 22 from those IPs. | |
| 123 | ||
| 124 | For a quick start to only allow SSH connections to the server, use this: | |
| 125 | ||
| 126 | #+begin_src sh | |
| 127 | sudo ufw default deny incoming | |
| 128 | sudo ufw default allow outgoing | |
| 129 | sudo ufw allow 22 | |
| 130 | sudo ufw enable | |
| 131 | #+end_src | |
| 132 | ||
| 133 | ** =ssh= | |
| 134 | ||
| 135 | 1. Using SSH Keys | |
| 136 | ||
| 137 | First, make sure you have an SSH keypair generated on the device(s) that | |
| 138 | you'll be using to log in to the server. If you don't have an SSH key, run | |
| 139 | this command: | |
| 140 | ||
| 141 | #+begin_src sh | |
| 142 | ssh-keygen | |
| 143 | #+end_src | |
| 144 | ||
| 145 | Now that we have an SSH key, copy it to the server with the following | |
| 146 | command, which will ask for the user's password before accepting the key: | |
| 147 | ||
| 148 | #+begin_src sh | |
| 149 | ssh-copy-id my_user@my_server | |
| 150 | #+end_src | |
| 151 | ||
| 152 | If you have multiple keys, you'll need to specify which to use. After it's | |
| 153 | complete, =ssh= back into the server as that user and make sure it doesn't | |
| 154 | ask for a password. | |
| 155 | ||
| 156 | 2. Disable Password & Root Authentication | |
| 157 | ||
| 158 | Now that we can access the server without a password, we will disable | |
| 159 | password authentication and disable anyone from using =ssh= to login as | |
| 160 | =root=. | |
| 161 | ||
| 162 | To do this, open the =sshd_config= file: | |
| 163 | ||
| 164 | #+begin_src sh | |
| 165 | sudo nano /etc/ssh/sshd_config | |
| 166 | #+end_src | |
| 167 | ||
| 168 | You'll need to update the parameters to the values below. If one of these | |
| 169 | rules is commented-out or doesn't exist, create the rule at the bottom of the | |
| 170 | file. | |
| 171 | ||
| 172 | #+begin_src config | |
| 173 | PermitRootLogin no | |
| 174 | PasswordAuthentication no | |
| 175 | PubkeyAuthentication yes | |
| 176 | #+end_src | |
| 177 | ||
| 178 | Finally, restart the =ssh= service: | |
| 179 | ||
| 180 | #+begin_src sh | |
| 181 | sudo systemctl restart sshd.service | |
| 182 | #+end_src | |
| 183 | ||
| 184 | To test that everything's working so far, open ANOTHER terminal and try | |
| 185 | logging in as =root= over SSH. It is very important that you keep your | |
| 186 | current SSH session open and test with an additional session, or you will | |
| 187 | lock yourself out at some point and will need to use a recovery method (e.g., | |
| 188 | hooking monitor up to home server) to get yourself back in. | |
| 189 | ||
| 190 | 3. Enable MFA for =ssh= | |
| 191 | ||
| 192 | This part is optional, but I highly recommend it. So far, we've ensured that | |
| 193 | no one can log into our user on the server without using our secret key, and | |
| 194 | we've ensured that no one can log in remotely as =root=. Next, you can enable | |
| 195 | MFA authentication for =ssh= connections. | |
| 196 | ||
| 197 | This process involves editing a couple files and installing an MFA package, | |
| 198 | so I will not include all the details in this post. To see how to configure | |
| 199 | MFA for =ssh=, see my other post: [[/blog/ssh-mfa.html][Enabling MFA for | |
| 200 | SSH]]. | |
| 201 | ||
| 202 | ** =fail2ban= | |
| 203 | ||
| 204 | I haven't written a post on how I use =fail2ban=, but it's quite simple. I use | |
| 205 | the default =sshd= jail, but you can always create new jails for respective | |
| 206 | applications or ports. For example, if you use Nginx as your web server, you can | |
| 207 | use the =nginx-http-auth= jail. | |
| 208 | ||
| 209 | In order to get it up and running, use the following commands: | |
| 210 | ||
| 211 | #+begin_src sh | |
| 212 | sudo apt install fail2ban | |
| 213 | sudo fail2ban-client start sshd | |
| 214 | sudo fail2ban-client status sshd | |
| 215 | #+end_src | |
| 216 | ||
| 217 | This should be used as a last-resort defense and shouldn't be a replacement for | |
| 218 | the security measures mentioned above. | |
| 219 | ||
| 220 | * Switch | |
| 221 | ||
| 222 | Between the router and any local devices is my managed switch, which is used to | |
| 223 | create VLANs. The example below shows how I would isolate the VLANs if I were | |
| 224 | starting to host a single service at home. | |
| 225 | ||
| 226 | ** 802.1Q VLAN Configuration | |
| 227 | ||
| 228 | In this configuration, port 8 is the public server that needs to be accessed | |
| 229 | from the outside. Port 23 is my 'dedicated service port' for this server. In | |
| 230 | order to SSH to this server, I need to plug my laptop into port 23 or else I | |
| 231 | cannot SSH. Otherwise, I'd need to hook up a monitor and keyboard directly to | |
| 232 | the server to manage it. | |
| 233 | ||
| 234 | #+begin_verse | |
| 235 | #+end_verse | |
| 236 | ||
| 237 | | VLAN ID | VLAN Name | Member Ports | Tagged Ports | Untagged Ports | | |
| 238 | |---------+-----------+--------------+--------------+----------------| | |
| 239 | | 1 | Default | 1-24 | 1-24 | | | |
| 240 | | 2 | Server | 1,8,23 | 1,8,23 | | | |
| 241 | ||
| 242 | ** 802.1Q VLAN PVID Setting | |
| 243 | ||
| 244 | Once the VLAN is created, I simply add the =VLAN ID= of =2= as the =PVID= for | |
| 245 | any related ports (in this case, see that ports =8= and =23= have a PVID of | |
| 246 | =2=). | |
| 247 | ||
| 248 | | Port | PVID | | |
| 249 | |------+------| | |
| 250 | | 1 | 1 | | |
| 251 | | 2 | 1 | | |
| 252 | | 3 | 1 | | |
| 253 | | 4 | 1 | | |
| 254 | | 5 | 1 | | |
| 255 | | 6 | 1 | | |
| 256 | | 7 | 1 | | |
| 257 | | 8 | 2 | | |
| 258 | | 9 | 1 | | |
| 259 | | 10 | 1 | | |
| 260 | | 11 | 1 | | |
| 261 | | 12 | 1 | | |
| 262 | | 13 | 1 | | |
| 263 | | 14 | 1 | | |
| 264 | | 15 | 1 | | |
| 265 | | 16 | 1 | | |
| 266 | | 17 | 1 | | |
| 267 | | 18 | 1 | | |
| 268 | | 19 | 1 | | |
| 269 | | 20 | 1 | | |
| 270 | | 21 | 1 | | |
| 271 | | 22 | 1 | | |
| 272 | | 23 | 2 | | |
| 273 | | 24 | 1 | | |
| 274 | ||
| 275 | * Router | |
| 276 | ||
| 277 | On my router, the configuration was as easy as opening the firewall settings and | |
| 278 | unblocking the ports I needed for my services (e.g., HTTP/S, Plex, SSH, MySQL, | |
| 279 | etc.). | |
| 280 | ||
| 281 | Since I'm relying on an ISP-provided modem/router combo for now (not by | |
| 282 | choice), I do not use any other advanced settings on my router that would | |
| 283 | inhibit any valid traffic to these services. | |
| 284 | ||
| 285 | The paragraph above regarding the ISP-owned router is no longer accurate as I | |
| 286 | now use the Ubiquiti Unifi Dream Machine Pro as my router. Within this router, I | |
| 287 | enabled port forwarding/firewall rules, segregate the network based on the | |
| 288 | device, and enable traffic restrictions (e.g., silently drop traffic from | |
| 289 | certain countries and threat categories). | |
| 290 | ||
| 291 | If you have the option with your ISP, I recommend using a personal router with | |
| 292 | software that you are familiar with so that you can explore all the options | |
| 293 | available to you. | |