Remove admin_token_auth from keystone pipelines
The presence of the admin_token_auth middleware is needed only to apply the keystone puppet task during the restore phase. This middleware allows to authorize with admin_token and configure things in Keystone. After the successful re-initialization of Keystone this middleware should be removed from pipelines alongside with restarting the keystone service because of security reasons. Change-Id: Id98d8f25270538ab850af936eff749f0277e0c58
This commit is contained in:
parent
9c44fad4f4
commit
7fbc2e57ef
|
@ -75,12 +75,9 @@ class KeystoneArchivator(PostgresArchivator):
|
|||
|
||||
def restore(self):
|
||||
keystone.unset_default_domain_id(magic_consts.KEYSTONE_CONF)
|
||||
keystone.add_admin_token_auth(magic_consts.KEYSTONE_PASTE, [
|
||||
"pipeline:public_api",
|
||||
"pipeline:admin_api",
|
||||
"pipeline:api_v3",
|
||||
])
|
||||
super(KeystoneArchivator, self).restore()
|
||||
with keystone.admin_token_auth(magic_consts.KEYSTONE_PASTE,
|
||||
magic_consts.KEYSTONE_PIPELINES):
|
||||
super(KeystoneArchivator, self).restore()
|
||||
|
||||
|
||||
class DatabasesArchivator(base.CollectionArchivator):
|
||||
|
|
|
@ -11,7 +11,9 @@
|
|||
# under the License.
|
||||
|
||||
from octane.handlers.backup_restore import base
|
||||
from octane import magic_consts
|
||||
from octane.util import auth
|
||||
from octane.util import keystone
|
||||
from octane.util import puppet
|
||||
from octane.util import subprocess
|
||||
|
||||
|
@ -31,5 +33,7 @@ class PuppetApplyTasks(base.Base):
|
|||
|
||||
def restore(self):
|
||||
subprocess.call(["systemctl", "stop"] + self.services)
|
||||
with auth.set_astute_password(self.context):
|
||||
with auth.set_astute_password(self.context), \
|
||||
keystone.admin_token_auth(magic_consts.KEYSTONE_PASTE,
|
||||
magic_consts.KEYSTONE_PIPELINES):
|
||||
puppet.apply_all_tasks()
|
||||
|
|
|
@ -90,3 +90,9 @@ NOVA_PATCHES = [
|
|||
SFTP_SERVER_BIN = '/usr/lib/sftp-server'
|
||||
|
||||
FUEL_KEYS_BASE_PATH = "/var/lib/fuel/keys"
|
||||
|
||||
KEYSTONE_PIPELINES = [
|
||||
"pipeline:public_api",
|
||||
"pipeline:admin_api",
|
||||
"pipeline:api_v3",
|
||||
]
|
||||
|
|
|
@ -267,11 +267,11 @@ def test_postgres_restore(mocker, cls, db, services):
|
|||
member = TestMember("postgres/{0}.sql".format(db), True, True)
|
||||
archive = TestArchive([member], cls)
|
||||
|
||||
mock_keystone = mock.Mock()
|
||||
mock_keystone = mock.MagicMock()
|
||||
mocker.patch("octane.util.keystone.unset_default_domain_id",
|
||||
new=mock_keystone.unset)
|
||||
mocker.patch("octane.util.keystone.add_admin_token_auth",
|
||||
new=mock_keystone.add)
|
||||
mocker.patch("octane.util.keystone.admin_token_auth",
|
||||
new=mock_keystone.admin_token)
|
||||
|
||||
mock_subprocess = mock.MagicMock()
|
||||
mocker.patch("octane.util.subprocess.call", new=mock_subprocess.call)
|
||||
|
@ -287,7 +287,7 @@ def test_postgres_restore(mocker, cls, db, services):
|
|||
cls(archive, mock_context).restore()
|
||||
member.assert_extract()
|
||||
|
||||
assert mock_subprocess.mock_calls == [
|
||||
expected_calls = [
|
||||
mock.call.call(["systemctl", "stop"] + services),
|
||||
mock.call.call(["sudo", "-u", "postgres", "dropdb", "--if-exists",
|
||||
db]),
|
||||
|
@ -296,6 +296,7 @@ def test_postgres_restore(mocker, cls, db, services):
|
|||
mock.call.popen().__enter__(),
|
||||
mock.call.popen().__exit__(None, None, None),
|
||||
]
|
||||
assert mock_subprocess.mock_calls == expected_calls
|
||||
mock_copyfileobj.assert_called_once_with(
|
||||
member,
|
||||
mock_subprocess.popen.return_value.__enter__.return_value.stdin,
|
||||
|
@ -312,13 +313,17 @@ def test_postgres_restore(mocker, cls, db, services):
|
|||
assert not mock_keystone.called
|
||||
else:
|
||||
assert not mock_patch.called
|
||||
expected_pipelines = [
|
||||
"pipeline:public_api",
|
||||
"pipeline:admin_api",
|
||||
"pipeline:api_v3",
|
||||
]
|
||||
assert mock_keystone.mock_calls == [
|
||||
mock.call.unset("/etc/keystone/keystone.conf"),
|
||||
mock.call.add("/etc/keystone/keystone-paste.ini", [
|
||||
"pipeline:public_api",
|
||||
"pipeline:admin_api",
|
||||
"pipeline:api_v3",
|
||||
]),
|
||||
mock.call.admin_token(
|
||||
"/etc/keystone/keystone-paste.ini", expected_pipelines),
|
||||
mock.call.admin_token().__enter__(),
|
||||
mock.call.admin_token().__exit__(None, None, None),
|
||||
]
|
||||
mock_set_astute_password.assert_called_once_with(mock_context)
|
||||
|
||||
|
@ -561,13 +566,23 @@ def test_post_restore_puppet_apply_tasks(mocker, mock_subprocess):
|
|||
mock_set_astute_password = mocker.patch(
|
||||
"octane.util.auth.set_astute_password")
|
||||
mock_apply = mocker.patch("octane.util.puppet.apply_all_tasks")
|
||||
mock_admin_token = mocker.patch("octane.util.keystone.admin_token_auth")
|
||||
|
||||
archivator = puppet.PuppetApplyTasks(None, context)
|
||||
archivator.restore()
|
||||
|
||||
mock_subprocess.assert_called_once_with(["systemctl", "stop", "ostf"])
|
||||
assert mock_apply.called
|
||||
mock_set_astute_password.assert_called_once_with(context)
|
||||
expected_pipelines = [
|
||||
"pipeline:public_api",
|
||||
"pipeline:admin_api",
|
||||
"pipeline:api_v3",
|
||||
]
|
||||
mock_admin_token.assert_called_once_with(
|
||||
"/etc/keystone/keystone-paste.ini", expected_pipelines)
|
||||
assert mock_subprocess.call_args_list == [
|
||||
mock.call(["systemctl", "stop", "ostf"]),
|
||||
]
|
||||
|
||||
|
||||
@pytest.mark.parametrize("nodes", [
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
# under the License.
|
||||
|
||||
import contextlib
|
||||
import copy
|
||||
|
||||
import mock
|
||||
import pytest
|
||||
|
@ -58,6 +59,45 @@ def test_unset_default_domain_id(mocker, parameters, writes):
|
|||
mock_update_file.assert_called_once_with("fakefilename")
|
||||
|
||||
|
||||
def test_admin_token_auth(mocker):
|
||||
mock_calls = mock.Mock()
|
||||
mocker.patch("octane.util.keystone.add_admin_token_auth",
|
||||
new=mock_calls.add)
|
||||
mocker.patch("octane.util.keystone.remove_admin_token_auth",
|
||||
new=mock_calls.remove)
|
||||
mocker.patch("octane.util.subprocess.call", new=mock_calls.subprocess)
|
||||
with keystone.admin_token_auth("fakefilename", "fakepipelines"):
|
||||
mock_calls.let()
|
||||
expected_calls = [
|
||||
mock.call.add("fakefilename", "fakepipelines"),
|
||||
mock.call.let(),
|
||||
mock.call.remove("fakefilename", "fakepipelines"),
|
||||
mock.call.subprocess(["systemctl", "restart", "openstack-keystone"]),
|
||||
]
|
||||
assert mock_calls.mock_calls == expected_calls
|
||||
|
||||
|
||||
@pytest.mark.parametrize(("items", "expected_items"), [
|
||||
(
|
||||
[
|
||||
["request_id", "token_auth"],
|
||||
["admin_token_auth", "token_auth"],
|
||||
],
|
||||
[
|
||||
["request_id", "admin_token_auth", "token_auth"],
|
||||
["admin_token_auth", "token_auth"],
|
||||
],
|
||||
),
|
||||
])
|
||||
def test_add_admin_token_auth(mocker, items, expected_items):
|
||||
items = copy.deepcopy(items)
|
||||
mock_replace = mocker.patch("octane.util.keystone.replace_pipeline_items")
|
||||
mock_replace.return_value.__enter__.return_value = items
|
||||
keystone.add_admin_token_auth("fakefilename", "fakepipelines")
|
||||
assert items == expected_items
|
||||
mock_replace.assert_called_once_with("fakefilename", "fakepipelines")
|
||||
|
||||
|
||||
@pytest.mark.parametrize(("parameters", "writes"), [
|
||||
([
|
||||
("[pipeline:public_api]\n", "pipeline:public_api", None, None),
|
||||
|
@ -81,10 +121,96 @@ def test_unset_default_domain_id(mocker, parameters, writes):
|
|||
"pipeline = request_id token_auth service_v3\n",
|
||||
])
|
||||
])
|
||||
def test_add_admin_token_auth(mocker, parameters, writes):
|
||||
def test_add_admin_token_auth_functional(mocker, parameters, writes):
|
||||
with verify_update_file(mocker, parameters, writes) as mock_update_file:
|
||||
keystone.add_admin_token_auth("fakefilename", [
|
||||
"pipeline:public_api",
|
||||
"pipeline:admin_api",
|
||||
])
|
||||
mock_update_file.assert_called_once_with("fakefilename")
|
||||
|
||||
|
||||
@pytest.mark.parametrize(("items", "expected_items"), [
|
||||
(
|
||||
[
|
||||
["request_id", "token_auth"],
|
||||
["admin_token_auth", "token_auth"],
|
||||
],
|
||||
[
|
||||
["request_id", "token_auth"],
|
||||
["token_auth"],
|
||||
],
|
||||
),
|
||||
])
|
||||
def test_remove_admin_token_auth(mocker, items, expected_items):
|
||||
items = copy.deepcopy(items)
|
||||
mock_replace = mocker.patch("octane.util.keystone.replace_pipeline_items")
|
||||
mock_replace.return_value.__enter__.return_value = items
|
||||
keystone.remove_admin_token_auth("fakefilename", "fakepipelines")
|
||||
assert items == expected_items
|
||||
mock_replace.assert_called_once_with("fakefilename", "fakepipelines")
|
||||
|
||||
|
||||
@pytest.mark.parametrize(("parameters", "writes"), [
|
||||
([
|
||||
("[pipeline:public_api]\n", "pipeline:public_api", None, None),
|
||||
("pipeline = request_id admin_token_auth token_auth public_service\n",
|
||||
"pipeline:public_api", "pipeline",
|
||||
"request_id admin_token_auth token_auth public_service"),
|
||||
("[pipeline:admin_api]\n", "pipeline:admin_api", None, None),
|
||||
("pipeline = request_id token_auth admin_service\n",
|
||||
"pipeline:admin_api", "pipeline",
|
||||
"request_id token_auth admin_service"),
|
||||
("[pipeline:api_v3]\n", "pipeline:api_v3", None, None),
|
||||
("pipeline = request_id admin_token_auth token_auth service_v3\n",
|
||||
"pipeline:api_v3", "pipeline",
|
||||
"request_id admin_token_auth token_auth service_v3"),
|
||||
], [
|
||||
"[pipeline:public_api]\n",
|
||||
"pipeline = request_id token_auth public_service\n",
|
||||
"[pipeline:admin_api]\n",
|
||||
"pipeline = request_id token_auth admin_service\n",
|
||||
"[pipeline:api_v3]\n",
|
||||
"pipeline = request_id admin_token_auth token_auth service_v3\n",
|
||||
])
|
||||
])
|
||||
def test_remove_admin_token_auth_functional(mocker, parameters, writes):
|
||||
with verify_update_file(mocker, parameters, writes) as mock_update_file:
|
||||
keystone.remove_admin_token_auth("fakefilename", [
|
||||
"pipeline:public_api",
|
||||
"pipeline:admin_api",
|
||||
])
|
||||
mock_update_file.assert_called_once_with("fakefilename")
|
||||
|
||||
|
||||
@pytest.mark.parametrize(("parameters", "writes"), [
|
||||
([
|
||||
("[pipeline:public_api]\n", "pipeline:public_api", None, None),
|
||||
("pipeline = token_auth public_service\n",
|
||||
"pipeline:public_api", "pipeline", "token_auth public_service"),
|
||||
("[pipeline:admin_api]\n", "pipeline:admin_api", None, None),
|
||||
("pipeline = request_id token_auth admin_service\n",
|
||||
"pipeline:admin_api", "pipeline",
|
||||
"request_id token_auth admin_service"),
|
||||
("[pipeline:api_v3]\n", "pipeline:api_v3", None, None),
|
||||
("pipeline = token_auth service_v3\n",
|
||||
"pipeline:api_v3", "pipeline", "token_auth service_v3"),
|
||||
], [
|
||||
"[pipeline:public_api]\n",
|
||||
"pipeline = token_auth public_service\n",
|
||||
"[pipeline:admin_api]\n",
|
||||
"pipeline = a token_auth admin_service c\n",
|
||||
"[pipeline:api_v3]\n",
|
||||
"pipeline = token_auth service_v3\n",
|
||||
])
|
||||
])
|
||||
def test_replace_pipelines_items(mocker, parameters, writes):
|
||||
pipelines = ["pipeline:admin_api"]
|
||||
with verify_update_file(mocker, parameters, writes) as mock_update_file:
|
||||
with keystone.replace_pipeline_items("fakefilename", pipelines) as \
|
||||
pipeline_items:
|
||||
for items in pipeline_items:
|
||||
items.insert(0, "a")
|
||||
items.remove("request_id")
|
||||
items.append("c")
|
||||
mock_update_file.assert_called_once_with("fakefilename")
|
||||
|
|
|
@ -10,6 +10,8 @@
|
|||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import contextlib
|
||||
|
||||
from octane.util import helpers
|
||||
from octane.util import subprocess
|
||||
|
||||
|
@ -22,14 +24,40 @@ def unset_default_domain_id(filename):
|
|||
new.write(line)
|
||||
|
||||
|
||||
@contextlib.contextmanager
|
||||
def admin_token_auth(filename, pipelines):
|
||||
add_admin_token_auth(filename, pipelines)
|
||||
yield
|
||||
remove_admin_token_auth(filename, pipelines)
|
||||
subprocess.call(["systemctl", "restart", "openstack-keystone"])
|
||||
|
||||
|
||||
def add_admin_token_auth(filename, pipelines):
|
||||
with subprocess.update_file(filename) as (old, new):
|
||||
with replace_pipeline_items(filename, pipelines) as pipeline_items:
|
||||
for items in pipeline_items:
|
||||
if "admin_token_auth" in items:
|
||||
continue
|
||||
token_auth_idx = items.index("token_auth")
|
||||
items.insert(token_auth_idx, "admin_token_auth")
|
||||
|
||||
|
||||
def remove_admin_token_auth(filename, pipelines):
|
||||
with replace_pipeline_items(filename, pipelines) as pipeline_items:
|
||||
for items in pipeline_items:
|
||||
if "admin_token_auth" in items:
|
||||
items.remove("admin_token_auth")
|
||||
|
||||
|
||||
@contextlib.contextmanager
|
||||
def replace_pipeline_items(filename, pipelines):
|
||||
def iterate_pipeline_items(old, new):
|
||||
for line, section, parameter, value in helpers.iterate_parameters(old):
|
||||
if section in pipelines and parameter == "pipeline" and \
|
||||
"admin_token_auth" not in value:
|
||||
if section in pipelines and parameter == "pipeline":
|
||||
items = value.split()
|
||||
token_auth_idx = items.index("token_auth")
|
||||
items.insert(token_auth_idx, "admin_token_auth")
|
||||
value = " ".join(items)
|
||||
line = "{0} = {1}\n".format(parameter, value)
|
||||
yield items
|
||||
new_value = " ".join(items)
|
||||
line = "{0} = {1}\n".format(parameter, new_value)
|
||||
new.write(line)
|
||||
|
||||
with subprocess.update_file(filename) as (old, new):
|
||||
yield iterate_pipeline_items(old, new)
|
||||
|
|
Loading…
Reference in New Issue