mirror of
https://github.com/ansible-collections/community.general.git
synced 2024-09-14 20:13:21 +02:00
listen_ports_facts: Added support for ss (#3708)
This commit is contained in:
parent
98cca3c19c
commit
245cee0ece
3 changed files with 141 additions and 26 deletions
|
@ -0,0 +1,3 @@
|
|||
---
|
||||
minor_changes:
|
||||
- listen_ports_facts - add support for ``ss`` command besides ``netstat`` (https://github.com/ansible-collections/community.general/pull/3708).
|
|
@ -13,11 +13,25 @@ module: listen_ports_facts
|
|||
author:
|
||||
- Nathan Davison (@ndavison)
|
||||
description:
|
||||
- Gather facts on processes listening on TCP and UDP ports using netstat command.
|
||||
- Gather facts on processes listening on TCP and UDP ports using the C(netstat) or C(ss) commands.
|
||||
- This module currently supports Linux only.
|
||||
requirements:
|
||||
- netstat
|
||||
- netstat or ss
|
||||
short_description: Gather facts on processes listening on TCP and UDP ports.
|
||||
notes:
|
||||
- |
|
||||
C(ss) returns all processes for each listen address and port.
|
||||
This plugin will return each of them, so multiple entries for the same listen address and port are likely in results.
|
||||
options:
|
||||
command:
|
||||
description:
|
||||
- Override which command to use for fetching listen ports.
|
||||
- 'By default module will use first found supported command on the system (in alphanumerical order).'
|
||||
type: str
|
||||
choices:
|
||||
- netstat
|
||||
- ss
|
||||
version_added: 4.1.0
|
||||
'''
|
||||
|
||||
EXAMPLES = r'''
|
||||
|
@ -181,10 +195,87 @@ def netStatParse(raw):
|
|||
return results
|
||||
|
||||
|
||||
def main():
|
||||
def ss_parse(raw):
|
||||
results = list()
|
||||
regex_conns = re.compile(pattern=r'\[?(.+?)\]?:([0-9]+)')
|
||||
regex_pid = re.compile(pattern=r'"(.*?)",pid=(\d+)')
|
||||
|
||||
lines = raw.splitlines()
|
||||
|
||||
if len(lines) == 0 or not lines[0].startswith('Netid '):
|
||||
# unexpected stdout from ss
|
||||
raise EnvironmentError('Unknown stdout format of `ss`: {0}'.format(raw))
|
||||
|
||||
# skip headers (-H arg is not present on e.g. Ubuntu 16)
|
||||
lines = lines[1:]
|
||||
|
||||
for line in lines:
|
||||
cells = line.split(None, 6)
|
||||
try:
|
||||
if len(cells) == 6:
|
||||
# no process column, e.g. due to unprivileged user
|
||||
process = str()
|
||||
protocol, state, recv_q, send_q, local_addr_port, peer_addr_port = cells
|
||||
else:
|
||||
protocol, state, recv_q, send_q, local_addr_port, peer_addr_port, process = cells
|
||||
except ValueError:
|
||||
# unexpected stdout from ss
|
||||
raise EnvironmentError(
|
||||
'Expected `ss` table layout "Netid, State, Recv-Q, Send-Q, Local Address:Port, Peer Address:Port" and optionally "Process", \
|
||||
but got something else: {0}'.format(line)
|
||||
)
|
||||
|
||||
conns = regex_conns.search(local_addr_port)
|
||||
pids = regex_pid.findall(process)
|
||||
if conns is None and pids is None:
|
||||
continue
|
||||
|
||||
if pids is None:
|
||||
# likely unprivileged user, so add empty name & pid
|
||||
# as we do in netstat logic to be consistent with output
|
||||
pids = [(str(), 0)]
|
||||
|
||||
address = conns.group(1)
|
||||
port = conns.group(2)
|
||||
for name, pid in pids:
|
||||
result = {
|
||||
'pid': int(pid),
|
||||
'address': address,
|
||||
'port': int(port),
|
||||
'protocol': protocol,
|
||||
'name': name
|
||||
}
|
||||
results.append(result)
|
||||
return results
|
||||
|
||||
|
||||
def main():
|
||||
commands_map = {
|
||||
'netstat': {
|
||||
'args': [
|
||||
'-p',
|
||||
'-l',
|
||||
'-u',
|
||||
'-n',
|
||||
'-t',
|
||||
],
|
||||
'parse_func': netStatParse
|
||||
},
|
||||
'ss': {
|
||||
'args': [
|
||||
'-p',
|
||||
'-l',
|
||||
'-u',
|
||||
'-n',
|
||||
'-t',
|
||||
],
|
||||
'parse_func': ss_parse
|
||||
},
|
||||
}
|
||||
module = AnsibleModule(
|
||||
argument_spec={},
|
||||
argument_spec=dict(
|
||||
command=dict(type='str', choices=list(sorted(commands_map)))
|
||||
),
|
||||
supports_check_mode=True,
|
||||
)
|
||||
|
||||
|
@ -220,18 +311,34 @@ def main():
|
|||
}
|
||||
|
||||
try:
|
||||
netstat_cmd = module.get_bin_path('netstat', True)
|
||||
command = None
|
||||
bin_path = None
|
||||
if module.params['command'] is not None:
|
||||
command = module.params['command']
|
||||
bin_path = module.get_bin_path(command, required=True)
|
||||
else:
|
||||
for c in sorted(commands_map):
|
||||
bin_path = module.get_bin_path(c, required=False)
|
||||
if bin_path is not None:
|
||||
command = c
|
||||
break
|
||||
|
||||
if bin_path is None:
|
||||
raise EnvironmentError(msg='Unable to find any of the supported commands in PATH: {0}'.format(", ".join(sorted(commands_map))))
|
||||
|
||||
# which ports are listening for connections?
|
||||
rc, stdout, stderr = module.run_command([netstat_cmd, '-plunt'])
|
||||
args = commands_map[command]['args']
|
||||
rc, stdout, stderr = module.run_command([bin_path] + args)
|
||||
if rc == 0:
|
||||
netstatOut = netStatParse(stdout)
|
||||
for p in netstatOut:
|
||||
parse_func = commands_map[command]['parse_func']
|
||||
results = parse_func(stdout)
|
||||
|
||||
for p in results:
|
||||
p['stime'] = getPidSTime(p['pid'])
|
||||
p['user'] = getPidUser(p['pid'])
|
||||
if p['protocol'] == 'tcp':
|
||||
if p['protocol'].startswith('tcp'):
|
||||
result['ansible_facts']['tcp_listen'].append(p)
|
||||
elif p['protocol'] == 'udp':
|
||||
elif p['protocol'].startswith('udp'):
|
||||
result['ansible_facts']['udp_listen'].append(p)
|
||||
except (KeyError, EnvironmentError) as e:
|
||||
module.fail_json(msg=to_native(e))
|
||||
|
|
|
@ -9,30 +9,25 @@
|
|||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
|
||||
- name: install netstat and netcat on deb
|
||||
apt:
|
||||
name: "{{ item }}"
|
||||
state: latest
|
||||
with_items:
|
||||
ansible.builtin.package:
|
||||
name:
|
||||
- net-tools
|
||||
- netcat
|
||||
state: latest
|
||||
when: ansible_os_family == "Debian"
|
||||
|
||||
- name: install netstat and netcat on rh < 7
|
||||
yum:
|
||||
name: "{{ item }}"
|
||||
state: latest
|
||||
with_items:
|
||||
ansible.builtin.package:
|
||||
name:
|
||||
- net-tools
|
||||
- nc.x86_64
|
||||
state: latest
|
||||
when: ansible_os_family == "RedHat" and ansible_distribution_major_version|int < 7
|
||||
|
||||
- name: install netstat and netcat on rh >= 7
|
||||
yum:
|
||||
name: "{{ item }}"
|
||||
- name: install netcat on rh >= 7
|
||||
ansible.builtin.package:
|
||||
name: 'nmap-ncat'
|
||||
state: latest
|
||||
with_items:
|
||||
- net-tools
|
||||
- nmap-ncat
|
||||
when: ansible_os_family == "RedHat" and ansible_distribution_major_version|int >= 7
|
||||
|
||||
- name: start UDP server on port 5555
|
||||
|
@ -63,6 +58,16 @@
|
|||
listen_ports_facts:
|
||||
when: ansible_os_family == "RedHat" or ansible_os_family == "Debian"
|
||||
|
||||
- name: Gather listening ports facts explicitly via netstat
|
||||
listen_ports_facts:
|
||||
command: 'netstat'
|
||||
when: (ansible_os_family == "RedHat" and ansible_distribution_major_version|int < 7) or ansible_os_family == "Debian"
|
||||
|
||||
- name: Gather listening ports facts explicitly via ss
|
||||
listen_ports_facts:
|
||||
command: 'ss'
|
||||
when: ansible_os_family == "RedHat" and ansible_distribution_major_version|int >= 7
|
||||
|
||||
- name: check for ansible_facts.udp_listen exists
|
||||
assert:
|
||||
that: ansible_facts.udp_listen is defined
|
||||
|
|
Loading…
Reference in a new issue