Validate rogue DHCP servers
This adds two validations that check for unexpected DHCP servers in the networks used for hardware introspection and provisioning. Both validations depend on a file that sends a DHCP request for each interface specified in its arguments and fails if there are any responses. Closes-Bug: #1620332 Change-Id: I22d4bf31e8f528e345b1a0a3ec972beea13d4f52
This commit is contained in:
parent
2445041593
commit
0a608f83bd
|
@ -0,0 +1,25 @@
|
|||
---
|
||||
- hosts: undercloud
|
||||
become: true
|
||||
vars:
|
||||
metadata:
|
||||
name: DHCP on the Introspection Network
|
||||
description: >
|
||||
An unexpected DHCP server on the network used for node
|
||||
introspection can cause some nodes to not be inspected.
|
||||
|
||||
This validations checks for the DHCP responses on the
|
||||
interface specified in ironic-inspector.conf.
|
||||
groups:
|
||||
- pre-introspection
|
||||
tasks:
|
||||
- name: Install scapy
|
||||
pip: name=scapy state=present virtualenv=/tmp/validations-venv
|
||||
- name: Look up the introspection interface
|
||||
ini: path=/etc/ironic-inspector/inspector.conf section=firewall key=dnsmasq_interface
|
||||
register: interface
|
||||
- name: Look up the introspection interface from the deprecated option
|
||||
ini: path=/etc/ironic-inspector/inspector.conf section=discoverd key=dnsmasq_interface
|
||||
register: interface_deprecated
|
||||
- name: Look for rogue DHCP servers
|
||||
script: files/rogue_dhcp.py {{ interface.value or interface_deprecated.value or 'br-ctlplane' }}
|
|
@ -0,0 +1,24 @@
|
|||
---
|
||||
- hosts: undercloud
|
||||
become: true
|
||||
vars:
|
||||
metadata:
|
||||
name: DHCP on the Provisioning Network
|
||||
description: >
|
||||
An unexpected DHCP server on the provisioning network can
|
||||
cause problems with deploying the Ironic nodes.
|
||||
|
||||
This validation checks for DHCP responses on the undercloud's
|
||||
provisioning interface (eth1 by default) and fails if there
|
||||
are any.
|
||||
groups:
|
||||
- pre-deployment
|
||||
tasks:
|
||||
- name: Get the path of tripleo undercloud config file
|
||||
hiera: name="tripleo_undercloud_conf_file"
|
||||
- name: Gather undercloud.conf values
|
||||
undercloud_conf: undercloud_conf_path={{ tripleo_undercloud_conf_file }}
|
||||
- name: Install scapy
|
||||
pip: name=scapy state=present virtualenv=/tmp/validations-venv
|
||||
- name: Look for DHCP responses
|
||||
script: files/rogue_dhcp.py {{ undercloud_conf.DEFAULT.local_interface|default('eth1') }}
|
|
@ -0,0 +1,62 @@
|
|||
#!/tmp/validations-venv/bin/python
|
||||
|
||||
# -*- coding: utf-8 -*-
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
# implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
# Disable scapy's warning to stderr:
|
||||
import logging
|
||||
import sys
|
||||
logging.getLogger("scapy.runtime").setLevel(logging.ERROR)
|
||||
|
||||
|
||||
from scapy.all import BOOTP
|
||||
from scapy.all import conf
|
||||
from scapy.all import DHCP
|
||||
from scapy.all import Ether
|
||||
from scapy.all import get_if_raw_hwaddr
|
||||
from scapy.all import IP
|
||||
from scapy.all import srp
|
||||
from scapy.all import UDP
|
||||
|
||||
|
||||
def find_dhcp_servers(timeout_sec, interface):
|
||||
conf.checkIPaddr = False
|
||||
fam, hw = get_if_raw_hwaddr(interface)
|
||||
dhcp_discover = (Ether(dst="ff:ff:ff:ff:ff:ff") /
|
||||
IP(src="0.0.0.0", dst="255.255.255.255") /
|
||||
UDP(sport=68, dport=67) /
|
||||
BOOTP(chaddr=hw) /
|
||||
DHCP(options=[("message-type", "discover"), "end"]))
|
||||
ans, unans = srp(dhcp_discover, multi=True,
|
||||
timeout=timeout_sec, verbose=False)
|
||||
|
||||
return [(unicode(packet[1][IP].src), packet[1][Ether].src)
|
||||
for packet in ans]
|
||||
|
||||
|
||||
def main():
|
||||
dhcp_servers = []
|
||||
for interface in sys.argv[1:]:
|
||||
dhcp_servers.extend(find_dhcp_servers(30, interface))
|
||||
if dhcp_servers:
|
||||
sys.stderr.write('Found {} DHCP servers:'.format(len(dhcp_servers)))
|
||||
for ip, mac in dhcp_servers:
|
||||
sys.stderr.write("\n* {} ({})".format(ip, mac))
|
||||
sys.exit(1)
|
||||
else:
|
||||
print("No DHCP servers found.")
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
Loading…
Reference in New Issue