First of all, I feel the need to state that a NAT itself is not a firewall (or a firewall-replacement), even though it’s commonly mistaken as one.
As far as I’m aware, syncthing currently has three NAT/Firewall traversal mechanisms:
Automatic port forwarding (UPnP, maybe NAT-PMP(?))
The first mechanism simply asks the router to forward one port to the computer, to allow inbound connections. The router needs to accept these forwards.
The hole-punching mechanisms can work without approval by the router. Those work on the assumption, that a NAT/firewall is configured to allow outbound connections, but not inbound. In this setting, when both devices are behind similar firewalls/NATs, you can simultaneously make outbound connection attempts from both devices to each other. This causes the firewall on both sides to detect an (approved) outgoing connection which will then unblock the flow of data, enabling an actual connection handshake.
The technical juice is a bit more complicated, as you need someone to coordinate the whole thing - both sides need to know the IP addresses (and ports) of the other peer first, which is usually exchanged through a third intermediate server. In case of NAT, knowledge about the topology and NAT-type is also useful for the application, for example to predict port mappings used by the NAT. These things are handled by protocols such as STUN.