Skip to content
This repository has been archived by the owner on Sep 16, 2022. It is now read-only.

Commit

Permalink
Merge pull request #791 from GreatFruitOmsk/750-cve-kernel-meta-packages
Browse files Browse the repository at this point in the history
CVE list: Show only the currently running kernel meta-package
  • Loading branch information
a-martynovich authored Mar 19, 2020
2 parents a7f2659 + 70596a1 commit 06f7d05
Show file tree
Hide file tree
Showing 8 changed files with 100 additions and 23 deletions.
12 changes: 7 additions & 5 deletions backend/device_registry/api_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -102,12 +102,13 @@ def post(self, request, *args, **kwargs):
device.set_deb_packages(deb_packages['packages'], os_release)
kernel_deb_package = data.get('kernel_package')
if kernel_deb_package:
device.kernel_deb_package = DebPackage.objects.get(name=kernel_deb_package['name'],
version=kernel_deb_package['version'],
arch=kernel_deb_package['arch'],
os_release_codename=os_release['codename'])
device.kernel_deb_package = device.deb_packages.get(name=kernel_deb_package['name'],
version=kernel_deb_package['version'],
arch=kernel_deb_package['arch'],
os_release_codename=os_release['codename'])
else:
device.kernel_deb_package = None
device.reboot_required = data.get('reboot_required')
device.cpu = data.get('cpu', {})
device.os_release = os_release
device.mysql_root_access = data.get('mysql_root_access')
Expand Down Expand Up @@ -154,7 +155,8 @@ def post(self, request, *args, **kwargs):
device.update_trust_score = True
device.save(update_fields=['last_ping', 'agent_version', 'audit_files', 'deb_packages_hash',
'update_trust_score', 'os_release', 'auto_upgrades',
'mysql_root_access', 'cpu', 'kernel_deb_package', 'default_password_users'])
'mysql_root_access', 'cpu', 'kernel_deb_package', 'reboot_required',
'default_password_users'])
# Un-snooze recommended actions which were "Fixed" (i.e. snoozed until next ping)
device.recommendedactionstatus_set.filter(status=RecommendedAction.Status.SNOOZED_UNTIL_PING) \
.update(status=RecommendedAction.Status.AFFECTED)
Expand Down
11 changes: 7 additions & 4 deletions backend/device_registry/celery_tasks/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import redis

from device_registry.models import Device, Vulnerability, DebPackage, DEBIAN_SUITES, UBUNTU_SUITES
from device_registry.models import UBUNTU_KERNEL_PACKAGES_RE_PATTERN
from profile_page.models import Profile

logger = logging.getLogger('django')
Expand Down Expand Up @@ -72,6 +73,7 @@ def update_packages_vulnerabilities(batch):
if vuln.is_vulnerable(package.source_version) and vuln.fix_available:
relations.append(Relation(debpackage_id=package.id, vulnerability_id=vuln.id))
counter += 1
# TODO: Execute ORM requests below in one transaction.
Relation.objects.filter(debpackage_id__in=package_ids).delete()
Relation.objects.bulk_create(relations, batch_size=10000, ignore_conflicts=True)
logger.info('finished')
Expand All @@ -87,10 +89,11 @@ def send_packages_to_vulns_update(task):
# In case of success set the lock's timeout to 2.5m.
with redis_conn.lock('vulns_lock', timeout=60 * 2.5, blocking_timeout=3):
logger.info('lock acquired.')
distro_suites = DEBIAN_SUITES + UBUNTU_SUITES + ('amzn2',)
package_ids = list(DebPackage.objects.filter(
processed=False, os_release_codename__in=distro_suites).order_by(
'os_release_codename', 'source_name').values_list('id', flat=True))
package_ids = list((DebPackage.objects.filter(
processed=False, os_release_codename__in=DEBIAN_SUITES + ('amzn2',)) |
DebPackage.objects.filter(processed=False, os_release_codename__in=UBUNTU_SUITES
).exclude(name__regex=UBUNTU_KERNEL_PACKAGES_RE_PATTERN)
).order_by('os_release_codename', 'source_name').values_list('id', flat=True))
logger.info('%d packages to process found.' % len(package_ids))
batch_size = 500
position = 0
Expand Down
40 changes: 40 additions & 0 deletions backend/device_registry/migrations/0088_auto_20200306_1702.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# Generated by Django 2.2.10 on 2020-03-06 17:02

