-
Notifications
You must be signed in to change notification settings - Fork 51
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #58460 from rkachach/fix_issue_oauth2_support
adding support for SSO based on auth2-proxy Reviewed-by: Adam King <[email protected]>
- Loading branch information
Showing
24 changed files
with
1,376 additions
and
147 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,139 @@ | ||
.. _deploy-cephadm-oauth2-proxy: | ||
|
||
================== | ||
OAuth2 Proxy | ||
================== | ||
|
||
Deploying oauth2-proxy | ||
====================== | ||
|
||
In Ceph releases starting from Squid, the `oauth2-proxy` service introduces an advanced method | ||
for managing authentication and access control for Ceph applications. This service integrates | ||
with external Identity Providers (IDPs) to provide secure, flexible authentication via the | ||
OIDC (OpenID Connect) protocol. `oauth2-proxy` acts as an authentication gateway, ensuring that | ||
access to Ceph applications including the Ceph Dashboard and monitoring stack is tightly controlled. | ||
|
||
To deploy the `oauth2-proxy` service, use the following command: | ||
|
||
.. prompt:: bash # | ||
|
||
ceph orch apply oauth2-proxy [--placement ...] ... | ||
|
||
Once applied, `cephadm` will re-configure the necessary components to use `oauth2-proxy` for authentication, | ||
thereby securing access to all Ceph applications. The service will handle login flows, redirect users | ||
to the appropriate IDP for authentication, and manage session tokens to facilitate seamless user access. | ||
|
||
|
||
Benefits of the oauth2-proxy service | ||
==================================== | ||
* ``Enhanced Security``: Provides robust authentication through integration with external IDPs using the OIDC protocol. | ||
* ``Seamless SSO``: Enables seamless single sign-on (SSO) across all Ceph applications, improving user access control. | ||
* ``Centralized Authentication``: Centralizes authentication management, reducing complexity and improving control over access. | ||
|
||
|
||
Security enhancements | ||
===================== | ||
|
||
The `oauth2-proxy` service ensures that all access to Ceph applications is authenticated, preventing unauthorized users from | ||
accessing sensitive information. Since it makes use of the `oauth2-proxy` open source project, this service integrates | ||
easily with a variety of `external IDPs <https://oauth2-proxy.github.io/oauth2-proxy/configuration/providers/>`_ to provide | ||
a secure and flexible authentication mechanism. | ||
|
||
|
||
High availability | ||
============================== | ||
`oauth2-proxy` is designed to integrate with an external IDP hence login high availability is not the responsibility of this | ||
service. In squid release high availability for the service itself is not supported yet. | ||
|
||
|
||
Accessing services with oauth2-proxy | ||
==================================== | ||
|
||
After deploying `oauth2-proxy`, access to Ceph applications will require authentication through the configured IDP. Users will | ||
be redirected to the IDP for login and then returned to the requested application. This setup ensures secure access and integrates | ||
seamlessly with the Ceph management stack. | ||
|
||
|
||
Service Specification | ||
===================== | ||
|
||
Before deploying `oauth2-proxy` service please remember to deploy the `mgmt-gateway` service by turning on the `--enable_auth` flag. i.e: | ||
|
||
.. prompt:: bash # | ||
|
||
ceph orch apply mgmt-gateway --enable_auth=true | ||
|
||
An `oauth2-proxy` service can be applied using a specification. An example in YAML follows: | ||
|
||
.. code-block:: yaml | ||
service_type: oauth2-proxy | ||
service_id: auth-proxy | ||
placement: | ||
hosts: | ||
- ceph0 | ||
spec: | ||
https_address: "0.0.0.0:4180" | ||
provider_display_name: "My OIDC Provider" | ||
client_id: "your-client-id" | ||
oidc_issuer_url: "http://192.168.100.1:5556/dex" | ||
client_secret: "your-client-secret" | ||
cookie_secret: "your-cookie-secret" | ||
ssl_certificate: | | ||
-----BEGIN CERTIFICATE----- | ||
MIIDtTCCAp2gAwIBAgIYMC4xNzc1NDQxNjEzMzc2MjMyXzxvQ7EcMA0GCSqGSIb3 | ||
DQEBCwUAMG0xCzAJBgNVBAYTAlVTMQ0wCwYDVQQIDARVdGFoMRcwFQYDVQQHDA5T | ||
[...] | ||
-----END CERTIFICATE----- | ||
ssl_certificate_key: | | ||
-----BEGIN PRIVATE KEY----- | ||
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQC5jdYbjtNTAKW4 | ||
/CwQr/7wOiLGzVxChn3mmCIF3DwbL/qvTFTX2d8bDf6LjGwLYloXHscRfxszX/4h | ||
[...] | ||
-----END PRIVATE KEY----- | ||
Fields specific to the ``spec`` section of the `oauth2-proxy` service are described below. More detailed | ||
description of the fields can be found on `oauth2-proxy <https://oauth2-proxy.github.io/oauth2-proxy/>`_ | ||
project documentation. | ||
|
||
|
||
.. py:currentmodule:: ceph.deployment.service_spec | ||
.. autoclass:: OAuth2ProxySpec | ||
:members: | ||
|
||
The specification can then be applied by running the below command. Once becomes available, cephadm will automatically redeploy | ||
the `mgmt-gateway` service while adapting its configuration to redirect the authentication to the newly deployed `oauth2-service`. | ||
|
||
.. prompt:: bash # | ||
|
||
ceph orch apply -i oauth2-proxy.yaml | ||
|
||
|
||
Limitations | ||
=========== | ||
|
||
A non-exhaustive list of important limitations for the `oauth2-proxy` service follows: | ||
|
||
* High-availability configurations for `oauth2-proxy` itself are not supported. | ||
* Proper configuration of the IDP and OAuth2 parameters is crucial to avoid authentication failures. Misconfigurations can lead to access issues. | ||
|
||
|
||
Default images | ||
~~~~~~~~~~~~~~ | ||
|
||
The `oauth2-proxy` service typically uses the default container image: | ||
|
||
:: | ||
|
||
DEFAULT_OAUTH2_PROXY = 'quay.io/oauth2-proxy/oauth2-proxy:v7.2.0' | ||
|
||
Admins can specify the image to be used by changing the `container_image_oauth2_proxy` cephadm module option. If there were already running daemon(s), | ||
you must redeploy the daemon(s) to apply the new image. | ||
|
||
For example: | ||
|
||
.. code-block:: bash | ||
ceph config set mgr mgr/cephadm/container_image_oauth2_proxy <new-oauth2-proxy-image> | ||
ceph orch redeploy oauth2-proxy |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,165 @@ | ||
import logging | ||
import os | ||
from typing import Dict, List, Tuple, Optional | ||
import re | ||
|
||
from ..call_wrappers import call, CallVerbosity | ||
from ..container_daemon_form import ContainerDaemonForm, daemon_to_container | ||
from ..container_types import CephContainer | ||
from ..context import CephadmContext | ||
from ..context_getters import fetch_configs | ||
from ..daemon_form import register as register_daemon_form | ||
from ..daemon_identity import DaemonIdentity | ||
from ..deployment_utils import to_deployment_container | ||
from ..constants import DEFAULT_OAUTH2_PROXY_IMAGE, UID_NOBODY, GID_NOGROUP | ||
from ..data_utils import dict_get, is_fsid | ||
from ..file_utils import populate_files, makedirs, recursive_chown | ||
from ..exceptions import Error | ||
|
||
|
||
logger = logging.getLogger() | ||
|
||
|
||
@register_daemon_form | ||
class OAuth2Proxy(ContainerDaemonForm): | ||
"""Define the configs for the jaeger tracing containers""" | ||
|
||
default_image = DEFAULT_OAUTH2_PROXY_IMAGE | ||
daemon_type = 'oauth2-proxy' | ||
required_files = [ | ||
'oauth2-proxy.conf', | ||
'oauth2-proxy.crt', | ||
'oauth2-proxy.key', | ||
] | ||
|
||
@classmethod | ||
def for_daemon_type(cls, daemon_type: str) -> bool: | ||
return cls.daemon_type == daemon_type | ||
|
||
def __init__( | ||
self, | ||
ctx: CephadmContext, | ||
fsid: str, | ||
daemon_id: str, | ||
config_json: Dict, | ||
image: str = DEFAULT_OAUTH2_PROXY_IMAGE, | ||
): | ||
self.ctx = ctx | ||
self.fsid = fsid | ||
self.daemon_id = daemon_id | ||
self.image = image | ||
self.files = dict_get(config_json, 'files', {}) | ||
self.validate() | ||
|
||
@classmethod | ||
def init( | ||
cls, ctx: CephadmContext, fsid: str, daemon_id: str | ||
) -> 'OAuth2Proxy': | ||
return cls(ctx, fsid, daemon_id, fetch_configs(ctx), ctx.image) | ||
|
||
@classmethod | ||
def create( | ||
cls, ctx: CephadmContext, ident: DaemonIdentity | ||
) -> 'OAuth2Proxy': | ||
return cls.init(ctx, ident.fsid, ident.daemon_id) | ||
|
||
@property | ||
def identity(self) -> DaemonIdentity: | ||
return DaemonIdentity(self.fsid, self.daemon_type, self.daemon_id) | ||
|
||
def container(self, ctx: CephadmContext) -> CephContainer: | ||
ctr = daemon_to_container(ctx, self) | ||
return to_deployment_container(ctx, ctr) | ||
|
||
def uid_gid(self, ctx: CephadmContext) -> Tuple[int, int]: | ||
return UID_NOBODY, GID_NOGROUP | ||
|
||
def get_daemon_args(self) -> List[str]: | ||
return [ | ||
'--config=/etc/oauth2-proxy.conf', | ||
'--tls-cert-file=/etc/oauth2-proxy.crt', | ||
'--tls-key-file=/etc/oauth2-proxy.key', | ||
] | ||
|
||
def default_entrypoint(self) -> str: | ||
return '' | ||
|
||
def create_daemon_dirs(self, data_dir: str, uid: int, gid: int) -> None: | ||
"""Create files under the container data dir""" | ||
if not os.path.isdir(data_dir): | ||
raise OSError('data_dir is not a directory: %s' % (data_dir)) | ||
logger.info('Writing oauth2-proxy config...') | ||
config_dir = os.path.join(data_dir, 'etc/') | ||
makedirs(config_dir, uid, gid, 0o755) | ||
recursive_chown(config_dir, uid, gid) | ||
populate_files(config_dir, self.files, uid, gid) | ||
|
||
def validate(self) -> None: | ||
if not is_fsid(self.fsid): | ||
raise Error(f'not an fsid: {self.fsid}') | ||
if not self.daemon_id: | ||
raise Error(f'invalid daemon_id: {self.daemon_id}') | ||
if not self.image: | ||
raise Error(f'invalid image: {self.image}') | ||
|
||
# check for the required files | ||
if self.required_files: | ||
for fname in self.required_files: | ||
if fname not in self.files: | ||
raise Error( | ||
'required file missing from config-json: %s' % fname | ||
) | ||
|
||
@staticmethod | ||
def get_version(ctx: CephadmContext, container_id: str) -> Optional[str]: | ||
"""Return the version of the oauth2-proxy container""" | ||
version = None | ||
out, err, code = call( | ||
ctx, | ||
[ | ||
ctx.container_engine.path, | ||
'exec', | ||
container_id, | ||
'oauth2-proxy', | ||
'--version', | ||
], | ||
verbosity=CallVerbosity.QUIET, | ||
) | ||
if code == 0: | ||
match = re.search(r'oauth2-proxy (v\d+\.\d+\.\d+)', out) | ||
if match: | ||
version = match.group(1) | ||
return version | ||
|
||
def customize_container_mounts( | ||
self, ctx: CephadmContext, mounts: Dict[str, str] | ||
) -> None: | ||
data_dir = self.identity.data_dir(ctx.data_dir) | ||
mounts.update( | ||
{ | ||
os.path.join( | ||
data_dir, 'etc/oauth2-proxy.conf' | ||
): '/etc/oauth2-proxy.conf:Z', | ||
os.path.join( | ||
data_dir, 'etc/oauth2-proxy.crt' | ||
): '/etc/oauth2-proxy.crt:Z', | ||
os.path.join( | ||
data_dir, 'etc/oauth2-proxy.key' | ||
): '/etc/oauth2-proxy.key:Z', | ||
} | ||
) | ||
|
||
def customize_container_args( | ||
self, ctx: CephadmContext, args: List[str] | ||
) -> None: | ||
uid, _ = self.uid_gid(ctx) | ||
other_args = [ | ||
'--user', | ||
str(uid), | ||
] | ||
args.extend(other_args) | ||
|
||
def customize_process_args( | ||
self, ctx: CephadmContext, args: List[str] | ||
) -> None: | ||
args.extend(self.get_daemon_args()) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.