mirror of
https://gitlab.com/apparmor/apparmor.git
synced 2025-03-04 08:24:42 +01:00
utils/aa-unconfined: fix netstat usage, use ss(8) by default
It was reported that converting the netstat command to examine processes bound to ipv6 addresses broke on OpenSUSE due to the version of nettools not supporting the short -4 -6 arguments. This patch switches to use the ss(8) utility from iproute2 by default (if ss is found) as netstat/net-tools is deprecated. Unfortunately, ss's '--family' argument does not accept multiple families, nor does passing '--family' multiple times with different arguments work either, so aa-unconfined invokes ss multiple times to gather the different socket families. It also fixes the invocation of netstat to use the "--protocol inet,inet6" arguments instead, which should return the same results as the short options. This patch provides command line arguments to manually switch using one tool or the other, as well as converting the invocations of ss and netstat to not use a shell, and documents these options in the aa-unconfined man page. Signed-off-by: Steve Beattie <steve@nxnw.org> Acked-by: Christian Boltz <apparmor@cboltz.de> Acked-by: John Johansen <john.johansen@canonical.com>
This commit is contained in:
parent
49fe1f712c
commit
4da5adbce4
2 changed files with 83 additions and 20 deletions
|
@ -1,6 +1,7 @@
|
||||||
#! /usr/bin/python3
|
#! /usr/bin/python3
|
||||||
# ----------------------------------------------------------------------
|
# ----------------------------------------------------------------------
|
||||||
# Copyright (C) 2013 Kshitij Gupta <kgupta8592@gmail.com>
|
# Copyright (C) 2013 Kshitij Gupta <kgupta8592@gmail.com>
|
||||||
|
# Copyright (C) 2016 Canonical, Ltd.
|
||||||
#
|
#
|
||||||
# This program is free software; you can redistribute it and/or
|
# This program is free software; you can redistribute it and/or
|
||||||
# modify it under the terms of version 2 of the GNU General Public
|
# modify it under the terms of version 2 of the GNU General Public
|
||||||
|
@ -15,6 +16,7 @@
|
||||||
import argparse
|
import argparse
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
|
import subprocess
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
import apparmor.aa as aa
|
import apparmor.aa as aa
|
||||||
|
@ -31,6 +33,9 @@ _ = init_translation()
|
||||||
|
|
||||||
parser = argparse.ArgumentParser(description=_("Lists unconfined processes having tcp or udp ports"))
|
parser = argparse.ArgumentParser(description=_("Lists unconfined processes having tcp or udp ports"))
|
||||||
parser.add_argument("--paranoid", action="store_true", help=_("scan all processes from /proc"))
|
parser.add_argument("--paranoid", action="store_true", help=_("scan all processes from /proc"))
|
||||||
|
bin_group = parser.add_mutually_exclusive_group()
|
||||||
|
bin_group.add_argument("--with-ss", action='store_true', help=_("use ss(8) to find listening processes (default)"))
|
||||||
|
bin_group.add_argument("--with-netstat", action='store_true', help=_("use netstat(8) to find listening processes"))
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
|
|
||||||
paranoid = args.paranoid
|
paranoid = args.paranoid
|
||||||
|
@ -39,26 +44,69 @@ aa_mountpoint = aa.check_for_apparmor()
|
||||||
if not aa_mountpoint:
|
if not aa_mountpoint:
|
||||||
raise aa.AppArmorException(_("It seems AppArmor was not started. Please enable AppArmor and try again."))
|
raise aa.AppArmorException(_("It seems AppArmor was not started. Please enable AppArmor and try again."))
|
||||||
|
|
||||||
pids = []
|
|
||||||
if paranoid:
|
|
||||||
pids = list(filter(lambda x: re.search(r"^\d+$", x), aa.get_subdirectories("/proc")))
|
|
||||||
else:
|
|
||||||
regex_tcp_udp = re.compile(r"^(tcp|udp|raw)6?\s+\d+\s+\d+\s+\S+\:(\d+)\s+\S+\:(\*|\d+)\s+(LISTEN|\d+|\s+)\s+(\d+)\/(\S+)")
|
|
||||||
import subprocess
|
|
||||||
if sys.version_info < (3, 0):
|
|
||||||
output = subprocess.check_output("LANG=C netstat -nlp46", shell=True).split("\n")
|
|
||||||
else:
|
|
||||||
#Python3 needs to translate a stream of bytes to string with specified encoding
|
|
||||||
output = str(subprocess.check_output("LANG=C netstat -nlp46", shell=True), encoding='utf8').split("\n")
|
|
||||||
|
|
||||||
|
def get_all_pids():
|
||||||
|
'''Return a set of all pids via walking /proc'''
|
||||||
|
return set(filter(lambda x: re.search(r"^\d+$", x), aa.get_subdirectories("/proc")))
|
||||||
|
|
||||||
|
|
||||||
|
def get_pids_ss():
|
||||||
|
'''Get a set of pids listening on network sockets via ss(8)'''
|
||||||
|
regex_lines = re.compile(r"^(tcp|udp|raw|p_dgr)\s.+\s+users:(?P<users>\(\(.*\)\))$")
|
||||||
|
regex_users_pids = re.compile(r'(\("[^"]+",(pid=)?(\d+),[^)]+\))')
|
||||||
|
|
||||||
|
pids = set()
|
||||||
|
my_env = os.environ.copy()
|
||||||
|
my_env['LANG'] = 'C'
|
||||||
|
my_env['PATH'] = '/bin:/usr/bin:/sbin:/usr/sbin'
|
||||||
|
for family in ['inet', 'inet6', 'link']:
|
||||||
|
cmd = ['ss', '-nlp', '--family', family]
|
||||||
|
if sys.version_info < (3, 0):
|
||||||
|
output = subprocess.check_output(cmd, shell=False, env=my_env).split("\n")
|
||||||
|
else:
|
||||||
|
# Python3 needs to translate a stream of bytes to string with specified encoding
|
||||||
|
output = str(subprocess.check_output(cmd, shell=False, env=my_env), encoding='utf8').split("\n")
|
||||||
|
|
||||||
|
for line in output:
|
||||||
|
match = regex_lines.search(line.strip())
|
||||||
|
if match:
|
||||||
|
users = match.group('users')
|
||||||
|
for (_, _, pid) in regex_users_pids.findall(users):
|
||||||
|
pids.add(pid)
|
||||||
|
return pids
|
||||||
|
|
||||||
|
|
||||||
|
def get_pids_netstat():
|
||||||
|
'''Get a set of pids listening on network sockets via netstat(8)'''
|
||||||
|
regex_tcp_udp = re.compile(r"^(tcp|udp|raw)6?\s+\d+\s+\d+\s+\S+\:(\d+)\s+\S+\:(\*|\d+)\s+(LISTEN|\d+|\s+)\s+(?P<pid>\d+)\/(\S+)")
|
||||||
|
|
||||||
|
cmd = ['netstat', '-nlp', '--protocol', 'inet,inet6']
|
||||||
|
my_env = os.environ.copy()
|
||||||
|
my_env['LANG'] = 'C'
|
||||||
|
my_env['PATH'] = '/bin:/usr/bin:/sbin:/usr/sbin'
|
||||||
|
if sys.version_info < (3, 0):
|
||||||
|
output = subprocess.check_output(cmd, shell=False, env=my_env).split("\n")
|
||||||
|
else:
|
||||||
|
# Python3 needs to translate a stream of bytes to string with specified encoding
|
||||||
|
output = str(subprocess.check_output(cmd, shell=False, env=my_env), encoding='utf8').split("\n")
|
||||||
|
|
||||||
|
pids = set()
|
||||||
for line in output:
|
for line in output:
|
||||||
match = regex_tcp_udp.search(line)
|
match = regex_tcp_udp.search(line)
|
||||||
if match:
|
if match:
|
||||||
pids.append(match.groups()[4])
|
pids.add(match.group('pid'))
|
||||||
# We can safely remove duplicate pid's?
|
return pids
|
||||||
pids = list(map(int, set(pids)))
|
|
||||||
|
|
||||||
for pid in sorted(pids):
|
|
||||||
|
pids = set()
|
||||||
|
if paranoid:
|
||||||
|
pids = get_all_pids()
|
||||||
|
elif args.with_ss or (not args.with_netstat and (os.path.exists('/bin/ss') or os.path.exists('/usr/bin/ss'))):
|
||||||
|
pids = get_pids_ss()
|
||||||
|
else:
|
||||||
|
pids = get_pids_netstat()
|
||||||
|
|
||||||
|
for pid in sorted(map(int, pids)):
|
||||||
try:
|
try:
|
||||||
prog = os.readlink("/proc/%s/exe"%pid)
|
prog = os.readlink("/proc/%s/exe"%pid)
|
||||||
except OSError:
|
except OSError:
|
||||||
|
|
|
@ -27,14 +27,29 @@ not have AppArmor profiles loaded
|
||||||
|
|
||||||
=head1 SYNOPSIS
|
=head1 SYNOPSIS
|
||||||
|
|
||||||
B<aa-unconfined [I<--paranoid>]>
|
B<aa-unconfined [I<--paranoid>] [I<--with-ss> | I<--with-netstat>]>
|
||||||
|
|
||||||
=head1 OPTIONS
|
=head1 OPTIONS
|
||||||
|
|
||||||
B<--paranoid>
|
=over 4
|
||||||
|
|
||||||
Displays all processes from F</proc> filesystem with tcp or udp ports
|
=item B<--paranoid>
|
||||||
that do not have AppArmor profiles loaded.
|
|
||||||
|
Displays all processes from F</proc> filesystem with tcp or udp ports
|
||||||
|
that do not have AppArmor profiles loaded.
|
||||||
|
|
||||||
|
=item B<--with-ss>
|
||||||
|
|
||||||
|
Use the ss(8) command to find processes listening on network sockets
|
||||||
|
(the default).
|
||||||
|
|
||||||
|
=item B<--with-netstat>
|
||||||
|
|
||||||
|
Use the netstat(8) command to find processes listening on network
|
||||||
|
sockets. This is also what aa-unconfined will fall back to when ss(8)
|
||||||
|
is not available.
|
||||||
|
|
||||||
|
=back
|
||||||
|
|
||||||
=head1 DESCRIPTION
|
=head1 DESCRIPTION
|
||||||
|
|
||||||
|
@ -58,7 +73,7 @@ L<https://bugs.launchpad.net/apparmor/+filebug>.
|
||||||
|
|
||||||
=head1 SEE ALSO
|
=head1 SEE ALSO
|
||||||
|
|
||||||
netstat(8), apparmor(7), apparmor.d(5), aa_change_hat(2), and
|
ss(8), netstat(8), apparmor(7), apparmor.d(5), aa_change_hat(2), and
|
||||||
L<http://wiki.apparmor.net>.
|
L<http://wiki.apparmor.net>.
|
||||||
|
|
||||||
=cut
|
=cut
|
||||||
|
|
Loading…
Add table
Reference in a new issue