Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 0 additions & 23 deletions .github/workflows/release-asset.yml

This file was deleted.

3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,3 +28,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- `HandlerStackReadyListener`: prepared for future event-based injection once a `HttpClientHandlerStackReadyEvent` is available in Nextcloud core
- `logdir_audit_http_client` config key with automatic fallback chain: explicit override → sibling of `logfile_audit` → sibling of `logfile` → `<datadirectory>/admin_audit_http_client_logs/`
- Log entries contain: request ID (`X-Nextcloud-ReqId`), timestamp, method, URI, HTTP version, status, request headers, response headers, cURL handler stats (`size_download`, `speed_download`, `total_time`, etc.), compression stats (`encoding`, `compressed_bytes`, `decompressed_bytes`, `ratio`)

[0.2.0]: https://github.com/ernolf/admin_audit_http_client/releases/tag/v0.2.0
[0.1.0]: https://github.com/ernolf/admin_audit_http_client/releases/tag/v0.1.0
162 changes: 49 additions & 113 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,15 @@

# admin_audit_http_client

[![REUSE status](https://api.reuse.software/badge/github.com/ernolf/admin_audit_http_client)](https://api.reuse.software/info/github.com/ernolf/admin_audit_http_client)
[![Latest release](https://img.shields.io/github/v/release/ernolf/admin_audit_http_client?sort=semver&color=0082c9)](https://github.com/ernolf/admin_audit_http_client/releases/latest)
[![Built with ncmake](https://img.shields.io/badge/built%20with-ncmake-0082c9)](https://github.com/ernolf/ncmake)

Nextcloud app that logs all outgoing HTTP requests made by Nextcloud's built-in HTTP client (`OC\Http\Client\Client`, Guzzle-based).

---
> [!IMPORTANT]
> **This app is in early development.**
> It is not available in the Nextcloud App Store — [installation is manual only](#installation).
> It has no built-in log rotation yet. Monitor log file sizes and rotate manually or via the system's logrotate daemon until log rotation is implemented.
> [!NOTE]
> The app has no built-in log rotation. Monitor log file sizes and rotate them with the system's logrotate daemon.

---
## Configuration (config/config.php)

All settings are optional.
Expand Down Expand Up @@ -62,10 +62,12 @@ Selects the log output format. Default: `'both'`.
| `'plain'` | `<host>.log` — one plain-text line per request |
| `'both'` | both files |

Every entry starts with the request ID: the ID of the server request that triggered the outgoing call (the same ID as in `nextcloud.log`), followed by a random per-request suffix. The ID is also sent to the remote server as `X-Nextcloud-ReqId` header; a caller-supplied header is used unchanged.

Plain-text example:

```
a3f9bc 2026-05-05T14:23:01+00:00 GET https://example.com/feed HTTP/2 200 compressed=4821 decompressed=18944 ratio=0.25 encoding=br Hdrs=Host,Accept-Encoding,User-Agent "Nextcloud/32 ..."
67527c3ff4b8-a3f9bc12 2026-05-05T14:23:01+00:00 GET https://example.com/feed HTTP/2 200 compressed=4821 decompressed=18944 ratio=0.25 encoding=br Hdrs=Host,Accept-Encoding,User-Agent "Nextcloud/32 ..."
```

```php
Expand Down Expand Up @@ -98,119 +100,53 @@ The following headers are always redacted and cannot be un-redacted: `Authorizat

## Installation

No npm, no composer, no build step required.
### From a release tarball

> [!NOTE]
> Installation is typically done as root or a privileged user, not as the web server user itself. After installing, set ownership to match your web server user:
>
> | Distribution | Web server user |
> |---|---|
> | Debian / Ubuntu | `www-data` |
> | CentOS / RHEL / Fedora (Apache) | `apache` |
> | CentOS / RHEL / Fedora (nginx) | `nginx` |
> | Arch Linux | `http` |

### From a release tarball (recommended)

Download the latest release from the [Releases page](https://github.com/ernolf/admin_audit_http_client/releases):

* <details>
<summary>Installation script</summary>

```bash
TAG=v0.2.0
NCDIR=/path/to/nextcloud
HTUSER=www-data
cd ${NCDIR}/apps
sudo curl -L https://github.com/ernolf/admin_audit_http_client/releases/download/${TAG}/admin_audit_http_client-${TAG}.tar.gz | sudo tar -xz
sudo chown -R ${HTUSER}: admin_audit_http_client
sudo -u ${HTUSER} php ${NCDIR}/occ app:enable admin_audit_http_client
```
</details>

### Nextcloud All-in-One (AIO)

* <details>
<summary>Installation script</summary>

```bash
TAG=v0.2.0
curl -L https://github.com/ernolf/admin_audit_http_client/releases/download/${TAG}/admin_audit_http_client-${TAG}.tar.gz \
| sudo docker exec -i --user www-data nextcloud-aio-nextcloud tar -xz -C /var/www/html/custom_apps/
sudo docker exec --user www-data -it nextcloud-aio-nextcloud php occ app:enable admin_audit_http_client
```
</details>

### Via git clone (development / always latest)

* <details>
<summary>Installation script</summary>

```bash
NCDIR=/path/to/nextcloud
HTUSER=www-data
cd ${NCDIR}/apps
git clone https://github.com/ernolf/admin_audit_http_client.git
sudo chown -R ${HTUSER}: admin_audit_http_client
sudo -u ${HTUSER} php ${NCDIR}/occ app:enable admin_audit_http_client
```
</details>
Download the latest release tarball from the [releases page](https://github.com/ernolf/admin_audit_http_client/releases) and extract it into your Nextcloud `apps/` directory, so the app lives at `apps/admin_audit_http_client/`. Then set ownership to your web server user and enable it:

```sh
tar -xzf admin_audit_http_client-x.y.z.tar.gz -C /path/to/nextcloud/apps/
chown -R www-data:www-data /path/to/nextcloud/apps/admin_audit_http_client
occ app:enable admin_audit_http_client
```

### From source

The build is driven by [ncmake](https://github.com/ernolf/ncmake) and runs entirely in throwaway containers, so the only requirement is **[podman](https://podman.io/)** (or Docker) — no PHP toolchain on the host. The first `make` fetches the shared ncmake Makefile once into `~/.cache/ncmake/`. Clone and build the tarball:

```sh
git clone https://github.com/ernolf/admin_audit_http_client.git
cd admin_audit_http_client
make build && make dist
```

This writes `build/artifacts/dist/admin_audit_http_client-x.y.z.tar.gz`. Install it exactly like a release tarball above.

If your Nextcloud is on the same machine (or reachable over SSH), you can skip the tarball and deploy straight into its `apps/` directory — `OCC=1` runs the full refresh cycle (`app:disable`, sync, `chown`, `app:enable`) in one go:

```sh
make build && make rsync TARGET=/path/to/nextcloud/apps/ OCC=1
```

`TARGET` is the `apps/` parent directory and may be a local path or a remote `user@host:` path.

For **Nextcloud All-in-One** (or any dockerized instance whose filesystem is not reachable from outside), `make cp` deploys into the running container instead:

```sh
make build && make cp TARGET=nextcloud-aio-nextcloud:/var/www/html/custom_apps/ OCC=1
```

### Update

Remove the old directory and reinstall — this avoids leftover files from previous versions.

**From a release tarball:**

* <details>
<summary>Update script</summary>

```bash
TAG=v0.2.0
NCDIR=/path/to/nextcloud
HTUSER=www-data
cd ${NCDIR}/apps
sudo -u ${HTUSER} php ${NCDIR}/occ app:remove admin_audit_http_client
sudo curl -L https://github.com/ernolf/admin_audit_http_client/releases/download/${TAG}/admin_audit_http_client-${TAG}.tar.gz | sudo tar -xz
sudo chown -R ${HTUSER}: admin_audit_http_client
sudo -u ${HTUSER} php ${NCDIR}/occ app:enable admin_audit_http_client
```
</details>

### Nextcloud All-in-One (AIO)

* <details>
<summary>Installation script</summary>

```bash
TAG=v0.2.0
sudo docker exec --user www-data -it nextcloud-aio-nextcloud php occ app:remove admin_audit_http_client
curl -L https://github.com/ernolf/admin_audit_http_client/releases/download/${TAG}/admin_audit_http_client-${TAG}.tar.gz \
| sudo docker exec -i --user www-data nextcloud-aio-nextcloud tar -xz -C /var/www/html/custom_apps/
sudo docker exec --user www-data -it nextcloud-aio-nextcloud php occ app:enable admin_audit_http_client
```
</details>

**Via git clone:**

* <details>
<summary>Update script</summary>

```bash
NCDIR=/path/to/nextcloud
HTUSER=www-data
cd ${NCDIR}/apps
sudo -u ${HTUSER} php ${NCDIR}/occ app:remove admin_audit_http_client
git clone https://github.com/ernolf/admin_audit_http_client.git
sudo chown -R ${HTUSER}: admin_audit_http_client
sudo -u ${HTUSER} php ${NCDIR}/occ app:enable admin_audit_http_client
```
</details>
- Tarball installations: run `occ app:remove admin_audit_http_client`, then install the new tarball as above — this avoids leftover files from previous versions.
- `make rsync`/`make cp` deployments: simply run the same command again after a `git pull`; the sync replaces the app directory as a whole.

## Roadmap

Architecture, data flow, planned features, and configuration options are documented in the
[Architecture & Roadmap](https://gist.github.com/ernolf/ecd9a66610be46f2afff840b1c70d513).
- **Event-based injection:** the middleware is injected via reflection into the private Guzzle client of `OC\Http\Client\Client`, because core dispatches no event that exposes the handler stack. Nextcloud 35 added an optional `$handler` parameter to `IClientService::newClient()`; a core-side `HttpClientHandlerStackReadyEvent` (or a comparable extension point) would allow dropping the reflection entirely. Until then the server-matrix CI job also runs against `master` and fails the moment core changes the private structure.
- **Built-in log rotation** (size/age based) instead of relying on the system's logrotate.

Background and data-flow notes: [Architecture & Roadmap](https://gist.github.com/ernolf/ecd9a66610be46f2afff840b1c70d513).

## Credits

Expand Down
8 changes: 4 additions & 4 deletions appinfo/info.xml
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,12 @@
<name>Auditing / HTTP Client Logging</name>
<summary>Logs all outgoing HTTP requests made by Nextcloud's internal HTTP client.</summary>
<description>
Extends admin_audit with logging of all outgoing HTTP requests made by Nextcloud's internal Guzzle-based HTTP client. Logs include method, URI, status code, HTTP version, request/response headers, cURL transfer statistics, and compression metrics.
No UI, no routes — pure audit logging to /var/log/nextcloud/client/.
Extends admin_audit with logging of all outgoing HTTP requests made by Nextcloud's internal Guzzle-based HTTP client. Logs include method, URI, status code, HTTP version, request and response headers, cURL transfer statistics, and compression metrics. Credential-bearing headers such as Authorization and Cookie are always redacted.
No UI, no routes — pure audit logging into per-host JSON and plain-text files below a configurable log directory.
</description>
<version>0.2.0</version>
<licence>agpl</licence>
<author mail="raphael.gradenwitz@googlemail.com">[ernolf] - Raphael Gradenwitz</author>
<author mail="raphael.gradenwitz@googlemail.com">[ernolf] - Raphael Gradenwitz</author>
<namespace>AdminAuditHttpClient</namespace>
<types>
<logging/>
Expand All @@ -27,6 +27,6 @@ No UI, no routes — pure audit logging to /var/log/nextcloud/client/.
<donation>https://github.com/sponsors/ernolf</donation>
<dependencies>
<php min-version="8.1" max-version="8.5"/>
<nextcloud min-version="32" max-version="34"/>
<nextcloud min-version="32" max-version="35"/>
</dependencies>
</info>
8 changes: 8 additions & 0 deletions lib/AppInfo/Application.php
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,14 @@ public function boot(IBootContext $context): void {
$config = $server->get(IConfig::class);
$request = $server->get(IRequest::class);

/**
* registerService on the server container is the only way to replace
* a server-wide service. OCP <= 34 marks it deprecated; OCP 35 narrows
* getServerContainer() to ContainerInterface, hiding it statically.
*
* @psalm-suppress UndefinedInterfaceMethod
* @psalm-suppress DeprecatedMethod
*/
$server->registerService(
IClientService::class,
fn () => new LoggingClientService($inner, $logger, $config, $request)
Expand Down
11 changes: 0 additions & 11 deletions psalm.xml
Original file line number Diff line number Diff line change
Expand Up @@ -28,15 +28,4 @@
<directory name="vendor" />
<directory name="vendor-bin/nextcloud-ocp" />
</extraFiles>
<issueHandlers>
<!-- The middleware injection relies on server internals; the legacy
accessors involved have no OCP replacement that exposes the Guzzle
handler stack. -->
<DeprecatedMethod>
<errorLevel type="suppress">
<referencedMethod name="OCP\IContainer::registerService" />
<referencedMethod name="GuzzleHttp\Client::getConfig" />
</errorLevel>
</DeprecatedMethod>
</issueHandlers>
</psalm>
Loading