aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore11
-rw-r--r--.gitmodules9
-rw-r--r--.reuse/dep510
-rw-r--r--LICENSES/CC0-1.0.txt121
-rw-r--r--Makefile88
-rw-r--r--README.md11
-rw-r--r--README.md.license5
-rw-r--r--container.scm99
-rwxr-xr-xfake-client-setup-mounts.sh18
-rw-r--r--fake-client.sh12
-rwxr-xr-xguix-container.sh167
-rw-r--r--hosts-extra10
-rwxr-xr-xrun-with-fake-mounts.sh17
m---------subrepos/koszko-org-website0
14 files changed, 578 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..dd17935
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,11 @@
+# SPDX-License-Identifier: CC0-1.0
+
+# Copyright (C) 2022 Wojtek Kosior <koszko@koszko.org>
+#
+# Available under the terms of Creative Commons Zero v1.0 Universal.
+
+container-runner
+log/
+pidfile
+hosts
+*.touchfile
diff --git a/.gitmodules b/.gitmodules
new file mode 100644
index 0000000..7216302
--- /dev/null
+++ b/.gitmodules
@@ -0,0 +1,9 @@
+# SPDX-License-Identifier: CC0-1.0
+
+# Copyright (C) 2022 Wojtek Kosior <koszko@koszko.org>
+#
+# Available under the terms of Creative Commons Zero v1.0 Universal.
+
+[submodule "koszko-org-website"]
+ path = subrepos/koszko-org-website
+ url = ../koszko-org-website
diff --git a/.reuse/dep5 b/.reuse/dep5
new file mode 100644
index 0000000..7a5313f
--- /dev/null
+++ b/.reuse/dep5
@@ -0,0 +1,10 @@
+Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
+Upstream-Name: koszko.org server configuration
+Upstream-Contact: W. Kosior <koszko@koszko.org>
+Source: https://git.koszko.org/koszko-org-server
+
+# Sample paragraph, commented out:
+#
+# Files: src/*
+# Copyright: $YEAR $NAME <$CONTACT>
+# License: ...
diff --git a/LICENSES/CC0-1.0.txt b/LICENSES/CC0-1.0.txt
new file mode 100644
index 0000000..0e259d4
--- /dev/null
+++ b/LICENSES/CC0-1.0.txt
@@ -0,0 +1,121 @@
+Creative Commons Legal Code
+
+CC0 1.0 Universal
+
+ CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE
+ LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN
+ ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS
+ INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES
+ REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS
+ PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM
+ THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED
+ HEREUNDER.
+
+Statement of Purpose
+
+The laws of most jurisdictions throughout the world automatically confer
+exclusive Copyright and Related Rights (defined below) upon the creator
+and subsequent owner(s) (each and all, an "owner") of an original work of
+authorship and/or a database (each, a "Work").
+
+Certain owners wish to permanently relinquish those rights to a Work for
+the purpose of contributing to a commons of creative, cultural and
+scientific works ("Commons") that the public can reliably and without fear
+of later claims of infringement build upon, modify, incorporate in other
+works, reuse and redistribute as freely as possible in any form whatsoever
+and for any purposes, including without limitation commercial purposes.
+These owners may contribute to the Commons to promote the ideal of a free
+culture and the further production of creative, cultural and scientific
+works, or to gain reputation or greater distribution for their Work in
+part through the use and efforts of others.
+
+For these and/or other purposes and motivations, and without any
+expectation of additional consideration or compensation, the person
+associating CC0 with a Work (the "Affirmer"), to the extent that he or she
+is an owner of Copyright and Related Rights in the Work, voluntarily
+elects to apply CC0 to the Work and publicly distribute the Work under its
+terms, with knowledge of his or her Copyright and Related Rights in the
+Work and the meaning and intended legal effect of CC0 on those rights.
+
+1. Copyright and Related Rights. A Work made available under CC0 may be
+protected by copyright and related or neighboring rights ("Copyright and
+Related Rights"). Copyright and Related Rights include, but are not
+limited to, the following:
+
+ i. the right to reproduce, adapt, distribute, perform, display,
+ communicate, and translate a Work;
+ ii. moral rights retained by the original author(s) and/or performer(s);
+iii. publicity and privacy rights pertaining to a person's image or
+ likeness depicted in a Work;
+ iv. rights protecting against unfair competition in regards to a Work,
+ subject to the limitations in paragraph 4(a), below;
+ v. rights protecting the extraction, dissemination, use and reuse of data
+ in a Work;
+ vi. database rights (such as those arising under Directive 96/9/EC of the
+ European Parliament and of the Council of 11 March 1996 on the legal
+ protection of databases, and under any national implementation
+ thereof, including any amended or successor version of such
+ directive); and
+vii. other similar, equivalent or corresponding rights throughout the
+ world based on applicable law or treaty, and any national
+ implementations thereof.
+
+2. Waiver. To the greatest extent permitted by, but not in contravention
+of, applicable law, Affirmer hereby overtly, fully, permanently,
+irrevocably and unconditionally waives, abandons, and surrenders all of
+Affirmer's Copyright and Related Rights and associated claims and causes
+of action, whether now known or unknown (including existing as well as
+future claims and causes of action), in the Work (i) in all territories
+worldwide, (ii) for the maximum duration provided by applicable law or
+treaty (including future time extensions), (iii) in any current or future
+medium and for any number of copies, and (iv) for any purpose whatsoever,
+including without limitation commercial, advertising or promotional
+purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each
+member of the public at large and to the detriment of Affirmer's heirs and
+successors, fully intending that such Waiver shall not be subject to
+revocation, rescission, cancellation, termination, or any other legal or
+equitable action to disrupt the quiet enjoyment of the Work by the public
+as contemplated by Affirmer's express Statement of Purpose.
+
+3. Public License Fallback. Should any part of the Waiver for any reason
+be judged legally invalid or ineffective under applicable law, then the
+Waiver shall be preserved to the maximum extent permitted taking into
+account Affirmer's express Statement of Purpose. In addition, to the
+extent the Waiver is so judged Affirmer hereby grants to each affected
+person a royalty-free, non transferable, non sublicensable, non exclusive,
+irrevocable and unconditional license to exercise Affirmer's Copyright and
+Related Rights in the Work (i) in all territories worldwide, (ii) for the
+maximum duration provided by applicable law or treaty (including future
+time extensions), (iii) in any current or future medium and for any number
+of copies, and (iv) for any purpose whatsoever, including without
+limitation commercial, advertising or promotional purposes (the
+"License"). The License shall be deemed effective as of the date CC0 was
+applied by Affirmer to the Work. Should any part of the License for any
+reason be judged legally invalid or ineffective under applicable law, such
+partial invalidity or ineffectiveness shall not invalidate the remainder
+of the License, and in such case Affirmer hereby affirms that he or she
+will not (i) exercise any of his or her remaining Copyright and Related
+Rights in the Work or (ii) assert any associated claims and causes of
+action with respect to the Work, in either case contrary to Affirmer's
+express Statement of Purpose.
+
+4. Limitations and Disclaimers.
+
+ a. No trademark or patent rights held by Affirmer are waived, abandoned,
+ surrendered, licensed or otherwise affected by this document.
+ b. Affirmer offers the Work as-is and makes no representations or
+ warranties of any kind concerning the Work, express, implied,
+ statutory or otherwise, including without limitation warranties of
+ title, merchantability, fitness for a particular purpose, non
+ infringement, or the absence of latent or other defects, accuracy, or
+ the present or absence of errors, whether or not discoverable, all to
+ the greatest extent permissible under applicable law.
+ c. Affirmer disclaims responsibility for clearing rights of other persons
+ that may apply to the Work or any use thereof, including without
+ limitation any person's Copyright and Related Rights in the Work.
+ Further, Affirmer disclaims responsibility for obtaining any necessary
+ consents, permissions or other rights required for any use of the
+ Work.
+ d. Affirmer understands and acknowledges that Creative Commons is not a
+ party to this document and has no duty or obligation with respect to
+ this CC0 or use of the Work.
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..3c5f7e8
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,88 @@
+# SPDX-License-Identifier: CC0-1.0
+
+# Copyright (C) 2022 Wojtek Kosior <koszko@koszko.org>
+#
+# Available under the terms of Creative Commons Zero v1.0 Universal.
+
+GUIX := guix
+
+# Almost all commands in this Makefilo are run through `guix time-machine` with
+# Guix revision fixed to the one from the commit below. This ensures that the
+# same working environment is always used.
+GUIX_COMMIT := a86979b41a49a8fcdaa887970ba594dbba701226
+
+GUIX_TM = $(GUIX) time-machine --commit=$(GUIX_COMMIT) --
+
+GUIX_LOAD_PATHS = $$( \
+ find subrepos/ -mindepth 1 -maxdepth 1 \
+ -exec printf '-L %s/guix-module-dir/' {} ';' \
+ )
+
+GUIX_SYS_CONTAINER = $(GUIX_TM) system container $(GUIX_LOAD_PATHS)
+
+GUIX_SHELL = $(GUIX_TM) shell
+
+KOSZKO_ORG_WEBSITE_INFO = \
+ subrepos/koszko-org-website/src/koszko_org_website.egg-info/PKG-INFO
+
+all: | container-runner.touchfile log
+
+$(KOSZKO_ORG_WEBSITE_INFO):
+ $(MAKE) -C subrepos/koszko-org-website dist
+
+container-runner: | container.scm $(KOSZKO_ORG_WEBSITE_INFO)
+ $(GUIX_SYS_CONTAINER) container.scm -r container-runner
+ touch container-runner.touchfile
+
+container-runner.touchfile: \
+ container.scm $(KOSZKO_ORG_WEBSITE_INFO)
+ $(MAKE) clean-runner
+ $(MAKE) container-runner
+
+hosts: hosts-extra /etc/hosts
+ cat $^ > $@
+
+log:
+ mkdir -p log
+
+start-container: guix-container.sh container-runner.touchfile | log
+ ./run-with-fake-mounts.sh \
+ ./$< start -e ./container-runner -p ./pidfile -l ./log
+
+stop-container: guix-container.sh
+ ./$< stop -e ./container-runner -p ./pidfile
+
+restart-container: guix-container.sh container-runner.touchfile | log
+ ./run-with-fake-mounts.sh \
+ ./$< restart -e ./container-runner -p ./pidfile -l ./log
+
+enter-container: pidfile
+ nsenter -a -t "$$(cat pidfile)" \
+ /run/current-system/profile/bin/bash --login
+
+fake-client: fake-client-setup-mounts.sh hosts
+ unshare --map-root-user --mount ./$< \
+ "$${SHELL:-/bin/sh}" "$$(id -ru)" "$$(id -rg)"
+
+install: container.scm sdists
+ cp guix-container.sh /etc/init.d/guix-container
+ mkdir -p /usr/local/bin
+ $(GUIX_SYS_CONTAINER) container.scm -r /usr/local/bin/guix-container
+
+reinstall:
+ rm -rf /usr/local/bin/guix-container
+ $(MAKE) install
+
+clean-runner:
+ rm -rf container-runner container-runner.touchfile
+
+clean: clean-runner
+ $(MAKE) -C subrepos/koszko-org-website clean
+ rm -rf log hosts
+
+.PHONY: all \
+ clean-runner clean \
+ start-container stop-container restart-container \
+ enter-container fake-client \
+ install reinstall \
+ sdists koszko-org-wesite-sdist
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..5db8552
--- /dev/null
+++ b/README.md
@@ -0,0 +1,11 @@
+# koszko.org server configuration
+
+This is Guix system (container) configuration used for the system serving
+https://koszko.org
+
+## Copying
+
+Contents of this repository are Copyright (C) 2022 Wojtek Kosior.
+The repository is [REUSE](https://reuse.software/)-compliant. Please look at
+the SPDX identifiers to determine the licensing of particular files or use the
+`reuse spdx` command to get a complete report.
diff --git a/README.md.license b/README.md.license
new file mode 100644
index 0000000..baf2b81
--- /dev/null
+++ b/README.md.license
@@ -0,0 +1,5 @@
+SPDX-License-Identifier: CC0-1.0
+
+Copyright (C) 2022 Wojtek Kosior
+
+Available under the terms of Creative Commons Zero v1.0 Universal.
diff --git a/container.scm b/container.scm
new file mode 100644
index 0000000..628e2b3
--- /dev/null
+++ b/container.scm
@@ -0,0 +1,99 @@
+;; SPDX-License-Identifier: CC0-1.0
+
+;; Copyright (C) 2022 Wojtek Kosior <koszko@koszko.org>
+;;
+;; Available under the terms of Creative Commons Zero v1.0 Universal.
+
+(use-modules (gnu))
+(use-modules (koszko-org-website))
+(use-package-modules web)
+(use-service-modules web)
+(use-service-modules shepherd)
+
+(define %koszko-org-virtualhost
+ (httpd-virtualhost
+ "*:80"
+ (list "\
+ ServerName koszko.org
+ ServerAlias www.koszko.org
+ DocumentRoot /srv/http/koszko.org
+ ServerAdmin koszko@koszko.org
+
+ <If \"%{HTTP_HOST} != 'koszko.org'\">
+ Redirect permanent / http://koszko.org/
+ </If>
+
+ Alias /sideload /srv/http/koszko.org
+
+ WSGIScriptReloading On
+ "
+ #~(let* ((script-rel "/share/koszko-org-website/wsgi.py")
+ (wsgi-file (string-append #$koszko-org-website script-rel)))
+ (format #f
+ "\
+ <Files ~s>
+ Require all granted
+ </Files>
+ WSGIScriptAlias / ~a
+ "
+ wsgi-file wsgi-file)))))
+
+(define %wsgi-module
+ (httpd-module
+ (name "wsgi_module")
+ (file (file-append mod-wsgi "/modules/mod_wsgi.so"))))
+
+;; logio is needed for the '%O' log format directive
+(define %logio-module
+ (httpd-module
+ (name "logio_module")
+ (file (file-append httpd "/modules/mod_logio.so"))))
+
+(define %logformat-combined
+ "\"%h %l %u %t \\\"%r\\\" %>s %O \\\"%{Referer}i\\\" \\\"%{User-Agent}i\\\"\"")
+
+(define %koszko-httpd-service
+ (service
+ httpd-service-type
+ (httpd-configuration
+ (config
+ (httpd-config-file
+ (server-name "koszko.org")
+ (error-log "/var/log/httpd/error.log")
+ (modules (cons* %wsgi-module %logio-module %default-httpd-modules))
+ (extra-config
+ (list
+ (string-join `("LogFormat" ,%logformat-combined "combined")) "\n"
+ "ErrorLog /var/log/httpd/error.log" "\n"
+ "CustomLog /var/log/httpd/access.log combined" "\n"
+ )))))))
+
+(operating-system
+ (host-name "koszko")
+ (timezone "Europe/Warsaw")
+
+ (file-systems (cons (file-system
+ (device (file-system-label "does-not-matter"))
+ (mount-point "/")
+ (type "ext4"))
+ %base-file-systems))
+ (bootloader (bootloader-configuration
+ (bootloader grub-bootloader)
+ (targets '("/dev/sdDOES-NOT-MATTER"))))
+ (services
+ (cons* %koszko-httpd-service
+ (simple-service 'koszko-org-website httpd-service-type
+ (list %koszko-org-virtualhost))
+ (service
+ (shepherd-service-type
+ 'dummy-network
+ (const
+ (shepherd-service
+ (documentation "Provide 'networking' without actually doing anything")
+ (provision '(networking))
+ (start #~(const #t))
+ (stop #~(const #t))
+ (respawn? #f)))
+ (description "Make other services assume network is there."))
+ #f)
+ %base-services)))
diff --git a/fake-client-setup-mounts.sh b/fake-client-setup-mounts.sh
new file mode 100755
index 0000000..c00a0e5
--- /dev/null
+++ b/fake-client-setup-mounts.sh
@@ -0,0 +1,18 @@
+#!/bin/sh
+
+# SPDX-License-Identifier: CC0-1.0
+
+# Copyright (C) 2022 Wojtek Kosior <koszko@koszko.org>
+#
+# Available under the terms of Creative Commons Zero v1.0 Universal.
+
+set -e
+
+SHELL_TO_USE="$1"
+USER_ID_TO_USE="$2"
+GROUP_ID_TO_USE="$3"
+
+mount --bind hosts /etc/hosts;
+mount -t tmpfs dummy /var/run/nscd 2>/dev/null || true;
+unshare --map-user="$USER_ID_TO_USE" --map-group="$GROUP_ID_TO_USE" \
+ "$SHELL_TO_USE"
diff --git a/fake-client.sh b/fake-client.sh
new file mode 100644
index 0000000..3c3ca7c
--- /dev/null
+++ b/fake-client.sh
@@ -0,0 +1,12 @@
+#!/bin/sh
+
+# SPDX-License-Identifier: CC0-1.0
+
+# Copyright (C) 2022 Wojtek Kosior <koszko@koszko.org>
+#
+# Available under the terms of Creative Commons Zero v1.0 Universal.
+
+set -e
+
+unshare --map-root-user --mount \
+ fake-client-setup-mounts.sh "${SHELL:-/bin/sh}" "$(id -u)"
diff --git a/guix-container.sh b/guix-container.sh
new file mode 100755
index 0000000..04781e8
--- /dev/null
+++ b/guix-container.sh
@@ -0,0 +1,167 @@
+#!/bin/sh
+
+# SPDX-License-Identifier: CC0-1.0
+
+# Copyright (C) 2022 Wojtek Kosior <koszko@koszko.org>
+#
+# Available under the terms of Creative Commons Zero v1.0 Universal.
+
+### BEGIN INIT INFO
+# Provides: guix-container
+# Required-Start: $local_fs $remote_fs $syslog
+# Required-Stop: $local_fs $remote_fs $syslog
+# Default-Start: 2 3 4 5
+# Default-Stop: 0 1 6
+# Short-Description: Start Wojtek's Guix container with various services
+### END INIT INFO
+
+set -e
+
+. /lib/lsb/init-functions
+
+if [ 0 != $(id -u) ]; then
+ log_action_msg "Script '$0' must be run as root"
+ exit 1
+fi
+
+PIDFILE=/run/guix-container.pid
+EXECUTABLE=/usr/local/bin/guix-container
+LOG_DIR=/var/log/guix-container
+MAX_CONTAINER_SPINUP_WAIT=60
+
+ACTION="$1"
+shift
+
+OPTIND=1
+while getopts qe:p:l:L: OPTION_LETTER ; do
+ case "$OPTION_LETTER" in
+ p) PIDFILE="$OPTARG" ;;
+ e) EXECUTABLE="$OPTARG" ;;
+ l) LOG_DIR="$OPTARG" ;;
+ w) MAX_CONTAINER_SPINUP_WAIT="$OPTARG" ;;
+ esac
+done
+
+GUILE_PID=
+SUCCESS=
+QUIET_EXIT=
+
+is_running() {
+ test -e "$PIDFILE" && test -n "$(ps -o pid= --pid $(cat "$PIDFILE"))"
+ return $?
+}
+
+network_rip() {
+ ip link delete veth-guix-out 2>/dev/null || true
+ ip netns delete guix-container-ns 2>/dev/null || true
+}
+
+stop() {
+ network_rip
+ /sbin/start-stop-daemon \
+ --stop --signal TERM --pidfile "$PIDFILE" --remove-pidfile --quiet \
+ --retry 60 2>/dev/null || true
+}
+
+onexit() {
+ if [ -z "$SUCCESS" ]; then
+ if [ "x$ACTION" = "xstart" -a -n "$GUILE_PID" ]; then
+ stop
+ kill $GUILE_PID >/dev/null || true
+ fi
+ if [ -z "$QUIET_EXIT" ]; then
+ log_failure_msg
+ fi
+ else
+ if [ -z "$QUIET_EXIT" ]; then
+ log_success_msg
+ fi
+ fi
+}
+
+start() {
+ KOSZKO_SIDELOAD_REAL=/var/www/koszko.org/html
+ KOSZKO_SIDELOAD_INSIDE=/srv/http/koszko.org
+ HTTP_DIR_SHARE_OPT=--share="$KOSZKO_SIDELOAD_REAL"="$KOSZKO_SIDELOAD_INSIDE"
+
+ mkdir -p "$(dirname "$LOG_DIR")"
+ mkdir --mode=700 -p "$LOG_DIR"
+
+ "$EXECUTABLE" $HTTP_DIR_SHARE_OPT \
+ > "$LOG_DIR"/stdout.log \
+ 2> "$LOG_DIR"/stderr.log &
+
+ GUILE_PID=$!
+ WAIT_TIME=0
+ SHEPHERD_PID=
+
+ while [ $WAIT_TIME -lt "$MAX_CONTAINER_SPINUP_WAIT" ]; do
+ sleep 1
+ WAIT_TIME=$((WAIT_TIME + 1))
+ SHEPHERD_PID=$(ps -o pid= --ppid $GUILE_PID || true)
+ if [ -n "$SHEPHERD_PID" ]; then
+ mkdir -p "$(dirname "$PIDFILE")"
+ printf '%s' $SHEPHERD_PID > "$PIDFILE"
+ break
+ fi
+ done
+
+ if [ -z "$SHEPHERD_PID" ]; then
+ exit 1
+ fi
+
+ network_rip
+
+ ip netns attach guix-container-ns $SHEPHERD_PID
+ ip link add veth-guix-out type veth peer name veth-guix-in
+ ip link set veth-guix-in netns guix-container-ns
+
+ ip link set veth-guix-out up
+ ip addr add 10.207.87.1/24 dev veth-guix-out
+
+ ip netns exec guix-container-ns ip link set lo up
+ ip netns exec guix-container-ns ip link set veth-guix-in up
+ ip netns exec guix-container-ns ip addr add 10.207.87.2/24 dev veth-guix-in
+}
+
+trap onexit EXIT
+
+case "$ACTION" in
+ start)
+ if is_running; then
+ log_daemon_msg "Guix container" "already running"
+ log_warning_msg
+ QUIET_EXIT=1
+ else
+ log_daemon_msg "Guix container" "starting"
+ start
+ fi
+ ;;
+ stop)
+ log_daemon_msg "Guix container" "stopping"
+ stop
+ ;;
+ restart)
+ QUIET_EXIT=1
+ "$0" stop "$@"
+ "$0" start "$@"
+ ;;
+ reload|force-reload)
+ QUIET_EXIT=1
+ "$0" stop "$@"
+ "$0" start "$@"
+ ;;
+ status)
+ status_of_proc -p "$PIDFILE" "$EXECUTABLE" "Guix container"
+ QUIET_EXIT=1
+ ;;
+ *)
+ log_action_msg "Usage: $0 {start|stop|status|restart|reload|force-reload}"
+ QUIET_EXIT=1
+ exit 2
+ ;;
+esac
+
+SUCCESS=1
+
+exit 0
diff --git a/hosts-extra b/hosts-extra
new file mode 100644
index 0000000..dd16317
--- /dev/null
+++ b/hosts-extra
@@ -0,0 +1,10 @@
+# SPDX-License-Identifier: CC0-1.0
+
+# Copyright (C) 2022 Wojtek Kosior <koszko@koszko.org>
+#
+# Available under the terms of Creative Commons Zero v1.0 Universal.
+
+10.207.87.2 koszko.org
+10.207.87.2 www.koszko.org
+10.207.87.2 koszkonutek-tmp.pl.eu.org
+10.207.87.2 www.koszkonutek-tmp.pl.eu.org
diff --git a/run-with-fake-mounts.sh b/run-with-fake-mounts.sh
new file mode 100755
index 0000000..75675ed
--- /dev/null
+++ b/run-with-fake-mounts.sh
@@ -0,0 +1,17 @@
+#!/bin/sh
+
+# SPDX-License-Identifier: CC0-1.0
+
+# Copyright (C) 2022 Wojtek Kosior <koszko@koszko.org>
+#
+# Available under the terms of Creative Commons Zero v1.0 Universal.
+
+set -e
+
+mkdir -p /var/www
+mount -t tmpfs dummy /var/www
+mkdir -p /var/www/koszko.org/html
+
+printf ':D\n' > /var/www/koszko.org/html/index.html
+
+"$@"
diff --git a/subrepos/koszko-org-website b/subrepos/koszko-org-website
new file mode 160000
+Subproject b582468bb70afba7196b12a8dcd022547426ad9