aboutsummaryrefslogtreecommitdiff
path: root/content/blog/2022-04-02-nginx-reverse-proxy.org
blob: f1aaa19dad89d229960d11c7043507a5ce26ff21 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
#+date: <2022-04-02>
#+title: Set-Up a Reverse Proxy with Nginx
#+description: 
#+slug: nginx-reverse-proxy

* What is a Reverse Proxy?

A reverse proxy is a server that is placed between local servers or
services and clients/users (e.g., the internet). The reverse proxy
intercepts all requests from clients at the network edge and uses its
configuration files to determine where each request should be sent.

** A Brief Example

For example, let's say that I run three servers in my home:

- Server01 (=example.com=)
- Server02 (=service01.example.com=)
- Server03 (=service02.example.com=)

I also run a reverse proxy in my home that intercepts all public
traffic:

- Reverse Proxy

Assume that I have a domain name (=example.com=) that allows clients to
request websites or services from my home servers.

In this case, the reverse proxy will intercept all traffic from
=example.com= that enters my network and determine if the client is
requesting valid data, based on my configuration.

If the user is requesting =example.com= and my configuration files say
that Server_{01} holds that data, Nginx will send the user to
Server_{01}. If I were to change the configuration so that =example.com=
is routed to Server_{02}, that same user would be sent to Server_{02}
instead.

#+begin_src txt
┌──────┐                                              ┌───────────┐
│ User │─┐                                         ┌──► Server_01 │
└──────┘ │                                         │  └───────────┘
         │    ┌──────────┐   ┌───────────────┐     │  ┌───────────┐
         ├────► Internet ├───► Reverse Proxy ├─────├──► Server_02 │
         │    └──────────┘   └───────────────┘     │  └───────────┘
┌──────┐ │                                         │  ┌───────────┐
│ User │─┘                                         └──► Server_03 │
└──────┘                                              └───────────┘
#+end_src

* Reverse Proxy Options

There are a lot of options when it comes to reverse proxy servers, so
I'm just going to list a few of the options I've heard recommended over
the last few years:

- [[https://nginx.com][Nginx]]
- [[https://caddyserver.com][Caddy]]
- [[https://traefik.io/][Traefik]]
- [[https://www.haproxy.org/][HAProxy]]
- [[https://ubuntu.com/server/docs/proxy-servers-squid][Squid]]

In this post, we will be using Nginx as our reverse proxy, running on
Ubuntu Server 20.04.4 LTS.

* Nginx Reverse Proxy Example

** Local Applications

You may be like me and have a lot of applications running on your local
network that you'd like to expose publicly with a domain.

In my case, I have services running in multiple Docker containers within
a single server and want a way to visit those services from anywhere
with a URL. For example, on my local network,
[[https://dashy.to][Dashy]] runs through port 4000 (=localhost:4000=)
and [[https://github.com/louislam/uptime-kuma][Uptime Kuma]] runs
through port 3001 (=localhost:3001=).

In order to expose these services to the public, I will need to do the
following:

1. Set up DNS records for a domain or subdomain (one per service) to
   point toward the IP address of the server.
2. Open up the server network's HTTP and HTTPS ports (80 & 443) so that
   the reverse proxy can accept traffic and determine where to send it.
3. Install the reverse proxy software.
4. Configure the reverse proxy to recognize which service should get
   traffic from any of the domains or subdomains.

** Step 1: DNS Configuration

To start, update your DNS configuration so that you have an =A= record
for each domain or subdomain.

The =A= records should point toward the public IP address of the server.
If you don't know the public IP address, log in to the server and run
the following command:

#+begin_src sh
curl ifconfig.co
#+end_src

In the DNS example below, =xxx.xxx.xxx.xxx= is the public IP address of
the server.

#+begin_src conf
example.com             A        xxx.xxx.xxx.xxx
uptime.example.com      A        xxx.xxx.xxx.xxx
dashy.example.com       A        xxx.xxx.xxx.xxx
www                     CNAME    example.com
#+end_src

Finally, ensure the DNS has propagated correctly with
[[https://dnschecker.org][DNS Checker]] by entering your domains or
subdomains in the search box and ensuring the results are showing the
correct IP address.

** Step 2: Open Network Ports

This step will be different depending on which router you have in your
home. If you're not sure, try to visit
[[http://192.168.1.1][192.168.1.1]] in your browser. Login credentials
are usually written on a sticker somewhere on your modem/router.

Once you're able to log in to your router, find the Port Forwarding
settings. You will need to forward ports =80= and =443= to whichever
machine is running the reverse proxy.

In my case, the table below shows the port-forwarding rules I've
created. In this table, =xxx.xxx.xxx.xxx= is the local device IP of the
reverse proxy server, it will probably be an IP between =192.168.1.1=
and =192.168.1.255=.

| NAME  | FROM PORT | DEST PORT/IP    | ENABLED |
|-------+-----------+-----------------+---------|
| HTTP  | 80        | xxx.xxx.xxx.xxx | TRUE    |
| HTTPS | 443       | xxx.xxx.xxx.xxx | TRUE    |

Once configured, these rules will direct all web traffic to your reverse
proxy.

** Step 3: Nginx Installation

To install Nginx, simply run the following command:

#+begin_src sh
sudo apt install nginx
#+end_src

If you have a firewall enabled, open up ports =80= and =443= on your
server so that Nginx can accept web traffic from the router.

For example, if you want to use =ufw= for web traffic and SSH, run the
following commands:

#+begin_src sh
sudo ufw allow 'Nginx Full'
sudo ufw allow SSH
sudo ufw enable
#+end_src

** Step 4: Nginx Configuration

Now that we have domains pointing toward the server, the only step left
is to configure the reverse proxy to direct traffic from domains to
local services.

To start, you'll need to create a configuration file for each domain in
=/etc/nginx/sites-available/=. They will look identical except for the
=server_name= variable and the =proxy_pass= port.

Dashy:

#+begin_src sh
nano /etc/nginx/sites-available/dashy.example.com
#+end_src

#+begin_src config
server {
    listen 80;
    server_name dashy.example.com;

    location / {
        proxy_pass http://localhost:4000;
    }
}
#+end_src

Uptime:

#+begin_src sh
nano /etc/nginx/sites-available/uptime.example.com
#+end_src

#+begin_src config
server {
    listen 80;
    server_name uptime.example.com;

    location / {
        proxy_pass http://localhost:3001;
    }
}
#+end_src

Once the configuration files are created, you will need to enable them
with the =symlink= command:

#+begin_src sh
sudo ln -s /etc/nginx/sites-available/dashy.example.com /etc/nginx/sites-enabled/
#+end_src

Voilà! Your local services should now be available through their URLs.

* HTTPS with Certbot

If you've followed along, you'll notice that your services are only
available via HTTP (not HTTPS).

If you want to enable HTTPS for your new domains, you will need to
generate SSL/TLS certificates for them. The easiest way to generate
certificates on Nginx is [[https://certbot.eff.org][Certbot]]:

#+begin_src sh
sudo apt install snapd; sudo snap install core; sudo snap refresh core
sudo snap install --classic certbot
sudo ln -s /snap/bin/certbot /usr/bin/certbot
sudo certbot --nginx
#+end_src