-
Notifications
You must be signed in to change notification settings - Fork 0
/
nginx_shell.nix
305 lines (271 loc) · 10.2 KB
/
nginx_shell.nix
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
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
# nixos-22.11 channel
# Apr 9, 2023, 9:59 PM EDT
{ nixpkgs_commit ? "ea96b4af6148114421fda90df33cf236ff5ecf1d"
, project_dir ? builtins.toString ./..
, nix_shell_dir ? "${project_dir}/_nix-shell"
, gunicorn_dir ? "${nix_shell_dir}/gunicorn"
, django_dir ? "${nix_shell_dir}/django"
, nginx_dir ? "${nix_shell_dir}/nginx"
, domain ? "lynx.societyfortheblind.org"
, ssl_cert
, private_key # that belongs to the public key in `ssl_cert`
}:
# > IMPORTANT
# >
# > 1. This Nix shell expression needs to be run
# > inside `nix/dev_shell.nix` Nix shell.
# >
# > 2. NGINX will be run as `sudo` as only HTTPS
# > is enabled (i.e., port 443).
# >
# > COROLLARY:
# > Testing on localhost will require jumping
# > all the necessary hoops (i.e, creating
# > self-signed certs, edit `/etc/hosts` or
# > local DNS, etc.).
# >
# > NOTE Is it ok to run NGINX as root?
# > Yes: https://unix.stackexchange.com/questions/134301/
# HOW TO CALL? {{- {{-
# ====================================================
#
# nix-shell \
# --argstr "ssl_cert" "$( readlink -f lynx-dev-server.crt )" \
# --argstr "private_key" "$( readlink -f lynx-dev-server.key )" \
# --argstr "domain" "lynx.dev" \
# nix/nginx_shell.nix
#
# See [HTTPS notes](./https-notes.md) on how
# to create the keys and SSL certificates
# for testing.
#
# To replace the calling shell, prefix the
# above command with `exec` (i.e., `exec
# nix-shell ...`).
# > NOTE STATIC ASSETS NOT SERVED WHEN USING "sudo"
# >
# > See TODO "groups & (system) users" in
# > `dev_shell.nix`.
# >
# > Permissions. The whole project is probably
# > served from a home directory, and NGINX's
# > `nobody` user does not have access to
# > it. For example, files and directories
# > in the "slate-2" repo have 644 and 755
# > permissions, respectively, but the home
# > directory has 750, meanning that `nobody`
# > either has to be the owner of the home
# > directory or it has to be in the group
# > that has read rights on the home dir.
# >
# > The following should help:
# >
# > sudo -a -G <home-dir-allowed-group> nobody
# }}- }}-
# HOW TO MONITOR? {{- {{-
# ====================================================
#
# watch -n 1 "{ ls -l _nix-shell/*/*pid ; echo ; sudo ps axf -o user,pid,tty,etime,cmd | egrep 'gunicorn|nginx|postgres' ; }"
#
# From this thread: https://unix.stackexchange.com/q/64736/85131
#
# }}- }}-
# TODO look into `nginx` package in Nixpkgs
# NOTE ERRORS ON FIRST RUN {{- {{-
# ===================
# There will probably be a lot of errors along the lines of:
#
# 2023/05/05 15:55:03 [emerg] 3106282#3106282: mkdir() "/var/cache/nginx/proxy" failed (13: Permission denied)
#
# This is because (as far as I was able to figure it
# out) the NGINX Nix package has been compiled with
# these hard paths that HAVE TO exist, even though
# they won't be touched (and some of them could be
# over-ridden; e.g., error.log - see below).`
#
# These have done the trick thus far:
#
# sudo mkdir -p /var/log/nginx/
# sudo touch /var/log/nginx/error.log
# sudo mkdir -p /var/cache/nginx/proxy
# sudo mkdir -p /var/cache/nginx/uwsgi
# sudo mkdir -p /var/cache/nginx/scgi
# sudo mkdir -p /var/cache/nginx/fastcgi
# sudo mkdir -p /var/cache/nginx/client_body
#
# # https://serverfault.com/questions/235154
# # (The user should be whoever starts NGINX.)
# sudo chown -R $(whoami):$(whoami) /var/{log,cache}/nginx
#
# As a one-liner:
#
# sudo mkdir -p /var/log/nginx/ && sudo touch /var/log/nginx/error.log && sudo mkdir -p /var/cache/nginx/proxy && sudo mkdir -p /var/cache/nginx/uwsgi && sudo mkdir -p /var/cache/nginx/scgi && sudo mkdir -p /var/cache/nginx/fastcgi && sudo mkdir -p /var/cache/nginx/client_body && sudo chown -R $(whoami):$(whoami) /var/{log,cache}/nginx
# }}- }}-
# OUTDATED NOTE? (2023-07-08) {{- {{-
# NOTE https://discourse.nixos.org/t/how-to-add-local-files-into-nginx-derivation-for-nix-shell/6603
# Opted to keep `nginx.conf` out of the store, because
# 1. it is already in version control
# 2. if changes need to be made, one would have to create another config to override it
# -- Although, are there merits to keep it in the store?
# + with NixOS, this would be a no brainer, but then the config would have to be rebuilt
# + if a default config is kept in the store, it could still be over-ridden with another one using `-c`
# Nonetheless, when this repo is deployed via a `shell.nix`, it may be more convenient to refer to a non-store config.
#
# let
# nginx-with-config = pkgs.writeScriptBin "nginx-alt" ''
# exec ${pkgs.nginx}/bin/nginx -c ${./nginx.conf} "$@"
# '';
#
# in
# }}- }}-
let
# NGINX doc's most valuable pages:
# + [Alphabetical index of directives](http://nginx.org/en/docs/dirindex.html)
# + [Alphabetical index of variables](http://nginx.org/en/docs/varindex.html)
pkgs = # {{-
import
# The downloaded archive will be (temporarily?) housed in the Nix store
# e.g., "/nix/store/gk9x7syd0ic6hjrf0fs6y4bsd16zgscg-source"
# (Try any of the `fetchTarball` commands below in `nix repl`, and it
# will print out the path.)
( builtins.fetchTarball nixpkgs_url) { config = {}; overlays = []; }
;
nixpkgs_url = "https://github.com/nixos/nixpkgs/tarball/${nixpkgs_commit}";
# }}-
timestamp = # {{-
builtins.readFile (
pkgs.runCommand
"timestamp"
{ when = builtins.currentTime; }
"echo -n `date -d @$when +%Y-%m-%d_%H-%M-%S` > $out"
)
;
# https://discourse.nixos.org/t/how-to-create-a-timestamp-in-a-nix-expression/30329
# }}-
# See NOTE 20230827 below
ssl_dummy_crt_path = "${nginx_dir}/dummy.crt";
ssl_dummy_key_path = "${nginx_dir}/dummy.key";
nginx_conf = # {{-
import
./nginx/nginx_conf.nix
{ inherit pkgs
django_dir
nginx_dir
ssl_dummy_key_path
ssl_dummy_crt_path
domain
ssl_cert
private_key
timestamp
;
}
;
# }}-
# NOTE See https://discourse.nixos.org/t/how-to-add-local-files-into-nginx-derivation-for-nix-shell/6603
# QUESTION Why use the `*Bin` version?
# ANSWER Because the NGINX executable will then be
# added to PATH automatically.
nginx_with_config =
pkgs.writeShellScriptBin
"nginx_lynx"
''
exec ${pkgs.nginx}/bin/nginx -c ${nginx_conf} "$@"
''
;
in
pkgs.mkShell {
# See `https-notes.md`
HTTPS_DOMAIN = domain;
buildInputs =
[ pkgs.inotify-tools
nginx_with_config
];
shellHook =
let
gunicorn_pidfile = "${gunicorn_dir}/${timestamp}.pid";
in
''
set -x
''
# TODO Convert to systemd service instead?
# See note above `inotifywait` below.
+ ''
( cd ${project_dir}/lynx && \
gunicorn \
--bind 127.0.0.1:8000 \
--workers 3 \
--log-level 'debug' \
--preload \
--capture-output \
--pid ${gunicorn_pidfile} \
--access-logfile "${gunicorn_dir}/access_${timestamp}.log" \
--error-logfile "${gunicorn_dir}/error_${timestamp}.log" \
mysite.wsgi:application \
--daemon
)
''
# NOTE Must wait for Gunicorn's pidfile,
# otherwise the clean-up section below won't
# register the proper command to shut down
# Gunicorn (the `cat` is evaluated right
# away, not lazily, so if the pidfile
# doesn't exist, the command will fail with
# `kill -s SIGTERM`).
+ ''
inotifywait --event create,moved_to,attrib --include '${timestamp}.pid$' ${gunicorn_dir}
''
# NOTE Why no `mkdir ${gunicorn_dir}`? {{-
# Because `gunicorn_dir` is already
# created in `dev_shell.nix`, which
# `nginx_shell.nix` currently depends on.
#
# ( There is a NOTE close to the
# top of `dev_shell.nix` contemplating
# whether the 2 should be merged. For
# now, Gunicorn can be tested with a
# simple Just recipe after running
# `dev_shell.nix`.
# )
# }}-
+ ''
mkdir -p ${nginx_dir}
''
# NOTE Why the dummy private key and SSL certificate? (20230827) {{-
#
# For the CATCH-ALL `server` block in
# `nginx_conf.nix`. In order to match
# on HTTPS requests for domains **not**
# configured in this NGINX instance,
# a server block dealing with HTTPS
# requests has to have valid configuration,
# which requires `ssl_certificate` and
# `ssl_certificate_key` parameters set up.
#
# Anyway, requests ending up in that block
# will get ignored, and no meaningful
# private key and SSL certificate will get
# exposed.
#
# See also https://serverfault.com/questions/1141304/
# }}-
+ ''
openssl req -nodes -new -x509 -subj "/CN=localhost" -keyout ${ssl_dummy_key_path} -out ${ssl_dummy_crt_path}
sudo $(which nginx_lynx)
''
# NOTE It may take some time for NGINX to shut
# down (e.g., after leaving the Nix shell);
# the line below in `ps ax` is a good sign:
#
# 2814526 ? S 0:00 nginx: worker process is shutting down
#
+ ''
trap \
"
sudo $(which nginx_lynx) -s quit
kill -s SIGTERM $( cat ${gunicorn_pidfile} )
" \
EXIT
''
;
}
# vim: set foldmethod=marker foldmarker={{-,}}- foldlevelstart=0 tabstop=2 shiftwidth=2 expandtab: