aboutsummaryrefslogtreecommitdiff
path: root/content/posts
diff options
context:
space:
mode:
authorMitja Felicijan <m@mitjafelicijan.com>2023-07-01 19:54:11 +0200
committerMitja Felicijan <m@mitjafelicijan.com>2023-07-01 19:54:11 +0200
commit47d2fc25bf97f7941131f8f23ab15dc3d1e16f47 (patch)
tree308c2dff3cb451555af7284f805a2fba16e9d9cd /content/posts
parent5c407433477a98f5a00c63062a96e6d816a9f73b (diff)
downloadmitjafelicijan.com-47d2fc25bf97f7941131f8f23ab15dc3d1e16f47.tar.gz
Post: Bringing all the projects under one roof
Diffstat (limited to 'content/posts')
-rw-r--r--content/posts/2023-07-01-bringing-all-of-my-projects-together-under-a-single-umbrella.md259
1 files changed, 259 insertions, 0 deletions
diff --git a/content/posts/2023-07-01-bringing-all-of-my-projects-together-under-a-single-umbrella.md b/content/posts/2023-07-01-bringing-all-of-my-projects-together-under-a-single-umbrella.md
new file mode 100644
index 0000000..e103d32
--- /dev/null
+++ b/content/posts/2023-07-01-bringing-all-of-my-projects-together-under-a-single-umbrella.md
@@ -0,0 +1,259 @@
1---
2title: "Bringing all of my projects together under a single umbrella"
3url: bringing-all-of-my-projects-together-under-a-single-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 on 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 - files static 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
109 root * /opt/projects/examples
110 file_server
111}
112```
113
114- The order is very important. Make sure that `order cgi before respond` is at
115 the top of the configuration file.
116- Also, when you run with Caddy v2, make sure you provide `adapter` argument
117 like this `/usr/bin/caddy run --watch --environ --config /etc/caddy/Caddyfile
118 --adapter caddyfile`. Otherwise, Caddy will try to use a different format for
119 config file.
120
121I did a small batch of tests with [Bash](https://www.gnu.org/software/bash/),
122[Tcl](https://www.tcl-lang.org/) and [Lua](https://www.lua.org/). Here is a
123cheat sheet if you need it.
124
125Let's get Bash out the way first.
126
127```bash
128#!/usr/bin/bash
129
130printf "Content-type: text/plain\n\n"
131
132printf "Hello from Bash\n\n"
133printf "PATH_INFO [%s]\n" $PATH_INFO
134printf "QUERY_STRING [%s]\n" $QUERY_STRING
135printf "\n"
136
137for i in {0..9..1}; do
138 printf "> %s\n" $i
139done
140
141exit 0
142```
143
144This one is for Tcl script.
145
146```tcl
147#!/usr/bin/tclsh
148
149puts "Content-type: text/plain\n"
150
151puts "Hello from Tcl\n"
152puts "PATH_INFO \[$env(PATH_INFO)\]"
153puts "QUERY_STRING \[$env(QUERY_STRING)\]"
154puts ""
155
156for {set i 0} {$i < 10} {incr i} {
157 puts "> $i"
158}
159```
160
161And for the final example, Lua.
162
163```lua
164#!/usr/bin/lua
165
166print("Content-type: text/plain\n")
167
168print("Hello from Lua\n")
169print(string.format("PATH_INFO [%s]", os.getenv("PATH_INFO")))
170print(string.format("QUERY_STRING [%s]", os.getenv("QUERY_STRING")))
171print()
172
173for i = 0, 9 do
174 print(string.format("> %d", i))
175end
176```
177
178## Basic authentication
179
180One thing was also to have an option for some sort of authentication, and
181something like [Basic access
182authentication](https://en.wikipedia.org/wiki/Basic_access_authentication) would
183be more than enough.
184
185Thankfully, Caddy supports this out of the box already. Below is an updated
186example.
187
188```Caddyfile
189{
190 order cgi before respond
191}
192
193examples.mitjafelicijan.com {
194 cgi /bash-test /opt/projects/examples/bash-test.sh
195 cgi /tcl-test /opt/projects/examples/tcl-test.tcl
196 cgi /lua-test /opt/projects/examples/lua-test.lua
197
198 root * /opt/projects/examples
199 file_server
200
201 basicauth * {
202 bob $2a$14$/wCgaf9oMnmQa20txB76u.nI1AldGMBT/1J7fXCfgOiRShwz/JOkK
203 }
204}
205```
206
207`basicauth *` matches everything under this domain/sub-domain and protects it
208with Basic Authentication.
209
210- `bob` is the username
211- `hash` is the password
212
213To generate these passwords, execute `caddy hash-password` and this will prompt
214you to insert a password twice and spit out a hashed password that you can put
215in your configuration file.
216
217Restart the server and you are ready to go.
218
219## Making Caddy a service with systemd
220
221After the tests were successful, I copied `caddy` to `/usr/bin/caddy` and copied
222`Caddyfile` to `/etc/caddy/Caddyfile`.
223
224Now off to the systemd. Each systemd service requires you to create a service
225file.
226
227- I created a `/etc/systemd/system/caddy.service` and put the following content
228 in the file.
229
230```systemd
231[Unit]
232Description=Caddy
233Documentation=https://caddyserver.com/docs/
234After=network.target network-online.target
235Requires=network-online.target
236
237[Service]
238Type=notify
239User=root
240Group=root
241ExecStart=/usr/bin/caddy run --environ --config /etc/caddy/Caddyfile --adapter caddyfile
242ExecReload=/usr/bin/caddy reload --config /etc/caddy/Caddyfile --force --adapter caddyfile
243TimeoutStopSec=5s
244LimitNOFILE=1048576
245LimitNPROC=512
246PrivateTmp=true
247ProtectSystem=full
248AmbientCapabilities=CAP_NET_ADMIN CAP_NET_BIND_SERVICE
249
250[Install]
251WantedBy=multi-user.target
252```
253
254- Then I enabled the service with `systemctl enable caddy.service`.
255- And then I started the service with `systemctl start caddy.service`.
256
257This was about all that I needed to do to get it running. Now I can easily add
258new subdomains and domains to the main configuration file and be done with
259it. No manual Let's Encrypt shenanigans needed.