Compare commits
8 Commits
trunk
...
eb896676cc
| Author | SHA1 | Date | |
|---|---|---|---|
| eb896676cc | |||
| 766c9bad62 | |||
| c26453415d | |||
| dce537b50c | |||
| b7e6a93bf9 | |||
| 95b3589d22 | |||
| 4ad479f8f9 | |||
| 0e848c5e7b |
62
Dockerfile
62
Dockerfile
@@ -1,64 +1,15 @@
|
|||||||
FROM buildpack-deps:trixie
|
FROM openresty/openresty:bookworm-buildpack
|
||||||
|
|
||||||
ARG version=3.8.2.1
|
WORKDIR /opt/app
|
||||||
|
|
||||||
|
ARG version=3.7.0.1
|
||||||
ARG pkgname=pandoc-$version-1-amd64.deb
|
ARG pkgname=pandoc-$version-1-amd64.deb
|
||||||
ARG pkgurl=https://github.com/jgm/pandoc/releases/download/$version/$pkgname
|
ARG pkgurl=https://github.com/jgm/pandoc/releases/download/$version/$pkgname
|
||||||
|
|
||||||
ARG RESTY_DEB_FLAVOR=""
|
|
||||||
ARG RESTY_DEB_VERSION="=1.27.1.2-1~bookworm1"
|
|
||||||
ARG RESTY_LUAROCKS_VERSION="3.12.2"
|
|
||||||
|
|
||||||
RUN apt-get update && apt-get install -y \
|
RUN apt-get update && apt-get install -y \
|
||||||
python3-pygments lua-inotify wget && \
|
python3-pygments lua-inotify wget && \
|
||||||
wget -q -O $pkgname $pkgurl && dpkg -i $pkgname && rm $pkgname
|
wget -q -O $pkgname $pkgurl && dpkg -i $pkgname && rm $pkgname
|
||||||
|
|
||||||
RUN wget -qO - https://openresty.org/package/pubkey.gpg | gpg --dearmor > /etc/apt/trusted.gpg.d/openresty-keyring.gpg \
|
|
||||||
&& chown root:root /etc/apt/trusted.gpg.d/openresty-keyring.gpg \
|
|
||||||
&& chmod ugo+r /etc/apt/trusted.gpg.d/openresty-keyring.gpg \
|
|
||||||
&& chmod go-w /etc/apt/trusted.gpg.d/openresty-keyring.gpg \
|
|
||||||
&& echo "\nTypes: deb\nURIs: https://openresty.org/package/debian\nSuites: bookworm\nComponents: openresty\nEnabled: yes\nSigned-By: /etc/apt/trusted.gpg.d/openresty-keyring.gpg" >> /etc/apt/sources.list.d/debian.sources \
|
|
||||||
&& DEBIAN_FRONTEND=noninteractive apt-get update \
|
|
||||||
&& DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
|
|
||||||
openresty${RESTY_DEB_FLAVOR}${RESTY_DEB_VERSION} \
|
|
||||||
openresty-resty${RESTY_DEB_FLAVOR}${RESTY_DEB_VERSION} \
|
|
||||||
openresty-opm${RESTY_DEB_FLAVOR}${RESTY_DEB_VERSION} \
|
|
||||||
openresty-openssl3${RESTY_DEB_FLAVOR}-dev \
|
|
||||||
openresty-pcre2${RESTY_DEB_FLAVOR}-dev \
|
|
||||||
openresty-zlib${RESTY_DEB_FLAVOR}-dev \
|
|
||||||
&& mkdir -p /var/run/openresty \
|
|
||||||
&& ln -sf /dev/stdout /usr/local/openresty${RESTY_DEB_FLAVOR}/nginx/logs/access.log \
|
|
||||||
&& ln -sf /dev/stderr /usr/local/openresty${RESTY_DEB_FLAVOR}/nginx/logs/error.log
|
|
||||||
|
|
||||||
# Install LuaRocks
|
|
||||||
RUN curl -fSL https://luarocks.github.io/luarocks/releases/luarocks-${RESTY_LUAROCKS_VERSION}.tar.gz -o luarocks-${RESTY_LUAROCKS_VERSION}.tar.gz \
|
|
||||||
&& tar xzf luarocks-${RESTY_LUAROCKS_VERSION}.tar.gz \
|
|
||||||
&& cd luarocks-${RESTY_LUAROCKS_VERSION} \
|
|
||||||
&& mkdir -p /usr/local/openresty/luajit \
|
|
||||||
&& ./configure \
|
|
||||||
--prefix=/usr/local/openresty/luajit \
|
|
||||||
--with-lua=/usr/local/openresty/luajit \
|
|
||||||
--with-lua-include=/usr/local/openresty/luajit/include/luajit-2.1 \
|
|
||||||
&& make build \
|
|
||||||
&& make install \
|
|
||||||
&& cd /tmp \
|
|
||||||
&& rm -rf luarocks-${RESTY_LUAROCKS_VERSION} luarocks-${RESTY_LUAROCKS_VERSION}.tar.gz
|
|
||||||
|
|
||||||
# Add additional binaries into PATH for convenience
|
|
||||||
ENV PATH="$PATH:/usr/local/openresty${RESTY_DEB_FLAVOR}/luajit/bin:/usr/local/openresty${RESTY_DEB_FLAVOR}/nginx/sbin:/usr/local/openresty${RESTY_DEB_FLAVOR}/bin"
|
|
||||||
|
|
||||||
# Add LuaRocks paths
|
|
||||||
# If OpenResty changes, these may need updating:
|
|
||||||
# /usr/local/openresty/bin/resty -e 'print(package.path)'
|
|
||||||
# /usr/local/openresty/bin/resty -e 'print(package.cpath)'
|
|
||||||
ENV LUA_PATH="/usr/local/openresty/site/lualib/?.ljbc;/usr/local/openresty/site/lualib/?/init.ljbc;/usr/local/openresty/lualib/?.ljbc;/usr/local/openresty/lualib/?/init.ljbc;/usr/local/openresty/site/lualib/?.lua;/usr/local/openresty/site/lualib/?/init.lua;/usr/local/openresty/lualib/?.lua;/usr/local/openresty/lualib/?/init.lua;./?.lua;/usr/local/openresty/luajit/share/luajit-2.1/?.lua;/usr/local/share/lua/5.1/?.lua;/usr/local/share/lua/5.1/?/init.lua;/usr/local/openresty/luajit/share/lua/5.1/?.lua;/usr/local/openresty/luajit/share/lua/5.1/?/init.lua"
|
|
||||||
ENV LUA_CPATH="/usr/local/openresty/site/lualib/?.so;/usr/local/openresty/lualib/?.so;./?.so;/usr/local/lib/lua/5.1/?.so;/usr/local/openresty/luajit/lib/lua/5.1/?.so;/usr/local/lib/lua/5.1/loadall.so;/usr/local/openresty/luajit/lib/lua/5.1/?.so"
|
|
||||||
|
|
||||||
WORKDIR /opt/app
|
|
||||||
|
|
||||||
# Copy nginx configuration files
|
|
||||||
COPY nginx.conf /usr/local/openresty${RESTY_DEB_FLAVOR}/nginx/conf/nginx.conf
|
|
||||||
COPY nginx.vh.default.conf /etc/nginx/conf.d/default.conf
|
|
||||||
|
|
||||||
RUN luarocks install sitegen
|
RUN luarocks install sitegen
|
||||||
|
|
||||||
# needed for sitegen watcher
|
# needed for sitegen watcher
|
||||||
@@ -84,8 +35,3 @@ RUN mkdir -p /var/www/certs/webdevcat.me \
|
|||||||
-addext "subjectAltName=DNS:webdevcat.me,DNS:git.webdevcat.me,DNS:apps.webdevcat.me"
|
-addext "subjectAltName=DNS:webdevcat.me,DNS:git.webdevcat.me,DNS:apps.webdevcat.me"
|
||||||
|
|
||||||
CMD ["sh", "-c", "openresty -p `pwd` -g 'daemon off;'"]
|
CMD ["sh", "-c", "openresty -p `pwd` -g 'daemon off;'"]
|
||||||
|
|
||||||
# Use SIGQUIT instead of default SIGTERM to cleanly drain requests
|
|
||||||
# See https://github.com/openresty/docker-openresty/blob/master/README.md#tips--pitfalls
|
|
||||||
STOPSIGNAL SIGQUIT
|
|
||||||
|
|
||||||
|
|||||||
3
Makefile
3
Makefile
@@ -5,9 +5,6 @@ serve:
|
|||||||
docker run --rm -it --init -v $(PWD):/opt/app -p 8080:80 $(image) \
|
docker run --rm -it --init -v $(PWD):/opt/app -p 8080:80 $(image) \
|
||||||
sh -c "openresty -p /opt/app -g 'daemon off;' -c conf/dev.nginx.conf"
|
sh -c "openresty -p /opt/app -g 'daemon off;' -c conf/dev.nginx.conf"
|
||||||
|
|
||||||
shell:
|
|
||||||
docker run --rm -it --init -v $(PWD):/opt/app -p 8080:80 $(image) bash
|
|
||||||
|
|
||||||
build:
|
build:
|
||||||
$(if $(file), $(docker-run) sitegen build $(file), $(docker-run) sitegen)
|
$(if $(file), $(docker-run) sitegen build $(file), $(docker-run) sitegen)
|
||||||
|
|
||||||
|
|||||||
50
README.md
50
README.md
@@ -1,26 +1,23 @@
|
|||||||
## requirements
|
## requirements
|
||||||
|
|
||||||
* Docker
|
`pandoc` utility must be installed
|
||||||
|
|
||||||
## up and running
|
- Debian Bookworm:
|
||||||
|
|
||||||
### build container image
|
$ apt-get update && apt-get install -y pandoc
|
||||||
|
|
||||||
|
## start default openresty
|
||||||
|
|
||||||
|
1. Run `docker run --rm -it --init -w /opt -v $PWD:/opt -p 80:80 openresty/openresty:1.27.1.2-0-bookworm-buildpack`
|
||||||
|
2. Visit `localhost` in web browser.
|
||||||
|
|
||||||
|
## start a custom site
|
||||||
|
|
||||||
|
### build docker image
|
||||||
|
|
||||||
$ make image-build
|
$ make image-build
|
||||||
|
|
||||||
### build site
|
### rebuild an existing docker image
|
||||||
|
|
||||||
$ make build
|
|
||||||
|
|
||||||
### start dev server
|
|
||||||
|
|
||||||
$ make serve
|
|
||||||
|
|
||||||
Visit `localhost:8080` in web browser
|
|
||||||
|
|
||||||
## getting around
|
|
||||||
|
|
||||||
### rebuild the container image
|
|
||||||
|
|
||||||
$ make image-rm image-build
|
$ make image-rm image-build
|
||||||
|
|
||||||
@@ -30,11 +27,9 @@ Visit `localhost:8080` in web browser
|
|||||||
|
|
||||||
### add an index page
|
### add an index page
|
||||||
|
|
||||||
#### create the file
|
|
||||||
|
|
||||||
$ docker run --rm -w /opt/app -v $PWD:/opt/app miti.sh sitegen page /opt/app index
|
$ docker run --rm -w /opt/app -v $PWD:/opt/app miti.sh sitegen page /opt/app index
|
||||||
|
|
||||||
#### add to `site.moon`
|
### add to `site.moon`
|
||||||
|
|
||||||
add "index.md"
|
add "index.md"
|
||||||
|
|
||||||
@@ -57,13 +52,19 @@ Visit `localhost:8080` in web browser
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
### build site
|
||||||
|
|
||||||
|
$ make build
|
||||||
|
|
||||||
### build a single file
|
### build a single file
|
||||||
|
|
||||||
$ make build file=index.html
|
$ make build file=index.html
|
||||||
|
|
||||||
### start a shell
|
### start dev server
|
||||||
|
|
||||||
$ make shell
|
$ make serve
|
||||||
|
|
||||||
|
Visit `localhost:8080` in web browser
|
||||||
|
|
||||||
### start watcher
|
### start watcher
|
||||||
|
|
||||||
@@ -89,13 +90,6 @@ example:
|
|||||||
|
|
||||||
$ make deploy
|
$ make deploy
|
||||||
|
|
||||||
### renew server TSL certs
|
|
||||||
|
|
||||||
SSH into server admin account and run:
|
|
||||||
|
|
||||||
$ cd www
|
|
||||||
$ dehydrated --config config -c
|
|
||||||
|
|
||||||
## gotchas
|
## gotchas
|
||||||
|
|
||||||
### What is error `cosmo failed: [string "..."]:62: cannot resume dead coroutine`?
|
### What is error `cosmo failed: [string "..."]:62: cannot resume dead coroutine`?
|
||||||
|
|||||||
Binary file not shown.
|
Before Width: | Height: | Size: 49 KiB |
BIN
html/images/openresty-splash-page.png
Normal file
BIN
html/images/openresty-splash-page.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 69 KiB |
117
nginx.conf
117
nginx.conf
@@ -1,117 +0,0 @@
|
|||||||
|
|
||||||
#user nobody;
|
|
||||||
worker_processes 1;
|
|
||||||
|
|
||||||
#error_log logs/error.log;
|
|
||||||
#error_log logs/error.log notice;
|
|
||||||
#error_log logs/error.log info;
|
|
||||||
|
|
||||||
#pid logs/nginx.pid;
|
|
||||||
|
|
||||||
|
|
||||||
events {
|
|
||||||
worker_connections 1024;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
http {
|
|
||||||
include mime.types;
|
|
||||||
default_type application/octet-stream;
|
|
||||||
|
|
||||||
#log_format main '$remote_addr - $remote_user [$time_local] "$request" '
|
|
||||||
# '$status $body_bytes_sent "$http_referer" '
|
|
||||||
# '"$http_user_agent" "$http_x_forwarded_for"';
|
|
||||||
|
|
||||||
#access_log logs/access.log main;
|
|
||||||
|
|
||||||
sendfile on;
|
|
||||||
#tcp_nopush on;
|
|
||||||
|
|
||||||
#keepalive_timeout 0;
|
|
||||||
keepalive_timeout 65;
|
|
||||||
|
|
||||||
#gzip on;
|
|
||||||
|
|
||||||
server {
|
|
||||||
listen 80;
|
|
||||||
server_name localhost;
|
|
||||||
|
|
||||||
#charset koi8-r;
|
|
||||||
|
|
||||||
#access_log logs/host.access.log main;
|
|
||||||
|
|
||||||
location / {
|
|
||||||
root html;
|
|
||||||
index index.html index.htm;
|
|
||||||
}
|
|
||||||
|
|
||||||
#error_page 404 /404.html;
|
|
||||||
|
|
||||||
# redirect server error pages to the static page /50x.html
|
|
||||||
#
|
|
||||||
error_page 500 502 503 504 /50x.html;
|
|
||||||
location = /50x.html {
|
|
||||||
root html;
|
|
||||||
}
|
|
||||||
|
|
||||||
# proxy the PHP scripts to Apache listening on 127.0.0.1:80
|
|
||||||
#
|
|
||||||
#location ~ \.php$ {
|
|
||||||
# proxy_pass http://127.0.0.1;
|
|
||||||
#}
|
|
||||||
|
|
||||||
# pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
|
|
||||||
#
|
|
||||||
#location ~ \.php$ {
|
|
||||||
# root html;
|
|
||||||
# fastcgi_pass 127.0.0.1:9000;
|
|
||||||
# fastcgi_index index.php;
|
|
||||||
# fastcgi_param SCRIPT_FILENAME /scripts$fastcgi_script_name;
|
|
||||||
# include fastcgi_params;
|
|
||||||
#}
|
|
||||||
|
|
||||||
# deny access to .htaccess files, if Apache's document root
|
|
||||||
# concurs with nginx's one
|
|
||||||
#
|
|
||||||
#location ~ /\.ht {
|
|
||||||
# deny all;
|
|
||||||
#}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
# another virtual host using mix of IP-, name-, and port-based configuration
|
|
||||||
#
|
|
||||||
#server {
|
|
||||||
# listen 8000;
|
|
||||||
# listen somename:8080;
|
|
||||||
# server_name somename alias another.alias;
|
|
||||||
|
|
||||||
# location / {
|
|
||||||
# root html;
|
|
||||||
# index index.html index.htm;
|
|
||||||
# }
|
|
||||||
#}
|
|
||||||
|
|
||||||
|
|
||||||
# HTTPS server
|
|
||||||
#
|
|
||||||
#server {
|
|
||||||
# listen 443 ssl;
|
|
||||||
# server_name localhost;
|
|
||||||
|
|
||||||
# ssl_certificate cert.pem;
|
|
||||||
# ssl_certificate_key cert.key;
|
|
||||||
|
|
||||||
# ssl_session_cache shared:SSL:1m;
|
|
||||||
# ssl_session_timeout 5m;
|
|
||||||
|
|
||||||
# ssl_ciphers HIGH:!aNULL:!MD5;
|
|
||||||
# ssl_prefer_server_ciphers on;
|
|
||||||
|
|
||||||
# location / {
|
|
||||||
# root html;
|
|
||||||
# index index.html index.htm;
|
|
||||||
# }
|
|
||||||
#}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,58 +0,0 @@
|
|||||||
# nginx.vh.default.conf -- docker-openresty
|
|
||||||
#
|
|
||||||
# This file is installed to:
|
|
||||||
# `/etc/nginx/conf.d/default.conf`
|
|
||||||
#
|
|
||||||
# It tracks the `server` section of the upstream OpenResty's `nginx.conf`.
|
|
||||||
#
|
|
||||||
# This config (and any other configs in `etc/nginx/conf.d/`) is loaded by
|
|
||||||
# default by the `include` directive in `/usr/local/openresty/nginx/conf/nginx.conf`.
|
|
||||||
#
|
|
||||||
# See https://github.com/openresty/docker-openresty/blob/master/README.md#nginx-config-files
|
|
||||||
#
|
|
||||||
|
|
||||||
|
|
||||||
server {
|
|
||||||
listen 80;
|
|
||||||
server_name localhost;
|
|
||||||
|
|
||||||
#charset koi8-r;
|
|
||||||
#access_log /var/log/nginx/host.access.log main;
|
|
||||||
|
|
||||||
location / {
|
|
||||||
root /usr/local/openresty/nginx/html;
|
|
||||||
index index.html index.htm;
|
|
||||||
}
|
|
||||||
|
|
||||||
#error_page 404 /404.html;
|
|
||||||
|
|
||||||
# redirect server error pages to the static page /50x.html
|
|
||||||
#
|
|
||||||
error_page 500 502 503 504 /50x.html;
|
|
||||||
location = /50x.html {
|
|
||||||
root /usr/local/openresty/nginx/html;
|
|
||||||
}
|
|
||||||
|
|
||||||
# proxy the PHP scripts to Apache listening on 127.0.0.1:80
|
|
||||||
#
|
|
||||||
#location ~ \.php$ {
|
|
||||||
# proxy_pass http://127.0.0.1;
|
|
||||||
#}
|
|
||||||
|
|
||||||
# pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
|
|
||||||
#
|
|
||||||
#location ~ \.php$ {
|
|
||||||
# root /usr/local/openresty/nginx/html;
|
|
||||||
# fastcgi_pass 127.0.0.1:9000;
|
|
||||||
# fastcgi_index index.php;
|
|
||||||
# fastcgi_param SCRIPT_FILENAME /scripts$fastcgi_script_name;
|
|
||||||
# include fastcgi_params;
|
|
||||||
#}
|
|
||||||
|
|
||||||
# deny access to .htaccess files, if Apache's document root
|
|
||||||
# concurs with nginx's one
|
|
||||||
#
|
|
||||||
#location ~ /\.ht {
|
|
||||||
# deny all;
|
|
||||||
#}
|
|
||||||
}
|
|
||||||
418
posts/2025-06-22-test-nginx-conf-directives.md
Normal file
418
posts/2025-06-22-test-nginx-conf-directives.md
Normal file
@@ -0,0 +1,418 @@
|
|||||||
|
$index
|
||||||
|
|
||||||
|
## Introduction
|
||||||
|
|
||||||
|
We'll need nginx and luarocks. Buildpack has luarocks installed.
|
||||||
|
|
||||||
|
docker pull openresty/openresty:bookworm-buildpack
|
||||||
|
|
||||||
|
$ docker run --rm -it -p 80:80 openresty/openresty:bookworm-buildpack
|
||||||
|
|
||||||
|
Visit `localhost` in browser. Should see OpenResty splash page.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
https://openresty.org/en/getting-started.html#prepare-directory-layout
|
||||||
|
|
||||||
|
$ mkdir -p logs/ conf/conf.d/ html/
|
||||||
|
|
||||||
|
https://github.com/openresty/docker-openresty?tab=readme-ov-file#nginx-config-files
|
||||||
|
|
||||||
|
$ docker run --rm -it -w /opt -v $PWD:/opt openresty/openresty:bookworm-buildpack \
|
||||||
|
cp /etc/nginx/conf.d/default.conf /opt/conf.d/
|
||||||
|
|
||||||
|
edit default.conf
|
||||||
|
change `root /usr/local/openresty/nginx/html;` to:
|
||||||
|
|
||||||
|
root /var/www;
|
||||||
|
|
||||||
|
`html/index.html`
|
||||||
|
|
||||||
|
```html
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
|
<title></title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
hello world!
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
```
|
||||||
|
|
||||||
|
Start nginx:
|
||||||
|
|
||||||
|
```console
|
||||||
|
$ docker run --rm -it -p 80:80 \
|
||||||
|
-v $PWD/conf/conf.d:/etc/nginx/conf.d -v $PWD/html:/var/www \
|
||||||
|
openresty/openresty:bookworm-buildpack
|
||||||
|
```
|
||||||
|
|
||||||
|
Then, in another console:
|
||||||
|
|
||||||
|
```console
|
||||||
|
$ curl -v localhost
|
||||||
|
* Trying 127.0.0.1:80...
|
||||||
|
* Connected to localhost (127.0.0.1) port 80 (#0)
|
||||||
|
> GET / HTTP/1.1
|
||||||
|
> Host: localhost
|
||||||
|
> User-Agent: curl/7.88.1
|
||||||
|
> Accept: */*
|
||||||
|
>
|
||||||
|
< HTTP/1.1 200 OK
|
||||||
|
< Server: openresty/1.27.1.2
|
||||||
|
< ...
|
||||||
|
<
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
...
|
||||||
|
<body>
|
||||||
|
hello world!
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
```
|
||||||
|
|
||||||
|
If we wanted to write a test for that, we need some packages from `luarocks`.
|
||||||
|
|
||||||
|
`Dockerfile`
|
||||||
|
|
||||||
|
```Dockerfile
|
||||||
|
FROM openresty/openresty:bookworm-buildpack
|
||||||
|
|
||||||
|
WORKDIR /opt/app
|
||||||
|
|
||||||
|
# needed for testing
|
||||||
|
RUN luarocks install busted
|
||||||
|
RUN luarocks install luajit-curl
|
||||||
|
RUN luarocks install luasocket # needed for testing nginx reverse proxy
|
||||||
|
```
|
||||||
|
|
||||||
|
```console
|
||||||
|
$ docker build -t test-nginx .
|
||||||
|
$ mkdir spec
|
||||||
|
```
|
||||||
|
|
||||||
|
`spec/nginx_spec.moon`
|
||||||
|
|
||||||
|
```moonscript
|
||||||
|
http = require "luajit-curl-helper.http"
|
||||||
|
|
||||||
|
req = (url) ->
|
||||||
|
request = http.init url
|
||||||
|
st = request\perform!
|
||||||
|
error request\lastError! if not st
|
||||||
|
request
|
||||||
|
|
||||||
|
describe "http://localhost", ->
|
||||||
|
it "sends /index.html", ->
|
||||||
|
request = req "http://localhost"
|
||||||
|
assert.same request\statusCode!, 200
|
||||||
|
assert.same request\statusMessage!, "OK"
|
||||||
|
assert.same request\body!\match("<body>%s+(.-)%s+</body>"), "hello world!"
|
||||||
|
```
|
||||||
|
|
||||||
|
Start the test server:
|
||||||
|
|
||||||
|
```console
|
||||||
|
$ ct=$(docker run --rm -d \
|
||||||
|
-v $PWD/conf/conf.d:/etc/nginx/conf.d \
|
||||||
|
-v $PWD/html:/var/www \
|
||||||
|
-v $PWD:/opt/app \
|
||||||
|
test-nginx)
|
||||||
|
```
|
||||||
|
|
||||||
|
Run the tests.
|
||||||
|
|
||||||
|
```console
|
||||||
|
$ docker exec -t $ct busted
|
||||||
|
●
|
||||||
|
1 success / 0 failures / 0 errors / 0 pending : 0.008246 seconds
|
||||||
|
```
|
||||||
|
|
||||||
|
Stop the test server.
|
||||||
|
|
||||||
|
```console
|
||||||
|
$ docker exec -t $ct openresty -s stop
|
||||||
|
```
|
||||||
|
|
||||||
|
## Edit hosts
|
||||||
|
|
||||||
|
Instead of `localhost` we'd like to use an actual domain name. We can do this
|
||||||
|
with the `--add-host` option. But before we do that, we want to make sure our
|
||||||
|
container does not have access to the internet, otherwise we might
|
||||||
|
unintentionally get a response from a domain's server on the internet rather
|
||||||
|
than from our test server.
|
||||||
|
|
||||||
|
$ docker network create --internal no-internet
|
||||||
|
|
||||||
|
Now we can start the test server with our host:
|
||||||
|
|
||||||
|
```console
|
||||||
|
$ ct=$(docker run --rm -d \
|
||||||
|
-v $PWD/conf/conf.d:/etc/nginx/conf.d \
|
||||||
|
-v $PWD/html:/var/www \
|
||||||
|
-v $PWD:/opt/app \
|
||||||
|
--network no-internet \
|
||||||
|
--add-host=domain.abc=127.0.0.1 \
|
||||||
|
test-nginx)
|
||||||
|
```
|
||||||
|
|
||||||
|
Update our test
|
||||||
|
|
||||||
|
request = req "http://localhost"
|
||||||
|
|
||||||
|
to
|
||||||
|
|
||||||
|
request = req "http://domain.abc"
|
||||||
|
|
||||||
|
Run the tests.
|
||||||
|
|
||||||
|
```console
|
||||||
|
$ docker exec -t $ct busted
|
||||||
|
●
|
||||||
|
1 success / 0 failures / 0 errors / 0 pending : 0.008246 seconds
|
||||||
|
```
|
||||||
|
|
||||||
|
Stop the test server.
|
||||||
|
|
||||||
|
```console
|
||||||
|
$ docker exec -t $ct openresty -s stop
|
||||||
|
```
|
||||||
|
|
||||||
|
## Add a test to make sure the test server is offline
|
||||||
|
|
||||||
|
```moonscript
|
||||||
|
describe "test environment", ->
|
||||||
|
it "can't connect to the internet", ->
|
||||||
|
assert.has_error (-> req "http://example.org"),
|
||||||
|
"Couldn't resolve host name"
|
||||||
|
```
|
||||||
|
|
||||||
|
## Create a Makefile
|
||||||
|
|
||||||
|
Let's create a `Makefile` to make running all these commands easier.
|
||||||
|
|
||||||
|
`Makefile`
|
||||||
|
|
||||||
|
```Makefile
|
||||||
|
image = test-nginx
|
||||||
|
loopback = 127.0.0.1
|
||||||
|
|
||||||
|
image-build:
|
||||||
|
docker build -t $(image) .
|
||||||
|
|
||||||
|
image-rm:
|
||||||
|
docker image rm $(image)
|
||||||
|
|
||||||
|
test:
|
||||||
|
@ct=$(shell docker run --rm -d \
|
||||||
|
-v $(PWD)/conf/conf.d:/etc/nginx/conf.d \
|
||||||
|
-v $(PWD)/html:/var/www \
|
||||||
|
-v $(PWD):/opt/app \
|
||||||
|
--network no-internet \
|
||||||
|
--add-host=domain.abc=$(loopback) \
|
||||||
|
$(image)); \
|
||||||
|
docker exec -t $$ct busted; \
|
||||||
|
docker exec $$ct openresty -s stop
|
||||||
|
```
|
||||||
|
|
||||||
|
Now we can run tests by running `make test`.
|
||||||
|
|
||||||
|
```console
|
||||||
|
$ make test
|
||||||
|
●●
|
||||||
|
2 successes / 0 failures / 0 errors / 0 pending : 0.008812 seconds
|
||||||
|
```
|
||||||
|
|
||||||
|
## SSL
|
||||||
|
|
||||||
|
We want our server to redirect all `http` requests to `https`.
|
||||||
|
|
||||||
|
Our test:
|
||||||
|
|
||||||
|
```moonscript
|
||||||
|
describe "http://domain.abc", ->
|
||||||
|
it "redirects to https", ->
|
||||||
|
request = req "http://domain.abc"
|
||||||
|
assert.same request\statusCode!, 301
|
||||||
|
assert.same request\statusMessage!, "Moved Permanently"
|
||||||
|
assert.same request\header!.Location, "https://domain.abc/"
|
||||||
|
```
|
||||||
|
|
||||||
|
```console
|
||||||
|
$ make test
|
||||||
|
●●◼
|
||||||
|
2 successes / 1 failure / 0 errors / 0 pending : 0.010449 seconds
|
||||||
|
|
||||||
|
Failure → .../luajit/lib/luarocks/rocks-5.1/busted/2.2.0-1/bin/busted @ 3
|
||||||
|
http://domain.abc redirects to https
|
||||||
|
spec/nginx_spec.moon:24: Expected objects to be the same.
|
||||||
|
Passed in:
|
||||||
|
(number) 301
|
||||||
|
Expected:
|
||||||
|
(number) 200
|
||||||
|
```
|
||||||
|
|
||||||
|
Make self-signed certs in Dockerfile:
|
||||||
|
|
||||||
|
```Dockerfile
|
||||||
|
RUN openssl req -x509 -newkey rsa:4096 -nodes \
|
||||||
|
-keyout /etc/ssl/private/domain.abc.pem \
|
||||||
|
-out /etc/ssl/certs/domain.abc.pem \
|
||||||
|
-sha256 -days 365 -subj '/CN=domain.abc' \
|
||||||
|
-addext "subjectAltName=DNS:domain.abc"
|
||||||
|
```
|
||||||
|
|
||||||
|
Edit `default.conf`:
|
||||||
|
|
||||||
|
```
|
||||||
|
server {
|
||||||
|
listen 80;
|
||||||
|
return 301 https://$host$request_uri;
|
||||||
|
}
|
||||||
|
|
||||||
|
server {
|
||||||
|
listen 443 ssl;
|
||||||
|
server_name domain.abc;
|
||||||
|
ssl_certificate /etc/ssl/certs/domain.abc.pem;
|
||||||
|
ssl_certificate_key /etc/ssl/private/domain.abc.pem;
|
||||||
|
```
|
||||||
|
|
||||||
|
Rebuild the image:
|
||||||
|
|
||||||
|
```console
|
||||||
|
$ make image-rm
|
||||||
|
$ make image-build
|
||||||
|
```
|
||||||
|
|
||||||
|
Run tests:
|
||||||
|
|
||||||
|
```console
|
||||||
|
$ make test
|
||||||
|
●◼●
|
||||||
|
2 successes / 1 failure / 0 errors / 0 pending : 0.009618 seconds
|
||||||
|
|
||||||
|
Failure → .../luajit/lib/luarocks/rocks-5.1/busted/2.2.0-1/bin/busted @ 3
|
||||||
|
http://domain.abc sends /index.html
|
||||||
|
spec/nginx_spec.moon:17: Expected objects to be the same.
|
||||||
|
Passed in:
|
||||||
|
(number) 200
|
||||||
|
Expected:
|
||||||
|
(number) 301
|
||||||
|
```
|
||||||
|
|
||||||
|
Fix test:
|
||||||
|
|
||||||
|
```moonscript
|
||||||
|
describe "https://domain.abc", ->
|
||||||
|
it "sends /index.html", ->
|
||||||
|
request = req "https://domain.abc"
|
||||||
|
```
|
||||||
|
|
||||||
|
Run tests:
|
||||||
|
|
||||||
|
```console
|
||||||
|
$ make test
|
||||||
|
●●●
|
||||||
|
3 successes / 0 failures / 0 errors / 0 pending : 0.017065 seconds
|
||||||
|
```
|
||||||
|
|
||||||
|
## Reverse proxy a subdomain to a Gitea unix socket
|
||||||
|
|
||||||
|
Add to `default.conf`:
|
||||||
|
|
||||||
|
```
|
||||||
|
server {
|
||||||
|
listen 443 ssl;
|
||||||
|
server_name git.domain.abc;
|
||||||
|
|
||||||
|
location / {
|
||||||
|
client_max_body_size 1024M;
|
||||||
|
proxy_pass http://unix:/run/gitea/gitea.socket;
|
||||||
|
proxy_set_header Connection $http_connection;
|
||||||
|
proxy_set_header Upgrade $http_upgrade;
|
||||||
|
proxy_set_header Host $host;
|
||||||
|
proxy_set_header X-Real-IP $remote_addr;
|
||||||
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||||
|
proxy_set_header X-Forwarded-Proto $scheme;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Add subdomain to certs in Dockerfile:
|
||||||
|
|
||||||
|
```
|
||||||
|
-addext "subjectAltName=DNS:domain.abc,DNS:git.domain.abc"
|
||||||
|
```
|
||||||
|
|
||||||
|
Add a test socket server:
|
||||||
|
|
||||||
|
`spec/unixstreamsrvr.moon`
|
||||||
|
|
||||||
|
```moonscript
|
||||||
|
-- modified from
|
||||||
|
-- https://github.com/lunarmodules/luasocket/blob/4844a48fbf76b0400fd7b7e4d15d244484019df1/test/unixstreamsrvr.lua
|
||||||
|
socket = require "socket"
|
||||||
|
socket.unix = require "socket.unix"
|
||||||
|
u = assert socket.unix.stream!
|
||||||
|
assert u\bind "/run/gitea/gitea.socket"
|
||||||
|
assert u\listen!
|
||||||
|
assert u\settimeout 1
|
||||||
|
c = assert u\accept!
|
||||||
|
|
||||||
|
while true
|
||||||
|
m = assert c\receive!
|
||||||
|
break if m == ""
|
||||||
|
print m
|
||||||
|
```
|
||||||
|
|
||||||
|
Add a spec:
|
||||||
|
|
||||||
|
```moonscript
|
||||||
|
describe "https://git.domain.abc", ->
|
||||||
|
it "reverse-proxy's request to a gitea unix socket", ->
|
||||||
|
socket = fname: "unixstreamsrvr.moon", dir: "/run/gitea", owner: "nobody"
|
||||||
|
basepath = debug.getinfo(1).short_src\match"^(.*)/[^/]*$" or "."
|
||||||
|
seconds = 0.1
|
||||||
|
|
||||||
|
os.execute "install -o #{socket.owner} -d #{socket.dir}"
|
||||||
|
cmd = "su -s /bin/bash -c 'moon %s' %s"
|
||||||
|
server = io.popen cmd\format "#{basepath}/#{socket.fname}", socket.owner
|
||||||
|
os.execute "sleep #{seconds}"
|
||||||
|
f = io.popen "find #{socket.dir} -type s -ls", "r"
|
||||||
|
result = with f\read "*a"
|
||||||
|
f\close!
|
||||||
|
assert.truthy result\match "nobody%s+nogroup.+#{socket.dir}/gitea.socket"
|
||||||
|
|
||||||
|
req "https://git.domain.abc"
|
||||||
|
|
||||||
|
reqheader = with server\read "*a"
|
||||||
|
server\close!
|
||||||
|
|
||||||
|
assert.truthy reqheader\match "Host: git.domain.abc"
|
||||||
|
```
|
||||||
|
|
||||||
|
Edit Makefile:
|
||||||
|
|
||||||
|
--add-host=git.domain.abc=$(loopback) \
|
||||||
|
|
||||||
|
Rebuild image:
|
||||||
|
|
||||||
|
```console
|
||||||
|
$ make image-rm image-build
|
||||||
|
```
|
||||||
|
|
||||||
|
Run tests:
|
||||||
|
|
||||||
|
```console
|
||||||
|
$ make test
|
||||||
|
●●●●
|
||||||
|
4 successes / 0 failures / 0 errors / 0 pending : 0.131619 seconds
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
## Conclusion
|
||||||
|
|
||||||
|
|
||||||
@@ -1,566 +0,0 @@
|
|||||||
{
|
|
||||||
title: "Test nginx Configuration Directives"
|
|
||||||
blurb: "We use MoonScript and some Lua packages to write tests for the
|
|
||||||
directives in our `nginx` configuration files."
|
|
||||||
}
|
|
||||||
$index
|
|
||||||
|
|
||||||
## Introduction
|
|
||||||
|
|
||||||
[`nginx`](https://docs.nginx.com/nginx/admin-guide/web-server/web-server/#rewrite-uris-in-requests)
|
|
||||||
configuration can contain any number of important directives (redirects and
|
|
||||||
rewrites, for example) that need to be verified for correctness. We can write
|
|
||||||
tests for directives and run them against a test server to ensure they are
|
|
||||||
correct.
|
|
||||||
|
|
||||||
To do this, we'll use...
|
|
||||||
|
|
||||||
- [MoonScript](https://moonscript.org) and (by extension) [Lua](https://www.lua.org/) programming languages
|
|
||||||
- `nginx` we'll get from [OpenResty](https://openresty.org/en/), a web platform
|
|
||||||
created by Chinese developer, [Yichun Zhang](https://agentzh.org/)
|
|
||||||
- the [Busted testing framework](https://lunarmodules.github.io/busted/)
|
|
||||||
- the Lua package manager, [LuaRocks](https://luarocks.org/)
|
|
||||||
- a fantastic little library, [`luajit-curl`](https://bitbucket.org/senanetworksinc/luajit-curl/src/master/),
|
|
||||||
from Japanese developer [SENA Networks, Inc](https://www.sena-networks.co.jp)
|
|
||||||
- another great library, written by volunteers, [LuaSocket](https://github.com/lunarmodules/luasocket)
|
|
||||||
- our favorite container manager, [Docker Engine](https://docs.docker.com/engine/)
|
|
||||||
|
|
||||||
## Setup
|
|
||||||
|
|
||||||
Since we require LuaRocks, we'll use a Buildpack tag, which comes with it
|
|
||||||
already installed.
|
|
||||||
|
|
||||||
```console
|
|
||||||
$ docker pull openresty/openresty:bookworm-buildpack
|
|
||||||
```
|
|
||||||
|
|
||||||
Start a server on `localhost`:
|
|
||||||
|
|
||||||
```console
|
|
||||||
$ docker run --rm -it -p 80:80 openresty/openresty:bookworm-buildpack
|
|
||||||
```
|
|
||||||
|
|
||||||
We can visit `localhost` in our browser and we should see the OpenResty splash
|
|
||||||
page.
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
## Get `nginx` running
|
|
||||||
|
|
||||||
First, let's [prepare the directory layout](https://openresty.org/en/getting-started.html#prepare-directory-layout).
|
|
||||||
|
|
||||||
```console
|
|
||||||
$ mkdir -p logs/ conf/conf.d/ html/
|
|
||||||
```
|
|
||||||
|
|
||||||
Next, we copy over [the default `nginx` config file](https://github.com/openresty/docker-openresty?tab=readme-ov-file#nginx-config-files).
|
|
||||||
|
|
||||||
```console
|
|
||||||
$ docker run --rm -it -w /opt -v $PWD:/opt openresty/openresty:bookworm-buildpack \
|
|
||||||
cp /etc/nginx/conf.d/default.conf /opt/conf.d/
|
|
||||||
```
|
|
||||||
|
|
||||||
Then, we update the root directive in `default.conf`:
|
|
||||||
|
|
||||||
::: filename-for-code-block
|
|
||||||
`conf/conf.d/default.conf`
|
|
||||||
:::
|
|
||||||
|
|
||||||
```diff
|
|
||||||
location / {
|
|
||||||
- root /usr/local/openresty/nginx/html;
|
|
||||||
+ root /var/www;
|
|
||||||
index index.html index.htm;
|
|
||||||
```
|
|
||||||
|
|
||||||
Now, let's add an index file.
|
|
||||||
|
|
||||||
::: filename-for-code-block
|
|
||||||
`html/index.html`
|
|
||||||
:::
|
|
||||||
|
|
||||||
```html
|
|
||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<meta charset="utf-8" />
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
||||||
<title></title>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
hello world!
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
```
|
|
||||||
|
|
||||||
Last, we start `nginx`:
|
|
||||||
|
|
||||||
```console
|
|
||||||
$ docker run --rm -it -p 80:80 \
|
|
||||||
-v $PWD/conf/conf.d:/etc/nginx/conf.d -v $PWD/html:/var/www \
|
|
||||||
openresty/openresty:bookworm-buildpack
|
|
||||||
```
|
|
||||||
|
|
||||||
Then, in another console, this should output our index file.
|
|
||||||
|
|
||||||
```console
|
|
||||||
$ curl -v localhost
|
|
||||||
* Trying 127.0.0.1:80...
|
|
||||||
* Connected to localhost (127.0.0.1) port 80 (#0)
|
|
||||||
> GET / HTTP/1.1
|
|
||||||
> Host: localhost
|
|
||||||
> User-Agent: curl/7.88.1
|
|
||||||
> Accept: */*
|
|
||||||
>
|
|
||||||
< HTTP/1.1 200 OK
|
|
||||||
< Server: openresty/1.27.1.2
|
|
||||||
< ...
|
|
||||||
<
|
|
||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en">
|
|
||||||
...
|
|
||||||
<body>
|
|
||||||
hello world!
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
```
|
|
||||||
|
|
||||||
## Test an HTTP request
|
|
||||||
|
|
||||||
If we want to write a test for that request, we need some packages from
|
|
||||||
LuaRocks. Let's add a `Dockerfile` to build an image with those packages
|
|
||||||
installed.
|
|
||||||
|
|
||||||
### Add a `Dockerfile`
|
|
||||||
|
|
||||||
```Dockerfile
|
|
||||||
FROM openresty/openresty:bookworm-buildpack
|
|
||||||
|
|
||||||
WORKDIR /opt/app
|
|
||||||
|
|
||||||
RUN luarocks install moonscript
|
|
||||||
RUN luarocks install busted
|
|
||||||
RUN luarocks install luajit-curl
|
|
||||||
RUN luarocks install luasocket
|
|
||||||
```
|
|
||||||
|
|
||||||
Now let's build our image:
|
|
||||||
|
|
||||||
```console
|
|
||||||
$ docker build -t test-nginx .
|
|
||||||
```
|
|
||||||
|
|
||||||
### Write the test
|
|
||||||
|
|
||||||
Let's first make a new directory where our tests will live.
|
|
||||||
|
|
||||||
```console
|
|
||||||
$ mkdir spec
|
|
||||||
```
|
|
||||||
|
|
||||||
Our test makes a cURL request against our test server:
|
|
||||||
|
|
||||||
::: filename-for-code-block
|
|
||||||
`spec/nginx_spec.moon`
|
|
||||||
:::
|
|
||||||
|
|
||||||
```moonscript
|
|
||||||
http = require "luajit-curl-helper.http"
|
|
||||||
|
|
||||||
req = (url) ->
|
|
||||||
request = http.init url
|
|
||||||
st = request\perform!
|
|
||||||
error request\lastError! if not st
|
|
||||||
request
|
|
||||||
|
|
||||||
describe "http://localhost", ->
|
|
||||||
it "sends /index.html", ->
|
|
||||||
request = req "http://localhost"
|
|
||||||
assert.same request\statusCode!, 200
|
|
||||||
assert.same request\statusMessage!, "OK"
|
|
||||||
assert.same request\body!\match("<body>%s+(.-)%s+</body>"), "hello world!"
|
|
||||||
```
|
|
||||||
|
|
||||||
### Run the test suite
|
|
||||||
|
|
||||||
Start the test server. We're going to use `text-nginx`, the image we just
|
|
||||||
built.
|
|
||||||
|
|
||||||
```console
|
|
||||||
$ ct=$(docker run --rm -d \
|
|
||||||
-v $PWD/conf/conf.d:/etc/nginx/conf.d \
|
|
||||||
-v $PWD/html:/var/www \
|
|
||||||
-v $PWD:/opt/app \
|
|
||||||
test-nginx)
|
|
||||||
```
|
|
||||||
|
|
||||||
Start the test run:
|
|
||||||
|
|
||||||
```console
|
|
||||||
$ docker exec -t $ct busted
|
|
||||||
●
|
|
||||||
1 success / 0 failures / 0 errors / 0 pending : 0.008246 seconds
|
|
||||||
```
|
|
||||||
|
|
||||||
Stop the test server.
|
|
||||||
|
|
||||||
```console
|
|
||||||
$ docker exec $ct openresty -s stop
|
|
||||||
```
|
|
||||||
|
|
||||||
## Create a `Makefile`
|
|
||||||
|
|
||||||
We now have a number of long `docker` commands, let's create a `Makefile`
|
|
||||||
to make running them easier.
|
|
||||||
|
|
||||||
::: filename-for-code-block
|
|
||||||
`Makefile`
|
|
||||||
:::
|
|
||||||
|
|
||||||
```Makefile
|
|
||||||
image = test-nginx
|
|
||||||
|
|
||||||
image-build:
|
|
||||||
docker build -t $(image) .
|
|
||||||
|
|
||||||
image-rm:
|
|
||||||
docker image rm $(image)
|
|
||||||
|
|
||||||
test:
|
|
||||||
@ct=$(shell docker run --rm -d \
|
|
||||||
-v $(PWD)/conf/conf.d:/etc/nginx/conf.d \
|
|
||||||
-v $(PWD)/html:/var/www \
|
|
||||||
-v $(PWD):/opt/app \
|
|
||||||
$(image)); \
|
|
||||||
docker exec -t $$ct busted; \
|
|
||||||
docker exec $$ct openresty -s stop
|
|
||||||
```
|
|
||||||
|
|
||||||
Now we can run the test suite with the command `make test`.
|
|
||||||
|
|
||||||
## Configure the domain name
|
|
||||||
|
|
||||||
Instead of `localhost` we'd like to use an actual domain name. We can do this
|
|
||||||
with the `--add-host` option. But before we do that, we want to make sure our
|
|
||||||
container does not have access to the internet, otherwise we might
|
|
||||||
unintentionally get a response from a domain's server on the internet rather
|
|
||||||
than from our test server.
|
|
||||||
|
|
||||||
### Ensure the test container is offline
|
|
||||||
|
|
||||||
We need to create a network that has no external access.
|
|
||||||
|
|
||||||
```console
|
|
||||||
$ docker network create --internal no-internet
|
|
||||||
```
|
|
||||||
|
|
||||||
Now we need to update our `Makefile` to add the test container to our
|
|
||||||
internal-only network:
|
|
||||||
|
|
||||||
```diff
|
|
||||||
test:
|
|
||||||
@ct=$(shell docker run --rm -d \
|
|
||||||
-v $(PWD)/conf/conf.d:/etc/nginx/conf.d \
|
|
||||||
-v $(PWD)/html:/var/www \
|
|
||||||
-v $(PWD):/opt/app \
|
|
||||||
+ --network no-internet \
|
|
||||||
$(image)); \
|
|
||||||
```
|
|
||||||
|
|
||||||
And now let's add a test in `spec/nginx_spec.moon` to make sure our test
|
|
||||||
environment is offline:
|
|
||||||
|
|
||||||
```moonscript
|
|
||||||
describe "test environment", ->
|
|
||||||
it "can't connect to the internet", ->
|
|
||||||
assert.has_error (-> req "http://example.org"),
|
|
||||||
"Couldn't resolve host name"
|
|
||||||
```
|
|
||||||
|
|
||||||
Let's run our tests:
|
|
||||||
|
|
||||||
```console
|
|
||||||
$ make test
|
|
||||||
●●
|
|
||||||
2 successes / 0 failures / 0 errors / 0 pending : 0.020207 seconds
|
|
||||||
```
|
|
||||||
|
|
||||||
### Replace `localhost` with a custom domain
|
|
||||||
|
|
||||||
To use a custom domain name instead of `localhost`, we will need to use the
|
|
||||||
`--add-host` option for the `docker run` command. Again, we edit `Makefile`:
|
|
||||||
|
|
||||||
```diff
|
|
||||||
test:
|
|
||||||
@ct=$(shell docker run --rm -d \
|
|
||||||
-v $(PWD)/conf/conf.d:/etc/nginx/conf.d \
|
|
||||||
-v $(PWD)/html:/var/www \
|
|
||||||
-v $(PWD):/opt/app \
|
|
||||||
--network no-internet \
|
|
||||||
+ --add-host=domain.abc=127.0.0.1 \
|
|
||||||
$(image)); \
|
|
||||||
```
|
|
||||||
|
|
||||||
Let's update our test to use the custom domain name:
|
|
||||||
|
|
||||||
::: filename-for-code-block
|
|
||||||
`spec/nginx_spec.moon`
|
|
||||||
:::
|
|
||||||
|
|
||||||
```diff
|
|
||||||
-describe "http://localhost", ->
|
|
||||||
+describe "http://domain.abc", ->
|
|
||||||
it "sends /index.html", ->
|
|
||||||
- request = req "http://localhost"
|
|
||||||
+ request = req "http://domain.abc"
|
|
||||||
assert.same request\statusCode!, 200
|
|
||||||
```
|
|
||||||
|
|
||||||
Verify our tests still pass.
|
|
||||||
|
|
||||||
```console
|
|
||||||
$ make test
|
|
||||||
●●
|
|
||||||
2 successes / 0 failures / 0 errors / 0 pending : 0.0224 seconds
|
|
||||||
```
|
|
||||||
|
|
||||||
## Test an HTTP redirect
|
|
||||||
|
|
||||||
We want our server to redirect all `http` requests to `https`.
|
|
||||||
|
|
||||||
### Write the test
|
|
||||||
|
|
||||||
Let's practice a bit of test-driven development and write our test first.
|
|
||||||
|
|
||||||
```moonscript
|
|
||||||
describe "http://domain.abc", ->
|
|
||||||
it "redirects to https", ->
|
|
||||||
request = req "http://domain.abc"
|
|
||||||
assert.same request\statusCode!, 301
|
|
||||||
assert.same request\statusMessage!, "Moved Permanently"
|
|
||||||
assert.same request\header!.Location, "https://domain.abc/"
|
|
||||||
```
|
|
||||||
|
|
||||||
We should now have one failing test.
|
|
||||||
|
|
||||||
```console
|
|
||||||
$ make test
|
|
||||||
●●◼
|
|
||||||
2 successes / 1 failure / 0 errors / 0 pending : 0.010449 seconds
|
|
||||||
|
|
||||||
Failure → .../luajit/lib/luarocks/rocks-5.1/busted/2.2.0-1/bin/busted @ 3
|
|
||||||
http://domain.abc redirects to https
|
|
||||||
spec/nginx_spec.moon:24: Expected objects to be the same.
|
|
||||||
Passed in:
|
|
||||||
(number) 301
|
|
||||||
Expected:
|
|
||||||
(number) 200
|
|
||||||
```
|
|
||||||
|
|
||||||
### Configure `nginx`
|
|
||||||
|
|
||||||
We're going to add the redirect directives, as well as a server name for our
|
|
||||||
domain and the directives for the SSL certificates we will generate.
|
|
||||||
|
|
||||||
```diff
|
|
||||||
+server {
|
|
||||||
+ listen 80;
|
|
||||||
+ return 301 https://$host$request_uri;
|
|
||||||
+}
|
|
||||||
|
|
||||||
server {
|
|
||||||
- listen 80;
|
|
||||||
+ listen 443 ssl;
|
|
||||||
+ server_name domain.abc;
|
|
||||||
+ ssl_certificate /etc/ssl/certs/domain.abc.pem;
|
|
||||||
+ ssl_certificate_key /etc/ssl/private/domain.abc.pem;
|
|
||||||
|
|
||||||
location / {
|
|
||||||
root /var/www;
|
|
||||||
index index.html index.htm;
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Generate self-signed SSL/TLS certs for testing
|
|
||||||
|
|
||||||
Add a command to our `Dockerfile` to generate self-signed certificates:
|
|
||||||
|
|
||||||
```Dockerfile
|
|
||||||
RUN openssl req -x509 -newkey rsa:4096 -nodes \
|
|
||||||
-keyout /etc/ssl/private/domain.abc.pem \
|
|
||||||
-out /etc/ssl/certs/domain.abc.pem \
|
|
||||||
-sha256 -days 365 -subj '/CN=domain.abc' \
|
|
||||||
-addext "subjectAltName=DNS:domain.abc"
|
|
||||||
```
|
|
||||||
|
|
||||||
Rebuild the image:
|
|
||||||
|
|
||||||
```console
|
|
||||||
$ make image-rm image-build
|
|
||||||
```
|
|
||||||
|
|
||||||
We need to update our previous test to use HTTPS instead of HTTP.
|
|
||||||
|
|
||||||
::: filename-for-code-block
|
|
||||||
`spec/nginx_spec.moon`
|
|
||||||
:::
|
|
||||||
|
|
||||||
```diff
|
|
||||||
-describe "http://domain.abc", ->
|
|
||||||
+describe "https://domain.abc", ->
|
|
||||||
it "sends /index.html", ->
|
|
||||||
- request = req "http://domain.abc"
|
|
||||||
+ request = req "https://domain.abc"
|
|
||||||
```
|
|
||||||
|
|
||||||
Run tests:
|
|
||||||
|
|
||||||
```console
|
|
||||||
$ make test
|
|
||||||
●●●
|
|
||||||
3 successes / 0 failures / 0 errors / 0 pending : 0.017065 seconds
|
|
||||||
```
|
|
||||||
|
|
||||||
## Test reverse proxy a subdomain request to a Unix socket
|
|
||||||
|
|
||||||
Let's say we have a running service that connects to a Unix socket. We want to
|
|
||||||
proxy the requests through `nginx` so that our service can respond to `https`
|
|
||||||
requests but can leave handling SSL/TLS to `nginx`.
|
|
||||||
|
|
||||||
### Configure `nginx`
|
|
||||||
|
|
||||||
We'll add another server block to `conf/conf.d/default.conf` for our subdomain,
|
|
||||||
`git.domain.abc`, with the proxy directives:
|
|
||||||
|
|
||||||
```nginx
|
|
||||||
server {
|
|
||||||
listen 443 ssl;
|
|
||||||
server_name git.domain.abc;
|
|
||||||
|
|
||||||
location / {
|
|
||||||
client_max_body_size 1024M;
|
|
||||||
proxy_pass http://unix:/run/gitea/gitea.socket;
|
|
||||||
proxy_set_header Connection $http_connection;
|
|
||||||
proxy_set_header Upgrade $http_upgrade;
|
|
||||||
proxy_set_header Host $host;
|
|
||||||
proxy_set_header X-Real-IP $remote_addr;
|
|
||||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
|
||||||
proxy_set_header X-Forwarded-Proto $scheme;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Add subdomain to SSL/TLS certs
|
|
||||||
|
|
||||||
Next, we need to add our subdomain to the generated SSL certs in the
|
|
||||||
`Dockerfile`:
|
|
||||||
|
|
||||||
```diff
|
|
||||||
RUN openssl req -x509 -newkey rsa:4096 -nodes \
|
|
||||||
-keyout /etc/ssl/private/domain.abc.pem \
|
|
||||||
-out /etc/ssl/certs/domain.abc.pem \
|
|
||||||
-sha256 -days 365 -subj '/CN=domain.abc' \
|
|
||||||
- -addext "subjectAltName=DNS:domain.abc"
|
|
||||||
+ -addext "subjectAltName=DNS:domain.abc,DNS:git.domain.abc"
|
|
||||||
```
|
|
||||||
|
|
||||||
### Add subdomain as a host
|
|
||||||
|
|
||||||
Let's assign the loopback address to a variable and then add our subdomain as a
|
|
||||||
host in our `Makefile`:
|
|
||||||
|
|
||||||
```diff
|
|
||||||
+loopback = 127.0.0.1
|
|
||||||
|
|
||||||
test:
|
|
||||||
@ct=$(shell docker run --rm -d \
|
|
||||||
-v $(PWD)/conf/conf.d:/etc/nginx/conf.d \
|
|
||||||
-v $(PWD)/html:/var/www \
|
|
||||||
-v $(PWD):/opt/app \
|
|
||||||
--network no-internet \
|
|
||||||
- --add-host=domain.abc=127.0.0.1 \
|
|
||||||
+ --add-host=domain.abc=$(loopback) \
|
|
||||||
+ --add-host=git.domain.abc=$(loopback) \
|
|
||||||
$(image)); \
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
### Add a test socket server
|
|
||||||
|
|
||||||
We need to start up a mock socket server for our test to ensure our request is
|
|
||||||
being proxied correctly. This is why we needed the LuaSocket library.
|
|
||||||
|
|
||||||
Copied and modified from [here](https://github.com/lunarmodules/luasocket/blob/4844a48fbf76b0400fd7b7e4d15d244484019df1/test/unixstreamsrvr.lua),
|
|
||||||
this should suit our purposes:
|
|
||||||
|
|
||||||
::: filename-for-code-block
|
|
||||||
`spec/unixstreamsrvr.moon`
|
|
||||||
:::
|
|
||||||
|
|
||||||
```moonscript
|
|
||||||
socket = require "socket"
|
|
||||||
socket.unix = require "socket.unix"
|
|
||||||
u = assert socket.unix.stream!
|
|
||||||
assert u\bind "/run/gitea/gitea.socket"
|
|
||||||
assert u\listen!
|
|
||||||
assert u\settimeout 1
|
|
||||||
c = assert u\accept!
|
|
||||||
|
|
||||||
while true
|
|
||||||
m = assert c\receive!
|
|
||||||
break if m == ""
|
|
||||||
print m
|
|
||||||
```
|
|
||||||
|
|
||||||
### Write the test
|
|
||||||
|
|
||||||
And now we can add our test:
|
|
||||||
|
|
||||||
::: filename-for-code-block
|
|
||||||
`spec/nginx_spec.moon`
|
|
||||||
:::
|
|
||||||
|
|
||||||
```moonscript
|
|
||||||
describe "https://git.domain.abc", ->
|
|
||||||
it "reverse-proxy's a subdomain request to a unix socket", ->
|
|
||||||
socket = fname: "unixstreamsrvr.moon", dir: "/run/gitea", owner: "nobody"
|
|
||||||
basepath = debug.getinfo(1).short_src\match"^(.*)/[^/]*$" or "."
|
|
||||||
seconds = 0.1
|
|
||||||
|
|
||||||
os.execute "install -o #{socket.owner} -d #{socket.dir}"
|
|
||||||
cmd = "su -s /bin/bash -c 'moon %s' %s"
|
|
||||||
server = io.popen cmd\format "#{basepath}/#{socket.fname}", socket.owner
|
|
||||||
os.execute "sleep #{seconds}" -- wait for server to start
|
|
||||||
f = io.popen "find #{socket.dir} -type s -ls", "r"
|
|
||||||
result = with f\read "*a"
|
|
||||||
f\close!
|
|
||||||
assert.truthy result\match "nobody%s+nogroup.+#{socket.dir}/gitea.socket"
|
|
||||||
|
|
||||||
req "https://git.domain.abc"
|
|
||||||
|
|
||||||
reqheader = with server\read "*a"
|
|
||||||
server\close!
|
|
||||||
|
|
||||||
assert.truthy reqheader\match "Host: git.domain.abc"
|
|
||||||
```
|
|
||||||
|
|
||||||
Because we modified the `Dockerfile`, we need to rebuild our image:
|
|
||||||
|
|
||||||
```console
|
|
||||||
$ make image-rm image-build
|
|
||||||
```
|
|
||||||
|
|
||||||
And if all went well, our test should pass.
|
|
||||||
|
|
||||||
```console
|
|
||||||
$ make test
|
|
||||||
●●●●
|
|
||||||
4 successes / 0 failures / 0 errors / 0 pending : 0.131619 seconds
|
|
||||||
```
|
|
||||||
|
|
||||||
## Conclusion
|
|
||||||
|
|
||||||
These are just a few examples of how to test `nginx` directives. Using these
|
|
||||||
tools, we can verify that changes to our server configuration are working the
|
|
||||||
way we intended.
|
|
||||||
|
|
||||||
@@ -23,11 +23,11 @@ span.linenos.special { color: #50fa7b; background-color: #6272a4; padding-left:
|
|||||||
.highlight .py-cpf { color: #6272a4 } /* Comment.PreprocFile */
|
.highlight .py-cpf { color: #6272a4 } /* Comment.PreprocFile */
|
||||||
.highlight .py-c1 { color: #6272a4 } /* Comment.Single */
|
.highlight .py-c1 { color: #6272a4 } /* Comment.Single */
|
||||||
.highlight .py-cs { color: #6272a4 } /* Comment.Special */
|
.highlight .py-cs { color: #6272a4 } /* Comment.Special */
|
||||||
.highlight .py-gd { color: #ff5555 } /* Generic.Deleted */
|
.highlight .py-gd { color: #8b080b } /* Generic.Deleted */
|
||||||
.highlight .py-ge { color: #f8f8f2; text-decoration: underline } /* Generic.Emph */
|
.highlight .py-ge { color: #f8f8f2; text-decoration: underline } /* Generic.Emph */
|
||||||
.highlight .py-gr { color: #f8f8f2 } /* Generic.Error */
|
.highlight .py-gr { color: #f8f8f2 } /* Generic.Error */
|
||||||
.highlight .py-gh { color: #f8f8f2; font-weight: bold } /* Generic.Heading */
|
.highlight .py-gh { color: #f8f8f2; font-weight: bold } /* Generic.Heading */
|
||||||
.highlight .py-gi { color: #50fa7b } /* Generic.Inserted */
|
.highlight .py-gi { color: #f8f8f2; font-weight: bold } /* Generic.Inserted */
|
||||||
.highlight .py-go { color: #f8f8f2 } /* Generic.Output */
|
.highlight .py-go { color: #f8f8f2 } /* Generic.Output */
|
||||||
.highlight .py-gp { color: #50fa7b } /* Generic.Prompt */
|
.highlight .py-gp { color: #50fa7b } /* Generic.Prompt */
|
||||||
.highlight .py-gs { color: #f8f8f2 } /* Generic.Strong */
|
.highlight .py-gs { color: #f8f8f2 } /* Generic.Strong */
|
||||||
|
|||||||
@@ -104,10 +104,6 @@ describe "https://miti.sh/posts/", ->
|
|||||||
|
|
||||||
describe "https://miti.sh/posts", ->
|
describe "https://miti.sh/posts", ->
|
||||||
it "sends /posts/index.html", ->
|
it "sends /posts/index.html", ->
|
||||||
with require "sitegen.path"
|
|
||||||
assert .exists("html/posts/index.html"),
|
|
||||||
"missing html/posts/index.html (try `make build file=blog.html`)"
|
|
||||||
|
|
||||||
request = req "https://miti.sh/posts"
|
request = req "https://miti.sh/posts"
|
||||||
assert.same request\statusCode!, 200
|
assert.same request\statusCode!, 200
|
||||||
assert.same request\statusMessage!, "OK"
|
assert.same request\statusMessage!, "OK"
|
||||||
|
|||||||
Reference in New Issue
Block a user