neiam
/oh-my-ex-oh-ircd
public · Issues · Pulls · Labels · Forks · Compare · Actions success · Badges · Packages
⭐
Log in to mark this repository.
Home page: offer TLS when a proxy terminates it
5e17a59 · Gabriel Morell · 2026-10-27 17:25
Message
{commit_body(@commit)}
Files changed
modified
config/runtime.exs
+8
−0
@@ -54,6 +54,14 @@ if pp = System.get_env("IRC_PROXY_PROTOCOL") do
| 54 | 54 | proxy_protocol: pp in ~w(1 true yes on) |
| 55 | 55 | end |
| 56 | 56 | |
| 57 | +# The port clients reach TLS on when the proxy terminates it in front of | |
| 58 | +# the plain listener (Traefik's `ircds` entrypoint, which forwards to 6667 | |
| 59 | +# with a PROXY header). Only for the home page's connection details; | |
| 60 | +# defaults to 6697 there. | |
| 61 | +if port = System.get_env("IRC_PROXY_TLS_PORT") do | |
| 62 | + config :oh_my_ex_oh_ircd, :proxy_tls_port, String.to_integer(port) | |
| 63 | +end | |
| 64 | + | |
| 57 | 65 | # Host cloaking base domain. Client IPs are shown as `<hash>.<cloak_host>` |
| 58 | 66 | # to non-opers. Explicit `IRC_CLOAK_HOST` wins; otherwise prod defaults to |
| 59 | 67 | # `PHX_HOST` so it's on out of the box. Disable with |
modified
lib/oh_my_ex_oh_ircd_web/live/home_live.ex
+18
−4
@@ -26,8 +26,21 @@ defmodule OhMyExOhIrcdWeb.HomeLive do
| 26 | 26 | listener_cfg = Application.get_env(:oh_my_ex_oh_ircd, OhMyExOhIrcd.IRC.Listener, []) |
| 27 | 27 | tls_cfg = Application.get_env(:oh_my_ex_oh_ircd, OhMyExOhIrcd.IRC.TLSListener, []) |
| 28 | 28 | port = Keyword.get(listener_cfg, :port, 6667) |
| 29 | − tls_enabled? = Keyword.get(tls_cfg, :enabled, false) | |
| 30 | − tls_port = Keyword.get(tls_cfg, :port, 6697) | |
| 29 | + | |
| 30 | + # TLS is either our own listener (ACME certificates, standalone) or the | |
| 31 | + # proxy in front of the plain one: behind PROXY protocol the pod speaks | |
| 32 | + # plaintext, and Traefik terminates TLS on its own port. | |
| 33 | + {tls_enabled?, tls_port, tls_via} = | |
| 34 | + cond do | |
| 35 | + Keyword.get(tls_cfg, :enabled, false) -> | |
| 36 | + {true, Keyword.get(tls_cfg, :port, 6697), "ACME"} | |
| 37 | + | |
| 38 | + Keyword.get(listener_cfg, :proxy_protocol, false) -> | |
| 39 | + {true, Application.get_env(:oh_my_ex_oh_ircd, :proxy_tls_port, 6697), "proxy"} | |
| 40 | + | |
| 41 | + true -> | |
| 42 | + {false, Keyword.get(tls_cfg, :port, 6697), nil} | |
| 43 | + end | |
| 31 | 44 | |
| 32 | 45 | # Prefer the TLS port in the snippets when it's available — irssi / |
| 33 | 46 | # weechat just need a flag flip. The plain port is still shown in the |
@@ -41,6 +54,7 @@ defmodule OhMyExOhIrcdWeb.HomeLive do
| 41 | 54 | |> assign(:port, port) |
| 42 | 55 | |> assign(:tls_enabled, tls_enabled?) |
| 43 | 56 | |> assign(:tls_port, tls_port) |
| 57 | + |> assign(:tls_via, tls_via) | |
| 44 | 58 | |> assign(:allow_anonymous, Keyword.get(listener_cfg, :allow_anonymous, false)) |
| 45 | 59 | |> assign(:channel_count, Stats.channel_count()) |
| 46 | 60 | |> assign(:user_count, Stats.user_count()) |
@@ -117,10 +131,10 @@ defmodule OhMyExOhIrcdWeb.HomeLive do
| 117 | 131 | <span class="ml-2 text-base-content">{@server_name}:{@port}</span> |
| 118 | 132 | </p> |
| 119 | 133 | <%= if @tls_enabled do %> |
| 120 | − <p> | |
| 134 | + <p id="connect-tls"> | |
| 121 | 135 | <span class="text-base-content/60">Server (TLS)</span> |
| 122 | 136 | <span class="ml-2 text-base-content">{@server_name}:{@tls_port}</span> |
| 123 | − <span class="badge badge-success badge-xs align-middle ml-2">ACME</span> | |
| 137 | + <span class="badge badge-success badge-xs align-middle ml-2">{@tls_via}</span> | |
| 124 | 138 | </p> |
| 125 | 139 | <% end %> |
| 126 | 140 | <%= if @allow_anonymous do %> |
added
test/oh_my_ex_oh_ircd_web/live/home_live_test.exs
+75
−0
@@ -0,0 +1,75 @@
| 1 | +defmodule OhMyExOhIrcdWeb.HomeLiveTest do | |
| 2 | + @moduledoc """ | |
| 3 | + The home page's connection details offer TLS wherever a client can get | |
| 4 | + it: our own listener (ACME), or the proxy in front of the plain one when | |
| 5 | + the listener expects PROXY protocol (Traefik terminates TLS there). | |
| 6 | + | |
| 7 | + Not async: it changes application env the whole node reads. | |
| 8 | + """ | |
| 9 | + use OhMyExOhIrcdWeb.ConnCase, async: false | |
| 10 | + | |
| 11 | + import Phoenix.LiveViewTest | |
| 12 | + | |
| 13 | + @app :oh_my_ex_oh_ircd | |
| 14 | + | |
| 15 | + setup do | |
| 16 | + saved = | |
| 17 | + for key <- [OhMyExOhIrcd.IRC.Listener, OhMyExOhIrcd.IRC.TLSListener, :proxy_tls_port], | |
| 18 | + do: {key, Application.fetch_env(@app, key)} | |
| 19 | + | |
| 20 | + on_exit(fn -> | |
| 21 | + for {key, value} <- saved do | |
| 22 | + case value do | |
| 23 | + {:ok, v} -> Application.put_env(@app, key, v) | |
| 24 | + :error -> Application.delete_env(@app, key) | |
| 25 | + end | |
| 26 | + end | |
| 27 | + end) | |
| 28 | + end | |
| 29 | + | |
| 30 | + defp listener(opts) do | |
| 31 | + base = Application.get_env(@app, OhMyExOhIrcd.IRC.Listener, []) | |
| 32 | + Application.put_env(@app, OhMyExOhIrcd.IRC.Listener, Keyword.merge(base, opts)) | |
| 33 | + end | |
| 34 | + | |
| 35 | + test "plain only: no TLS line", %{conn: conn} do | |
| 36 | + listener(proxy_protocol: false) | |
| 37 | + Application.put_env(@app, OhMyExOhIrcd.IRC.TLSListener, enabled: false) | |
| 38 | + | |
| 39 | + {:ok, lv, _} = live(conn, ~p"/") | |
| 40 | + | |
| 41 | + refute has_element?(lv, "#connect-tls") | |
| 42 | + end | |
| 43 | + | |
| 44 | + test "behind the proxy: TLS on 6697 by default, and the snippets use it", %{conn: conn} do | |
| 45 | + listener(proxy_protocol: true) | |
| 46 | + Application.put_env(@app, OhMyExOhIrcd.IRC.TLSListener, enabled: false) | |
| 47 | + Application.delete_env(@app, :proxy_tls_port) | |
| 48 | + | |
| 49 | + {:ok, lv, html} = live(conn, ~p"/") | |
| 50 | + | |
| 51 | + assert has_element?(lv, "#connect-tls", ":6697") | |
| 52 | + assert has_element?(lv, "#connect-tls", "proxy") | |
| 53 | + assert html =~ "-tls -tls_verify" | |
| 54 | + end | |
| 55 | + | |
| 56 | + test "behind the proxy: the port IRC_PROXY_TLS_PORT names", %{conn: conn} do | |
| 57 | + listener(proxy_protocol: true) | |
| 58 | + Application.put_env(@app, OhMyExOhIrcd.IRC.TLSListener, enabled: false) | |
| 59 | + Application.put_env(@app, :proxy_tls_port, 7000) | |
| 60 | + | |
| 61 | + {:ok, lv, _} = live(conn, ~p"/") | |
| 62 | + | |
| 63 | + assert has_element?(lv, "#connect-tls", ":7000") | |
| 64 | + end | |
| 65 | + | |
| 66 | + test "our own TLS listener wins over the proxy", %{conn: conn} do | |
| 67 | + listener(proxy_protocol: true) | |
| 68 | + Application.put_env(@app, OhMyExOhIrcd.IRC.TLSListener, enabled: true, port: 6698) | |
| 69 | + | |
| 70 | + {:ok, lv, _} = live(conn, ~p"/") | |
| 71 | + | |
| 72 | + assert has_element?(lv, "#connect-tls", ":6698") | |
| 73 | + assert has_element?(lv, "#connect-tls", "ACME") | |
| 74 | + end | |
| 75 | +end |
Parents: 96a50ad