mirror of
https://github.com/nextcloud/documentation.git
synced 2026-01-02 17:59:36 +07:00
Merge pull request #13960 from nextcloud/feat/add-exapp-harp-docs
feat(exapp_development): Add documentation for ExApp HaRP
This commit is contained in:
@@ -0,0 +1,253 @@
|
||||
Adapting ExApps to HaRP
|
||||
=======================
|
||||
|
||||
.. mermaid::
|
||||
|
||||
graph LR
|
||||
Client[Client] -->|connects| NC[Nextcloud Proxy]
|
||||
NC -->|connects| HaRP[HaRP - FRP proxy]
|
||||
HaRP -->|forwards| ExApp[ExApp container]
|
||||
ExApp -->|runs FRP client| HaRP
|
||||
AppAPI[Nextcloud AppAPI] -->|manage certs| ExApp
|
||||
|
||||
Summary
|
||||
-------
|
||||
|
||||
HaRP is a reverse proxy system designed to simplify the deployment workflow
|
||||
for Nextcloud 32’s AppAPI.
|
||||
|
||||
It enables direct communication between clients and ExApps, bypassing
|
||||
the Nextcloud instance to improve performance and reduce the complexity
|
||||
traditionally associated with `DockerSocketProxy` setups.
|
||||
|
||||
HaRP provides an `FRP-based <https://github.com/fatedier/frp>`_
|
||||
transport for ExApps and recommends copying
|
||||
`start.sh <https://github.com/nextcloud/HaRP/blob/main/exapps_dev/start.sh>`_
|
||||
into your ExApp image and using it as the container entrypoint.
|
||||
The script installs or starts the FRP client and executes your app process.
|
||||
|
||||
.. warning::
|
||||
|
||||
We strongly recommend starting support for HaRP in ExApps from the start
|
||||
of Nextcloud 32, as the old `DSP <https://github.com/nextcloud/docker-socket-proxy>`_
|
||||
way will be deprecated and marked for removal in Nextcloud 35.
|
||||
|
||||
Adding HaRP support is fully compatible with the existing DSP system,
|
||||
so you won’t need to maintain two separate release types of your ExApp.
|
||||
|
||||
Key integration considerations
|
||||
------------------------------
|
||||
|
||||
- **Connecting to HaRP with FRPC**: Your ExApp does not need to expose any ports to the host
|
||||
or be reachable from the Nextcloud server. The FRP client (`FRPC`) inside your ExApp
|
||||
container will create an outbound connection to HaRP, which will proxy
|
||||
requests from clients to your ExApp.
|
||||
- **File permissions**: AppAPI may copy certificate files into the container and
|
||||
execute commands inside it. If your container runs the main process as a
|
||||
non-root service user, AppAPI's file writes or execs may fail unless the
|
||||
receiving paths are writable/readable by that user.
|
||||
- **Certs and FRP config**: HaRP expects FRP cert files to be accessible under
|
||||
`/certs/frp` (client.crt, client.key, ca.crt). The FRP client configuration
|
||||
path used by many example `start.sh` scripts is `/frpc.toml`.
|
||||
- **Root-only commands**: Some setup steps (for example updating CA bundles with
|
||||
`update-ca-certificates`) require root; AppAPI may need to run those using
|
||||
`docker exec ...` when setting up containers.
|
||||
|
||||
Steps needed to adapt an ExApp
|
||||
--------------------------------------
|
||||
|
||||
1. Copy the `start.sh <https://github.com/nextcloud/HaRP/blob/main/exapps_dev/start.sh>`_
|
||||
script from the exapps_dev folder of the HaRP repository into your Docker image
|
||||
(e.g., using a `COPY` instruction).
|
||||
|
||||
2. In your ExApp's Dockerfile, set the `ENTRYPOINT` to execute `start.sh` followed by
|
||||
the **command and arguments required to launch** your actual application. The `start.sh`
|
||||
script will launch the FRP client if needed and then use `exec` to
|
||||
run the command you provide as arguments.
|
||||
|
||||
3. Ensure the `curl` command-line utility is installed in your ExApp's Docker image,
|
||||
as it's needed by the following script to download the FRP client.
|
||||
|
||||
4. Add the following lines to your Dockerfile to automatically include the FRP
|
||||
client binaries in your Docker image:
|
||||
|
||||
.. code-block:: dockerfile
|
||||
|
||||
# Download and install FRP client
|
||||
RUN set -ex; \
|
||||
ARCH=$(uname -m); \
|
||||
if [ "$ARCH" = "aarch64" ]; then \
|
||||
FRP_URL="https://raw.githubusercontent.com/nextcloud/HaRP/main/exapps_dev/frp_0.61.1_linux_arm64.tar.gz"; \
|
||||
else \
|
||||
FRP_URL="https://raw.githubusercontent.com/nextcloud/HaRP/main/exapps_dev/frp_0.61.1_linux_amd64.tar.gz"; \
|
||||
fi; \
|
||||
echo "Downloading FRP client from $FRP_URL"; \
|
||||
curl -L "$FRP_URL" -o /tmp/frp.tar.gz; \
|
||||
tar -C /tmp -xzf /tmp/frp.tar.gz; \
|
||||
mv /tmp/frp_0.61.1_linux_* /tmp/frp; \
|
||||
cp /tmp/frp/frpc /usr/local/bin/frpc; \
|
||||
chmod +x /usr/local/bin/frpc; \
|
||||
rm -rf /tmp/frp /tmp/frp.tar.gz
|
||||
|
||||
.. note::
|
||||
|
||||
For Alpine 3.21 Linux you can just install FRP from repo using apk add frp command.
|
||||
|
||||
Running your ExApp with a non-root user
|
||||
----------------------------------------
|
||||
|
||||
.. note::
|
||||
|
||||
In `Docker Build best practices <https://docs.docker.com/build/building/best-practices/#user>`_,
|
||||
it is recommended to run application containers as non-root users for security reasons
|
||||
whenever possible.
|
||||
|
||||
To run the main process as a non-root user while remaining compatible with HaRP and AppAPI,
|
||||
ensure the following:
|
||||
|
||||
1. Keep image default user as `root`, drop to less privileged service user at runtime.
|
||||
|
||||
- Make it easy for AppAPI to perform privileged operations (copying files,
|
||||
setting permissions, running `update-ca-certificates`) by leaving the
|
||||
container default user as `root` in the image.
|
||||
- Drop privileges for the main process in the `ENTRYPOINT` using `gosu`
|
||||
or `su-exec` so the runtime process runs as a non-root service user.
|
||||
|
||||
Example snippet (Dockerfile):
|
||||
|
||||
.. code-block:: dockerfile
|
||||
|
||||
FROM python:3.12-alpine AS app
|
||||
|
||||
ARG USER=serviceuser
|
||||
|
||||
ENV USER=$USER
|
||||
ENV HOME=/home/$USER
|
||||
ENV GOSU_VERSION=1.19
|
||||
|
||||
# ... other Dockerfile instructions ..
|
||||
|
||||
# Install GOSU
|
||||
RUN set -eux; \
|
||||
\
|
||||
apk add --no-cache --virtual .gosu-deps \
|
||||
ca-certificates \
|
||||
dpkg \
|
||||
gnupg \
|
||||
; \
|
||||
\
|
||||
dpkgArch="$(dpkg --print-architecture | awk -F- '{ print $NF }')"; \
|
||||
wget -O /usr/local/bin/gosu "https://github.com/tianon/gosu/releases/download/$GOSU_VERSION/gosu-$dpkgArch"; \
|
||||
wget -O /usr/local/bin/gosu.asc "https://github.com/tianon/gosu/releases/download/$GOSU_VERSION/gosu-$dpkgArch.asc"; \
|
||||
\
|
||||
export GNUPGHOME="$(mktemp -d)"; \
|
||||
gpg --batch --keyserver hkps://keys.openpgp.org --recv-keys B42F6819007F00F88E364FD4036A9C25BF357DD4; \
|
||||
gpg --batch --verify /usr/local/bin/gosu.asc /usr/local/bin/gosu; \
|
||||
gpgconf --kill all; \
|
||||
rm -rf "$GNUPGHOME" /usr/local/bin/gosu.asc; \
|
||||
\
|
||||
apk del --no-network .gosu-deps; \
|
||||
\
|
||||
chmod +x /usr/local/bin/gosu
|
||||
|
||||
# Use gosu in combination with start.sh
|
||||
ENTRYPOINT ["/bin/sh", "-c", "exec gosu \"$USER\" /start.sh python3 -u main.py"]
|
||||
|
||||
.. note::
|
||||
|
||||
See the `gosu documentation <https://github.com/tianon/gosu/blob/master/INSTALL.md>`_
|
||||
for more details.
|
||||
|
||||
2. Ensure FRP config and cert paths are prepared for the service user
|
||||
|
||||
- **Create `/frpc.toml`** or the directory that will contain it at image build
|
||||
time and set ownership to the service user so at runtime `start.sh` can
|
||||
write to it without requiring root.
|
||||
- **Create directory `/certs/frp`** and make it readable by the service user.
|
||||
AppAPI will copy cert files into that folder by using a `docker cp ...` command
|
||||
with the default container user (which is still `root`). By setting the directory
|
||||
owner to the service user, we will ensure the service user can read the certs at runtime.
|
||||
|
||||
Use some similar commands in your Dockerfile:
|
||||
|
||||
.. code-block:: dockerfile
|
||||
|
||||
RUN touch /frpc.toml && \
|
||||
mkdir -p /certs/frp && \
|
||||
chown $USER:$USER /frpc.toml && \
|
||||
chown -R $USER:$USER /certs/frp && \
|
||||
chmod 600 /frpc.toml
|
||||
|
||||
**Putting it all together:**
|
||||
|
||||
.. code-block:: dockerfile
|
||||
|
||||
FROM python:3.12-alpine AS app
|
||||
|
||||
ARG USER=serviceuser
|
||||
|
||||
ENV USER=$USER
|
||||
ENV HOME=/home/$USER
|
||||
ENV GOSU_VERSION=1.19
|
||||
|
||||
# Install dependencies and create service user. You might want to
|
||||
# add additional packages depending on your app requirements.
|
||||
# Make sure curl and FRP are installed.
|
||||
RUN apk update && \
|
||||
apk add --no-cache curl frp ca-certificates && \
|
||||
adduser -D $USER && \
|
||||
touch /frpc.toml && \
|
||||
mkdir -p /certs/frp && \
|
||||
chown $USER:$USER /frpc.toml && \
|
||||
chown -R $USER:$USER /certs/frp && \
|
||||
chmod 600 /frpc.toml
|
||||
|
||||
# Install GOSU
|
||||
RUN set -eux; \
|
||||
\
|
||||
apk add --no-cache --virtual .gosu-deps \
|
||||
ca-certificates \
|
||||
dpkg \
|
||||
gnupg \
|
||||
; \
|
||||
\
|
||||
dpkgArch="$(dpkg --print-architecture | awk -F- '{ print $NF }')"; \
|
||||
wget -O /usr/local/bin/gosu "https://github.com/tianon/gosu/releases/download/$GOSU_VERSION/gosu-$dpkgArch"; \
|
||||
wget -O /usr/local/bin/gosu.asc "https://github.com/tianon/gosu/releases/download/$GOSU_VERSION/gosu-$dpkgArch.asc"; \
|
||||
\
|
||||
export GNUPGHOME="$(mktemp -d)"; \
|
||||
gpg --batch --keyserver hkps://keys.openpgp.org --recv-keys B42F6819007F00F88E364FD4036A9C25BF357DD4; \
|
||||
gpg --batch --verify /usr/local/bin/gosu.asc /usr/local/bin/gosu; \
|
||||
gpgconf --kill all; \
|
||||
rm -rf "$GNUPGHOME" /usr/local/bin/gosu.asc; \
|
||||
\
|
||||
apk del --no-network .gosu-deps; \
|
||||
\
|
||||
chmod +x /usr/local/bin/gosu
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
# Copy your app code
|
||||
COPY --chown=$USER:$USER <files> .
|
||||
|
||||
# Copy the start.sh script and make it executable
|
||||
COPY --chown=$USER:$USER start.sh /start.sh
|
||||
RUN chmod +x /start.sh && \
|
||||
chown -R $USER:$USER /app && \
|
||||
pip install -r requirements.txt
|
||||
|
||||
# Run the start.sh as entrypoint with non-root user and point it to your app
|
||||
ENTRYPOINT ["/bin/sh", "-c", "exec gosu \"$USER\" /start.sh python3 -u main.py"]
|
||||
|
||||
Integration test example
|
||||
-------------------------
|
||||
|
||||
An example test suite used to validate HaRP support for an ExApp is available
|
||||
in the `workflow_ocr_backend` repository (example commit that added HaRP
|
||||
support and tests):
|
||||
|
||||
- https://github.com/R0Wi-DEV/workflow_ocr_backend/blob/f5ae6efb6e4a3307328a188898968abf000511ab/test/test_harp_integration.py
|
||||
|
||||
This test demonstrates automated verification of FRP connection and runtime
|
||||
behaviour; it can be used as a reference when adding CI checks for HaRP
|
||||
compatibility.
|
||||
@@ -13,4 +13,5 @@ grouped from other parts of documentation.
|
||||
ExAppDevelopmentSteps
|
||||
ExAppOverview
|
||||
ExAppLifecycle
|
||||
ExAppHarpIntegration
|
||||
|
||||
|
||||
Reference in New Issue
Block a user