skeptic - I need to do a bit more research to remember how I did it! So I will just outline the issues and the general approach I took to fixing them
There are 2 basic protocols involved, one is SIP itself and the other is the real time media protocol that actually carries the voice traffic.
Getting SIP working is relatively easy (although having a dynamic address may cause a few stability issues - theoretically Asterisk can handle it by using the DNS name and dynamic DNS like you described, but I haven't tried it so I don't know how successful it is) Essentially you just need to port forward in the way you would expect.
The voice traffic is the hard bit, because SIP is only used to initiate the session and signal. The voice session is not fixed to well-known ports on either end. There is an Asterisk config file somewhere that defines the port range used, and the recommended range is 10000-20000 (wow!) The way I set it up is to forward all those ports to the core and it worked straight away. It feels uncomfortable, but it is certainly true what someone said on a board somewhere, that if someone is port scanning you, they will scan all the ports so opening 10000 ports isn't really any more exposed than just one. They can't attack the external LAN as long as your router has antispoofing (pretty much most do I think)
I also created a core input rule to cover those ports, but I don't know if that is actually necessary. There is also an Asterisk config file option somewhere that tells it to tell the peer to discover the IP address itself, but embed the internal IP address in the SIP message - i think that option is needed as well.