from django.db import migrations, models

from device_registry.models import UBUNTU_SUITES, UBUNTU_KERNEL_PACKAGES_RE_PATTERN


def reset_kernel_packages_vulns(apps, schema_editor):
# Delete vulns of kernel-related packages.
DebPackageVulnerability = apps.get_model('device_registry', 'DebPackage').vulnerabilities.through
# Ubuntu.
DebPackageVulnerability.objects.filter(
debpackage__os_release_codename__in=UBUNTU_SUITES, debpackage__name__regex=UBUNTU_KERNEL_PACKAGES_RE_PATTERN
).delete()


class Migration(migrations.Migration):

dependencies = [
('device_registry', '0087_auto_20200318_0652'),
]

operations = [
migrations.AddField(
model_name='device',
name='reboot_required',
field=models.BooleanField(blank=True, db_index=True, null=True),
),
migrations.AlterField(
model_name='vulnerability',
name='name',
field=models.CharField(db_index=True, max_length=64),
),
migrations.AlterField(
model_name='vulnerability',
name='fix_available',
field=models.BooleanField(db_index=True),
),
migrations.RunPython(reset_kernel_packages_vulns)
]
18 changes: 9 additions & 9 deletions backend/device_registry/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,9 @@
apt_pkg.init()

DEBIAN_SUITES = ('jessie', 'stretch', 'buster') # Supported Debian suite names.
UBUNTU_SUITES = ('xenial', 'bionic') # Supported Ubuntu suite names.
UBUNTU_SUITES = ('xenial', 'bionic') # Supported Ubuntu suite (16.04, 18.04) names.
UBUNTU_KERNEL_PACKAGES_RE_PATTERN = r'linux-(?:(?:|aws-|oem-|gcp-|kvm-|oracle-|azure-|raspi2-|gke-|oem-osp1-)headers|' \
r'image|modules)-.+'
IPV4_ANY = '0.0.0.0'
IPV6_ANY = '::'
FTP_PORT = 21
Expand Down Expand Up @@ -73,6 +75,9 @@ class Arch(Enum):
class Meta:
unique_together = ['name', 'version', 'arch', 'os_release_codename']

def __str__(self):
return f'{self.name}:{self.version}:{self.arch}:{self.os_release_codename}'


class Device(models.Model):
class SshdIssueItem(NamedTuple):
Expand Down Expand Up @@ -111,6 +116,7 @@ class SshdIssueItem(NamedTuple):
deb_packages_hash = models.CharField(max_length=32, blank=True)
cpu = JSONField(blank=True, default=dict)
kernel_deb_package = models.ForeignKey(DebPackage, null=True, on_delete=models.SET_NULL, related_name='+')
reboot_required = models.BooleanField(null=True, blank=True, db_index=True)
audit_files = JSONField(blank=True, default=list)
os_release = JSONField(blank=True, default=dict)
auto_upgrades = models.BooleanField(null=True, blank=True)
Expand Down Expand Up @@ -475,12 +481,6 @@ def set_meta_tags(self):
if all_devices_tag not in self.tags:
self.tags.add(all_devices_tag)

@property
def vulnerable_packages(self):
if self.deb_packages_hash and self.deb_packages.exists() and self.os_release and \
self.os_release.get('codename') in DEBIAN_SUITES + UBUNTU_SUITES + ('amzn2',):
return self.deb_packages.filter(vulnerabilities__isnull=False).distinct().order_by('name')

