diff --git a/.github/workflows/build-and-push.yml b/.github/workflows/build-and-push.yml index 6f181278..945ebfef 100644 --- a/.github/workflows/build-and-push.yml +++ b/.github/workflows/build-and-push.yml @@ -54,7 +54,7 @@ jobs: - name: Run systemtest run: | make setup - docker compose up --detach --wait --wait-timeout=10 web + docker compose up --detach --wait --wait-timeout=10 web nginx docker compose run --rm web shell systemtest/test_env.sh local - name: Set Docker image tag to "latest" for updates of the main branch diff --git a/Makefile b/Makefile index a8ddec35..115fd080 100644 --- a/Makefile +++ b/Makefile @@ -53,7 +53,7 @@ my.env: .PHONY: build build: my.env ## | Build docker images. ${DC} --progress plain build ${DOCKER_BUILD_OPTS} --build-arg userid=${ANTENNA_UID} --build-arg groupid=${ANTENNA_GID} deploy-base - ${DC} --progress plain build fakesentry gcs-emulator statsd + ${DC} --progress plain build fakesentry gcs-emulator statsd nginx touch .docker-build .PHONY: setup @@ -65,7 +65,8 @@ run: my.env .docker-build ## | Run the webapp and services. ${DC} up \ --attach web \ --attach fakesentry \ - web fakesentry + --attach nginx \ + web fakesentry nginx .PHONY: devcontainerbuild devcontainerbuild: .env ## | Build VS Code development container. diff --git a/docker-compose.yml b/docker-compose.yml index 0fee233d..3f726eb0 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -56,8 +56,6 @@ services: env_file: - docker/config/local_dev.env - my.env - ports: - - "${EXPOSE_ANTENNA_PORT:-8000}:8000" command: web links: - fakesentry @@ -102,7 +100,11 @@ services: build: context: docker/images/gcs-emulator image: local/antenna_gcs_emulator - command: -port 8001 -scheme http + command: >- + -port 8001 + -scheme http + -external-url http://gcs-emulator:8001 + -public-host gcs-emulator:8001 ports: - "${EXPOSE_GCS_EMULATOR_PORT:-8001}:8001" healthcheck: @@ -110,3 +112,15 @@ services: interval: 1s timeout: 3s retries: 5 + + nginx: + build: + context: docker/images/nginx + image: local/antenna_nginx + environment: + - UPSTREAM=web:8000 + - CLIENT_BODY_TIMEOUT=2s + ports: + - "${EXPOSE_ANTENNA_PORT:-8000}:8080" + links: + - web diff --git a/docker/images/nginx/Dockerfile b/docker/images/nginx/Dockerfile new file mode 100644 index 00000000..dcf81e61 --- /dev/null +++ b/docker/images/nginx/Dockerfile @@ -0,0 +1,13 @@ +FROM alpine:latest AS htpasswd +RUN apk add --update apache2-utils && rm -rf /var/cache/apk/* +RUN mkdir -p /etc/htpasswd && htpasswd -bc /etc/htpasswd/antenna.htpasswd developer password + +FROM us-west1-docker.pkg.dev/moz-fx-platform-artifacts/platform-shared-images/nginx-unprivileged:1.22 +RUN rm /etc/nginx/conf.d/* +COPY --from=htpasswd /etc/htpasswd/antenna.htpasswd /etc/htpasswd/antenna.htpasswd +COPY nginx.conf /etc/nginx/nginx.conf +COPY *.conf.template /etc/nginx/templates/ +# The default command runs /docker-entrypoint.d/20-envsubst-on-templates.sh, which compiles +# /etc/nginx/templates/*.template files with envsubst and writes them to /etc/nginx/conf.d/ +ENV UPSTREAM=127.0.0.1:8000 +ENV CLIENT_BODY_TIMEOUT=70s diff --git a/docker/images/nginx/client_body_timeout.conf.template b/docker/images/nginx/client_body_timeout.conf.template new file mode 100644 index 00000000..796b8e1b --- /dev/null +++ b/docker/images/nginx/client_body_timeout.conf.template @@ -0,0 +1,3 @@ +# must be higher than GCLB timeout so that GCLB returns 408 and +# known misbehaving clients don't trigger HTTP 5XX alerts +client_body_timeout $CLIENT_BODY_TIMEOUT; diff --git a/docker/images/nginx/nginx.conf b/docker/images/nginx/nginx.conf new file mode 100644 index 00000000..db59b2fd --- /dev/null +++ b/docker/images/nginx/nginx.conf @@ -0,0 +1,93 @@ +# The nginx docker container has ln -sf /dev/stderr /var/log/nginx/error.log +error_log /var/log/nginx/error.log warn; +pid /tmp/nginx.pid; + +events { + worker_connections 2048; +} + +http { + include /etc/nginx/mime.types; + + default_type application/octet-stream; + + log_format main escape=json + '{' + '"time":"$time_local",' + '"remote_addr":"$remote_addr",' + '"remote_user":"$remote_user",' + '"request":"$request",' + '"status": "$status",' + '"log_type": "access",' + '"bytes_sent": $body_bytes_sent,' + '"request_time": $request_time,' + '"referrer":"$http_referer",' + '"user_agent":"$http_user_agent",' + '"x_forwarded_for":"$http_x_forwarded_for",' + '"x_forwarded_proto":"$http_x_forwarded_proto",' + '"trace":"$http_x_cloud_trace_context"' + '}'; + + # The nginx docker container has ln -sf /dev/stdout /var/log/nginx/access.log + access_log /var/log/nginx/access.log main; + + sendfile on; + + keepalive_timeout 620; + + server_tokens off; + + gzip on; + gzip_http_version 1.1; + gzip_comp_level 1; + gzip_vary on; + gzip_proxied any; + gzip_disable "MSIE [1-6]\.(?!.*SV1)"; + gzip_min_length 1100; + gzip_types + text/plain + text/css + application/json + application/x-javascript + text/xml + application/xml + application/xml+rss + text/javascript + application/javascript; + + server { + listen 8080; + + client_max_body_size 25m; + + add_header Strict-Transport-Security "max-age=31536000" always; + + location / { + proxy_set_header x-forwarded-proto $http_x_forwarded_proto; + proxy_set_header x-forwarded-for $proxy_add_x_forwarded_for; + proxy_set_header Host $http_host; + proxy_redirect off; + proxy_pass http://upstream_docker; + } + + location = /nginx_status { + stub_status on; + access_log off; + allow 127.0.0.1; + deny all; + } + + location = /__nginxheartbeat__ { + return 200; + } + + location /__broken__ { + proxy_set_header Host $http_host; + proxy_pass http://upstream_docker; + auth_basic "antenna private"; + auth_basic_user_file "/etc/htpasswd/antenna.htpasswd"; + } + } + + include /etc/nginx/conf.d/*.conf; +} diff --git a/docker/images/nginx/upstream.conf.template b/docker/images/nginx/upstream.conf.template new file mode 100644 index 00000000..9e115bac --- /dev/null +++ b/docker/images/nginx/upstream.conf.template @@ -0,0 +1,3 @@ +upstream upstream_docker { + server $UPSTREAM; +} diff --git a/systemtest/test_env.sh b/systemtest/test_env.sh index 5067ac6f..01ca6af8 100755 --- a/systemtest/test_env.sh +++ b/systemtest/test_env.sh @@ -24,11 +24,11 @@ fi case $1 in "local") # Whether or not we're running behind nginx and to run nginx tests - export NGINX_TESTS=0 + export NGINX_TESTS=1 # Whether or not we can verify the file was saved (need access to GCS) export POST_CHECK=1 # The host to submit to - export HOST=http://web:8000/ + export HOST=http://nginx:8080/ ;; "stage" | "gcp-stage") export NGINX_TESTS=1