aboutsummaryrefslogtreecommitdiff
path: root/content/posts/2023-07-01-bringing-all-of-my-projects-together-under-one-umbrella.md
diff options
context:
space:
mode:
authorMitja Felicijan <m@mitjafelicijan.com>2023-07-08 23:25:41 +0200
committerMitja Felicijan <m@mitjafelicijan.com>2023-07-08 23:25:41 +0200
commitcd6644ea4ddc78597934ab0ef5ba50e3c3daa927 (patch)
tree03de331a8db6386dfd6fa75155bfbcea6b4feaf3 /content/posts/2023-07-01-bringing-all-of-my-projects-together-under-one-umbrella.md
parent84ed124529ffeee1590295b8de3a8faf51848680 (diff)
downloadmitjafelicijan.com-cd6644ea4ddc78597934ab0ef5ba50e3c3daa927.tar.gz
Moved to a simpler SSG
Diffstat (limited to 'content/posts/2023-07-01-bringing-all-of-my-projects-together-under-one-umbrella.md')
-rw-r--r--content/posts/2023-07-01-bringing-all-of-my-projects-together-under-one-umbrella.md280
1 files changed, 0 insertions, 280 deletions
diff --git a/content/posts/2023-07-01-bringing-all-of-my-projects-together-under-one-umbrella.md b/content/posts/2023-07-01-bringing-all-of-my-projects-together-under-one-umbrella.md
deleted file mode 100644
index 4031df0..0000000
--- a/content/posts/2023-07-01-bringing-all-of-my-projects-together-under-one-umbrella.md
+++ /dev/null
@@ -1,280 +0,0 @@
1---
2title: "Bringing all of my projects together under one umbrella"
3url: bringing-all-of-my-projects-together-under-one-umbrella.html
4date: 2023-07-01T18:49:07+02:00
5draft: false
6---
7
8## What is the issue anyway?
9
10Over the years, I have accumulated a bunch of virtual servers on my
11[DigitalOcean](https://www.digitalocean.com/) account for small experimental
12projects I dabble in. And this has resulted in quite a bill. I mean, I wouldn't
13care if these projects were actually being used. But there were just being there
14unused and wasting resources. Which makes this an unnecessary burden for me.
15
16Most of them are just small HTML pages that have an endpoint or two to read data
17from or to, and for that reason I wrote servers left and right. To be honest,
18all of those things could have been done with [CGI
19scripts](https://en.wikipedia.org/wiki/Common_Gateway_Interface) and that would
20have been more than enough.
21
22Recently, I decided to stop language hopping and focus on a simpler stack which
23includes C, Go and Lua. And I can accomplish all the things I am interested in.
24
25## Finding a web server replacement
26
27Usually I had [Nginx](https://nginx.org/en/) in front of these small web servers
28and I had to manage SSL certificates and all that jazz. I am bored with these
29things. I don't want to manage any of this bullshit anymore.
30
31So the logical move forward was to find a solid alternative for this. I have
32ended up on [Caddy server](https://caddyserver.com/). I've used it in the past
33but kind of forgotten about it. What I really like about it is an ease of use
34and a bunch of out of the box functionalities that come with it.
35
36These are the _pitch_ points from their website:
37
38- **Secure by Default**: Caddy is the only web server that uses HTTPS by
39 default. A hardened TLS stack with modern protocols preserves privacy and
40 exposes MITM attacks.
41- **Config API**: As its primary mode of configuration, Caddy's REST API makes
42 it easy to automate and integrate with your apps.
43- **No Dependencies**: Because Caddy is written in Go, its binaries are entirely
44 self-contained and run on every platform, including containers without libc.
45- **Modular Stack**: Take back control over your compute edge. Caddy can be
46 extended with everything you need using plugins.
47
48I had just a few requirements:
49
50- Automatic SSL
51- Static file server
52- Basic authentication
53- CGI script support
54
55And the vanilla version does all of it, but CGI scripts. But that can easily be
56fixed with their modular approach. You can do this on their website and build a
57custom version of the server, or do it with Docker.
58
59This is a `Dockerfile` I used to build a custom server.
60
61```Dockerfile
62FROM caddy:builder AS builder
63
64RUN xcaddy build \
65 --with github.com/aksdb/caddy-cgi
66
67FROM caddy:latest
68RUN apk add --no-cache nano
69
70COPY --from=builder /usr/bin/caddy /usr/bin/caddy
71```
72
73## Getting rid of all the unnecessary virtual machines
74
75The next step was to get a handle on the number of virtual servers I have all
76over the place.
77
78I decided to move all the projects and services into two main VMs:
79
80- personal server (still Nginx)
81 - git server
82 - static file server
83 - personal blog
84- projects server (Caddy server)
85 - personal experiments
86 - other projects
87
88I will focus on projects' server in this post since it's more interesting.
89
90## Testing CGI scripts
91
92The first thing I tested was how CGI scripts work under Caddy. This is
93particularly import to me because almost all of my experiments and mini projects
94need this to work.
95
96To configure Caddy server, you must provide the server with a configuration
97file. By default, it's called `Caaddyfile`.
98
99```caddyfile
100{
101 order cgi before respond
102}
103
104examples.mitjafelicijan.com {
105 cgi /bash-test /opt/projects/examples/bash-test.sh
106 cgi /tcl-test /opt/projects/examples/tcl-test.tcl
107 cgi /lua-test /opt/projects/examples/lua-test.lua
108 cgi /python-test /opt/projects/examples/python-test.py
109
110 root * /opt/projects/examples
111 file_server
112}
113```
114
115- The order is very important. Make sure that `order cgi before respond` is at
116 the top of the configuration file.
117- Also, when you run with Caddy v2, make sure you provide `adapter` argument
118 like this `/usr/bin/caddy run --watch --environ --config /etc/caddy/Caddyfile
119 --adapter caddyfile`. Otherwise, Caddy will try to use a different format for
120 config file.
121
122I did a small batch of tests with [Bash](https://www.gnu.org/software/bash/),
123[Tcl](https://www.tcl-lang.org/), [Lua](https://www.lua.org/) and
124[Python](https://www.python.org/). Here is a cheat sheet if you need it.
125
126Let's get Bash out of the way first.
127
128```bash
129#!/usr/bin/bash
130
131printf "Content-type: text/plain\n\n"
132
133printf "Hello from Bash\n\n"
134printf "PATH_INFO [%s]\n" $PATH_INFO
135printf "QUERY_STRING [%s]\n" $QUERY_STRING
136printf "\n"
137
138for i in {0..9..1}; do
139 printf "> %s\n" $i
140done
141
142exit 0
143```
144
145This one is for Tcl script.
146
147```tcl
148#!/usr/bin/tclsh
149
150puts "Content-type: text/plain\n"
151
152puts "Hello from Tcl\n"
153puts "PATH_INFO \[$env(PATH_INFO)\]"
154puts "QUERY_STRING \[$env(QUERY_STRING)\]"
155puts ""
156
157for {set i 0} {$i < 10} {incr i} {
158 puts "> $i"
159}
160```
161
162And for all you Python enjoyers.
163
164```python
165#!/usr/bin/python3
166
167import os
168
169print("Content-type: text/plain\n")
170
171print("Hello from Python\n")
172print("PATH_INFO [{}]".format(os.environ['PATH_INFO']))
173print("QUERY_STRING [{}]".format(os.environ['QUERY_STRING']))
174print("")
175
176for i in range(10):
177 print("> {}".format(i))
178```
179
180And for the final example, Lua.
181
182```lua
183#!/usr/bin/lua
184
185print("Content-type: text/plain\n")
186
187print("Hello from Lua\n")
188print(string.format("PATH_INFO [%s]", os.getenv("PATH_INFO")))
189print(string.format("QUERY_STRING [%s]", os.getenv("QUERY_STRING")))
190print()
191
192for i = 0, 9 do
193 print(string.format("> %d", i))
194end
195```
196
197## Basic authentication
198
199One thing was also to have an option for some sort of authentication, and
200something like [Basic access
201authentication](https://en.wikipedia.org/wiki/Basic_access_authentication) would
202be more than enough.
203
204Thankfully, Caddy supports this out of the box already. Below is an updated
205example.
206
207```Caddyfile
208{
209 order cgi before respond
210}
211
212examples.mitjafelicijan.com {
213 cgi /bash-test /opt/projects/examples/bash-test.sh
214 cgi /tcl-test /opt/projects/examples/tcl-test.tcl
215 cgi /lua-test /opt/projects/examples/lua-test.lua
216 cgi /python-test /opt/projects/examples/python-test.py
217
218 root * /opt/projects/examples
219 file_server
220
221 basicauth * {
222 bob $2a$14$/wCgaf9oMnmQa20txB76u.nI1AldGMBT/1J7fXCfgOiRShwz/JOkK
223 }
224}
225```
226
227`basicauth *` matches everything under this domain/sub-domain and protects it
228with Basic Authentication.
229
230- `bob` is the username
231- `hash` is the password
232
233To generate these passwords, execute `caddy hash-password` and this will prompt
234you to insert a password twice and spit out a hashed password that you can put
235in your configuration file.
236
237Restart the server and you are ready to go.
238
239## Making Caddy a service with systemd
240
241After the tests were successful, I copied `caddy` to `/usr/bin/caddy` and copied
242`Caddyfile` to `/etc/caddy/Caddyfile`.
243
244Now off to the systemd. Each systemd service requires you to create a service
245file.
246
247- I created a `/etc/systemd/system/caddy.service` and put the following content
248 in the file.
249
250```systemd
251[Unit]
252Description=Caddy
253Documentation=https://caddyserver.com/docs/
254After=network.target network-online.target
255Requires=network-online.target
256
257[Service]
258Type=notify
259User=root
260Group=root
261ExecStart=/usr/bin/caddy run --environ --config /etc/caddy/Caddyfile --adapter caddyfile
262ExecReload=/usr/bin/caddy reload --config /etc/caddy/Caddyfile --force --adapter caddyfile
263TimeoutStopSec=5s
264LimitNOFILE=1048576
265LimitNPROC=512
266PrivateTmp=true
267ProtectSystem=full
268AmbientCapabilities=CAP_NET_ADMIN CAP_NET_BIND_SERVICE
269
270[Install]
271WantedBy=multi-user.target
272```
273
274- You might need to reload systemd with `systemctl daemon-reload`.
275- Then I enabled the service with `systemctl enable caddy.service`.
276- And then I started the service with `systemctl start caddy.service`.
277
278This was about all that I needed to do to get it running. Now I can easily add
279new subdomains and domains to the main configuration file and be done with
280it. No manual Let's Encrypt shenanigans needed.