Project-Scoped Resource Validation Flow in connection.py: OpenStack Multi-Tenant Security

The project-scoped resource validation flow guarantees strict tenant isolation by verifying that every requested OpenStack resource either belongs to the authenticated project or is explicitly marked as public, thereby preventing cross-project data leakage.

The mcp-openstack-ops repository implements a robust security layer to ensure multi-tenant isolation when interacting with OpenStack APIs. At the heart of this implementation lies the project-scoped resource validation flow, a comprehensive pipeline defined in src/mcp_openstack_ops/connection.py that filters all resource access through project ownership verification.

Core Components of the Validation Pipeline

The validation architecture consists of four interconnected helpers that form a defensive perimeter around resource operations:

  1. get_current_project_id() – Resolves the authenticated session's project identifier.
  2. validate_resource_ownership() – Inspects individual resources for ownership or public visibility.
  3. find_resource_by_name_or_id() – Locates specific resources while applying ownership filters.
  4. get_project_scoped_resources() – Retrieves and sanitizes entire service collections.

These functions work sequentially to ensure that tools in the tools/ package cannot accidentally expose resources from other tenants.

Resolving the Current Project Context

The foundation of the project-scoped resource validation flow rests on accurately identifying the active project. The get_current_project_id() function (lines 43-73 of connection.py) implements a resilient discovery mechanism:

def get_current_project_id() -> str:
    ...

Token inspection forms the primary resolution strategy. The function retrieves the authentication token via conn.identity.get_token() and extracts the project_id field (falling back to legacy tenant_id when necessary). If the token lacks this metadata, the implementation executes a name-based fallback, iterating through conn.identity.projects() to locate a project matching the OS_PROJECT_NAME environment variable. This dual-strategy approach ensures compatibility across different OpenStack identity configurations.

Enforcing Single-Resource Ownership Boundaries

Once the current project is established, the validate_resource_ownership() function (lines 78-112) serves as the gatekeeper for individual resource access. This helper accepts any resource object and determines whether it belongs to the current project or qualifies as publicly accessible:

def validate_resource_ownership(resource: Any, resource_type: str = "resource") -> bool:
    ...

Public resource shortcuts optimize the validation process. If a resource carries an is_public flag set to true, the function immediately returns True. Flavor handling receives special treatment—flavors typically lack project_id attributes and are treated as public resources by default. For image visibility, the logic recognizes public, community, and shared visibility states as valid bypass conditions.

When public shortcuts don't apply, the function performs project ID extraction by checking multiple attribute names: project_id, tenant_id, and owner. If none exist, the resource is classified as a system resource and permitted. Finally, the extracted identifier is compared against the current project ID; mismatches generate warnings and return False, effectively blocking unauthorized access.

Scoped Resource Discovery and Retrieval

For operations requiring specific resource lookup, find_resource_by_name_or_id() (lines 135-176) combines search functionality with ownership validation:

def find_resource_by_name_or_id(resources, name_or_id: str, resource_type: str = "resource") -> Optional[Any]:
    ...

The implementation iterates through the supplied resources iterable, collecting candidates where either name or id matches the query parameter. Each candidate then passes through validate_resource_ownership(). The function returns None if no owned matches exist, or the first valid match when multiple candidates are found (logging a warning about ambiguity).

For bulk operations, get_project_scoped_resources() (lines 188-207) provides comprehensive collection filtering:

def get_project_scoped_resources(conn, service_attr: str, resource_type: str = "resource") -> list:
    ...

This helper obtains the service object via getattr(conn, service_attr) (e.g., "compute" for Nova), converts the service iterator to a list, and builds a filtered result set containing only resources passing the ownership validation.

Practical Implementation Examples

Retrieving a Server by Name with Project Scoping

When searching for a specific instance, tools combine connection establishment with the validation flow:

from mcp_openstack_ops.connection import get_openstack_connection, find_resource_by_name_or_id

def get_my_server(name_or_id):
    conn = get_openstack_connection()
    all_servers = conn.compute.servers()
    server = find_resource_by_name_or_id(
        all_servers,
        name_or_id,
        resource_type="server"
    )
    if server:
        print(f"Found server {server.id} in current project")
    else:
        print("Server not found or not owned by this project")

This pattern ensures that even if another project contains a server with an identical name, only the current project's resources are returned.

Listing All Project-Scoped Networks

To enumerate network resources without cross-project leakage:

from mcp_openstack_ops.connection import get_openstack_connection, get_project_scoped_resources

def list_my_networks():
    conn = get_openstack_connection()
    networks = get_project_scoped_resources(conn, "network", "network")
    for net in networks:
        print(f"{net.id}: {net.name}")

The function automatically excludes networks owned by other tenants while preserving public networks when applicable.

Pre-Deletion Ownership Verification

Before executing destructive operations, explicit validation provides an additional safety layer:

from mcp_openstack_ops.connection import get_openstack_connection, validate_resource_ownership

def delete_my_volume(volume_id):
    conn = get_openstack_connection()
    volume = conn.block_storage.get_volume(volume_id)
    if validate_resource_ownership(volume, "volume"):
        conn.block_storage.delete_volume(volume, ignore_missing=False)
        print("Volume deleted")
    else:
        print("Cannot delete: volume belongs to another project")

Summary

The project-scoped resource validation flow in connection.py provides a comprehensive security framework for OpenStack automation:

  • Token-based project resolution ensures accurate identification of the authenticated context through get_current_project_id().
  • Multi-factor ownership checks in validate_resource_ownership() handle public resources, flavors, images, and system objects appropriately.
  • Integrated search and filter operations via find_resource_by_name_or_id() and get_project_scoped_resources() prevent cross-project data exposure.
  • Verbose warning logs alert operators when ownership mismatches occur, aiding in debugging without breaking execution flows.

Frequently Asked Questions

How does the validation flow handle public images in OpenStack?

Public images bypass project ownership checks through the visibility attribute. The validate_resource_ownership() function recognizes images with visibility set to public, community, or shared as valid resources regardless of their owner field, allowing tools to discover and utilize shared infrastructure images while maintaining isolation for private assets.

What happens when a resource lacks a project_id attribute?

Resources without project_id, tenant_id, or owner attributes are classified as system resources and permitted by default. This design accommodates OpenStack service objects like public flavors and certain system images that exist outside standard project ownership models but are intended for universal access.

Can the validation flow be bypassed for administrative operations?

The validation flow cannot be bypassed through the provided helper functions in connection.py. Administrative tools requiring cross-project visibility must use the raw OpenStack SDK connection methods directly rather than the find_resource_by_name_or_id() or get_project_scoped_resources() wrappers, ensuring that standard operations maintain strict project isolation by default.

Where is the project-scoped validation logic located in the codebase?

The core validation logic resides in src/mcp_openstack_ops/connection.py, specifically lines 43-207, implementing the four primary helper functions. Consumer tools throughout src/mcp_openstack_ops/tools/ import these functions to enforce project isolation, with test coverage provided in test_project_isolation.py to verify the isolation mechanisms function correctly across different resource types.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →