diff --git a/.github/workflows/run-automatest-tests.yml b/.github/workflows/run-automatest-tests.yml index e5b13e2..c47aefc 100644 --- a/.github/workflows/run-automatest-tests.yml +++ b/.github/workflows/run-automatest-tests.yml @@ -6,28 +6,37 @@ jobs: test: runs-on: ubuntu-22.04 steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - - name: Build image for tests - run: docker build -t proxy-tester -f Dockerfile.test . + - name: Build image for tests (bookworm, OpenSSL 3) + run: docker compose build epp_proxy + + - name: Verify OpenSSL and OTP in image + run: | + docker compose run --rm epp_proxy bash -l -c \ + 'openssl version; erl -noshell -eval "io:format(\"OTP ~s crypto=~p~n\", [erlang:system_info(otp_release), crypto:info_lib()]), halt()."' - name: Start the container - run: docker run -d --name tester proxy-tester - + run: docker compose up -d epp_proxy + - name: Install rebar dependencies - run: docker exec -i tester bash -l -c "source /root/.asdf/asdf.sh && rebar3 as test get-deps" + run: docker compose exec -T epp_proxy bash -l -c "source /root/.asdf/asdf.sh && rebar3 as test get-deps" - name: Integrated Ruby app setup - run: docker exec -d tester bash -l -c "cd apps/epp_proxy/priv/test_backend_app && bundle install && bundle exec rackup" + run: docker compose exec -d epp_proxy bash -l -c "cd apps/epp_proxy/priv/test_backend_app && bundle install && bundle exec rackup -p 9292 -o 0.0.0.0 -D" - name: Compile for tests run: | sleep 15 - docker exec -i tester bash -l -c "source /root/.asdf/asdf.sh && rebar3 as test compile" + docker compose exec -T epp_proxy bash -l -c "source /root/.asdf/asdf.sh && rebar3 as test compile" - name: Run tests run: | - docker exec -i -e DEBUG=1 tester bash -l -c "source /root/.asdf/asdf.sh && rebar3 ct --sys_config config/test.config --readable=false --cover --verbose=true" - + docker compose exec -T -e DEBUG=1 epp_proxy bash -l -c "source /root/.asdf/asdf.sh && rebar3 ct --sys_config config/test.config --readable=false --cover --verbose=true" + - name: Show test coverage - run: docker exec -i tester bash -l -c "source /root/.asdf/asdf.sh && rebar3 cover --verbose" \ No newline at end of file + run: docker compose exec -T epp_proxy bash -l -c "source /root/.asdf/asdf.sh && rebar3 cover --verbose" + + - name: Stop container + if: always() + run: docker compose down \ No newline at end of file diff --git a/.tool-versions b/.tool-versions index eb0e586..50ec09d 100644 --- a/.tool-versions +++ b/.tool-versions @@ -1,3 +1,3 @@ -erlang 23.3.4.20 -ruby 3.2.2 -rebar 3.15.2 +erlang 26.2.5.5 +ruby 3.4.9 +rebar 3.23.0 diff --git a/COMMANDS b/COMMANDS new file mode 100644 index 0000000..ae61959 --- /dev/null +++ b/COMMANDS @@ -0,0 +1,28 @@ +# Docker (from epp_proxy/ — see docker-compose.yml) + +- Run all tests (OpenSSL 3): +./scripts/docker-ct.sh + +- Build image: +docker compose build + +- Run specific test suite: +docker compose run --rm epp_proxy bash -l -c "source ~/.asdf/asdf.sh && rebar3 ct --sys_config config/test.config --suite apps/epp_proxy/test/epp_http_client_SUITE" + +- Run all tests manually (start test backend in another shell or use docker-ct.sh): +docker compose run --rm epp_proxy bash -l -c "source ~/.asdf/asdf.sh && rebar3 ct --sys_config config/test.config" + +- Debugging shell: +docker compose run --rm epp_proxy bash -l -c "source ~/.asdf/asdf.sh && rebar3 shell --config config/test.config" + +- Show loaded configuration (in Erlang shell): +application:get_all_env(epp_proxy). + +- Quit shell: +halt(). + +- Production tarball: +docker compose run --rm epp_proxy bash -l -c "source ~/.asdf/asdf.sh && rebar3 as prod tar" + +# Full registry stack (epp_proxy + Rails EPP) — ../docker-images/docker-compose.yml +cd ../docker-images && docker compose up epp epp_proxy diff --git a/Dockerfile b/Dockerfile index a5e36b1..2ce6376 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,18 +1,17 @@ -FROM debian:bullseye-slim +FROM debian:bookworm-slim SHELL ["/bin/bash", "-o", "pipefail", "-c"] COPY ./docker/apt/sources.list /etc/apt/ -# Install all dependencies in a single layer to reduce image size RUN apt-get update && apt-get install -y -qq \ wget \ git \ build-essential \ - libncurses5-dev \ automake \ autoconf \ curl \ ca-certificates \ + libssl3 \ libssl-dev \ libreadline-dev \ libdpkg-perl \ @@ -22,41 +21,39 @@ RUN apt-get update && apt-get install -y -qq \ perl \ procps \ inotify-tools \ - libssl1.1 \ perl-base \ zlib1g-dev \ - # Additional dependencies for Erlang build libncurses-dev \ libsctp-dev \ - # Documentation tools to prevent build failures xsltproc \ libxml2-utils \ - # Dependencies for Ruby 3.2.2 libffi-dev \ libyaml-dev \ && apt-get clean \ && rm -rf /var/lib/apt/lists/* -# Set environment variables for Erlang build ENV KERL_CONFIGURE_OPTIONS="--disable-debug --without-javac --without-wx --without-odbc --disable-hipe --without-jinterface --without-docs" ENV KERL_BUILD_DOCS="no" ENV KERL_DOC_TARGETS="" ENV KERL_INSTALL_HTMLDOCS="no" ENV KERL_INSTALL_MANPAGES="no" -RUN git clone https://github.com/asdf-vm/asdf.git --branch v0.6.3 "$HOME"/.asdf && \ +RUN git clone https://github.com/asdf-vm/asdf.git --branch v0.14.0 "$HOME"/.asdf && \ echo '. $HOME/.asdf/asdf.sh' >> "$HOME"/.bashrc && \ - echo '. $HOME/.asdf/asdf.sh' >> "$HOME"/.profile + echo '. $HOME/.asdf/asdf.sh' >> "$HOME"/.profile ENV PATH="${PATH}:/root/.asdf/shims:/root/.asdf/bin" +ENV GIT_TERMINAL_PROMPT=0 RUN mkdir -p /opt/erlang/epp_proxy WORKDIR /opt/erlang/epp_proxy COPY .tool-versions ./ -RUN asdf plugin-add erlang -RUN . $HOME/.asdf/asdf.sh && asdf install -RUN asdf global erlang $(grep erlang .tool-versions | cut -d' ' -f2) -RUN asdf plugin-add ruby -RUN asdf plugin-add rebar -RUN asdf install +RUN . "$HOME"/.asdf/asdf.sh && \ + asdf plugin add erlang && \ + asdf plugin add ruby && \ + asdf plugin add rebar && \ + asdf install && \ + asdf global erlang "$(grep erlang .tool-versions | awk '{print $2}')" && \ + asdf global ruby "$(grep ruby .tool-versions | awk '{print $2}')" && \ + asdf global rebar "$(grep rebar .tool-versions | awk '{print $2}')" diff --git a/Dockerfile.Erlang-ruby.base b/Dockerfile.Erlang-ruby.base index a2ae864..25be7aa 100644 --- a/Dockerfile.Erlang-ruby.base +++ b/Dockerfile.Erlang-ruby.base @@ -1,42 +1,48 @@ -FROM debian:bullseye-slim +FROM debian:bookworm-slim + +COPY ./docker/apt/sources.list /etc/apt/ RUN apt-get update && apt-get install -y \ wget \ git \ - build-essential=* \ - libncurses5-dev=* \ - automake=* \ - autoconf=* \ - curl=* \ - ca-certificates=* \ - libssl-dev=* \ - libreadline-dev=* \ - libdpkg-perl=* \ - liberror-perl=* \ - libc6=* \ + build-essential \ + libncurses-dev \ + automake \ + autoconf \ + curl \ + ca-certificates \ + libssl3 \ + libssl-dev \ + libreadline-dev \ + libdpkg-perl \ + liberror-perl \ + libc6 \ libc-dev \ - perl=* \ - procps=* \ - inotify-tools=* \ - libssl1.1=* \ - perl-base=* \ + perl \ + procps \ + inotify-tools \ + perl-base \ zlib1g-dev \ && apt-get clean \ && rm -rf /var/lib/apt/lists/* -RUN git clone https://github.com/asdf-vm/asdf.git --branch v0.6.3 "$HOME"/.asdf && \ +RUN git clone https://github.com/asdf-vm/asdf.git --branch v0.14.0 "$HOME"/.asdf && \ echo '. $HOME/.asdf/asdf.sh' >> "$HOME"/.bashrc && \ - echo '. $HOME/.asdf/asdf.sh' >> "$HOME"/.profile + echo '. $HOME/.asdf/asdf.sh' >> "$HOME"/.profile ENV PATH="${PATH}:/root/.asdf/shims:/root/.asdf/bin" RUN mkdir -p /opt/erlang/epp_proxy/release WORKDIR /opt/erlang/epp_proxy +ENV GIT_TERMINAL_PROMPT=0 + COPY .tool-versions ./ -RUN asdf plugin-add erlang -RUN asdf install -RUN asdf global erlang $(grep erlang .tool-versions | cut -d' ' -f2) -RUN asdf plugin-add ruby -RUN asdf plugin-add rebar -RUN asdf install \ No newline at end of file +RUN . "$HOME"/.asdf/asdf.sh && \ + asdf plugin add erlang && \ + asdf plugin add ruby && \ + asdf plugin add rebar && \ + asdf install && \ + asdf global erlang "$(grep erlang .tool-versions | awk '{print $2}')" && \ + asdf global ruby "$(grep ruby .tool-versions | awk '{print $2}')" && \ + asdf global rebar "$(grep rebar .tool-versions | awk '{print $2}')" diff --git a/Dockerfile.amd64.legacy b/Dockerfile.amd64.legacy deleted file mode 100644 index 98ca2ce..0000000 --- a/Dockerfile.amd64.legacy +++ /dev/null @@ -1,58 +0,0 @@ -FROM debian:buster-slim - -SHELL ["/bin/bash", "-o", "pipefail", "-c"] -COPY ./docker/apt/sources.list /etc/apt/ - -RUN apt-get update && apt-get -t buster install -y -qq wget \ - && apt-get clean \ - && rm -rf /var/lib/apt/lists/* - - -RUN apt-get update && apt-get install -y -qq git \ - && apt-get clean \ - && rm -rf /var/lib/apt/lists/* - -RUN apt-get update && apt-get install -y \ - build-essential=* \ - libncurses5-dev=* \ - automake=* \ - autoconf=* \ - curl=* \ - ca-certificates=* \ - libssl-dev=* \ - libreadline-dev=* \ - libdpkg-perl=* \ - liberror-perl=* \ - libc6=* \ - libc-dev \ - perl=* \ - procps=* \ - inotify-tools=* \ - libssl1.1=* \ - perl-base=* \ - zlib1g-dev \ - && apt-get clean \ - && rm -rf /var/lib/apt/lists/* - -# RUN (groupadd -g 999 asdf || true) -# RUN (adduser --shell /bin/bash --home /asdf --disabled-password -gid 999 -u 999 asdf || true) -# ENV PATH="${PATH}:/asdf/.asdf/shims:/asdf/.asdf/bin" -# USER asdf -# WORKDIR /asdf - -RUN git clone https://github.com/asdf-vm/asdf.git --branch v0.6.3 "$HOME"/.asdf && \ - echo '. $HOME/.asdf/asdf.sh' >> "$HOME"/.bashrc && \ - echo '. $HOME/.asdf/asdf.sh' >> "$HOME"/.profile - -ENV PATH="${PATH}:/root/.asdf/shims:/root/.asdf/bin" - -RUN mkdir -p /opt/erlang/epp_proxy -WORKDIR /opt/erlang/epp_proxy - -COPY .tool-versions ./ -RUN asdf plugin-add erlang -RUN asdf install -RUN asdf global erlang $(grep erlang .tool-versions | cut -d' ' -f2) -RUN asdf plugin-add ruby -RUN asdf plugin-add rebar -RUN asdf install diff --git a/Dockerfile.k8s b/Dockerfile.k8s index adc7df3..a743f56 100644 --- a/Dockerfile.k8s +++ b/Dockerfile.k8s @@ -1,18 +1,17 @@ -FROM debian:bullseye-slim AS build +FROM debian:bookworm-slim AS build SHELL ["/bin/bash", "-o", "pipefail", "-c"] COPY ./docker/apt/sources.list /etc/apt/ -# Install build dependencies RUN apt-get update && apt-get install -y -qq \ wget \ git \ build-essential \ - libncurses5-dev \ automake \ autoconf \ curl \ ca-certificates \ + libssl3 \ libssl-dev \ libreadline-dev \ libdpkg-perl \ @@ -22,7 +21,6 @@ RUN apt-get update && apt-get install -y -qq \ perl \ procps \ inotify-tools \ - libssl1.1 \ perl-base \ zlib1g-dev \ libncurses-dev \ @@ -32,14 +30,13 @@ RUN apt-get update && apt-get install -y -qq \ && apt-get clean \ && rm -rf /var/lib/apt/lists/* -# Set environment variables for Erlang build ENV KERL_CONFIGURE_OPTIONS="--disable-debug --without-javac --without-wx --without-odbc --disable-hipe --without-jinterface --without-docs" ENV KERL_BUILD_DOCS="no" ENV KERL_DOC_TARGETS="" ENV KERL_INSTALL_HTMLDOCS="no" ENV KERL_INSTALL_MANPAGES="no" -RUN git clone https://github.com/asdf-vm/asdf.git --branch v0.6.3 "$HOME"/.asdf && \ +RUN git clone https://github.com/asdf-vm/asdf.git --branch v0.14.0 "$HOME"/.asdf && \ echo '. $HOME/.asdf/asdf.sh' >> "$HOME"/.bashrc && \ echo '. $HOME/.asdf/asdf.sh' >> "$HOME"/.profile @@ -48,58 +45,46 @@ ENV PATH="${PATH}:/root/.asdf/shims:/root/.asdf/bin" RUN mkdir -p /opt/erlang/epp_proxy WORKDIR /opt/erlang/epp_proxy +ENV GIT_TERMINAL_PROMPT=0 + COPY .tool-versions ./ -RUN asdf plugin-add erlang -RUN ERLANG_VERSION=$(grep erlang .tool-versions | cut -d' ' -f2) && \ - . $HOME/.asdf/asdf.sh && asdf install erlang $ERLANG_VERSION -RUN asdf global erlang $(grep erlang .tool-versions | cut -d' ' -f2) -RUN asdf plugin-add rebar -RUN REBAR_VERSION=$(grep rebar .tool-versions | cut -d' ' -f2) && \ - . $HOME/.asdf/asdf.sh && asdf install rebar $REBAR_VERSION -RUN asdf global rebar $(grep rebar .tool-versions | cut -d' ' -f2) - -# Copy application files +RUN . "$HOME"/.asdf/asdf.sh && \ + asdf plugin add erlang && \ + asdf plugin add rebar && \ + asdf install erlang "$(grep erlang .tool-versions | awk '{print $2}')" && \ + asdf install rebar "$(grep rebar .tool-versions | awk '{print $2}')" && \ + asdf global erlang "$(grep erlang .tool-versions | awk '{print $2}')" && \ + asdf global rebar "$(grep rebar .tool-versions | awk '{print $2}')" + COPY rebar.config rebar.lock ./ COPY config ./config COPY apps ./apps -# Build the release -RUN . $HOME/.asdf/asdf.sh && rebar3 as prod release +RUN . "$HOME"/.asdf/asdf.sh && rebar3 as prod release -# Second stage: runtime image -FROM debian:bullseye-slim +FROM debian:bookworm-slim -# Install runtime dependencies only RUN apt-get update && apt-get install -y -qq \ - libssl1.1 \ + libssl3 \ libncurses6 \ libsctp1 \ procps \ + ca-certificates \ && apt-get clean \ && rm -rf /var/lib/apt/lists/* -# Create app directory RUN mkdir -p /opt/erlang/epp_proxy WORKDIR /opt/erlang/epp_proxy -# Copy the release from the build stage COPY --from=build /opt/erlang/epp_proxy/_build/prod/rel/epp_proxy ./ -# Create a non-root user to run the application RUN groupadd -r epp && useradd -r -g epp epp RUN chown -R epp:epp /opt/erlang/epp_proxy USER epp -# Expose the EPP port EXPOSE 700 -# Set environment variables ENV RELX_REPLACE_OS_VARS=true ENV NODE_NAME=epp_proxy@127.0.0.1 -# Health check -# HEALTHCHECK --interval=30s --timeout=5s --start-period=5s --retries=3 \ -# CMD ps aux | grep "beam" | grep -v grep || exit 1 - -# Command to run the application CMD ["./bin/epp_proxy", "foreground"] diff --git a/Dockerfile.release.legacy b/Dockerfile.release.legacy deleted file mode 100644 index c0d68f6..0000000 --- a/Dockerfile.release.legacy +++ /dev/null @@ -1,13 +0,0 @@ -FROM internetee/erlang-ruby:21.3.8-2.6.3 AS build -COPY . ./scr/ - -WORKDIR /opt/erlang/epp_proxy/scr - -RUN rebar3 as prod release - -FROM internetee/erlang-ruby:21.3.8-2.6.3 -LABEL org.opencontainers.image.source=https://github.com/internetee/epp_proxy - - -COPY --from=build /opt/erlang/epp_proxy/scr/_build/prod/rel/epp_proxy ./release -RUN ln -s /opt/erlang/epp_proxy/release/bin/epp_proxy ./epp_proxy diff --git a/Dockerfile.staging b/Dockerfile.staging index a5e36b1..2ce6376 100644 --- a/Dockerfile.staging +++ b/Dockerfile.staging @@ -1,18 +1,17 @@ -FROM debian:bullseye-slim +FROM debian:bookworm-slim SHELL ["/bin/bash", "-o", "pipefail", "-c"] COPY ./docker/apt/sources.list /etc/apt/ -# Install all dependencies in a single layer to reduce image size RUN apt-get update && apt-get install -y -qq \ wget \ git \ build-essential \ - libncurses5-dev \ automake \ autoconf \ curl \ ca-certificates \ + libssl3 \ libssl-dev \ libreadline-dev \ libdpkg-perl \ @@ -22,41 +21,39 @@ RUN apt-get update && apt-get install -y -qq \ perl \ procps \ inotify-tools \ - libssl1.1 \ perl-base \ zlib1g-dev \ - # Additional dependencies for Erlang build libncurses-dev \ libsctp-dev \ - # Documentation tools to prevent build failures xsltproc \ libxml2-utils \ - # Dependencies for Ruby 3.2.2 libffi-dev \ libyaml-dev \ && apt-get clean \ && rm -rf /var/lib/apt/lists/* -# Set environment variables for Erlang build ENV KERL_CONFIGURE_OPTIONS="--disable-debug --without-javac --without-wx --without-odbc --disable-hipe --without-jinterface --without-docs" ENV KERL_BUILD_DOCS="no" ENV KERL_DOC_TARGETS="" ENV KERL_INSTALL_HTMLDOCS="no" ENV KERL_INSTALL_MANPAGES="no" -RUN git clone https://github.com/asdf-vm/asdf.git --branch v0.6.3 "$HOME"/.asdf && \ +RUN git clone https://github.com/asdf-vm/asdf.git --branch v0.14.0 "$HOME"/.asdf && \ echo '. $HOME/.asdf/asdf.sh' >> "$HOME"/.bashrc && \ - echo '. $HOME/.asdf/asdf.sh' >> "$HOME"/.profile + echo '. $HOME/.asdf/asdf.sh' >> "$HOME"/.profile ENV PATH="${PATH}:/root/.asdf/shims:/root/.asdf/bin" +ENV GIT_TERMINAL_PROMPT=0 RUN mkdir -p /opt/erlang/epp_proxy WORKDIR /opt/erlang/epp_proxy COPY .tool-versions ./ -RUN asdf plugin-add erlang -RUN . $HOME/.asdf/asdf.sh && asdf install -RUN asdf global erlang $(grep erlang .tool-versions | cut -d' ' -f2) -RUN asdf plugin-add ruby -RUN asdf plugin-add rebar -RUN asdf install +RUN . "$HOME"/.asdf/asdf.sh && \ + asdf plugin add erlang && \ + asdf plugin add ruby && \ + asdf plugin add rebar && \ + asdf install && \ + asdf global erlang "$(grep erlang .tool-versions | awk '{print $2}')" && \ + asdf global ruby "$(grep ruby .tool-versions | awk '{print $2}')" && \ + asdf global rebar "$(grep rebar .tool-versions | awk '{print $2}')" diff --git a/Dockerfile.test b/Dockerfile.test index 8aa2c54..5c5859b 100644 --- a/Dockerfile.test +++ b/Dockerfile.test @@ -1,18 +1,17 @@ -FROM debian:bullseye-slim +FROM debian:bookworm-slim SHELL ["/bin/bash", "-o", "pipefail", "-c"] COPY ./docker/apt/sources.list /etc/apt/ -# Install all dependencies in a single layer to reduce image size RUN apt-get update && apt-get install -y -qq \ wget \ git \ build-essential \ - libncurses5-dev \ automake \ autoconf \ curl \ ca-certificates \ + libssl3 \ libssl-dev \ libreadline-dev \ libdpkg-perl \ @@ -22,47 +21,43 @@ RUN apt-get update && apt-get install -y -qq \ perl \ procps \ inotify-tools \ - libssl1.1 \ perl-base \ zlib1g-dev \ - # Additional dependencies for Erlang build libncurses-dev \ libsctp-dev \ - # Documentation tools to prevent build failures xsltproc \ libxml2-utils \ - # Dependencies for Ruby 3.2.2 libffi-dev \ libyaml-dev \ && apt-get clean \ && rm -rf /var/lib/apt/lists/* -# Set environment variables for Erlang build ENV KERL_CONFIGURE_OPTIONS="--disable-debug --without-javac --without-wx --without-odbc --disable-hipe --without-jinterface --without-docs" ENV KERL_BUILD_DOCS="no" ENV KERL_DOC_TARGETS="" ENV KERL_INSTALL_HTMLDOCS="no" ENV KERL_INSTALL_MANPAGES="no" -RUN git clone https://github.com/asdf-vm/asdf.git --branch v0.6.3 "$HOME"/.asdf && \ +RUN git clone https://github.com/asdf-vm/asdf.git --branch v0.14.0 "$HOME"/.asdf && \ echo '. $HOME/.asdf/asdf.sh' >> "$HOME"/.bashrc && \ - echo '. $HOME/.asdf/asdf.sh' >> "$HOME"/.profile + echo '. $HOME/.asdf/asdf.sh' >> "$HOME"/.profile ENV PATH="${PATH}:/root/.asdf/shims:/root/.asdf/bin" +ENV GIT_TERMINAL_PROMPT=0 RUN mkdir -p /opt/erlang/epp_proxy WORKDIR /opt/erlang/epp_proxy COPY .tool-versions ./ -RUN asdf plugin-add erlang -RUN . $HOME/.asdf/asdf.sh && asdf install -RUN asdf global erlang $(grep erlang .tool-versions | cut -d' ' -f2) -RUN asdf plugin-add ruby -RUN asdf plugin-add rebar -RUN asdf install +RUN . "$HOME"/.asdf/asdf.sh && \ + asdf plugin add erlang && \ + asdf plugin add ruby && \ + asdf plugin add rebar && \ + asdf install && \ + asdf global erlang "$(grep erlang .tool-versions | awk '{print $2}')" && \ + asdf global ruby "$(grep ruby .tool-versions | awk '{print $2}')" && \ + asdf global rebar "$(grep rebar .tool-versions | awk '{print $2}')" -# Copy the application code COPY . . -# Keep container running for tests -CMD ["tail", "-f", "/dev/null"] \ No newline at end of file +CMD ["tail", "-f", "/dev/null"] diff --git a/README.md b/README.md index 5411962..8d48974 100644 --- a/README.md +++ b/README.md @@ -56,9 +56,40 @@ accept the connections and spin off workers, those are supervised in "one for on Build ----- -You need Erlang/OTP release 21 and Rebar3 to build it. No other versions than 21 were tested. - $ rebar3 compile +### Requirements + +| Component | Version | +|-----------|---------| +| Erlang/OTP | **26+** (see `.tool-versions`, e.g. 26.2.5.5) | +| Rebar3 | 3.23+ | +| OpenSSL | **3.x** (linked at Erlang build time and available at runtime) | +| Ruby (tests only) | see `.tool-versions` (for the Roda test backend) | + +Releases are built and tested on **Debian bookworm** with OpenSSL 3 (see `Dockerfile`, `Dockerfile.test`, +`docker-compose.yml`). + +**Do not** compile legacy OTP 23 on modern hosts (Debian trixie, Ubuntu 24.04, etc.) that only ship +OpenSSL 3 — use Docker or install OTP 26 against OpenSSL 3 (asdf/kerl). + +### Dependencies + +Key Hex deps (see `rebar.config`): **lager 3.9.2** (OTP 26-compatible), **hackney ~> 4.0**, erlsom. + +After changing versions in `rebar.config`, refresh the lockfile and rebuild: + +```bash +rebar3 upgrade +rebar3 compile +``` + +Commit both `rebar.config` and `rebar.lock`. `rebar3 get-deps` alone does not apply version bumps. + +### Compile and release + +```bash +rebar3 compile +``` epp_proxy should be deployed as a self-contained Erlang application (release). You can create one with one of the following commands: @@ -106,6 +137,19 @@ Configuration for the application tries to emulate the mod_epp configuration as to make migration easier. The configuration is placed in `config/sys.config` file, it takes a format of Erlang property list. +Example configuration files in `config/`: + +* `sys.config` – default configuration used for real deployments. Values such as `tls_port`, + `epp_session_url` and certificate paths are typically provided via environment variables (eg. + `${TLS_PORT}`, `${EPP_SESSION_URL}`), so the same file can be reused across environments. +* `docker.config` – configuration tuned for running inside Docker. It uses hardcoded ports, + certificate paths under `/opt/ca/...` and EPP endpoints pointing to the `epp` container + (eg. `http://epp:3000/epp/…`). +* `test.config` – local development/test configuration. It enables `dev_mode`, uses local ports + and points EPP endpoints to `http://localhost:9292/...`, with test CA material under + `test_ca/`. +* `syscopy.config` – example staging-style config with fixed URLs and certificate paths. + *Configuration variables* | Variable name | Expected values | Apache equivalent | Definition @@ -120,6 +164,7 @@ of Erlang property list. | `certfile_path` | `/opt/ca/server.crt.pem` | SSLCertificateFile | Where is the server certificate located. Can be inside apps/epp_proxy/priv or absolute path. | `keyfile_path` | `/opt/ca/server.key.pem` | SSLCertificateKeyFile | Where is the server key located. Can be inside apps/epp_proxy/priv or absolute path. | `crlfile_path` | `/opt/ca/crl.pem` | SSLCARevocationFile | Where is the CRL file located. Can be inside apps/epp_proxy/priv or absolute path. When not set, not CRL check is performed. +| `require_client_certs` | `true`, `false` | None | When `true` (default), TLS clients must present a valid client certificate. When `false`, client certificates are optional but verified if provided. Changing this at runtime requires restarting `epp_tls_acceptor` (see tests). Migrating from mod_epp @@ -136,9 +181,10 @@ Checklist of steps to perform if you want to migrate from mod_epp, but still use Testing ---- -The application comes with test suite written with common_test. For integration -tests, there is a small Roda application located in `apps/epp_proxy/priv/test_backend_app`. -It has been written with Ruby 3.2.2. +The application comes with a Common Test suite. For integration tests, there is a small Roda +application in `apps/epp_proxy/priv/test_backend_app` (Ruby version in `.tool-versions`). + +CI runs tests in Docker via `.github/workflows/run-automatest-tests.yml` (bookworm, OpenSSL 3). There is also a number of generated ssl certificates that are used only for testing. Those are valid until 2029 and they are located in `apps/epp_proxy/priv/test_ca`. The password for test CA @@ -152,24 +198,38 @@ $ /bin/bash -l -c "cd apps/epp_proxy/priv/test_backend_app && bundle install" $ /bin/bash -l -c "cd apps/epp_proxy/priv/test_backend_app && bundle exec rackup --pid pidfile -D" ``` -The easiest way to run tests is using Docker: +The easiest way to run tests is using Docker from this directory (Debian bookworm + +OpenSSL 3 — see `docker-compose.yml`): ```bash -# Run all tests -docker compose run --rm epp_proxy bash -c "cd /opt/erlang/epp_proxy && rebar3 ct" +# Build image and run full CT suite (starts Ruby test backend automatically) +./scripts/docker-ct.sh + +# Or step by step: +docker compose build +docker compose run --rm epp_proxy bash -l -c "source ~/.asdf/asdf.sh && rebar3 ct --sys_config config/test.config" -# Run a specific test suite -docker compose run --rm epp_proxy bash -c "cd /opt/erlang/epp_proxy && rebar3 ct --suite apps/epp_proxy/test/epp_http_client_SUITE" +# Run a specific test suite (start test backend first — see docker-ct.sh) +docker compose run --rm epp_proxy bash -l -c "source ~/.asdf/asdf.sh && rebar3 ct --sys_config config/test.config --suite apps/epp_proxy/test/tls_client_SUITE" -# Start a shell for debugging -docker compose run --rm epp_proxy bash -c "cd /opt/erlang/epp_proxy && rebar3 shell" +# TLS suites: tls_client_SUITE (mutual TLS), tls_client_optional_cert_SUITE (optional client certs) -# Then in the Erlang shell: -application:get_all_env(epp_proxy). -# To exit the shell: -halt(). +# Production release tarball +docker compose run --rm epp_proxy bash -l -c "source ~/.asdf/asdf.sh && rebar3 as prod tar" + +# Interactive shell +docker compose run --rm epp_proxy bash -l -c "source ~/.asdf/asdf.sh && rebar3 shell --config config/test.config" +``` + +Verify OpenSSL linkage: + +```bash +docker compose run --rm epp_proxy bash -l -c 'openssl version && erl -noshell -eval "io:format(\"~p~n\", [crypto:info_lib()]), halt()."' ``` +To run epp_proxy together with the registry Rails EPP app, use +`registry/docker-images/docker-compose.yml` (`config/docker.config`, port 700). + After you finish testing, you can stop the process by reading the stored pid: $ kill `cat apps/epp_proxy/priv/test_backend_app/pidfile` diff --git a/apps/epp_proxy/src/epp_http_client.erl b/apps/epp_proxy/src/epp_http_client.erl index ae69d02..5b54285 100644 --- a/apps/epp_proxy/src/epp_http_client.erl +++ b/apps/epp_proxy/src/epp_http_client.erl @@ -17,9 +17,10 @@ request(#epp_request{} = Request) -> case hackney:request(Method, URL, Headers, Payload, Options) of - {error, Error} -> log_and_return_canned(Error, Request); - {Status, _StatusCode, _Headers, ClientRef} -> - {ok, Body} = hackney:body(ClientRef), {Status, Body} + {error, Error} -> + log_and_return_canned(Error, Request); + {ok, StatusCode, _Headers, Body} -> + {StatusCode, Body} end. request_builder(Map) -> request_from_map(Map). diff --git a/apps/epp_proxy/src/epp_proxy.app.src b/apps/epp_proxy/src/epp_proxy.app.src index 26eabc0..26484f1 100644 --- a/apps/epp_proxy/src/epp_proxy.app.src +++ b/apps/epp_proxy/src/epp_proxy.app.src @@ -1,6 +1,6 @@ {application, epp_proxy, [{description, "epp_proxy: EPP to HTTP translator"}, - {vsn, "git"}, + {vsn, "0.1.0"}, {registered, []}, {mod, {epp_proxy_app, []}}, {applications, diff --git a/apps/epp_proxy/src/epp_tls_acceptor.erl b/apps/epp_proxy/src/epp_tls_acceptor.erl index a3c0080..5177455 100644 --- a/apps/epp_proxy/src/epp_tls_acceptor.erl +++ b/apps/epp_proxy/src/epp_tls_acceptor.erl @@ -21,9 +21,11 @@ start_link(Port) -> []). init(Port) -> + RequireClientCerts = require_client_certs(), DefaultOptions = [binary, {packet, raw}, {active, false}, {reuseaddr, true}, - {verify, verify_peer}, {depth, 1}, + {verify, verify_peer}, + {fail_if_no_peer_cert, RequireClientCerts}, {depth, 1}, {cacertfile, ca_cert_file()}, {certfile, cert_file()}, {keyfile, key_file()}], Options = handle_crl_check_options(DefaultOptions), @@ -82,6 +84,14 @@ key_file() -> {ok, KeyFile} -> epp_util:path_for_file(KeyFile) end. +%% Whether client certificates are required. +%% If not configured, default to true to preserve existing behavior. +require_client_certs() -> + case application:get_env(epp_proxy, require_client_certs) of + undefined -> true; + {ok, Bool} -> Bool + end. + crl_file() -> case application:get_env(epp_proxy, crlfile_path) of undefined -> undefined; diff --git a/apps/epp_proxy/test/tls_client_SUITE.erl b/apps/epp_proxy/test/tls_client_SUITE.erl index b198b43..62f516d 100644 --- a/apps/epp_proxy/test/tls_client_SUITE.erl +++ b/apps/epp_proxy/test/tls_client_SUITE.erl @@ -30,14 +30,16 @@ init_per_suite(Config) -> application:ensure_all_started(epp_proxy), application:ensure_all_started(hackney), CWD = code:priv_dir(epp_proxy), - Options = [binary, - {certfile, filename:join(CWD, "test_ca/certs/client.crt.pem")}, - {keyfile, filename:join(CWD, "test_ca/private/client.key.pem")}, - {active, false}], - RevokedOptions = [binary, - {certfile, filename:join(CWD, "test_ca/certs/revoked.crt.pem")}, - {keyfile, filename:join(CWD, "test_ca/private/revoked.key.pem")}, - {active, false}], + Options = client_ssl_options( + CWD, + filename:join(CWD, "test_ca/certs/client.crt.pem"), + filename:join(CWD, "test_ca/private/client.key.pem") + ), + RevokedOptions = client_ssl_options( + CWD, + filename:join(CWD, "test_ca/certs/revoked.crt.pem"), + filename:join(CWD, "test_ca/private/revoked.key.pem") + ), [{ssl_options, Options}, {revoked_options, RevokedOptions} | Config]. end_per_suite(Config) -> @@ -207,6 +209,15 @@ error_test_case(Config) -> "Command syntax error."), ok. +%% Client presents a certificate (server requires mutual TLS) but does not +%% verify the server chain/hostname — this suite tests epp_proxy, not PKI. +client_ssl_options(_CWD, CertFile, KeyFile) -> + [binary, + {verify, verify_none}, + {certfile, CertFile}, + {keyfile, KeyFile}, + {active, false}]. + %% Helper functions: length_of_data(Data) -> EPPEnvelope = binary:part(Data, {0, 4}), diff --git a/apps/epp_proxy/test/tls_client_optional_cert_SUITE.erl b/apps/epp_proxy/test/tls_client_optional_cert_SUITE.erl new file mode 100644 index 0000000..1b68a46 --- /dev/null +++ b/apps/epp_proxy/test/tls_client_optional_cert_SUITE.erl @@ -0,0 +1,70 @@ +-module(tls_client_optional_cert_SUITE). + +-include_lib("common_test/include/ct.hrl"). + +-export([all/0, init_per_suite/1, end_per_suite/1, + connect_without_client_cert_test/1, connect_with_client_cert_test/1]). + +all() -> [connect_without_client_cert_test, connect_with_client_cert_test]. + +init_per_suite(Config) -> + application:ensure_all_started(hackney), + application:ensure_all_started(epp_proxy), + application:set_env(epp_proxy, require_client_certs, false), + ok = restart_tls_acceptor(), + CWD = code:priv_dir(epp_proxy), + WithCert = client_ssl_options( + filename:join(CWD, "test_ca/certs/client.crt.pem"), + filename:join(CWD, "test_ca/private/client.key.pem") + ), + [{with_cert, WithCert} | Config]. + +end_per_suite(Config) -> + application:set_env(epp_proxy, require_client_certs, true), + ok = restart_tls_acceptor(), + Config. + +connect_without_client_cert_test(_Config) -> + Options = [binary, {verify, verify_none}, {active, false}], + {ok, Socket} = ssl:connect("localhost", 1443, Options, 2000), + {ok, _Data} = ssl:recv(Socket, 0, 1200), + ok. + +connect_with_client_cert_test(Config) -> + Options = proplists:get_value(with_cert, Config), + {ok, Socket} = ssl:connect("localhost", 1443, Options, 2000), + {ok, _Data} = ssl:recv(Socket, 0, 1200), + ok. + +client_ssl_options(CertFile, KeyFile) -> + [binary, + {verify, verify_none}, + {certfile, CertFile}, + {keyfile, KeyFile}, + {active, false}]. + +%% ssl:listen options are fixed at acceptor init; restart after set_env so +%% fail_if_no_peer_cert picks up the new require_client_certs value. +restart_tls_acceptor() -> + Pid = whereis(epp_tls_acceptor), + true = is_pid(Pid), + Ref = erlang:monitor(process, Pid), + exit(Pid, shutdown), + receive + {'DOWN', Ref, process, Pid, _} -> ok + after 5000 -> + ct:fail("epp_tls_acceptor did not shut down") + end, + wait_for_tls_acceptor(_Attempts = 50), + ok. + +wait_for_tls_acceptor(0) -> + ct:fail("epp_tls_acceptor did not restart"); +wait_for_tls_acceptor(N) -> + case whereis(epp_tls_acceptor) of + undefined -> + timer:sleep(100), + wait_for_tls_acceptor(N - 1); + _Pid -> + ok + end. diff --git a/config/syscopy.config b/config/syscopy.config new file mode 100644 index 0000000..c2f3ae4 --- /dev/null +++ b/config/syscopy.config @@ -0,0 +1,36 @@ +%% -*- erlang -*- +[ + {epp_proxy, [ + %% Enables or disable TCP connections without TLS (true/false) + {dev_mode, false}, + %% TCP port, only available if dev_mode is set to true. + {tcp_port, 1700}, + %% TLS port, specified in RFC to 700, but can be set to anything else + %% in case that is needed. + {tls_port, 700}, + %% When set to true, you can connect to EPP over HTTPS endpoints without + %% verifying their TLS certificates. + {insecure, false}, + %% URL of EPP endpoints. Can be pointed at a web server (Apache/NGINX) + %% Can contain port (https://some-host:3000/epp/session) + %% Honors the prepended protocol (http / https). + {epp_session_url, "https://registry.test/epp/session/"}, + {epp_command_url, "https://registry.test/epp/command/"}, + {epp_error_url, "https://registry.test/epp/error/"}, + %% Path to root CA that should check the client certificates. + {cacertfile_path, "/opt/shared/ca/certs/ca.crt.pem"}, + %% Path to server's certficate file. + {certfile_path, "/opt/shared/ca/certs/cert.pem"}, + + %% Path to server's key file. + {keyfile_path, "/opt/shared/ca/certs/key.pem"}, + + %% Path to CRL file. When this option is undefined, no CRL check is performed. + {crlfile_path, "/opt/shared/ca/certs/key.pem"}]}, + {lager, [ + {handlers, [ + {lager_console_backend, [{level, debug}]}, + {lager_syslog_backend, ["epp_proxy", local0, debug]} + ]} + ]} +]. diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..c7ff39e --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,24 @@ +# epp_proxy local development and tests (Debian bookworm + OpenSSL 3). +# +# docker compose build +# ./scripts/docker-ct.sh +# docker compose run --rm epp_proxy bash -l -c "source ~/.asdf/asdf.sh && rebar3 shell --config config/test.config" +# +# For the full registry stack (epp_proxy + Rails EPP app), use +# registry/docker-images/docker-compose.yml instead. + +services: + epp_proxy: + build: + context: . + dockerfile: Dockerfile.test + image: epp_proxy:dev + working_dir: /opt/erlang/epp_proxy + volumes: + - .:/opt/erlang/epp_proxy + ports: + - "700:700" + - "3333:3333" + stdin_open: true + tty: true + command: ["tail", "-f", "/dev/null"] diff --git a/docker/apt/sources.list b/docker/apt/sources.list index 4b137ea..74b7ee2 100644 --- a/docker/apt/sources.list +++ b/docker/apt/sources.list @@ -1,7 +1,7 @@ -deb http://deb.debian.org/debian/ bullseye main contrib non-free -deb http://deb.debian.org/debian-security/ bullseye-security main contrib non-free -deb http://deb.debian.org/debian/ bullseye-updates main contrib non-free +deb http://deb.debian.org/debian bookworm main contrib non-free +deb http://deb.debian.org/debian-security bookworm-security main contrib non-free +deb http://deb.debian.org/debian bookworm-updates main contrib non-free -# Legacy -# deb http://deb.debian.org/debian/ buster main contrib non-free -# deb http://deb.debian.org/debian/ buster-backports main contrib non-free +# Legacy (OpenSSL 1.1) — bullseye +# deb http://deb.debian.org/debian bullseye main contrib non-free +# deb http://deb.debian.org/debian-security bullseye-security main contrib non-free diff --git a/rebar.config b/rebar.config index aac0dea..c724a31 100644 --- a/rebar.config +++ b/rebar.config @@ -1,10 +1,12 @@ +{minimum_otp_vsn, "26"}. + {erl_opts, [debug_info, {parse_transform, lager_transform}]}. -{deps, [{hackney, "1.15.1"}, +{deps, [{hackney, "~> 4.0"}, {syslog, {git, "https://github.com/Vagabond/erlang-syslog.git", {branch, "master"}}}, - {lager, "3.7.0"}, + {lager, "3.9.2"}, {lager_syslog, {git, "https://github.com/erlang-lager/lager_syslog.git", {branch, "master"}}}, - {erlsom, "1.5.0"}]}. + {erlsom, "1.5.2"}]}. {ct_opts, [ {sys_config, "./config/test.config"} @@ -32,12 +34,6 @@ ] }. -{provider_hooks, [ - {pre, [{tar, {appup, tar}}]}, - {post, [{compile, {appup, compile}}, - {clean, {appup, clean}}]} -]}. - {overrides, [{override, syslog, [ {plugins, [pc]}, @@ -54,11 +50,15 @@ ]}. {profiles, [{prod, [{relx, [{dev_mode, false}, - {include_erts, true}]}] - }, + {include_erts, true}]}, + {plugins, [rebar3_appup_plugin]}, + {provider_hooks, [ + {pre, [{tar, {appup, tar}}]}, + {post, [{compile, {appup, compile}}, + {clean, {appup, clean}}]} + ]}]}, {test, [{deps, [proper]}]}] }. {plugins, [rebar3_auto, - {erl_tidy_prv_fmt, ".*", {git, "https://github.com/tsloughter/erl_tidy.git", {branch, "master"}}}, - rebar3_appup_plugin]}. + {erl_tidy_prv_fmt, ".*", {git, "https://github.com/tsloughter/erl_tidy.git", {branch, "master"}}}]}. diff --git a/rebar.lock b/rebar.lock index c08bb4f..237d8ff 100644 --- a/rebar.lock +++ b/rebar.lock @@ -1,46 +1,46 @@ {"1.2.0", -[{<<"certifi">>,{pkg,<<"certifi">>,<<"2.5.1">>},1}, - {<<"erlsom">>,{pkg,<<"erlsom">>,<<"1.5.0">>},0}, +[{<<"certifi">>,{pkg,<<"certifi">>,<<"2.16.0">>},1}, + {<<"erlsom">>,{pkg,<<"erlsom">>,<<"1.5.2">>},0}, {<<"goldrush">>,{pkg,<<"goldrush">>,<<"0.1.9">>},1}, - {<<"hackney">>,{pkg,<<"hackney">>,<<"1.15.1">>},0}, - {<<"idna">>,{pkg,<<"idna">>,<<"6.0.0">>},1}, - {<<"lager">>,{pkg,<<"lager">>,<<"3.7.0">>},0}, + {<<"h2">>,{pkg,<<"h2">>,<<"0.4.0">>},1}, + {<<"hackney">>,{pkg,<<"hackney">>,<<"4.0.0">>},0}, + {<<"idna">>,{pkg,<<"idna">>,<<"7.1.0">>},1}, + {<<"lager">>,{pkg,<<"lager">>,<<"3.9.2">>},0}, {<<"lager_syslog">>, {git,"https://github.com/erlang-lager/lager_syslog.git", {ref,"126dd0284fcac9b01613189a82facf8d803411a2"}}, 0}, - {<<"metrics">>,{pkg,<<"metrics">>,<<"1.0.1">>},1}, - {<<"mimerl">>,{pkg,<<"mimerl">>,<<"1.3.0">>},1}, - {<<"parse_trans">>,{pkg,<<"parse_trans">>,<<"3.4.2">>},2}, - {<<"ssl_verify_fun">>,{pkg,<<"ssl_verify_fun">>,<<"1.1.4">>},1}, + {<<"mimerl">>,{pkg,<<"mimerl">>,<<"1.5.0">>},1}, + {<<"parse_trans">>,{pkg,<<"parse_trans">>,<<"3.4.1">>},1}, + {<<"quic">>,{pkg,<<"quic">>,<<"1.0.0">>},1}, + {<<"ssl_verify_fun">>,{pkg,<<"ssl_verify_fun">>,<<"1.1.7">>},1}, {<<"syslog">>, {git,"https://github.com/Vagabond/erlang-syslog.git", - {ref,"2c38c70450f80b44711c7c1702171f32f881d4a6"}}, - 0}, - {<<"unicode_util_compat">>,{pkg,<<"unicode_util_compat">>,<<"0.4.1">>},2}]}. + {ref,"614a1fde7a8654bee2815b599bbdaae1bb0323b5"}}, + 0}]}. [ {pkg_hash,[ - {<<"certifi">>, <<"867CE347F7C7D78563450A18A6A28A8090331E77FA02380B4A21962A65D36EE5">>}, - {<<"erlsom">>, <<"C5A5CDD0EE0E8DCA62BCC4B13FF08DA24FDEFC16CCD8B25282A2FDA2BA1BE24A">>}, + {<<"certifi">>, <<"A4EDFC1D2DA3424D478A3271133BF28E0EC5E6FD8C009AAB5A4AE980CB165CE9">>}, + {<<"erlsom">>, <<"3E47C53A199136FB4D20D5479EDB7C9229F31624534C062633951C8D14DCD276">>}, {<<"goldrush">>, <<"F06E5D5F1277DA5C413E84D5A2924174182FB108DABB39D5EC548B27424CD106">>}, - {<<"hackney">>, <<"9F8F471C844B8CE395F7B6D8398139E26DDCA9EBC171A8B91342EE15A19963F4">>}, - {<<"idna">>, <<"689C46CBCDF3524C44D5F3DDE8001F364CD7608A99556D8FBD8239A5798D4C10">>}, - {<<"lager">>, <<"563AB17CD32134A3DD17EC3B3622E6D8F827506AA4F8C489158879BED87D980B">>}, - {<<"metrics">>, <<"25F094DEA2CDA98213CECC3AEFF09E940299D950904393B2A29D191C346A8486">>}, - {<<"mimerl">>, <<"D0CD9FC04B9061F82490F6581E0128379830E78535E017F7780F37FEA7545726">>}, - {<<"parse_trans">>, <<"C352DDC1A0D5E54F9B1654D45F9C432EEF76F9CEA371C55DDFF769EF688FDB74">>}, - {<<"ssl_verify_fun">>, <<"F0EAFFF810D2041E93F915EF59899C923F4568F4585904D010387ED74988E77B">>}, - {<<"unicode_util_compat">>, <<"D869E4C68901DD9531385BB0C8C40444EBF624E60B6962D95952775CAC5E90CD">>}]}, + {<<"h2">>, <<"289AE4833F5062337F43DBF503E39A32A7472D888EE792E8D6FE4732986A24FD">>}, + {<<"hackney">>, <<"DFC90299EDC31553898CD32567F1D46FDB477AFED9ECA694B9C879DDADFA3D62">>}, + {<<"idna">>, <<"1067A13043538129602D2F2CE6899D8713125C7D19734AA557CE2E3EA55BD4F1">>}, + {<<"lager">>, <<"4CAB289120EB24964E3886BD22323CB5FEFE4510C076992A23AD18CF85413D8C">>}, + {<<"mimerl">>, <<"F35ACA6F23242339B3666E0AC0702379E362B469D0AEA167F6CC713547E777ED">>}, + {<<"parse_trans">>, <<"6E6AA8167CB44CC8F39441D05193BE6E6F4E7C2946CB2759F015F8C56B76E5FF">>}, + {<<"quic">>, <<"2FE86FEEDAB19A2CD4E1355553FB7F30CD7968C66D79E45E331A8CA60FDF4661">>}, + {<<"ssl_verify_fun">>, <<"354C321CF377240C7B8716899E182CE4890C5938111A1296ADD3EC74CF1715DF">>}]}, {pkg_hash_ext,[ - {<<"certifi">>, <<"805ABD97539CAF89EC6D4732C91E62BA9DA0CDA51AC462380BBD28EE697A8C42">>}, - {<<"erlsom">>, <<"55A9DBF9CFA77FCFC108BD8E2C4F9F784DEA228A8F4B06EA10B684944946955A">>}, + {<<"certifi">>, <<"8A64F6669D85E9CC0E5086FCF29A5B13DE57A13EFA23D3582874B9A19303F184">>}, + {<<"erlsom">>, <<"4E765CC677FB30509F7B628FF2914E124CF4DCC0FAC1C0A62EE4DCEE24215B5D">>}, {<<"goldrush">>, <<"99CB4128CFFCB3227581E5D4D803D5413FA643F4EB96523F77D9E6937D994CEB">>}, - {<<"hackney">>, <<"C2790C9F0F7205F4A362512192DEE8179097394400E745E4D20BAB7226A8EAAD">>}, - {<<"idna">>, <<"4BDD305EB64E18B0273864920695CB18D7A2021F31A11B9C5FBCD9A253F936E2">>}, - {<<"lager">>, <<"97DC7E1C9E7289C3167F417E71FFE1DF28218537967E076800ECEDB1C28C9E48">>}, - {<<"metrics">>, <<"69B09ADDDC4F74A40716AE54D140F93BEB0FB8978D8636EADED0C31B6F099F16">>}, - {<<"mimerl">>, <<"A1E15A50D1887217DE95F0B9B0793E32853F7C258A5CD227650889B38839FE9D">>}, - {<<"parse_trans">>, <<"4C25347DE3B7C35732D32E69AB43D1CEEE0BEAE3F3B3ADE1B59CBD3DD224D9CA">>}, - {<<"ssl_verify_fun">>, <<"603561DC0FD62F4F2EA9B890F4E20E1A0D388746D6E20557CAFB1B16950DE88C">>}, - {<<"unicode_util_compat">>, <<"1D1848C40487CDB0B30E8ED975E34E025860C02E419CB615D255849F3427439D">>}]} + {<<"h2">>, <<"A7EC6A7DAE06F006DA235E80C96C957DC6F39636037ABEBBE466476BE627DA61">>}, + {<<"hackney">>, <<"56EEA3F252B8A672756986B1BEE827D75C933F9C335D3E0C54F57F48345F272C">>}, + {<<"idna">>, <<"6AE959A025BF36DF61A8CAB8508D9654891B5426A84C44D82DEAFFD6DDF8C71F">>}, + {<<"lager">>, <<"7F904D9E87A8CB7E66156ED31768D1C8E26EBA1D54F4BC85B1AA4AC1F6340C28">>}, + {<<"mimerl">>, <<"DB648CE065BAE14EA84CA8B5DD123F42F49417CEF693541110BF6F9E9BE9ECC4">>}, + {<<"parse_trans">>, <<"620A406CE75DADA827B82E453C19CF06776BE266F5A67CFF34E1EF2CBB60E49A">>}, + {<<"quic">>, <<"52F59E0BBA92ECEE80A7E685F92B8F498E7ED01F6DD05A682808AFB70279AFCC">>}, + {<<"ssl_verify_fun">>, <<"FE4C190E8F37401D30167C8C405EDA19469F34577987C76DDE613E838BBC67F8">>}]} ]. diff --git a/scripts/docker-ct.sh b/scripts/docker-ct.sh new file mode 100755 index 0000000..bf9ded6 --- /dev/null +++ b/scripts/docker-ct.sh @@ -0,0 +1,30 @@ +#!/usr/bin/env bash +# Run Common Test via docker-compose.yml in this repo (bookworm + OpenSSL 3). +set -euo pipefail + +ROOT="$(cd "$(dirname "$0")/.." && pwd)" +cd "$ROOT" + +echo "==> Building epp_proxy image..." +docker compose build epp_proxy + +echo "==> OpenSSL / OTP in container:" +docker compose run --rm epp_proxy bash -l -c \ + 'openssl version; erl -noshell -eval "io:format(\"OTP ~s, crypto: ~p~n\", [erlang:system_info(otp_release), crypto:info_lib()]), halt()."' + +echo "==> Running rebar3 ct (test backend + TLS suites)..." +docker compose run --rm epp_proxy bash -l -c ' + set -euo pipefail + source "$HOME/.asdf/asdf.sh" + cd apps/epp_proxy/priv/test_backend_app + bundle install --quiet + bundle exec rackup -p 9292 -o 0.0.0.0 & + RACK_PID=$! + trap "kill $RACK_PID 2>/dev/null || true" EXIT + sleep 5 + cd /opt/erlang/epp_proxy + rebar3 as test compile + rebar3 ct --sys_config config/test.config --readable=false --cover +' + +echo "==> Done."