From 2268331f7330008ea373d2a3c0001df0eba088b7 Mon Sep 17 00:00:00 2001 From: Sami Al-Dury Date: Tue, 31 Oct 2023 13:54:18 +0100 Subject: [PATCH] feat: configurable DNS resolver --- Makefile | 6 ++- README.md | 17 +++---- docker-compose.yml | 8 ++-- nginx/entrypoint.sh | 15 ++++-- nginx/templates/default.conf.template | 66 +++++++++++++-------------- test/proxy.test.ts | 50 +++++++++++++------- 6 files changed, 93 insertions(+), 69 deletions(-) diff --git a/Makefile b/Makefile index a9f85d1..797f4ac 100644 --- a/Makefile +++ b/Makefile @@ -20,9 +20,13 @@ help: ##@ Development +.PHONY: install +install: ## install dep + pnpm install + .PHONY: dev dev: ## run TS (watch mode) - docker compose up + docker compose up --build ##@ Build diff --git a/README.md b/README.md index 4ec3053..7a4dc4b 100644 --- a/README.md +++ b/README.md @@ -56,14 +56,15 @@ You have now installed and deployed the Smartlook relay proxy. You can check if | Name | Type | Default value | Description | Notes | | --------------------- | -------- | ------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------ | -| `ERROR_LOG_LEVEL` | `string` | `error` | [Nginx error log level](https://nginx.org/en/docs/ngx_core_module.html#error_log). One of `debug`, `info`, `notice`, `warn`, `error`, `crit`, `alert`, `emerg`. | Can be overridden | -| `CLIENT_MAX_BODY_SIZE` | `string` | `20m` | [Nginx client_max_body_size](https://nginx.org/en/docs/http/ngx_http_core_module.html#client_max_body_size). | Can be overridden | -| `PROXY_PORT` | `number` | `8000` | Port which will the HTTP server listen on | Can be overridden | -| `LISTEN_IPV6` | `boolean` | `false` (unset) | If set to `true`, the server will also listen on IPv6. The port will be the value of `PROXY_PORT` environment variable | Can be overridden | -| `WEB_SDK_HOST` | `string` | `web-sdk.smartlook.com` | Smartlook Web SDK host | Only edit this value if using a region other than EU, or if instructed by customer support | -| `MANAGER_HOST` | `string` | `manager.eu.smartlook.cloud` | Smartlook Manager host | Only edit this value if using a region other than EU, or if instructed by customer support | -| `WEB_SDK_WRITER_HOST` | `string` | `web-writer.eu.smartlook.cloud` | Smartlook Web Writer host | Only edit this value if using a region other than EU, or if instructed by customer support | -| `ASSETS_PROXY_HOST` | `string` | `assets-proxy.smartlook.cloud` | Smartlook Assets Proxy host | Only edit this value if using a region other than EU, or if instructed by customer support | +| `ERROR_LOG_LEVEL` | `string` | `error` | [Nginx error log level](https://nginx.org/en/docs/ngx_core_module.html#error_log). One of `debug`, `info`, `notice`, `warn`, `error`, `crit`, `alert`, `emerg`. | Can be safely overridden. | +| `CLIENT_MAX_BODY_SIZE` | `string` | `20m` | [Nginx client_max_body_size](https://nginx.org/en/docs/http/ngx_http_core_module.html#client_max_body_size). | Can be safely overridden. | +| `PROXY_PORT` | `number` | `8000` | Port which will the HTTP server listen on. | Can be safely overridden. | +| `LISTEN_IPV6` | `boolean` | `false` (unset) | If set to `true`, the server will also listen on IPv6. The port will be the value of `PROXY_PORT` environment variable. | Can be safely overridden. | +| `DNS_RESOLVER` | `string` | `1.1.1.1 1.0.0.1 valid=5m` | [Nginx resolver](https://nginx.org/en/docs/http/ngx_http_core_module.html#resolver). | Can be safely overridden. | | +| `WEB_SDK_HOST` | `string` | `web-sdk.smartlook.com` | Smartlook Web SDK host | Only edit this value if using a region other than EU, or if instructed by customer support. | +| `MANAGER_HOST` | `string` | `manager.eu.smartlook.cloud` | Smartlook Manager host | Only edit this value if using a region other than EU, or if instructed by customer support. | +| `WEB_SDK_WRITER_HOST` | `string` | `web-writer.eu.smartlook.cloud` | Smartlook Web Writer host | Only edit this value if using a region other than EU, or if instructed by customer support. | +| `ASSETS_PROXY_HOST` | `string` | `assets-proxy.smartlook.cloud` | Smartlook Assets Proxy host | Only edit this value if using a region other than EU, or if instructed by customer support. | ## Regional setup diff --git a/docker-compose.yml b/docker-compose.yml index 1bd5b5b..6540acf 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,8 +1,8 @@ -version: '3' - services: nginx: - image: nginx:1-alpine + build: + context: . + dockerfile: Dockerfile container_name: nginx restart: unless-stopped ports: @@ -10,6 +10,8 @@ services: environment: COMMIT_SHA: dev PROXY_PORT: 8000 + DNS_RESOLVER: '127.0.0.11 valid=0s' + LISTEN_IPV6: 'true' CLIENT_MAX_BODY_SIZE: 20m ERROR_LOG_LEVEL: error WEB_SDK_HOST: web-sdk.smartlook.com diff --git a/nginx/entrypoint.sh b/nginx/entrypoint.sh index 1b47328..0f17ccf 100755 --- a/nginx/entrypoint.sh +++ b/nginx/entrypoint.sh @@ -4,11 +4,16 @@ set -e nginx_conf=/etc/nginx/conf.d/default.conf +echo -e "\n\nSmartlook Relay Proxy - initializing..." + if [ "$LISTEN_IPV6" = "true" ]; then - echo "" - echo "Smartlook Relay Proxy" - echo "Environment variable LISTEN_IPV6 is set to ${LISTEN_IPV6}, enabling IPv6 support." - echo "Adding listen directive to nginx configuration." - echo "" + echo -e "\nEnvironment variable LISTEN_IPV6 is set to \`${LISTEN_IPV6}\`, enabling IPv6 support." sed -i "/server {/a \ listen [::]:${PROXY_PORT};" $nginx_conf fi + +if [ -n "$DNS_RESOLVER" ]; then + echo -e "\nEnvironment variable DNS_RESOLVER is set to \`${DNS_RESOLVER}\`, replacing default resolver." + sed -i "s/resolver .*/resolver ${DNS_RESOLVER};/" $nginx_conf +fi + +echo -e "\nSmartlook Relay Proxy - initialization finished. Starting nginx...\n\n" diff --git a/nginx/templates/default.conf.template b/nginx/templates/default.conf.template index 8019034..66f25d5 100644 --- a/nginx/templates/default.conf.template +++ b/nginx/templates/default.conf.template @@ -5,6 +5,8 @@ map $status $loggable { server { listen ${PROXY_PORT}; + + proxy_cache off; server_tokens off; client_max_body_size ${CLIENT_MAX_BODY_SIZE}; @@ -18,78 +20,72 @@ server { return 200 '{ "app": "smartlook-relay-proxy", "version": "${COMMIT_SHA}" }'; } + set $manager_host ${MANAGER_HOST}; + set $assets_proxy_host ${ASSETS_PROXY_HOST}; + set $web_sdk_writer_host ${WEB_SDK_WRITER_HOST}; + set $mobile_sdk_writer_host ${MOBILE_SDK_WRITER_HOST}; + set $web_sdk_host ${WEB_SDK_HOST}; + + proxy_pass_header Server; + + resolver 1.1.1.1 1.0.0.1 valid=5m; + location /manager/ { - proxy_pass https://${MANAGER_HOST}/; + rewrite ^/manager(/.*)$ $1 break; + proxy_pass https://$manager_host; - proxy_set_header Host ${MANAGER_HOST}; - proxy_set_header X-Forwarded-Host ${MANAGER_HOST}; + proxy_set_header Host $manager_host; + proxy_set_header X-Forwarded-Host $manager_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; proxy_set_header X-Forwarded-By smartlook-relay-proxy; - - proxy_pass_header Server; - - proxy_redirect default; } location /assets/ { - proxy_pass https://${ASSETS_PROXY_HOST}/; + rewrite ^/assets(/.*)$ $1 break; + proxy_pass https://$assets_proxy_host; - proxy_set_header Host ${ASSETS_PROXY_HOST}; - proxy_set_header X-Forwarded-Host ${ASSETS_PROXY_HOST}; + proxy_set_header Host $assets_proxy_host; + proxy_set_header X-Forwarded-Host $assets_proxy_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; proxy_set_header X-Forwarded-By smartlook-relay-proxy; - - proxy_pass_header Server; - - proxy_redirect default; } location /web-writer/ { - proxy_pass https://${WEB_SDK_WRITER_HOST}/; + rewrite ^/web-writer(/.*)$ $1 break; + proxy_pass https://$web_sdk_writer_host; - proxy_set_header Host ${WEB_SDK_WRITER_HOST}; - proxy_set_header X-Forwarded-Host ${WEB_SDK_WRITER_HOST}; + proxy_set_header Host $web_sdk_writer_host; + proxy_set_header X-Forwarded-Host $web_sdk_writer_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; proxy_set_header X-Forwarded-By smartlook-relay-proxy; - - proxy_pass_header Server; - - proxy_redirect off; } location /sdk-writer/ { - proxy_pass https://${MOBILE_SDK_WRITER_HOST}/; + rewrite ^/sdk-writer(/.*)$ $1 break; + proxy_pass https://$mobile_sdk_writer_host; - proxy_set_header Host ${MOBILE_SDK_WRITER_HOST}; - proxy_set_header X-Forwarded-Host ${MOBILE_SDK_WRITER_HOST}; + proxy_set_header Host $mobile_sdk_writer_host; + proxy_set_header X-Forwarded-Host $mobile_sdk_writer_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; proxy_set_header X-Forwarded-By smartlook-relay-proxy; - - proxy_pass_header Server; - - proxy_redirect default; } location / { - proxy_pass https://${WEB_SDK_HOST}/; + proxy_pass https://$web_sdk_host; - proxy_set_header Host ${WEB_SDK_HOST}; - proxy_set_header X-Forwarded-Host ${WEB_SDK_HOST}; + proxy_set_header Host $web_sdk_host; + proxy_set_header X-Forwarded-Host $web_sdk_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; proxy_set_header X-Forwarded-By smartlook-relay-proxy; - - proxy_pass_header Server; - - proxy_redirect default; } } diff --git a/test/proxy.test.ts b/test/proxy.test.ts index 615425a..e7b8368 100644 --- a/test/proxy.test.ts +++ b/test/proxy.test.ts @@ -1,4 +1,12 @@ /* eslint-disable @typescript-eslint/no-unsafe-assignment */ + +type StatusResponse = { + ok: boolean + version: string +} + +type CommonStatusResponse = [StatusResponse, StatusResponse] + describe('Proxy server', () => { it('Should return relay proxy status on /proxy/status', async () => { const response = await fetch('http://localhost:80/proxy/status') @@ -19,10 +27,10 @@ describe('Proxy server', () => { fetch('http://localhost:80/manager/status'), ]) - const [ogResponseJson, proxyResponseJson] = await Promise.all([ + const [ogResponseJson, proxyResponseJson] = (await Promise.all([ ogResponse.json(), proxyResponse.json(), - ]) + ])) as CommonStatusResponse expect(ogResponse.status).toBe(200) expect(proxyResponse.status).toBe(200) @@ -32,7 +40,9 @@ describe('Proxy server', () => { expect(proxyResponse.headers.get('content-type')).toBe( 'application/json; charset=utf-8', ) - expect(ogResponseJson).toEqual(proxyResponseJson) + + expect(ogResponseJson.ok).toBe(proxyResponseJson.ok) + expect(ogResponseJson.version).toBe(proxyResponseJson.version) }) it('Should return assets proxy status on /assets/status', async () => { @@ -41,10 +51,10 @@ describe('Proxy server', () => { fetch('http://localhost:80/assets/status'), ]) - const [ogResponseJson, proxyResponseJson] = await Promise.all([ + const [ogResponseJson, proxyResponseJson] = (await Promise.all([ ogResponse.json(), proxyResponse.json(), - ]) + ])) as CommonStatusResponse expect(ogResponse.status).toBe(200) expect(proxyResponse.status).toBe(200) @@ -52,7 +62,9 @@ describe('Proxy server', () => { expect(proxyResponse.headers.get('content-type')).toBe( 'application/json', ) - expect(ogResponseJson).toEqual(proxyResponseJson) + + expect(ogResponseJson.ok).toBe(proxyResponseJson.ok) + expect(ogResponseJson.version).toBe(proxyResponseJson.version) }) it('Should return web writer status on /web-writer/status', async () => { @@ -61,10 +73,10 @@ describe('Proxy server', () => { fetch('http://localhost:80/web-writer/status'), ]) - const [ogResponseJson, proxyResponseJson] = await Promise.all([ - ogResponse.json() as Promise<{ version: string }>, - proxyResponse.json() as Promise<{ version: string }>, - ]) + const [ogResponseJson, proxyResponseJson] = (await Promise.all([ + ogResponse.json(), + proxyResponse.json(), + ])) as CommonStatusResponse expect(ogResponse.status).toBe(200) expect(proxyResponse.status).toBe(200) @@ -74,7 +86,9 @@ describe('Proxy server', () => { expect(proxyResponse.headers.get('content-type')).toBe( 'application/json; charset=utf-8', ) - expect(ogResponseJson.version).toEqual(proxyResponseJson.version) + + expect(ogResponseJson.ok).toBe(proxyResponseJson.ok) + expect(ogResponseJson.version).toBe(proxyResponseJson.version) }) it('Should return sdk writer status on /sdk-writer/status', async () => { @@ -83,10 +97,10 @@ describe('Proxy server', () => { fetch('http://localhost:80/sdk-writer/status'), ]) - const [ogResponseJson, proxyResponseJson] = await Promise.all([ + const [ogResponseJson, proxyResponseJson] = (await Promise.all([ ogResponse.json(), proxyResponse.json(), - ]) + ])) as CommonStatusResponse expect(ogResponse.status).toBe(200) expect(proxyResponse.status).toBe(200) @@ -96,7 +110,8 @@ describe('Proxy server', () => { expect(proxyResponse.headers.get('content-type')).toBe( 'application/json; charset=utf-8', ) - expect(ogResponseJson).toEqual(proxyResponseJson) + expect(ogResponseJson.ok).toBe(proxyResponseJson.ok) + expect(ogResponseJson.version).toBe(proxyResponseJson.version) }) it('Should return web sdk status on /status', async () => { @@ -105,10 +120,10 @@ describe('Proxy server', () => { fetch('http://localhost:80/status'), ]) - const [ogResponseJson, proxyResponseJson] = await Promise.all([ + const [ogResponseJson, proxyResponseJson] = (await Promise.all([ ogResponse.json(), proxyResponse.json(), - ]) + ])) as CommonStatusResponse expect(ogResponse.status).toBe(200) expect(proxyResponse.status).toBe(200) @@ -116,7 +131,8 @@ describe('Proxy server', () => { expect(proxyResponse.headers.get('content-type')).toBe( 'application/json', ) - expect(ogResponseJson).toEqual(proxyResponseJson) + expect(ogResponseJson.ok).toBe(proxyResponseJson.ok) + expect(ogResponseJson.version).toBe(proxyResponseJson.version) }) it('Should return web sdk recorder on /recorder.js', async () => {