A WebRTC connection involves three entities:
- the two peers (the offerer and the answerer), which are usually web browsers but may also be servers;
- the signalling server, which is usually the web server the peers are interacting with.
When the peers decide to connect to each other, they exchange a set of messages (the offer, the answer, the ICE candidates, collectively known as SDP messages). These messages contain various data, such as information about supported codecs and cryptographic keys, and in particular they contain the IP addresses of the peers. Since these messages are communicated to the signalling server, the signalling server may use them to learn about your IP addresses.
The peers decide which of their IP addresses to include in the SDP messages. Ideally, when using a VPN, only the VPN's IP addresses should be included. However, since the SDP is generated by the browser, doing that correctly requires cooperation between the VPN and the web browser, something that is difficult to do properly.
To work around the issue, recent browsers do not include local (RFC 1918) addresses in the SDP. This works fine if your host only has RFC 1918 addresses, but fails if it has global addresses, for example because it has IPv6 connectivity. You may how your browser behaves with the Trickle-ICE script, which displays the addresses communicated by your browser to a signalling server.
In my opinion, the only reliable way to keep your IP private is to run your browser in a virtual machine that only knows about your VPN address. Any other workaround is just too error-prone to be reliable.