Consegui fazer isso usando um fragmento. Aqui está um exemplo do código que usei. Provavelmente vou mover esse método para um procedimento armazenado.
Repo.all(MyProj.User,
from u in MyProj.User,
join: un in MyProj.UserNode, on: u.id == un.user_id,
join: nt in fragment("""
(
WITH RECURSIVE node_tree AS (
SELECT *
FROM nodes
WHERE nodes.id = ?
UNION ALL
SELECT n.*
FROM nodes n
INNER JOIN node_tree nt ON nt.parent_id == n.id
)
) SELECT * FROM node_tree
""", ^node_id), on: un.node_id == nt.id
)