@property
def cve_count(self):
"""
Expand Down Expand Up @@ -985,14 +985,14 @@ class Urgency(IntEnum):
HIGH = 3

os_release_codename = models.CharField(max_length=64, db_index=True)
name = models.CharField(max_length=64)
name = models.CharField(max_length=64, db_index=True)
package = models.CharField(max_length=64, db_index=True)
is_binary = models.BooleanField()
unstable_version = models.CharField(max_length=64, blank=True)
other_versions = ArrayField(models.CharField(max_length=64), blank=True)
urgency = models.PositiveSmallIntegerField(choices=[(tag, tag.value) for tag in Urgency])
remote = models.BooleanField(null=True)
fix_available = models.BooleanField()
fix_available = models.BooleanField(db_index=True)
pub_date = models.DateField(null=True)

def is_vulnerable(self, src_ver):
Expand Down
13 changes: 13 additions & 0 deletions backend/device_registry/recommended_actions.py
Original file line number Diff line number Diff line change
Expand Up @@ -497,6 +497,19 @@ def severity(cls, param=None):
return Severity.MED


# OS reboot required action.
class RebootRequiredAction(SimpleAction, metaclass=ActionMeta):
_severity = Severity.MED

@classmethod
def _affected_devices(cls, qs):
return qs.filter(reboot_required=True)

@classmethod
def _is_affected(cls, device) -> bool:
return device.reboot_required is True


# Automatic security update disabled action.
class AutoUpdatesAction(SimpleAction, metaclass=ActionMeta):
@classmethod
Expand Down
16 changes: 12 additions & 4 deletions backend/device_registry/tests/test_recommended_actions.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@
from device_registry.models import Device, DeviceInfo, FirewallState, PortScan, DebPackage, Vulnerability, \
GlobalPolicy, RecommendedAction, RecommendedActionStatus
from device_registry.recommended_actions import DefaultCredentialsAction, FirewallDisabledAction, AutoUpdatesAction, \
VulnerablePackagesAction, MySQLDefaultRootPasswordAction, \
FtpServerAction, CpuVulnerableAction, BaseAction, ActionMeta, Action, \
PUBLIC_SERVICE_PORTS, GithubAction, EnrollAction, INSECURE_SERVICES, InsecureServicesAction, \
SSHD_CONFIG_PARAMS_INFO, OpensshIssueAction, PubliclyAccessibleServiceAction, Severity, SimpleAction, ParamStatus
VulnerablePackagesAction, MySQLDefaultRootPasswordAction, FtpServerAction, CpuVulnerableAction, ActionMeta, \
Action, PUBLIC_SERVICE_PORTS, GithubAction, EnrollAction, INSECURE_SERVICES, InsecureServicesAction, \
SSHD_CONFIG_PARAMS_INFO, OpensshIssueAction, PubliclyAccessibleServiceAction, Severity, SimpleAction, ParamStatus, \
RebootRequiredAction

from freezegun import freeze_time

Expand Down Expand Up @@ -586,3 +586,11 @@ def enable_action(self):
pkg.save()
self.device.cpu = {'vendor': 'GenuineIntel'}
self.device.save()


class RebootRequiredActionTest(TestsMixin, TestCase):
action_class = RebootRequiredAction

def enable_action(self):
self.device.reboot_required = True
self.device.save(update_fields=['reboot_required'])
2 changes: 1 addition & 1 deletion backend/device_registry/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -697,7 +697,7 @@ def get_context_data(self, **kwargs):
vuln_names = Vulnerability.objects.filter(vuln_query)\
.values('name').distinct()
vuln_pub_dates_qs = Vulnerability.objects.filter(name__in=vuln_names) \
.values('name').annotate(pubdate=Max('pub_date')).distinct()
.values('name').distinct().annotate(pubdate=Max('pub_date'))
# Build a lookup dictionary for CVE publication dates.
vuln_pub_dates = {v['name']: v['pubdate'] for v in vuln_pub_dates_qs}

Expand Down
11 changes: 11 additions & 0 deletions backend/recommended_actions.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -503,3 +503,14 @@
terminal_code: |
$ export CLAIM_TOKEN="{key}"
$ curl -sL https://install.wott.io | sudo -E bash
- title: Reboot required
class: RebootRequiredAction
short: |
Reboot required for kernel upgrade to complete.
long: |
You have recently installed a new Linux kernel, but the system is currently running an older version. In order to apply the kernel upgrade, you need to reboot your system.
terminal_title: |
To reboot the node, run the following command:
terminal_code: |
$ sudo shutdown -r now

0 comments on commit 06f7d05

Please sign in to comment.