diff --git a/zun/api/controllers/v1/containers.py b/zun/api/controllers/v1/containers.py index ddf0e882c..7fb86f4c5 100644 --- a/zun/api/controllers/v1/containers.py +++ b/zun/api/controllers/v1/containers.py @@ -361,6 +361,18 @@ class ContainersController(base.Controller): requested_volumes = self._build_requested_volumes(context, mounts) + privileged = container_dict.pop('privileged', None) + if privileged is not None: + api_utils.version_check('privileged', '1.21') + policy.enforce(context, "container:create:privileged", + action="container:create:privileged") + try: + container_dict['privileged'] = strutils.bool_from_string( + privileged, strict=True) + except ValueError: + raise exception.InvalidValue(_('privileged values are: ' + 'true, false, True, False')) + # Valiadtion accepts 'None' so need to convert it to None if container_dict.get('image_driver'): container_dict['image_driver'] = api_utils.string_or_none( diff --git a/zun/api/controllers/v1/schemas/containers.py b/zun/api/controllers/v1/schemas/containers.py index 40a1b0ccf..ef6e94b63 100644 --- a/zun/api/controllers/v1/schemas/containers.py +++ b/zun/api/controllers/v1/schemas/containers.py @@ -37,6 +37,7 @@ _legacy_container_properties = { 'disk': parameter_types.disk, 'availability_zone': parameter_types.availability_zone, 'auto_heal': parameter_types.boolean, + 'privileged': parameter_types.boolean, } legacy_container_create = { diff --git a/zun/api/controllers/v1/views/containers_view.py b/zun/api/controllers/v1/views/containers_view.py index a80a258ce..78aa976a2 100644 --- a/zun/api/controllers/v1/views/containers_view.py +++ b/zun/api/controllers/v1/views/containers_view.py @@ -47,6 +47,7 @@ _basic_keys = ( 'hostname', 'disk', 'auto_heal', + 'privileged', ) diff --git a/zun/api/controllers/versions.py b/zun/api/controllers/versions.py index 7fa599d59..27a6abacd 100644 --- a/zun/api/controllers/versions.py +++ b/zun/api/controllers/versions.py @@ -53,10 +53,11 @@ REST_API_VERSION_HISTORY = """REST API Version History: * 1.18 - Modify the response of network list * 1.19 - Intoduce container resize API * 1.20 - Convert type of 'command' from string to list + * 1.21 - Add support privileged """ BASE_VER = '1.1' -CURRENT_MAX_VER = '1.20' +CURRENT_MAX_VER = '1.21' class Version(object): diff --git a/zun/api/rest_api_version_history.rst b/zun/api/rest_api_version_history.rst index 72cc5d109..56d081328 100644 --- a/zun/api/rest_api_version_history.rst +++ b/zun/api/rest_api_version_history.rst @@ -172,3 +172,8 @@ user documentation. ---- Convert type of 'command' from string to list + +1.21 +---- + + Support privileged container diff --git a/zun/common/policies/base.py b/zun/common/policies/base.py index e4d55e0d2..2104f3394 100644 --- a/zun/common/policies/base.py +++ b/zun/common/policies/base.py @@ -15,6 +15,7 @@ from oslo_policy import policy ROLE_ADMIN = 'role:admin' RULE_ADMIN_OR_OWNER = 'is_admin:True or project_id:%(project_id)s' RULE_ADMIN_API = 'rule:context_is_admin' +RULE_DENY_EVERYBODY = 'rule:deny_everybody' rules = [ policy.RuleDefault( @@ -28,7 +29,11 @@ rules = [ policy.RuleDefault( name='admin_api', check_str=RULE_ADMIN_API - ) + ), + policy.RuleDefault( + name="deny_everybody", + check_str="!", + description="Default rule for deny everybody."), ] diff --git a/zun/common/policies/container.py b/zun/common/policies/container.py index 1ab4af6dc..9bee5e3d3 100644 --- a/zun/common/policies/container.py +++ b/zun/common/policies/container.py @@ -39,6 +39,19 @@ rules = [ } ] ), + policy.DocumentedRuleDefault( + name=CONTAINER % 'create:privileged', + check_str=base.RULE_DENY_EVERYBODY, + description=('Create a new privileged container.' + 'Warning: the privileged container has a big security ' + 'risk so be caution if you want to enable this feature'), + operations=[ + { + 'path': '/v1/containers', + 'method': 'POST' + } + ] + ), policy.DocumentedRuleDefault( name=CONTAINER % 'delete', check_str=base.RULE_ADMIN_OR_OWNER, diff --git a/zun/container/docker/driver.py b/zun/container/docker/driver.py index d9bcb21b5..f4f2466ab 100644 --- a/zun/container/docker/driver.py +++ b/zun/container/docker/driver.py @@ -271,6 +271,7 @@ class DockerDriver(driver.ContainerDriver): runtime = container.runtime or CONF.container_runtime host_config = {} + host_config['privileged'] = container.privileged host_config['runtime'] = runtime host_config['binds'] = binds kwargs['volumes'] = [b['bind'] for b in binds.values()] diff --git a/zun/db/sqlalchemy/alembic/versions/105626c4f972_add_privileged_to_container.py b/zun/db/sqlalchemy/alembic/versions/105626c4f972_add_privileged_to_container.py new file mode 100644 index 000000000..71bce2c3c --- /dev/null +++ b/zun/db/sqlalchemy/alembic/versions/105626c4f972_add_privileged_to_container.py @@ -0,0 +1,35 @@ +# 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. + + +"""add privileged to container + +Revision ID: 105626c4f972 +Revises: 3e80bbfd8da7 +Create Date: 2018-07-26 15:05:10.567715 + +""" + +# revision identifiers, used by Alembic. +revision = '105626c4f972' +down_revision = '3e80bbfd8da7' +branch_labels = None +depends_on = None + +from alembic import op +import sqlalchemy as sa + + +def upgrade(): + op.add_column('container', + sa.Column('privileged', sa.Boolean(), + nullable=True)) diff --git a/zun/db/sqlalchemy/models.py b/zun/db/sqlalchemy/models.py index bf5189059..bd925fbd6 100644 --- a/zun/db/sqlalchemy/models.py +++ b/zun/db/sqlalchemy/models.py @@ -170,6 +170,7 @@ class Container(Base): capsule_id = Column(Integer, ForeignKey('capsule.id', ondelete='CASCADE')) started_at = Column(DateTime) + privileged = Column(Boolean, default=False) class VolumeMapping(Base): diff --git a/zun/objects/container.py b/zun/objects/container.py index 09684fbb3..2838acc9e 100644 --- a/zun/objects/container.py +++ b/zun/objects/container.py @@ -63,7 +63,8 @@ class Container(base.ZunPersistentObject, base.ZunObject): # Version 1.31: Add 'started_at' attribute # Version 1.32: Add 'exec_instances' attribute # Version 1.33: Change 'command' to List type - VERSION = '1.33' + # Version 1.34: Add privileged to container + VERSION = '1.34' fields = { 'id': fields.IntegerField(), @@ -105,6 +106,7 @@ class Container(base.ZunPersistentObject, base.ZunObject): 'started_at': fields.DateTimeField(tzinfo_aware=False, nullable=True), 'exec_instances': fields.ListOfObjectsField('ExecInstance', nullable=True), + 'privileged': fields.BooleanField(nullable=True), } @staticmethod diff --git a/zun/tests/unit/api/base.py b/zun/tests/unit/api/base.py index d4638de90..8b63044d3 100644 --- a/zun/tests/unit/api/base.py +++ b/zun/tests/unit/api/base.py @@ -26,7 +26,7 @@ from zun.tests.unit.db import base PATH_PREFIX = '/v1' -CURRENT_VERSION = "container 1.20" +CURRENT_VERSION = "container 1.21" class FunctionalTest(base.DbTestCase): diff --git a/zun/tests/unit/api/controllers/test_root.py b/zun/tests/unit/api/controllers/test_root.py index 33e607124..66f0b14e0 100644 --- a/zun/tests/unit/api/controllers/test_root.py +++ b/zun/tests/unit/api/controllers/test_root.py @@ -28,7 +28,7 @@ class TestRootController(api_base.FunctionalTest): 'default_version': {'id': 'v1', 'links': [{'href': 'http://localhost/v1/', 'rel': 'self'}], - 'max_version': '1.20', + 'max_version': '1.21', 'min_version': '1.1', 'status': 'CURRENT'}, 'description': 'Zun is an OpenStack project which ' @@ -37,7 +37,7 @@ class TestRootController(api_base.FunctionalTest): 'versions': [{'id': 'v1', 'links': [{'href': 'http://localhost/v1/', 'rel': 'self'}], - 'max_version': '1.20', + 'max_version': '1.21', 'min_version': '1.1', 'status': 'CURRENT'}]} diff --git a/zun/tests/unit/container/docker/test_docker_driver.py b/zun/tests/unit/container/docker/test_docker_driver.py index d6ed9ac35..20684e3e4 100644 --- a/zun/tests/unit/container/docker/test_docker_driver.py +++ b/zun/tests/unit/container/docker/test_docker_driver.py @@ -135,6 +135,7 @@ class TestDockerDriver(base.DriverTestCase): host_config['binds'] = {} host_config['network_mode'] = 'fake-network' host_config['storage_opt'] = {'size': '20G'} + host_config['privileged'] = False self.mock_docker.create_host_config.assert_called_once_with( **host_config) @@ -201,6 +202,7 @@ class TestDockerDriver(base.DriverTestCase): host_config['binds'] = {} host_config['network_mode'] = 'fake-network' host_config['storage_opt'] = {'size': '20G'} + host_config['privileged'] = False self.mock_docker.create_host_config.assert_called_once_with( **host_config) @@ -265,6 +267,7 @@ class TestDockerDriver(base.DriverTestCase): host_config['binds'] = {} host_config['network_mode'] = 'fake-network' host_config['storage_opt'] = {'size': '20G'} + host_config['privileged'] = False self.mock_docker.create_host_config.assert_called_once_with( **host_config) diff --git a/zun/tests/unit/db/utils.py b/zun/tests/unit/db/utils.py index ca50ed5ab..da4cb40b3 100644 --- a/zun/tests/unit/db/utils.py +++ b/zun/tests/unit/db/utils.py @@ -102,6 +102,7 @@ def get_test_container(**kwargs): 'auto_heal': kwargs.get('auto_heal', False), 'capsule_id': kwargs.get('capsule_id', 42), 'started_at': kwargs.get('started_at'), + 'privileged': kwargs.get('privileged', False), } diff --git a/zun/tests/unit/objects/test_objects.py b/zun/tests/unit/objects/test_objects.py index 8ed5bef4c..9b97eeee2 100644 --- a/zun/tests/unit/objects/test_objects.py +++ b/zun/tests/unit/objects/test_objects.py @@ -344,7 +344,7 @@ class TestObject(test_base.TestCase, _TestObject): # For more information on object version testing, read # https://docs.openstack.org/zun/latest/ object_data = { - 'Container': '1.33-5eac0a995f25329ca566fdddde45c759', + 'Container': '1.34-22c46c6ae571b83295c3dac74fe8772f', 'VolumeMapping': '1.1-50df6202f7846a136a91444c38eba841', 'Image': '1.1-330e6205c80b99b59717e1cfc6a79935', 'MyObj': '1.0-34c4b1aadefd177b13f9a2f894cc23cd',