diff --git a/playbooks/roles/iptables/README.rst b/playbooks/roles/iptables/README.rst
index b7368cd88a..cc2976fc4b 100644
--- a/playbooks/roles/iptables/README.rst
+++ b/playbooks/roles/iptables/README.rst
@@ -50,14 +50,40 @@ Install and configure iptables
 
    A list of public UDP ports to open.
 
+.. zuul:rolevar:: iptables_rules
+   :default: []
+
+   A list of iptables ingress rules.  Each item is a string
+   containing the iptables command line options for the rule. These
+   will be expanded to cover IPv4 and IPv6.
+
 .. zuul:rolevar:: iptables_rules_v4
    :default: []
 
-   A list of iptables v4 rules.  Each item is a string containing the
-   iptables command line options for the rule.
+   A list of iptables v4 ingress rules.  Each item is a string
+   containing the iptables command line options for the rule.
 
 .. zuul:rolevar:: iptables_rules_v6
    :default: []
 
-   A list of iptables v6 rules.  Each item is a string containing the
-   iptables command line options for the rule.
+   A list of iptables v6 ingress rules.  Each item is a string
+   containing the iptables command line options for the rule.
+
+.. zuul:rolevar:: iptables_egress_rules
+   :default: []
+
+   A list of iptables egress rules.  Each item is a string
+   containing the iptables command line options for the rule. These
+   will be expanded to cover IPv4 and IPv6.
+
+.. zuul:rolevar:: iptables_egress_rules_v4
+   :default: []
+
+   A list of iptables v4 egress rules.  Each item is a string
+   containing the iptables command line options for the rule.
+
+.. zuul:rolevar:: iptables_egress_rules_v6
+   :default: []
+
+   A list of iptables v6 egress rules.  Each item is a string
+   containing the iptables command line options for the rule.
diff --git a/playbooks/roles/iptables/defaults/main.yaml b/playbooks/roles/iptables/defaults/main.yaml
index 8752607609..bc94938924 100644
--- a/playbooks/roles/iptables/defaults/main.yaml
+++ b/playbooks/roles/iptables/defaults/main.yaml
@@ -1,4 +1,7 @@
 iptables_allowed_hosts: []
+iptables_egress_rules: []
+iptables_egress_rules_v4: '{{ iptables_egress_rules }}'
+iptables_egress_rules_v6: '{{ iptables_egress_rules }}'
 iptables_public_ports: []
 iptables_public_tcp_ports: '{{ iptables_public_ports }}'
 iptables_public_udp_ports: '{{ iptables_public_ports }}'
diff --git a/playbooks/roles/iptables/templates/rules.v4.j2 b/playbooks/roles/iptables/templates/rules.v4.j2
index 9fd3e78d18..c6aabda6b5 100644
--- a/playbooks/roles/iptables/templates/rules.v4.j2
+++ b/playbooks/roles/iptables/templates/rules.v4.j2
@@ -3,6 +3,7 @@
 :FORWARD DROP [0:0]
 :OUTPUT ACCEPT [0:0]
 :openstack-INPUT - [0:0]
+:openstack-OUTPUT - [0:0]
 -A INPUT -j openstack-INPUT
 -A openstack-INPUT -i lo -j ACCEPT
 -A openstack-INPUT -p icmp --icmp-type any -j ACCEPT
@@ -18,7 +19,7 @@
 {% for port in iptables_public_udp_ports -%}
 -A openstack-INPUT -m udp -p udp --dport {{ port }} -j ACCEPT
 {% endfor -%}
-# Per-host rules
+# Per-host ingress rules
 {% for rule in iptables_rules_v4 -%}
 -A openstack-INPUT {{ rule }}
 {% endfor -%}
@@ -35,4 +36,10 @@
 {% endfor -%}
 {% endfor -%}
 -A openstack-INPUT -j REJECT --reject-with icmp-admin-prohibited
+# Egress filtering
+-A OUTPUT -j openstack-OUTPUT
+# Per-host egress rules
+{% for rule in iptables_egress_rules_v4 -%}
+-A openstack-OUTPUT {{ rule }}
+{% endfor -%}
 COMMIT
diff --git a/playbooks/roles/iptables/templates/rules.v6.j2 b/playbooks/roles/iptables/templates/rules.v6.j2
index d5a792b9df..64671e09cf 100644
--- a/playbooks/roles/iptables/templates/rules.v6.j2
+++ b/playbooks/roles/iptables/templates/rules.v6.j2
@@ -3,6 +3,7 @@
 :FORWARD DROP [0:0]
 :OUTPUT ACCEPT [0:0]
 :openstack-INPUT - [0:0]
+:openstack-OUTPUT - [0:0]
 -A INPUT -j openstack-INPUT
 -A openstack-INPUT -i lo -j ACCEPT
 -A openstack-INPUT -p icmpv6 -j ACCEPT
@@ -17,7 +18,7 @@
 {% for port in iptables_public_udp_ports -%}
 -A openstack-INPUT -m udp -p udp --dport {{ port }} -j ACCEPT
 {% endfor -%}
-# Per-host rules
+# Per-host ingress rules
 {% for rule in iptables_rules_v6 -%}
 -A openstack-INPUT {{ rule }}
 {% endfor -%}
@@ -34,4 +35,10 @@
 {% endfor -%}
 {% endfor -%}
 -A openstack-INPUT -j REJECT --reject-with icmp6-adm-prohibited
+# Egress filtering
+-A OUTPUT -j openstack-OUTPUT
+# Per-host egress rules
+{% for rule in iptables_egress_rules_v6 -%}
+-A openstack-OUTPUT {{ rule }}
+{% endfor -%}
 COMMIT
diff --git a/playbooks/zuul/templates/group_vars/all.yaml.j2 b/playbooks/zuul/templates/group_vars/all.yaml.j2
index 1f077ff3f9..cb80bcbd6a 100644
--- a/playbooks/zuul/templates/group_vars/all.yaml.j2
+++ b/playbooks/zuul/templates/group_vars/all.yaml.j2
@@ -9,3 +9,6 @@ bastion_ipv6: {{ bastion_ipv6 }}
 {% endif %}
 bastion_public_key: {{ bastion_public_key }}
 iptables_test_public_tcp_ports: {{ iptables_test_public_tcp_ports }}
+iptables_egress_rules:
+  - -o lo -j ACCEPT
+  - -p tcp -m tcp --dport 25 --tcp-flags FIN,SYN,RST,ACK SYN -j REJECT --reject-with tcp-reset
diff --git a/testinfra/util.py b/testinfra/util.py
index 127e581dcc..1bc81b0070 100644
--- a/testinfra/util.py
+++ b/testinfra/util.py
@@ -89,6 +89,7 @@ def get_ips(value, family=None):
 def verify_iptables(host):
     rules = host.iptables.rules()
     rules = [x.strip() for x in rules]
+    print('Comparing against rules:\n%s' % rules)
 
     needed_rules = [
         '-P INPUT ACCEPT',
@@ -100,6 +101,7 @@ def verify_iptables(host):
         '-A openstack-INPUT -p icmp -m icmp --icmp-type any -j ACCEPT',
         '-A openstack-INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT',
         '-A openstack-INPUT -p tcp -m state --state NEW -m tcp --dport 22 -j ACCEPT',
+        '-A openstack-OUTPUT -p tcp -m tcp --dport 25 --tcp-flags FIN,SYN,RST,ACK SYN -j REJECT --reject-with tcp-reset',
         '-A openstack-INPUT -j REJECT --reject-with icmp-admin-prohibited'
     ]
     for rule in needed_rules: