Implement privsep boilerplate in cyborg.
This includes implementing a first trivial example of how to use privsep to run something as root, specifically the gpu driver. FPGA and other drivers should implement as well in the future. For reference: https://review.opendev.org/#/c/566479/4 https://docs.openstack.org/oslo.privsep/latest/user/index.html#converting-from-rootwrap-to-privsep Change-Id: Ibff356d9a7f57bc99cc26de90d81ff92948f37c4
This commit is contained in:
parent
8adf4b9955
commit
45ade8a10b
@ -16,18 +16,19 @@
|
||||
"""
|
||||
Utils for GPU driver.
|
||||
"""
|
||||
import re
|
||||
import subprocess
|
||||
|
||||
from oslo_concurrency import processutils
|
||||
from oslo_log import log as logging
|
||||
from oslo_serialization import jsonutils
|
||||
|
||||
import re
|
||||
|
||||
from cyborg.accelerator.common import utils
|
||||
from cyborg.common import constants
|
||||
from cyborg.objects.driver_objects import driver_attach_handle
|
||||
from cyborg.objects.driver_objects import driver_controlpath_id
|
||||
from cyborg.objects.driver_objects import driver_deployable
|
||||
from cyborg.objects.driver_objects import driver_device
|
||||
import cyborg.privsep
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
@ -42,15 +43,27 @@ GPU_INFO_PATTERN = re.compile("(?P<devices>[0-9a-fA-F]{4}:[0-9a-fA-F]{2}:"
|
||||
# GPU.
|
||||
|
||||
|
||||
@cyborg.privsep.sys_admin_pctxt.entrypoint
|
||||
def lspci_privileged():
|
||||
cmd = ['lspci', '-nnn', '-D']
|
||||
return processutils.execute(*cmd)
|
||||
|
||||
|
||||
def get_pci_devices(pci_flags, vendor_id=None):
|
||||
device_for_vendor_out = []
|
||||
all_device_out = []
|
||||
lspci_out = lspci_privileged()[0].split('\n')
|
||||
for i in range(len(lspci_out)):
|
||||
if any(x in lspci_out[i] for x in pci_flags):
|
||||
all_device_out.append(lspci_out[i])
|
||||
if vendor_id and vendor_id in lspci_out[i]:
|
||||
device_for_vendor_out.append(lspci_out[i])
|
||||
return device_for_vendor_out if vendor_id else all_device_out
|
||||
|
||||
|
||||
def discover_vendors():
|
||||
cmd = "sudo lspci -nnn -D | grep -E '%s'"
|
||||
cmd = cmd % "|".join(GPU_FLAGS)
|
||||
# FIXME(wangzhh): Use oslo.privsep instead of subprocess here to prevent
|
||||
# shell injection attacks.
|
||||
p = subprocess.Popen(cmd, stdout=subprocess.PIPE, shell=True)
|
||||
p.wait()
|
||||
gpus = p.stdout.readlines()
|
||||
vendors = set()
|
||||
gpus = get_pci_devices(GPU_FLAGS)
|
||||
for gpu in gpus:
|
||||
m = GPU_INFO_PATTERN.match(gpu)
|
||||
if m:
|
||||
@ -59,17 +72,9 @@ def discover_vendors():
|
||||
return vendors
|
||||
|
||||
|
||||
def discover_gpus(vender_id=None):
|
||||
cmd = "sudo lspci -nnn -D| grep -E '%s'"
|
||||
cmd = cmd % "|".join(GPU_FLAGS)
|
||||
if vender_id:
|
||||
cmd = cmd + "| grep " + vender_id
|
||||
# FIXME(wangzhh): Use oslo.privsep instead of subprocess here to prevent
|
||||
# shell injection attacks.
|
||||
p = subprocess.Popen(cmd, stdout=subprocess.PIPE, shell=True)
|
||||
p.wait()
|
||||
gpus = p.stdout.readlines()
|
||||
def discover_gpus(vendor_id=None):
|
||||
gpu_list = []
|
||||
gpus = get_pci_devices(GPU_FLAGS, vendor_id)
|
||||
for gpu in gpus:
|
||||
m = GPU_INFO_PATTERN.match(gpu)
|
||||
if m:
|
||||
|
@ -13,21 +13,23 @@
|
||||
|
||||
"""The Cyborg Agent Service."""
|
||||
|
||||
import shlex
|
||||
import sys
|
||||
|
||||
from oslo_config import cfg
|
||||
from oslo_privsep import priv_context
|
||||
from oslo_service import service
|
||||
|
||||
from cyborg.common import constants
|
||||
from cyborg.common import service as cyborg_service
|
||||
|
||||
|
||||
CONF = cfg.CONF
|
||||
|
||||
|
||||
def main():
|
||||
# Parse config file and command line options, then start logging
|
||||
cyborg_service.prepare_service(sys.argv)
|
||||
priv_context.init(root_helper=shlex.split('sudo'))
|
||||
|
||||
mgr = cyborg_service.RPCService('cyborg.agent.manager',
|
||||
'AgentManager',
|
||||
|
31
cyborg/privsep/__init__.py
Normal file
31
cyborg/privsep/__init__.py
Normal file
@ -0,0 +1,31 @@
|
||||
# Copyright 2019 ZTE Corporation
|
||||
# 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.
|
||||
|
||||
"""Setup privsep decorator."""
|
||||
|
||||
from oslo_privsep import capabilities
|
||||
from oslo_privsep import priv_context
|
||||
|
||||
sys_admin_pctxt = priv_context.PrivContext(
|
||||
'cyborg',
|
||||
cfg_section='cyborg_sys_admin',
|
||||
pypath=__name__ + '.sys_admin_pctxt',
|
||||
# TODO(yumeng):
|
||||
# CAP_SYS_ADMIN has a lot of scary powers, so
|
||||
# consider breaking this out into a separate minimal context.
|
||||
capabilities=[capabilities.CAP_CHOWN,
|
||||
capabilities.CAP_DAC_OVERRIDE,
|
||||
capabilities.CAP_DAC_READ_SEARCH,
|
||||
capabilities.CAP_FOWNER,
|
||||
capabilities.CAP_SYS_ADMIN],
|
||||
)
|
@ -13,7 +13,6 @@
|
||||
# under the License.
|
||||
|
||||
import mock
|
||||
import subprocess
|
||||
|
||||
from oslo_serialization import jsonutils
|
||||
|
||||
@ -43,17 +42,17 @@ class TestGPUDriverUtils(base.TestCase):
|
||||
super(TestGPUDriverUtils, self).setUp()
|
||||
self.p = p()
|
||||
|
||||
@mock.patch.object(subprocess, 'Popen', autospec=True)
|
||||
def test_discover_vendors(self, mock_popen):
|
||||
mock_popen.return_value = self.p
|
||||
gpu_venders = utils.discover_vendors()
|
||||
self.assertEqual(1, len(gpu_venders))
|
||||
@mock.patch('cyborg.accelerator.drivers.gpu.utils.lspci_privileged')
|
||||
def test_discover_vendors(self, mock_devices):
|
||||
mock_devices.return_value = self.p.stdout.readlines()
|
||||
gpu_vendors = utils.discover_vendors()
|
||||
self.assertEqual(1, len(gpu_vendors))
|
||||
|
||||
@mock.patch.object(subprocess, 'Popen', autospec=True)
|
||||
def test_discover_gpus(self, mock_popen):
|
||||
mock_popen.return_value = self.p
|
||||
vender_id = '10de'
|
||||
gpu_list = utils.discover_gpus(vender_id)
|
||||
@mock.patch('cyborg.accelerator.drivers.gpu.utils.lspci_privileged')
|
||||
def test_discover_gpus(self, mock_devices_for_vendor):
|
||||
mock_devices_for_vendor.return_value = self.p.stdout.readlines()
|
||||
vendor_id = '10de'
|
||||
gpu_list = utils.discover_gpus(vendor_id)
|
||||
self.assertEqual(1, len(gpu_list))
|
||||
attach_handle_list = [
|
||||
{'attach_type': 'PCI',
|
||||
|
@ -0,0 +1,14 @@
|
||||
---
|
||||
security:
|
||||
- |
|
||||
Privsep transitions. Cyborg is transitioning from using the older
|
||||
style rootwrap privilege escalation path to the new style Oslo privsep
|
||||
path. This should improve performance and security of Cyborg
|
||||
in the long term.
|
||||
- |
|
||||
Privsep daemons are now started by Cyborg when required. These
|
||||
daemons can be started via rootwrap if required. rootwrap configs
|
||||
therefore need to be updated to include new privsep daemon invocations.
|
||||
- |
|
||||
Use oslo.privsep instead of subprocess to execute sudo related shell
|
||||
operations can prevent shell injection attacks.
|
@ -28,3 +28,4 @@ jsonpatch>=1.16,!=1.20 # BSD
|
||||
psutil>=3.2.2 # BSD
|
||||
mock>=2.0.0 # BSD
|
||||
python-glanceclient>=2.3.0 # Apache-2.0
|
||||
oslo.privsep>=1.32.0 # Apache-2.0
|
||||
|
Loading…
Reference in New Issue
Block a user