1
0
mirror of https://github.com/coturn/coturn.git synced 2026-10-24 00:08:57 +03:00
Commit Graph

211 Commits

Author SHA1 Message Date
Kai Ren
1986df21e4 docker: Upgrade Coturn to 4.18.0 version 2026-09-08 13:15:43 +02:00
tangxinfa
1dfa48d29e fix: external ip must valid if the provider responses EDNS (#2060)
Google may response IP and EDNS records, e.g.

$ dig @ns1.google.com o-o.myaddr.l.google.com TXT -4 +short | tr -d \"
    edns0-client-subnet 103.252.16.0/24
    172.253.5.26

It pass the is_valid_ip() checking, but not a valid ip address, fix it
by examine the first line only.
2026-08-28 17:26:36 -07:00
Pavel Punsky
1e3570dd1e Advertise IPv6 allocations under their real relayed transport address (#2058)
Fixes #1322.

The issue reports `clnet_allocate: !!!: relay addr cannot be received
(2)` when asking `turnutils_uclient` for an IPv6 relay. It turns out to
be two independent bugs that both end in that same line, one on each
side of the wire. Both are reproduced by the new test, which fails on
`master` and passes here.

## What the RFC requires

RFC 8656 Section 3 defines the relayed transport address as *"A
transport address on the TURN server that is used for relaying packets
between the client and a peer"*, and Section 7.2 leaves the server two
outcomes for an Allocate carrying `REQUESTED-ADDRESS-FAMILY`:

> If the server does not support the address family requested by the
client in REQUESTED-ADDRESS-FAMILY, or if the allocation of the
requested address family is disabled by local policy, it MUST generate
an Allocate error response, and it MUST include an ERROR-CODE attribute
with the 440 (Address Family not Supported) response code.

Section 7.3 puts one duty on the client:

> If the client receives an Allocate success response, then it MUST
check that the mapped address and the relayed transport address or
addresses are part of an address family or families that the client
understands and is prepared to handle.

## 1. `--external-ip` was applied across address families (server)

`external_ip` is a single `ioa_addr` and `--external-ip` may only be
given once, but it was substituted for the relayed address of *both*
families. On a server with an IPv4 `--external-ip`, an IPv6 allocation
was therefore advertised under an unrelated IPv4 address that no peer
can reach — neither an IPv6 relay nor a 440.

```
--relay-ip=127.0.0.1 --relay-ip=::1 --external-ip=127.0.0.1, client over ::1 with -x
INFO IPv4. Received relay addr: 127.0.0.1:30654
INFO clnet_allocate: !!!: relay addr cannot be received (2)
```

The relay socket really was IPv6; only the advertised address was wrong.
As a second effect, the substituted family made the response draw
`LIFETIME` from the IPv4 relay session that was never created for that
allocation.

`turn_set_xor_relayed_addr()` now applies the external IP only to a
relay of its own family, which is the same rule the
`ioa_addr_add_mapping()` setup path already follows in `mainrelay.c`.
The option's documentation is updated to match.

## 2. `turnutils_uclient` rejected an IPv6 relay it can handle (client)

The acceptance test took an IPv6 relayed address only when IPv6 had been
requested *explicitly*, and consulted the global
`default_address_family` rather than the `af` argument the caller passed
to `clnet_allocate()`. Against a server running
`--allocation-default-address-family=keep` (or the deprecated
`--keep-address-family`), which answers an IPv6 client with an IPv6
relay and no `REQUESTED-ADDRESS-FAMILY` in play, a perfectly usable
relay was refused — as reported in the issue thread. Section 7.3 asks
only that the client refuse a family it *cannot handle*, so the check
now rejects solely on a contradiction with the family this allocation
requested.

Note this does not make `keep` conformant — it is a deliberate
deviation, and `turnserver -h` says as much (*"It breaks RFC6156
section-4.2 (violates default IPv4)"*). The client simply stops
rejecting a relay it is able to use.

## Testing

`examples/run_tests_ipv6_relay.sh` covers both configurations end to
end, asserting a completed relayed round trip rather than just a
successful allocate. It uses ports off the defaults the other scripts
share, and SKIPs where no IPv6 loopback is bindable. Wired into the
clang, cmake, linux and macos workflows.

Verified red before green: reverting only `src/` and rebuilding turns
both cases into failures.

- macOS: build + `ctest` 18/18; Linux container: build + `ctest` 17/17
- Linux: `run_tests.sh`, `run_tests_conf.sh`, `run_tests_mobile.sh`,
`run_tests_multiplex_peer.sh`, `run_tests_rfc5780.sh`,
`run_tests_stateless_nonce.sh`, `run_tests_ipv6_relay.sh`,
`run_tests_dtls_default.sh` — all OK
- Fuzzing smoke tests (ASan targets 0 and 1) — OK
- Packaged Docker image (debian, arm64) + bats — 19 tests, 0 failures, 2
skipped

## Deliberately not in this PR

- The dual-allocation `508` guard when `external_ip_set` is left as is;
with family-aware rewriting it could now be relaxed to allow a
v4-rewritten + v6-as-is pair, but that is a separate behavioral change.
- `--external-ip` stays single-family. A per-family form would be the
complete answer for IPv6-NAT deployments.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-08-23 13:52:18 -07:00
Pavel Punsky
b3dc634b37 Build docker images with optimizations on (#2057)
Enable `-O3` for docker images
Enable optimizations where makes sense in cmake builds
2026-08-21 18:43:33 -07:00
Kai Ren
34230574e9 docker: Upgrade Coturn to 4.17.2 version 2026-08-10 15:05:52 +03:00
Kai Ren
3f995360ed docker: Upgrade Coturn to 4.17.1 version 2026-08-10 15:04:36 +03:00
Kai Ren
28e7f6afd3 docker: Fix test for image version 2026-08-05 12:34:51 +03:00
Kai Ren
98b8f0e734 docker: Upgrade Coturn to 4.17.0 version 2026-08-05 12:03:11 +03:00
Pavel Punsky
62662ec53e Log format: ISO-8601 timestamp by default, no thread id, space delimiter (#2031)
## What

Makes the default log line parseable by a log shipper with no
configuration. Rebased on `master` (`441d3e9c`, which now includes
#2030).

Before:
```
0: (18446744073709551615): INFO: Listener address to use: 127.0.0.1
```
After:
```
2026-08-02T17:32:31.297-0700 INFO Listener address to use: 127.0.0.1
```

Two commits:

| Commit | Change |
|---|---|
| `Cache the rendered log timestamp per thread, add millisecond
precision` | perf + sub-second resolution |
| `Log format: ISO-8601 by default, drop thread id, space delimiter` |
the format change itself |

## Why

**ISO-8601 by default.** The previous default was a counter of seconds
since process start — no wall-clock time at all, so nothing downstream
can order or window on it. `--new-log-timestamp` now also honours its
boolean argument; it previously ignored the value and could only be
turned *on*, so with the default flipped there would have been no way
back. `--new-log-timestamp=false` restores the counter.

**Thread id removed.** It was emitted as a bare unlabelled `(N)`,
indistinguishable from any parenthesised number in a message body.
Worse, it was gated on `#ifdef SYS_gettid` — a test for whether the
*constant* exists, not whether the syscall does what was intended. macOS
defines `SYS_gettid` as a different syscall, so the call failed with
`ESRCH` and every line on every macOS build carried
`(18446744073709551615)`, i.e. `(unsigned long)(pid_t)-1`:

```
SYS_gettid defined = 286
syscall(SYS_gettid) = -1  errno=3 (No such process)
as (unsigned long)(pid_t)r = 18446744073709551615
```

Dropping it also removes the deprecated `syscall(2)` use on macOS.

**Space delimiter.** `:` cannot delimit anything a parser can rely on —
message bodies are full of it (`realm <north.gov> user <>: ...`). These
two changes are coupled: the ISO timestamp emitted *no* trailing
separator (the `(tid): ` supplied it), so removing the thread id alone
would have produced `...-0700INFO`. The renderer now emits only the
timestamp and the caller adds one space.

**Millisecond precision** (first commit). Log shippers need sub-second
resolution to order records within a second, which a busy relay produces
in bulk. `strftime()` has no sub-second conversion, so `%f` is expanded
to milliseconds (`%%f` is left alone) and the default format is
`%FT%T.%f%z`.

**Timestamp caching** (first commit). Everything down to the second only
changes once a second, but was re-rendered per line — and
`localtime_r()` takes a lock inside libc, so relay threads serialised on
it and per-line cost *grew* with thread count. Now cached in
thread-local storage keyed on the second, with only the millisecond
digits patched in per line. A generation counter bumped by
`set_turn_log_timestamp_format()` invalidates live caches on a format
change; a format rendering longer than the cache falls back to the
caller's buffer.

Timestamp rendering, 12 threads on a 12-core machine:

| threads | before (no ms) | after (with ms) |
|---|---:|---:|
| 1 | 163 ns/line | 24 ns/line |
| 4 | 53 ns/line | 6 ns/line |
| 12 | **304 ns/line** | **3 ns/line** |

## Acceptance check

Output run against the stock logstash pattern `%{TIMESTAMP_ISO8601:ts}
%{LOGLEVEL:level} %{GREEDYDATA:msg}` — **43/44 startup lines parse with
no further configuration**, fields extracting correctly.

The one failure is pre-existing: the banner at `mainrelay.c:3211` embeds
`\n\n` *inside* the message, so its text lands on a line with no prefix.
The right fix is newline normalisation in the logger, not patching call
sites; deferred to the `--log-format` work.

## Compatibility

This is a visible break for anyone parsing coturn logs.
`--new-log-timestamp=false` restores the old timestamp but not the
thread id or the colons — there is no single flag that restores the
previous line verbatim. Worth pairing with the remaining logging changes
in one release and one changelog entry.

## Testing

All re-run after the rebase onto `441d3e9c`.

**Linux (Docker)**
- `ctest` 17/17
- `run_tests.sh`, `run_tests_conf.sh`, `run_tests_mobile.sh`,
`run_tests_multiplex_peer.sh`, `run_tests_rfc5780.sh`,
`run_tests_stateless_nonce.sh`, `run_tests_dtls_default.sh` — all
`rc=0`, zero `FAIL` lines
- Both timestamp modes confirmed on glibc: `2026-08-03T00:46:59.742+0000
INFO ...` and, with `--new-log-timestamp=false`, `0 INFO ...`

**macOS**
- `ctest` 18/18; `run_tests.sh`, `run_tests_conf.sh`,
`run_tests_mobile.sh`, `run_tests_stateless_nonce.sh` — all `rc=0`, zero
`FAIL` lines
- `clang-format` clean

**Fuzzing smoke**
- `fuzzing/run-local.sh ASan 0 -runs=1` and `ASan 1 -runs=1` — both
clean, no new findings

**Format matrix**, by hand on both platforms — default,
`--new-log-timestamp=false`, explicit on, and the same three through the
**config-file** path (a separate parser, also changed).

**Timestamp-cache correctness** — milliseconds vary per line (51
distinct timestamps across 7 seconds under traffic); format edge cases:
no `%f`, `%f` alone, `%f` at start, `%f` at end, `100%%f-%T.%f` (escaped
`%%f` stays literal while the real one expands), and a >128-byte format
hitting the fallback.

The MSVC branch of `turn_wall_clock()` (`GetSystemTimeAsFileTime`) is
inspection-only — there is no MSVC toolchain here, so CI is the first
real check on it.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-08-02 19:32:25 -07:00
Pavel Punsky
9f2728261c Remove syslog from the Docker Compose turnserver.conf (#2029)
## What

Deletes the `syslog` directive and its comment from
`docker/coturn/turnserver.conf`.

Four lines removed. No source changes.

## Why

`docker/coturn/turnserver.conf` is bind-mounted at
`/etc/turnserver.conf` by all five Compose stacks
(`docker-compose-{all,mysql,postgresql,redis,mongodb}.yml`), and `/etc/`
is in `config_file_search_dirs` (`src/apps/common/apputils.c:1042`),
ahead of `/etc/coturn/` — so it is read. The `coturn` service declares
no `command:` or `entrypoint:` override, so the image CMD from
`docker/coturn/debian/Dockerfile` applies:

```
CMD ["--log-file=stdout", "--external-ip=$(detect-external-ip)"]
```

The config and the CMD contradict each other, and the config wins. In
`set_rtpfile()` (`src/apps/common/ns_turn_utils.c:425`), `to_syslog` is
checked **before** the `log-file` handling:

```c
if (to_syslog) {
  return;
} else if (!_rtpfile) {
  ...  /* the --log-file=stdout branch, which sets no_stdout_log = 1 */
```

So `--log-file=stdout` never takes effect and `no_stdout_log` is never
set. Log lines then leave through the unconditional `fwrite(s, so_far,
1, stdout)` at `ns_turn_utils.c:642` instead of the `fprintf()` +
`fflush()` path at `:658`.

That path **never flushes**. Two consequences:

1. **A stopped container loses its whole log.** stdout to a pipe is
fully buffered, and nothing flushes it, so unless enough volume
accumulated to force a flush, everything sits in the buffer when the
process dies.
2. A `syslog()` call is made per line to a socket the container does not
provide.

## Measurement

Against the built image, config mounted exactly as Compose mounts it, DB
directives stripped so output is deterministic:

| Config | SIGTERM (`docker stop`) | SIGKILL |
|---|---:|---:|
| `syslog` (current) | **0 lines** | **0 lines** |
| removed (this PR) | 44 lines | 42 lines |

This is the ordinary `docker stop` / `docker compose down` path, not
just an abrupt-kill edge case.

## Scope note

This does **not** affect the published `coturn/coturn` image. Both
Dockerfiles run `rm -f /out/etc/coturn/turnserver.conf.default`
(`debian/Dockerfile:81`, `alpine/Dockerfile:82`) and this file is never
`COPY`d in, so the image ships with no config and its CMD already works
as intended. The bug is confined to the Compose stacks and to anyone who
starts from this file when following the "specify your own configuration
file" instructions in `docker/coturn/README.md`.

`#syslog-facility` is left in place — it is already commented out and
documents a real option.

## Testing

- Docker image built (`make docker.image dockerfile=debian
platform=linux/arm64`) and the Bats suite run: **19 tests, 0 failures, 2
skipped**
- Before/after runs against the built image with the config
bind-mounted, under both SIGTERM and SIGKILL — table above
- Confirmed the edited config still parses cleanly and is picked up from
`/etc/turnserver.conf` (`Default realm: example.org` comes from it)

## Context

Part of a series of small changes ahead of work on structured log output
for ELK ingestion. The `fwrite`-vs-`fprintf` split above is also where
the unlocked stdout write lives; that one is a source fix and is tracked
separately.
2026-08-01 22:53:04 -07:00
Kai Ren
8b8cbd2c9d docker: Upgrade Coturn to 4.16.0 version 2026-07-30 02:31:04 +03:00
Pavel Punsky
283662c348 Split --stun-backward-compatibility: move RFC 3489 handling to its own deprecated flag (#2008)
## Problem

`--stun-backward-compatibility` controls two unrelated behaviors:

- **(a)** accepting obsolete **RFC 3489** ("classic" STUN) Binding
requests, which carry no magic cookie;
- **(b)** adding the deprecated `MAPPED-ADDRESS` attribute to modern
**RFC 5389** Binding responses, alongside `XOR-MAPPED-ADDRESS`.

Only (a) is the obsolete part. (b) serves clients that speak RFC 5389
correctly but cannot parse `XOR-MAPPED-ADDRESS`, and (b) is what the
amplification warning in `turnserver.conf` is actually about. So
retiring RFC 3489 support can't be done by deleting the flag — that
would silently drop (b) too.

## Change

Move (a) behind a new `--rfc3489-compatibility`, which logs a
deprecation warning at startup naming the removal release.
`--stun-backward-compatibility` keeps (b) only.

Setting **both** reproduces the previous single-flag behavior exactly,
so operators have a mechanical migration path.

| Flags | Modern request | Old (cookie-less) request |
|---|---|---|
| none (default) | `XOR-MAPPED` | no response |
| `--stun-backward-compatibility` | `XOR-MAPPED` + `MAPPED` | no
response |
| `--rfc3489-compatibility` | `XOR-MAPPED` | `MAPPED` + `SOFTWARE`,
cookie echoed |
| both | `XOR-MAPPED` + `MAPPED` | `MAPPED` + `SOFTWARE`, cookie echoed
|

Nothing changes for anyone who does not set either flag; both default to
off.

### A bug the split exposed

Because the two halves are now independent, the old-STUN path can no
longer borrow (b) to supply an address. `MAPPED-ADDRESS` is the **only**
address attribute RFC 3489 defines, but it was gated purely on
`stun_backward_compatibility` — so `--rfc3489-compatibility` alone would
have answered classic-STUN clients with an **address-less** response
(`[SOFTWARE]` and nothing else).

`stun_set_binding_response_str()` now always emits `MAPPED-ADDRESS` when
building an old-STUN response. On the modern path it stays opt-in, since
it widens the response and raises the amplification gain.

### Other bits

- The UDP classifier follows the new flag, so an old-STUN datagram is
only spared `--drop-invalid-packets` when the option that can actually
process it is enabled.
- Corrects the flag's description in both shipped `turnserver.conf`
files, which disagreed with each other and with the code: the
`examples/` copy claimed the option *disables* `MAPPED-ADDRESS`, and the
`docker/` copy said "instead of" rather than "in addition to"
`XOR-MAPPED-ADDRESS`.
- `README.md` / `STATUS.md` / man page now mark RFC 3489 support as
deprecated.

## Context

RFC 5389 deprecated the RFC 3489 mechanisms in 2008; RFC 8489 dropped
the compatibility language entirely. This PR is step 1 of a two-step
retirement — it decouples the useful half from the obsolete half and
starts the deprecation clock. Step 2 (deleting the RFC 3489 request path
and the `old_stun_*` API, ~300 lines) is left for a major-version
window, since those functions are installed as public `libturnclient`
API.

## Testing

- New unit test pins the address-attribute matrix across both knobs;
verified it fails without the `MAPPED-ADDRESS` fix above.
- `ctest`: 16/16 macOS, 15/15 Linux.
- All six `examples/run_tests*.sh` clean on macOS and in a Linux
container (including `run_tests_rfc5780.sh`, which shares the
CHANGE-REQUEST code path — 0 FAIL / 0 SKIP in the container; it skips on
macOS without a `127.0.0.2` alias).
- Both fuzz targets smoke-clean; packaged Docker image tests 19 passed /
0 failed.
- All four flag combinations verified end-to-end via CLI **and** config
file with a raw prober, since `turnutils_stunclient` deliberately cannot
send a cookie-less request.
- `clang-format-15` clean.
2026-07-26 12:08:00 -07:00
Kai Ren
f3ef47fbaa docker: Upgrade Coturn to 4.15.0 version 2026-07-22 13:44:40 +03:00
Kai Ren
a792defb28 docker: Update Debian "trixie" to 20260713 snapshot 2026-07-22 13:42:06 +03:00
Paul Wayper
54fe73b9f9 Implement a minimum log level option (#1222)
The `-v` or `--verbose` options change a few things, but they have
almost no effect
on the `turnserver`. This implements a `log_min_level` setting and a
`--log-min-level`
option to allow the user to set a minimum level below which log messages
are not
emitted.

The option takes a level *name* — `debug`, `info`, `warning` or `error`
(case-insensitive) — and is available both as a command-line flag and as
a
config-file key:

```
turnserver --log-min-level=warning
```
```ini
# turnserver.conf
log-min-level=warning
```

Scale, most to least verbose: `debug` (the default, i.e. current
behaviour) →
`info` → `warning` → `error`. An unrecognised value is reported and
ignored
rather than silently reinterpreted, so no argument can leave the server
mute.

Documented in `README.turnserver`, the `turnserver(1)` man page, and
both example
configs. Covered by a new unit test, `tests/test_log_min_level.c`.

---

### Note: this also drops the `CONTROL` log level

The `TURN_LOG_LEVEL_CONTROL` level is removed from the `TURN_LOG_LEVEL`
enum.
Nothing in the tree ever logged at that level — it existed only as an
enum value
and two `switch` arms in `ns_turn_utils.c` — so no logging behaviour
changes.

Two consequences worth flagging for reviewers:

- **It is a public-header/ABI break.** `ns_turn_utils.h` is installed as
a public
header (`${includedir}/turn`), so `TURN_LOG_LEVEL` is shipped API.
Removing the
  middle enumerator renumbers `TURN_LOG_LEVEL_WARNING` (3 → 2) and
  `TURN_LOG_LEVEL_ERROR` (4 → 3). Anything already compiled against
`libturnclient` that passes `TURN_LOG_LEVEL_ERROR` still passes `4`,
which is
now above every level, so its messages would be dropped. This deserves a
  changelog note on release.

- **This is why `--log-min-level` takes a name and not a number.** With
the levels
renumbered, a numeric argument is a moving target across releases: an
earlier
revision of this PR documented `4 = errors only`, which after the enum
change
sat above every level and silenced the server entirely, fatal errors
included.
  Level names cannot drift out of sync with the enum that way.

Relatedly, `log_min_level` is typed as `TURN_LOG_LEVEL` rather than
`int`.
The enum has no negative enumerators, so it takes an unsigned underlying
type;
as an `int`, a negative minimum was promoted in `level < log_min_level`
and
filtered out *every* message instead of none.

---------

Signed-off-by: Paul Wayper <paulway@redhat.com>
Co-authored-by: Pavel Punsky <eakraly@users.noreply.github.com>
2026-07-14 10:27:46 -10:00
Kai Ren
ae16d101b1 docker: Update Debian "trixie" to 20260623 snapshot 2026-07-06 09:20:38 +03:00
Kai Ren
190f91dbfc Upgrade Docker image to 4.14.0 Coturn version 2026-06-22 11:16:54 +03:00
Kai Ren
25ce2c8e06 Update Alpine to 3.24.1 version to fix OpenSSL CVEs in Docker image
- CVE-2026-45447
- CVE-2026-34182
- CVE-2026-34183
- CVE-2026-42764
- CVE-2026-45445
- CVE-2026-7383
- CVE-2026-9076
- CVE-2026-34180
- CVE-2026-34181
- CVE-2026-42766
- CVE-2026-42767
- CVE-2026-42768
- CVE-2026-42769
- CVE-2026-42770
- CVE-2026-45446
2026-06-22 11:16:20 +03:00
Pavel Punsky
47ce433cde Add per-source rate-limiting of UDP 401 Unauthorized responses (#1957)
TURN/STUN long-term auth answers the first unauthenticated request with
a 401 Unauthorized carrying REALM/NONCE. On UDP, where source addresses
are spoofable and the 401 is larger than the request that triggers it,
the server becomes a reflection/amplification vector: an attacker forges
a victim's source IP and bounces amplified 401s at it.

This adds an opt-in mitigation that caps how many UDP 401 responses the
server emits toward any single source IP per second. Past the cap the
server stays silent, denying both the reflection and the amplification.
Authenticated relay traffic is untouched — only requests that reach the
401 branch spend budget.

Flags (off by default):
  --unauthorized-ratelimit            Enable the mitigation (UDP only).
--unauthorized-ratelimit-rps=<n> Max 401 responses per source IP per
second. Default 10.

Implementation:
- Lock-free, fixed 4096-bucket direct-mapped table (~80 KiB, no
malloc/free, no lock, no list scan on the hot path); the source IP
appears only as a 32-bit tag, never as a pointer. The port is stripped
from the key so an attacker cannot evade the limit by rotating the
source port. A fixed 1-second window backs the per-second limit.
- Colliding addresses share the live bucket budget instead of receiving
a fresh allowance, so hash collisions can never expand reflected output.
- Drop and collision diagnostics are each latched to one log line per
(bucket, window) to avoid log amplification.
- Portable 32-bit atomics over C11 <stdatomic.h> with an MSVC
Interlocked* fallback; the on/off flag and the per-second threshold are
pointers into turn_params so every relay thread sees live CLI values
without per-thread copies.

Observability (Prometheus):
- turn_unauthenticated_401_{requests,responses,dropped_responses}
- turn_ratelimit_{hash_collisions,occupied_buckets,total_buckets}

Tests & docs:
- Unity unit test (tests/test_ratelimit.c) covering live-bucket
collisions, occupancy tracking, and the collision counter.
- End-to-end positive/negative system test (run_tests_ratelimit_401.sh)
and a Prometheus counter test (run_tests_prom.sh).
- A 401-response-flood load harness under examples/loadtest/.
- docs/401-ratelimit.md design note, plus man page, README, Prometheus
docs, and the example/Docker turnserver.conf entries.
2026-06-21 14:25:09 -07:00
Pavel Punsky
53f3ed9104 Prom https (#1956)
## Summary

Adds an optional **HTTPS mode for the Prometheus `/metrics` endpoint**,
and bundles three supporting improvements that surfaced while building
and testing it: more reliable `libmicrohttpd` detection, a fix for slow
`turnserver` startup on macOS, and expanded/robust Prometheus tests.

## Changes

### 1. Optional HTTPS for the Prometheus exporter
The exporter previously served `/metrics` over plain HTTP only. It can
now optionally serve over HTTPS via libmicrohttpd's TLS support.

- `--prometheus-tls` — serve the metrics endpoint over HTTPS instead of
HTTP.
- `--prometheus-cert <file>` / `--prometheus-key <file>` — PEM cert/key
for the endpoint; default to the server's own `--cert` / `--pkey` when
unset.

`start_prometheus_server()` loads the cert/key into memory, enables
`MHD_USE_TLS`, and passes `MHD_OPTION_HTTPS_MEM_CERT/KEY`. If TLS is
requested but no cert/key can be loaded (or libmicrohttpd lacks TLS
support), it logs an error and does not start. The startup log now notes
the `(http)`/`(https)` scheme. `docs/Prometheus.md` documents the new
flags.

*(`src/apps/relay/prom_server.c`, `mainrelay.c`, `mainrelay.h`,
`docs/Prometheus.md`)*

### 2. Reliable `libmicrohttpd` detection in `configure`
The autotools `configure` probed libmicrohttpd with a bare `cc
-lmicrohttpd` link test (no include/lib paths), so it failed to find
Homebrew-installed libmicrohttpd on macOS and silently disabled
Prometheus — even though OpenSSL/libevent (also Homebrew) were found via
pkg-config. `configure` now detects libmicrohttpd via `pkg-config` first
(matching how libssl/libevent are already detected), falling back to the
bare link probe on platforms without a `.pc` file. `./configure && make`
now builds the exporter on macOS without manual `CFLAGS`/`LDFLAGS`.

*(`configure`)*

### 3. Faster `turnserver` startup on macOS
macOS lacks `pthread_barrier_*`, so the thread-startup rendezvous in
`netengine.c` degraded to a flat `sleep(5)` — adding ~5s to every
startup (and it was racy: threads proceeded even if setup took longer
than the fixed sleep). Replaced the shim with a real portable barrier
built on a mutex + condition variable, behind a unified macro API:
**native `pthread_barrier` is still used on Linux**, the portable
barrier is used only where it is unavailable. The previous racy
`sched_yield()` spin-wait fallback is removed as well.

*(`src/apps/relay/netengine.c`)*

### 4. Prometheus test coverage + robustness
`examples/run_tests_prom.sh` now also covers the HTTPS endpoint
(explicit cert/key, cert/key inherited from `--cert`/`--pkey`, and a
negative test that an unreadable cert keeps the endpoint down, including
a check that the TLS endpoint rejects plaintext HTTP). Readiness is now
detected by polling the endpoint / server log instead of fixed `sleep`
intervals, making the suite both faster (~70s → ~2s) and less flaky.

*(`examples/run_tests_prom.sh`)*

## New flags

| Flag | Description |
|---|---|
| `--prometheus-tls` | Serve `/metrics` over HTTPS. Requires
libmicrohttpd built with TLS support. |
| `--prometheus-cert <file>` | PEM certificate for the HTTPS endpoint
(default: the server's `--cert`). |
| `--prometheus-key <file>` | PEM private key for the HTTPS endpoint
(default: the server's `--pkey`). |

## Validation

- **macOS** (portable-barrier path): `clang-format` clean, 10/10 unit
tests, and `run_tests.sh` / `run_tests_conf.sh` /
`run_tests_multiplex_peer.sh` / `run_tests_prom.sh` all pass. Exporter
now reachable at startup `t=0` (was `t=5`).
- **Linux** (Docker, native-`pthread_barrier` path): clean build, 9/9
unit tests, and all four system-test suites pass (TCP/TLS/UDP/DTLS).

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-20 23:02:01 -07:00
Pavel Punsky
1ec81eff10 Build Prometheus exporter from vendored local sources (#1955)
## Summary

The Prometheus exporter previously linked the
[digitalocean/prometheus-client-c](https://github.com/digitalocean/prometheus-client-c)
(`prom`) library, which is **no longer maintained**. This replaces it
with a minimal, self-contained Prometheus client under `src/prometheus/`
that is built straight from coturn's own sources and implements exactly
the slice of the API coturn uses: counters, gauges, a single default
registry, and the text-exposition serializer.

The vendored client is **drop-in API-compatible**, so `prom_server.c` is
unchanged. Histograms, summaries, custom registries and the upstream
`promhttp` handler are intentionally omitted (coturn serves `/metrics`
with its own libmicrohttpd handler).

The exporter is now **always compiled in**; the runtime `--prometheus`
flag still controls whether it starts. **libmicrohttpd** — actively
maintained — is the only remaining external dependency and is now
required by the build.

## Changes

- **`src/prometheus/{prom.h,prom.c}`** — vendored client. Thread-safe
(per-metric mutex on the increment path, read under lock in the bridge),
label-aware, with proper Prometheus value/label escaping and clean
integer formatting for counters. No deps beyond pthread/libc.
- **CMake** — replace `find_package(Prometheus)` with required
`find_package(MicroHTTPD)`; add `cmake/FindMicroHTTPD.cmake`; remove
dead `cmake/FindPrometheus.cmake` and the `WITH_PROMETHEUS` option.
- **`configure` / `Makefile.in`** — drop `-lprom`, compile the vendored
source.
- **Docker** (`debian`, `alpine`) and **CI** (`ubuntu-build-deps`,
`mingw`) — remove the `prometheus-client-c` build/install steps; keep
libmicrohttpd.
- **`docs/Prometheus.md`** rewritten.
- **`tests/test_prometheus.c`** — Unity tests for counter
accumulation/monotonicity, gauge inc/dec, and label-value escaping.

## Validation

- Full build green; all unit tests pass (incl. new `test_prometheus`);
`clang-format-15` clean.
- Live exporter scraped over HTTP — all `run_tests_prom.sh` scenarios
(default / custom address / port / path, plus the negative case) pass.

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-20 18:58:30 -07:00
Kai Ren
7efb6c74d1 Upgrade Docker image to 4.13.1 Coturn version 2026-06-15 12:24:28 +03:00
Kai Ren
1937559e24 Upgrade Docker image to 4.13.0 Coturn version 2026-06-15 10:42:59 +03:00
Kai Ren
e980138252 Upgrade Alpine 3.24 version in Docker image 2026-06-15 10:27:15 +03:00
Kai Ren
fff7aa5ca0 Update Debian "trixie" to 20260610 snapshot in Docker image 2026-06-15 10:25:36 +03:00
Mészáros Mihály
5a90c92e09 Fix missing sqlite3 dependendcy (#1941)
The "sqlite_empty_db" or "sqlite/turndb" Makefile target needs sqlite
cmd.
Without it no turndb created.
2026-06-11 08:28:44 -07:00
Kai Ren
732c249b92 Upgrade Docker image to 4.11.0 Coturn version
- Update Debian "trixie" to 20260518 snapshot in Docker image
2026-05-25 19:45:54 +03:00
Kai Ren
78c1f7c7ce Upgrade Docker image to 4.11.0 Coturn version 2026-05-09 13:12:33 +03:00
Kai Ren
259f0d3c67 Update Debian "trixie" to 20260406 snapshot in Docker image 2026-05-09 13:10:35 +03:00
tyranron
c3a17d06fd Update Alpine to 3.23.4 version to fix CVEs in Docker image
musl
- CVE-2026-6042
- CVE-2026-40200

openssl
- CVE-2026-31790
- CVE-2026-28387
- CVE-2026-28388
- CVE-2026-28389
- CVE-2026-28390
- CVE-2026-31789

zlib
- CVE-2026-22184
- CVE-2026-27171
2026-04-16 12:01:21 +03:00
tyranron
315e185591 Upgrade Docker image to 4.10.0 Coturn version 2026-04-14 11:58:48 +03:00
tyranron
7ffd0300b9 Update Debian "trixie" to 20260406 snapshot in Docker image 2026-04-07 15:58:12 +03:00
Pavel Punsky
69aa0e4401 Keep only NEV_UDP_SOCKET_PER_THREAD network engine (#1849)
Remove the two engine implementations (NEV_UDP_SOCKET_PER_SESSION and
NEV_UDP_SOCKET_PER_ENDPOINT) and all the dispatch/selection logic around
them. NEV_UDP_SOCKET_PER_THREAD is now the sole, unconditional
implementation.

- mainrelay.h: removed _NET_ENG_VERSION enum, typedef, and
net_engine_version / net_engine_version_txt struct fields
- mainrelay.c: removed NE_TYPE_OPT CLI option, set_network_engine(),
per-endpoint branch in print_features(), and all remaining
net_engine_version references
- netengine.c: removed run_udp_listener_thread(),
setup_socket_per_endpoint_udp_listener_servers() (~190 lines),
setup_socket_per_session_udp_listener_servers() (~90 lines); simplified
setup_barriers(), setup_relay_server(), run_general_relay_thread(),
setup_general_relay_servers(), and setup_server() by eliminating all
engine-type conditionals
- turn_admin_server.c: replaced dynamic engine version lookups with
hardcoded values (3 / "UDP thread per CPU core") in CLI and HTTPS status
handlers

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-06 19:26:46 -07:00
Pavel Punsky
6e8dd731a7 Update config and Readme files about deprecated TLSv1/1.1 (#1848)
This commit only updates documentation - the change has been done in
#1693
2026-04-05 20:49:46 -07:00
tyranron
ba571ad795 Update Debian "trixie" to 20260316 snapshot in Docker image 2026-03-17 13:43:57 +02:00
Pavel Punsky
9467af5041 CLI interface is disabled by default (#1830)
cli interface is ON by default which creates a security risk (even
though requires a password) and recommended to be disabled.
Instead of just recommendation, this PR disables CLI by default and now
requires an explicit flag to enable it

If using old configuration or cli arguments to turnserver - it will log
an error message about `--no-cli` being deprecated while doing nothing
(already disabled). This log line will be removed in the future
2026-03-06 18:00:45 -08:00
Pavel Punsky
20d8e38297 Disable reason string in response messages to reduce amplification factor (#1829)
Disable the messages by default - they can be re-enabled using
`--include-reason-string` option

As a result of not sending reason string (which is optional by standard
and provide debugging information for the actual numeric error code)
response message size can be decreased by up to NNN bytes.
2026-03-06 17:59:21 -08:00
tyranron
e4b7f09c00 Upgrade Docker image to 4.9.0 Coturn version 2026-02-24 11:12:50 +02:00
tyranron
8dc340f9be Update Debian "trixie" to 20260202 snapshot in Docker image 2026-02-04 17:12:33 +02:00
tyranron
53c405a712 Update Alpine to 3.23.3 version to fix OpenSSL CVEs in Docker image
- CVE-2025-11187
- CVE-2025-15467
- CVE-2025-15468
- CVE-2025-15469
- CVE-2025-66199
- CVE-2025-68160
- CVE-2025-69418
- CVE-2025-69419
- CVE-2025-69420
- CVE-2025-69421
- CVE-2026-22795
- CVE-2026-22796
2026-01-29 12:42:56 +01:00
tyranron
80e20f58f3 Update Debian "trixie" to 20260112 snapshot in Docker image 2026-01-13 13:10:32 +01:00
tyranron
48fb3dd287 Upgrade Docker image to 4.8.0 Coturn version 2026-01-06 16:48:21 +01:00
tyranron
a7202a72dd Update Debian "trixie" to 20251229 snapshot in Docker image 2025-12-30 12:33:55 +01:00
Pavel Punsky
59921981bb Implement configurable buffer sizes (#1780)
# Description

Replace the hardcoded buffer sizes inside coturn to make them
configurable for different use cases (low bitrate use cases can save
memory and high bitrate use case can avoid congestion) - based on #1089

Add this feature in both sides (listener and relay connections).

# Tests

For now it is only the automated CI tests.
Confirmed with debugger that buffer sizes are set according to the
arguments.
2025-12-25 14:06:28 -08:00
tyranron
6a0b3a648a Update Alpine to 3.23.2 version in Docker image 2025-12-18 14:44:24 +01:00
tyranron
5a6c9f3ce4 Update Debian "trixie" to 20251208 snapshot in Docker image 2025-12-09 12:33:53 +01:00
tyranron
a668d4b4cc Upgrade Alpine to 3.23 version in Docker image 2025-12-04 15:34:15 +01:00
tyranron
60b6e2d816 Update Debian "trixie" to 20251117 snapshot in Docker image 2025-11-18 13:57:46 +01:00
tyranron
e1d3ba8a69 Update Debian "trixie" to 20251103 snapshot in Docker image 2025-11-05 12:04:30 +02:00
tyranron
bc8d0ca36b Update Debian "trixie" to 20251020 snapshot in Docker image 2025-10-22 14:28:29 +03:00