Publish post 'Test nginx Configuration Directives' #3
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 |
415
posts/2025-06-22-test-nginx-conf-directives.md
Normal file
415
posts/2025-06-22-test-nginx-conf-directives.md
Normal file
@ -0,0 +1,415 @@
|
||||
$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-proxies request to a gitea unix socket", ->
|
||||
Path = require "sitegen.path"
|
||||
socket_fname = "unixstreamsrvr.moon"
|
||||
socket_dir = "/run/gitea"
|
||||
socket_owner = "nobody"
|
||||
basepath = Path.basepath debug.getinfo(1).short_src
|
||||
|
||||
Path.exec "install", "-o", socket_owner, "-d", socket_dir
|
||||
cmd = "su -s /bin/bash -c 'moon %s' %s"
|
||||
server = io.popen cmd\format Path.join(basepath, socket_fname), socket_owner
|
||||
Path.exec "sleep", "0.1"
|
||||
result = Path.read_exec "find", socket_dir, "-type", "s", "-ls"
|
||||
assert.truthy result\match "nobody%s+nogroup.+" .. Path.join(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) \
|
||||
|
||||
|
||||
```console
|
||||
$ make image-rm image-build
|
||||
```
|
||||
|
||||
```console
|
||||
$ make test
|
||||
●●●●
|
||||
4 successes / 0 failures / 0 errors / 0 pending : 0.131619 seconds
|
||||
```
|
||||
|
||||
|
||||
## Conclusion
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user