console: introduce framework for RFB authentication
Introduce a framework for providing RFB authentication scheme implementations. This will be later used by the websocket RFB security proxy code. Change-Id: I98403ca922b83a460a4e7baa12bd5f596a79c940 Co-authored-by: Stephen Finucane <sfinucan@redhat.com> Implements: bp websocket-proxy-to-host-security
This commit is contained in:
parent
4d520e3cae
commit
3c7770f1af
|
@ -14,6 +14,7 @@
|
|||
# under the License.
|
||||
|
||||
from oslo_config import cfg
|
||||
from oslo_config import types
|
||||
|
||||
vnc_group = cfg.OptGroup(
|
||||
'vnc',
|
||||
|
@ -212,6 +213,24 @@ Related options:
|
|||
|
||||
* novncproxy_host
|
||||
* novncproxy_base_url
|
||||
"""),
|
||||
cfg.ListOpt(
|
||||
'auth_schemes',
|
||||
item_type=types.String(
|
||||
choices=['none']
|
||||
),
|
||||
default=['none'],
|
||||
help="""
|
||||
The authentication schemes to use with the compute node.
|
||||
|
||||
Control what RFB authentication schemes are permitted for connections between
|
||||
the proxy and the compute host. If multiple schemes are enabled, the first
|
||||
matching scheme will be used, thus the strongest schemes should be listed
|
||||
first.
|
||||
|
||||
Possible values:
|
||||
|
||||
* "none": allow connection without authentication
|
||||
"""),
|
||||
]
|
||||
|
||||
|
|
|
@ -0,0 +1,64 @@
|
|||
# Copyright (c) 2014-2017 Red Hat, Inc
|
||||
#
|
||||
# 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.
|
||||
|
||||
import abc
|
||||
import enum
|
||||
|
||||
import six
|
||||
|
||||
VERSION_LENGTH = 12
|
||||
|
||||
AUTH_STATUS_FAIL = b"\x00"
|
||||
AUTH_STATUS_PASS = b"\x01"
|
||||
|
||||
|
||||
class AuthType(enum.IntEnum):
|
||||
|
||||
INVALID = 0
|
||||
NONE = 1
|
||||
VNC = 2
|
||||
RA2 = 5
|
||||
RA2NE = 6
|
||||
TIGHT = 16
|
||||
ULTRA = 17
|
||||
TLS = 18 # Used by VINO
|
||||
VENCRYPT = 19 # Used by VeNCrypt and QEMU
|
||||
SASL = 20 # SASL type used by VINO and QEMU
|
||||
ARD = 30 # Apple remote desktop (screen sharing)
|
||||
MSLOGON = 0xfffffffa # Used by UltraVNC
|
||||
|
||||
|
||||
@six.add_metaclass(abc.ABCMeta)
|
||||
class RFBAuthScheme(object):
|
||||
|
||||
@abc.abstractmethod
|
||||
def security_type(self):
|
||||
"""Return the security type supported by this scheme
|
||||
|
||||
Returns the nova.console.rfb.auth.AuthType.XX
|
||||
constant representing the scheme implemented.
|
||||
"""
|
||||
pass
|
||||
|
||||
@abc.abstractmethod
|
||||
def security_handshake(self, compute_sock):
|
||||
"""Perform security-type-specific functionality.
|
||||
|
||||
This method is expected to return the socket-like
|
||||
object used to communicate with the server securely.
|
||||
|
||||
Should raise exception.RFBAuthHandshakeFailed if
|
||||
an error occurs
|
||||
"""
|
||||
pass
|
|
@ -0,0 +1,24 @@
|
|||
# Copyright (c) 2014-2016 Red Hat, Inc
|
||||
#
|
||||
# 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.
|
||||
|
||||
from nova.console.rfb import auth
|
||||
|
||||
|
||||
class RFBAuthSchemeNone(auth.RFBAuthScheme):
|
||||
|
||||
def security_type(self):
|
||||
return auth.AuthType.NONE
|
||||
|
||||
def security_handshake(self, compute_sock):
|
||||
return compute_sock
|
|
@ -0,0 +1,44 @@
|
|||
# Copyright (c) 2014-2017 Red Hat, Inc
|
||||
#
|
||||
# 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.
|
||||
|
||||
from oslo_config import cfg
|
||||
|
||||
from nova.console.rfb import authnone
|
||||
from nova import exception
|
||||
|
||||
CONF = cfg.CONF
|
||||
|
||||
|
||||
class RFBAuthSchemeList(object):
|
||||
|
||||
AUTH_SCHEME_MAP = {
|
||||
"none": authnone.RFBAuthSchemeNone,
|
||||
}
|
||||
|
||||
def __init__(self):
|
||||
self.schemes = {}
|
||||
|
||||
for name in CONF.vnc.auth_schemes:
|
||||
scheme = self.AUTH_SCHEME_MAP[name]()
|
||||
|
||||
self.schemes[scheme.security_type()] = scheme
|
||||
|
||||
def find_scheme(self, desired_types):
|
||||
for security_type in desired_types:
|
||||
if security_type in self.schemes:
|
||||
return self.schemes[security_type]
|
||||
|
||||
raise exception.RFBAuthNoAvailableScheme(
|
||||
allowed_types=", ".join([str(s) for s in self.schemes.keys()]),
|
||||
desired_types=", ".join([str(s) for s in desired_types]))
|
|
@ -1767,6 +1767,15 @@ class SecurityProxyNegotiationFailed(NovaException):
|
|||
msg_fmt = _("Failed to negotiate security type with server: %(reason)s")
|
||||
|
||||
|
||||
class RFBAuthHandshakeFailed(NovaException):
|
||||
msg_fmt = _("Failed to complete auth handshake: %(reason)s")
|
||||
|
||||
|
||||
class RFBAuthNoAvailableScheme(NovaException):
|
||||
msg_fmt = _("No matching auth scheme: allowed types: '%(allowed_types)s', "
|
||||
"desired types: '%(desired_types)s'")
|
||||
|
||||
|
||||
class InvalidWatchdogAction(Invalid):
|
||||
msg_fmt = _("Provided watchdog action (%(action)s) is not supported.")
|
||||
|
||||
|
|
|
@ -0,0 +1,72 @@
|
|||
# Copyright (c) 2016 Red Hat, Inc
|
||||
#
|
||||
# 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.
|
||||
|
||||
import mock
|
||||
|
||||
from nova.console.rfb import auth
|
||||
from nova.console.rfb import authnone
|
||||
from nova.console.rfb import auths
|
||||
from nova import exception
|
||||
from nova import test
|
||||
|
||||
|
||||
class RFBAuthSchemeListTestCase(test.NoDBTestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(RFBAuthSchemeListTestCase, self).setUp()
|
||||
|
||||
self.flags(auth_schemes=["none"], group="vnc")
|
||||
|
||||
def test_load_ok(self):
|
||||
schemelist = auths.RFBAuthSchemeList()
|
||||
|
||||
security_types = sorted(schemelist.schemes.keys())
|
||||
self.assertEqual(security_types, [auth.AuthType.NONE])
|
||||
|
||||
def test_load_unknown(self):
|
||||
"""Ensure invalid auth schemes are not supported.
|
||||
|
||||
We're really testing oslo_policy functionality, but this case is
|
||||
esoteric enough to warrant this.
|
||||
"""
|
||||
self.assertRaises(ValueError, self.flags,
|
||||
auth_schemes=['none', 'wibble'], group='vnc')
|
||||
|
||||
def test_find_scheme_ok(self):
|
||||
schemelist = auths.RFBAuthSchemeList()
|
||||
|
||||
scheme = schemelist.find_scheme(
|
||||
[auth.AuthType.TIGHT,
|
||||
auth.AuthType.NONE])
|
||||
|
||||
self.assertIsInstance(scheme, authnone.RFBAuthSchemeNone)
|
||||
|
||||
def test_find_scheme_fail(self):
|
||||
schemelist = auths.RFBAuthSchemeList()
|
||||
|
||||
self.assertRaises(exception.RFBAuthNoAvailableScheme,
|
||||
schemelist.find_scheme,
|
||||
[auth.AuthType.TIGHT])
|
||||
|
||||
def test_find_scheme_priority(self):
|
||||
schemelist = auths.RFBAuthSchemeList()
|
||||
|
||||
tight = mock.MagicMock(spec=auth.RFBAuthScheme)
|
||||
schemelist.schemes[auth.AuthType.TIGHT] = tight
|
||||
|
||||
scheme = schemelist.find_scheme(
|
||||
[auth.AuthType.TIGHT,
|
||||
auth.AuthType.NONE])
|
||||
|
||||
self.assertEqual(tight, scheme)
|
|
@ -0,0 +1,36 @@
|
|||
# Copyright (c) 2014-2016 Red Hat, Inc
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# 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.
|
||||
|
||||
import mock
|
||||
|
||||
from nova.console.rfb import auth
|
||||
from nova.console.rfb import authnone
|
||||
from nova import test
|
||||
|
||||
|
||||
class RFBAuthSchemeNoneTestCase(test.NoDBTestCase):
|
||||
|
||||
def test_handshake(self):
|
||||
scheme = authnone.RFBAuthSchemeNone()
|
||||
|
||||
sock = mock.MagicMock()
|
||||
ret = scheme.security_handshake(sock)
|
||||
|
||||
self.assertEqual(sock, ret)
|
||||
|
||||
def test_types(self):
|
||||
scheme = authnone.RFBAuthSchemeNone()
|
||||
|
||||
self.assertEqual(auth.AuthType.NONE, scheme.security_type())
|
|
@ -0,0 +1,8 @@
|
|||
---
|
||||
features:
|
||||
- |
|
||||
Added a number of new configuration options to the ``[vnc]`` group, which
|
||||
together allow for the configuration of authentication used between the
|
||||
*nova-novncproxy* server and the compute node VNC server.
|
||||
|
||||
- ``auth_schemes``
|
Loading…
Reference in New Issue