-
Notifications
You must be signed in to change notification settings - Fork 805
Expand file tree
/
Copy pathweb.template.yml
More file actions
457 lines (406 loc) · 17.3 KB
/
web.template.yml
File metadata and controls
457 lines (406 loc) · 17.3 KB
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
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
base_image: discourse/base:2.0.20250722-0020
env:
# You can have redis on a different box
RAILS_ENV: 'production'
UNICORN_WORKERS: 3
UNICORN_SIDEKIQS: 1
# stop heap doubling in size so aggressively, this conserves memory
RUBY_GC_HEAP_GROWTH_MAX_SLOTS: 40000
RUBY_GC_HEAP_INIT_SLOTS: 400000
RUBY_GC_HEAP_OLDOBJECT_LIMIT_FACTOR: 1.5
DISCOURSE_DB_SOCKET: /var/run/postgresql
DISCOURSE_DB_HOST:
DISCOURSE_DB_PORT:
params:
version: tests-passed
home: /var/www/discourse
upload_size: 10m
nginx_worker_connections: 4000
run:
- exec: thpoff echo "thpoff is installed!"
- exec:
tag: precompile
cmd:
- /usr/local/bin/ruby -e 'if ENV["DISCOURSE_SMTP_ADDRESS"] == "smtp.example.com"; puts "Aborting! Mail is not configured!"; exit 1; end'
- /usr/local/bin/ruby -e 'if ENV["DISCOURSE_HOSTNAME"] == "discourse.example.com"; puts "Aborting! Domain is not configured!"; exit 1; end'
- /usr/local/bin/ruby -e 'if (ENV["DISCOURSE_CDN_URL"] || "")[0..1] == "//"; puts "Aborting! CDN must have a protocol specified. Once fixed you should rebake your posts now to correct all posts."; exit 1; end'
# TODO: move to base image (anacron can not be fired up using rc.d)
- exec: rm -f /etc/cron.d/anacron
- file:
path: /etc/cron.d/anacron
contents: |
SHELL=/bin/sh
PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
30 7 * * * root /usr/sbin/anacron -s >/dev/null
- file:
path: /etc/runit/1.d/copy-env
chmod: "+x"
contents: |
#!/bin/bash
env > ~/boot_env
conf=/var/www/discourse/config/discourse.conf
# find DISCOURSE_ env vars, strip the leader, lowercase the key
/usr/local/bin/ruby -e 'ENV.each{|k,v| puts "#{$1.downcase} = '\''#{v}'\''" if k =~ /^DISCOURSE_(.*)/}' | install -m 600 -o discourse /dev/stdin ${conf}
- file:
path: /etc/service/unicorn/run
chmod: "+x"
contents: |
#!/bin/bash
exec 2>&1
# redis
# postgres
cd $home
chown -R discourse:www-data /shared/log/rails
# before precompile
if [[ -z "$PRECOMPILE_ON_BOOT" ]]; then
PRECOMPILE_ON_BOOT=1
fi
if [ -f /usr/local/bin/create_db ] && [ "$CREATE_DB_ON_BOOT" = "1" ]; then /usr/local/bin/create_db; fi;
if [ "$MIGRATE_ON_BOOT" = "1" ]; then su discourse -c 'bundle exec rake db:migrate'; fi
if [ "$PRECOMPILE_ON_BOOT" = "1" ]; then SKIP_EMBER_CLI_COMPILE=1 su discourse -c 'bundle exec rake assets:precompile'; fi
LD_PRELOAD=$RUBY_ALLOCATOR HOME=/home/discourse USER=discourse exec thpoff chpst -u discourse:www-data -U discourse:www-data bundle exec config/unicorn_launcher -E production -c config/unicorn.conf.rb
- file:
path: /etc/service/nginx/run
chmod: "+x"
contents: |
#!/bin/sh
exec 2>&1
exec /usr/sbin/nginx
- file:
path: /etc/runit/3.d/01-nginx
chmod: "+x"
contents: |
#!/bin/bash
sv stop nginx
- file:
path: /etc/runit/3.d/02-unicorn
chmod: "+x"
contents: |
#!/bin/bash
sv stop unicorn
- exec:
cd: $home
hook: code
cmd:
- sudo -H -E -u discourse git clean -f
# TODO Remove the special handling of shallow clones when everyone uses images without that clone type
- |-
sudo -H -E -u discourse bash -c '
set -o errexit
if [ $(git rev-parse --is-shallow-repository) == "true" ]; then
git remote set-branches --add origin main
git remote set-branches origin $version
git fetch --depth 1 origin $version
else
git fetch --tags --prune-tags --prune --force origin
fi
'
- |-
sudo -H -E -u discourse bash -c '
set -o errexit
if [[ $(git symbolic-ref --short HEAD) == $version ]] ; then
git pull
else
git -c advice.detachedHead=false checkout $version
fi
'
- sudo -H -E -u discourse git config user.discourse-version $version
- mkdir -p tmp
- chown discourse:www-data tmp
- mkdir -p tmp/pids
- mkdir -p tmp/sockets
- touch tmp/.gitkeep
- mkdir -p /shared/log/rails
- bash -c "touch -a /shared/log/rails/{production,production_errors,unicorn.stdout,unicorn.stderr,sidekiq}.log"
- bash -c "ln -s /shared/log/rails/{production,production_errors,unicorn.stdout,unicorn.stderr,sidekiq}.log $home/log"
- bash -c "mkdir -p /shared/{uploads,backups}"
- bash -c "ln -s /shared/{uploads,backups} $home/public"
- bash -c "mkdir -p /shared/tmp/{backups,restores}"
- bash -c "ln -s /shared/tmp/{backups,restores} $home/tmp"
- chown -R discourse:www-data /shared/log/rails /shared/uploads /shared/backups /shared/tmp
# scrub broken symlinks from plugins that have been removed
- "[ ! -d public/plugins ] || find public/plugins/ -maxdepth 1 -xtype l -delete"
- exec:
cmd:
- "cp $home/config/nginx.sample.conf /etc/nginx/conf.d/discourse.conf"
- "rm /etc/nginx/sites-enabled/default"
- "mkdir -p /var/nginx/cache"
# Stop building the container if the Nginx outlets are missing
- "grep -q 'outlets/before-server' /etc/nginx/conf.d/discourse.conf || ( >&2 echo 'The \"before-server\" Nginx outlet is missing. This version of discourse_docker is not compatible with the chosen Discourse version.' ; exit 1 )"
- "grep -q 'outlets/server' /etc/nginx/conf.d/discourse.conf || ( >&2 echo 'The \"server\" Nginx outlet is missing. This version of discourse_docker is not compatible with the chosen Discourse version.' ; exit 1 )"
- "grep -q 'outlets/discourse' /etc/nginx/conf.d/discourse.conf || ( >&2 echo 'The \"discourse\" Nginx outlet is missing. This version of discourse_docker is not compatible with the chosen Discourse version.' ; exit 1 )"
# Create placeholders for the Nginx outlets
- "mkdir -p /etc/nginx/conf.d/outlets/before-server"
- "touch /etc/nginx/conf.d/outlets/before-server/20-redirect-http-to-https.conf"
- "touch /etc/nginx/conf.d/outlets/before-server/30-ratelimited.conf"
- "mkdir -p /etc/nginx/conf.d/outlets/server"
- "touch /etc/nginx/conf.d/outlets/server/10-http.conf"
- "touch /etc/nginx/conf.d/outlets/server/20-https.conf"
- "touch /etc/nginx/conf.d/outlets/server/30-offline-page.conf"
- "mkdir -p /etc/nginx/conf.d/outlets/discourse"
- "touch /etc/nginx/conf.d/outlets/discourse/20-https.conf"
- "touch /etc/nginx/conf.d/outlets/discourse/30-ratelimited.conf"
- replace:
filename: /etc/nginx/nginx.conf
from: pid /run/nginx.pid;
to: daemon off;
- replace:
filename: "/etc/nginx/nginx.conf"
from: /worker_connections.+$/
to: worker_connections $nginx_worker_connections;
- replace:
filename: "/etc/nginx/conf.d/discourse.conf"
from: /client_max_body_size.+$/
to: client_max_body_size $upload_size;
- exec:
cmd:
# Move `listen 80` to an outlet
- sed -i 's#listen 80;##g' /etc/nginx/conf.d/discourse.conf
- |-
if [ -f "/proc/net/if_inet6" ]; then
echo "listen 80;\nlisten [::]:80;" > /etc/nginx/conf.d/outlets/server/10-http.conf
else
echo "listen 80;" > /etc/nginx/conf.d/outlets/server/10-http.conf
fi
- exec:
cmd: echo "done configuring web"
hook: web_config
- exec:
cd: $home
hook: web
cmd:
# install bundler version to match Gemfile.lock
- gem install bundler --conservative -v $(awk '/BUNDLED WITH/ { getline; gsub(/ /,""); print $0 }' Gemfile.lock)
- find $home ! -user discourse -exec chown discourse {} \+
- exec:
cd: $home
hook: yarn
cmd:
- |-
if [ -f yarn.lock ]; then
if [ -d node_modules/.pnpm ]; then
echo "This version of Discourse uses yarn, but pnpm node_modules are preset. Cleaning up..."
find ./node_modules ./app/assets/javascripts/*/node_modules -mindepth 1 -maxdepth 1 -exec rm -rf {} +
fi
su discourse -c 'yarn install --frozen-lockfile && yarn cache clean'
else
su discourse -c 'CI=1 pnpm install --frozen-lockfile && pnpm prune'
fi
- exec:
cd: $home
hook: bundle_exec
cmd:
- su discourse -c 'bundle install --jobs $(($(nproc) - 1)) --retry 3'
- su discourse -c 'bundle clean'
- su discourse -c 'find /var/www/discourse/vendor/bundle -name cache -not -path "*/gems/*" -type d -exec rm -rf {} +'
- su discourse -c 'find /var/www/discourse/vendor/bundle -name tmp -type d -exec rm -rf {} +'
- exec:
cd: $home
cmd:
- su discourse -c 'LOAD_PLUGINS=0 bundle exec rake plugin:pull_compatible_all'
hook: plugin_compatibility
raise_on_fail: false
- exec:
cd: $home
tag: migrate
hook: db_migrate
cmd:
- su discourse -c 'bundle exec rake db:migrate'
- exec:
cd: $home
tag: build
hook: assets_precompile_build
cmd:
- su discourse -c 'SKIP_DB_AND_REDIS=1 bundle exec rake assets:precompile'
- exec:
cd: $home
tag: precompile
hook: assets_precompile
cmd:
- su discourse -c 'bundle exec rake themes:update maxminddb:refresh assets:precompile:css'
- replace:
tag: precompile
filename: /etc/service/unicorn/run
from: "# before precompile"
to: "PRECOMPILE_ON_BOOT=0"
- file:
path: /usr/local/bin/discourse
chmod: +x
contents: |
#!/bin/bash
(cd /var/www/discourse && RAILS_ENV=production sudo -H -E -u discourse bundle exec script/discourse "$@")
- file:
path: /usr/local/bin/rails
chmod: +x
contents: |
#!/bin/bash
(cd /var/www/discourse && RAILS_ENV=production sudo -H -E -u discourse bundle exec script/rails "$@")
- file:
path: /usr/local/bin/rake
chmod: +x
contents: |
#!/bin/bash
(cd /var/www/discourse && RAILS_ENV=production sudo -H -E -u discourse bundle exec bin/rake "$@")
- file:
path: /usr/local/bin/rbtrace
chmod: +x
contents: |
#!/bin/bash
(cd /var/www/discourse && RAILS_ENV=production sudo -H -E -u discourse bundle exec rbtrace "$@")
- file:
path: /usr/local/bin/stackprof
chmod: +x
contents: |
#!/bin/bash
(cd /var/www/discourse && RAILS_ENV=production sudo -H -E -u discourse bundle exec stackprof "$@")
- file:
path: /etc/update-motd.d/10-web
chmod: +x
contents: |
#!/bin/bash
echo
echo Use: rails, rake or discourse to execute commands in production
echo
- file:
path: /etc/logrotate.d/rails
contents: |
/shared/log/rails/*.log
{
rotate 7
dateext
daily
missingok
delaycompress
compress
sharedscripts
postrotate
sv 1 unicorn
endscript
}
- file:
path: /etc/logrotate.d/nginx
contents: |
/var/log/nginx/*.log {
daily
missingok
rotate 7
compress
delaycompress
create 0644 www-data www-data
sharedscripts
postrotate
sv 1 nginx
endscript
}
# move state out of the container this fancy is done to support rapid rebuilds of containers,
# we store anacron and logrotate state outside the container to ensure its maintained across builds
# later move this snipped into an initialization script
# we also ensure all the symlinks we need to /shared are in place in the correct structure
# this allows us to bootstrap on one machine and then run on another
- file:
path: /etc/runit/1.d/00-ensure-links
chmod: +x
contents: |
#!/bin/bash
if [[ ! -L /var/lib/logrotate ]]; then
rm -fr /var/lib/logrotate
mkdir -p /shared/state/logrotate
ln -s /shared/state/logrotate /var/lib/logrotate
fi
if [[ ! -L /var/spool/anacron ]]; then
rm -fr /var/spool/anacron
mkdir -p /shared/state/anacron-spool
ln -s /shared/state/anacron-spool /var/spool/anacron
fi
if [[ ! -d /shared/log/rails ]]; then
mkdir -p /shared/log/rails
chown -R discourse:www-data /shared/log/rails
fi
if [[ ! -d /shared/uploads ]]; then
mkdir -p /shared/uploads
chown -R discourse:www-data /shared/uploads
fi
if [[ ! -d /shared/backups ]]; then
mkdir -p /shared/backups
chown -R discourse:www-data /shared/backups
fi
rm -rf /shared/tmp/{backups,restores}
mkdir -p /shared/tmp/{backups,restores}
chown -R discourse:www-data /shared/tmp/{backups,restores}
- file:
path: /etc/runit/1.d/01-cleanup-web-pids
chmod: +x
contents: |
#!/bin/bash
/bin/rm -f /var/www/discourse/tmp/pids/*.pid
# change login directory to Discourse home
- file:
path: /root/.bash_profile
chmod: 644
contents: |
cd $home
- file:
path: /usr/local/etc/ImageMagick-7/policy.xml
contents: |
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE policymap [
<!ELEMENT policymap (policy)+>
<!ATTLIST policymap xmlns CDATA #FIXED ''>
<!ELEMENT policy EMPTY>
<!ATTLIST policy xmlns CDATA #FIXED '' domain NMTOKEN #REQUIRED
name NMTOKEN #IMPLIED pattern CDATA #IMPLIED rights NMTOKEN #IMPLIED
stealth NMTOKEN #IMPLIED value CDATA #IMPLIED>
]>
<!--
Configure ImageMagick policies.
Domains include system, delegate, coder, filter, path, or resource.
Rights include none, read, write, execute and all. Use | to combine them,
for example: "read | write" to permit read from, or write to, a path.
Use a glob expression as a pattern.
Suppose we do not want users to process MPEG video images:
<policy domain="delegate" rights="none" pattern="mpeg:decode" />
Here we do not want users reading images from HTTP:
<policy domain="coder" rights="none" pattern="HTTP" />
The /repository file system is restricted to read only. We use a glob
expression to match all paths that start with /repository:
<policy domain="path" rights="read" pattern="/repository/*" />
Lets prevent users from executing any image filters:
<policy domain="filter" rights="none" pattern="*" />
Any large image is cached to disk rather than memory:
<policy domain="resource" name="area" value="1GP"/>
Define arguments for the memory, map, area, width, height and disk resources
with SI prefixes (.e.g 100MB). In addition, resource policies are maximums
for each instance of ImageMagick (e.g. policy memory limit 1GB, -limit 2GB
exceeds policy maximum so memory limit is 1GB).
Rules are processed in order. Here we want to restrict ImageMagick to only
read or write a small subset of proven web-safe image types:
<policy domain="delegate" rights="none" pattern="*" />
<policy domain="filter" rights="none" pattern="*" />
<policy domain="coder" rights="none" pattern="*" />
<policy domain="coder" rights="read|write" pattern="{GIF,JPEG,PNG,WEBP}" />
-->
<policymap>
<!-- <policy domain="system" name="shred" value="2"/> -->
<!-- <policy domain="system" name="precision" value="6"/> -->
<!-- <policy domain="system" name="memory-map" value="anonymous"/> -->
<!-- <policy domain="system" name="max-memory-request" value="256MiB"/> -->
<!-- <policy domain="resource" name="temporary-path" value="/tmp"/> -->
<policy domain="resource" name="memory" value="1GiB"/>
<policy domain="resource" name="map" value="2GiB"/>
<policy domain="resource" name="width" value="64KP"/>
<policy domain="resource" name="height" value="64KP"/>
<!-- <policy domain="resource" name="list-length" value="128"/> -->
<policy domain="resource" name="area" value="4GP"/>
<policy domain="resource" name="disk" value="8GiB"/>
<!-- <policy domain="resource" name="file" value="768"/> -->
<!-- <policy domain="resource" name="thread" value="4"/> -->
<!-- <policy domain="resource" name="throttle" value="0"/> -->
<!-- <policy domain="resource" name="time" value="3600"/> -->
<!-- <policy domain="coder" rights="none" pattern="MVG" /> -->
<policy domain="module" rights="none" pattern="{PS,PS2,PS3,EPS,XPS}" />
<!-- <policy domain="delegate" rights="none" pattern="HTTPS" /> -->
<!-- <policy domain="path" rights="none" pattern="@*" /> -->
<!-- <policy domain="cache" name="memory-map" value="anonymous"/> -->
<!-- <policy domain="cache" name="synchronize" value="True"/> -->
<!-- <policy domain="cache" name="shared-secret" value="passphrase" stealth="true"/> -->
</policymap>