missing / in front of "ip"
Why do not use directly RouterOS script instead of install python just for repeat 2 lines???
# "pinhole name/comment"= "outside_port", "protocol", "inside_host", "inside_port"
{ :local rules { "Joe web server"= "80", "tcp", "192.168.1.35", "80" \
; "trusted inside SSH"= "1999", "tcp", "192.168.1.40", "22" }
:local "inside_subnet" "192.168.15.0/24"
:local "build_hairpins" true ; # set this if you want the inside hairpin rules built as well
:put "\1Bc\r\n/ip firewall nat"
foreach comment,map in=$rules do={
:put "add action=dst-nat chain=dstnat in-interface-list=WAN dst-port=$($map->0) protocol=$($map->1) \
to-addresses=$($map->2) to-ports=$($map->3) comment=\"$comment\""
:if ($"build_hairpins") do={
:put "add action=dst-nat chain=dstnat dst-address-list=WAN dst-port=$($map->0) protocol=$($map->1) \
to-addresses=$($map->2) to-ports=$($map->3) src-address=$"inside_subnet" comment=\"$comment hairpin\""}}}
Ignoring comments and whitespace are also less line of code
I prefer to put each hairpin under the relative rule, instead to separate rules and hairpin.
I prefer also to put only one time "/ip firewall nat" instead of repeating it continuosly.
But the behaviour of your python script can be exactly copied.
Your same behaviour, except the added fix for "ip":
# "pinhole name/comment"= "outside_port", "protocol", "inside_host", "inside_port"
{ :local rules { "Joe web server"= "80", "tcp", "192.168.1.35", "80" \
; "trusted inside SSH"= "1999", "tcp", "192.168.1.40", "22" }
:local "inside_subnet" "192.168.15.0/24"
:local "build_hairpins" true ; # set this if you want the inside hairpin rules built as well
foreach comment,map in=$rules do={
:put "/ip firewall nat add comment=\"$comment\" action=dst-nat chain=dstnat protocol=$($map->1) \
dst-port=$($map->0) in-interface-list=WAN to-addresses=$($map->2) to-ports=$($map->3)"}
:if ($"build_hairpins") do={foreach comment,map in=$rules do={
:put "/ip firewall nat add action=dst-nat chain=dstnat dst-address-list=WAN protocol=$($map->1) dst-port=$($map->0) \
src-address=$"inside_subnet" to-addresses=$($map->2) to-ports=$($map->3) comment=\"$comment hairpin\""}}}