Why Row-Level Security Matters in Milvus 2.6
Milvus, a popular vector database, thrives in scenarios requiring fast similarity search and analytics over large-scale vector data. With the rise of multi-tenant deployments and cloud-native architectures, enforcing fine-grained access control becomes critical. Row-level security (RLS) allows administrators to restrict data access at the individual row level, ensuring users only see data they are authorized to access.
Before this backport, Milvus 2.6 lacked RLS support, limiting its usability in secure, multi-tenant environments. This article explores the technical challenge of backporting the RLS feature from a newer Milvus version into 2.6, highlighting the intricacies of adapting modern security logic to an older codebase.
The Problem: Missing Row-Level Security in Milvus 2.6
Milvus 2.6 users faced a significant limitation: no ability to enforce row-level access policies during queries, data manipulation language (DML) operations, or bulk imports. This gap meant that all users with access to a collection could potentially retrieve or modify any data in it, violating the principle of least privilege.
The original RLS implementation introduced in later Milvus versions depended on a set of APIs, data structures, and synchronization mechanisms absent in 2.6. For example:
- Advanced metadata management: Newer versions use enhanced metadata stores to keep track of policies and tags.
- Proxy synchronization: Proxies maintain consistent snapshots of policies and tags to enforce security uniformly.
- Policy evaluation logic: Complex evaluation engines determine whether a given user query or DML operation complies with RLS policies.
- Protobuf and import parsing: Updated serialization formats and import mechanisms support richer policy data.
These dependencies made a straightforward backport impossible, as 2.6's codebase lacked the necessary infrastructure.
The Technical Solution: Backporting RLS with Compatibility
The core challenge was to isolate the RLS feature set from its newer dependencies and adapt it to fit within the constraints of Milvus 2.6. The backport involved several key technical steps:
1. Metadata Handling Adaptation
RLS relies heavily on metadata to store policies and tags associated with rows. The backport reimplemented metadata handling using 2.6's existing storage and retrieval mechanisms, ensuring that policy data could be stored, updated, and queried without relying on newer metadata APIs.
2. Proxy Synchronization Without New Membership Filters
In newer versions, proxies synchronize policy and tag snapshots using advanced membership filters to maintain consistency. Since these filters were unavailable in 2.6, the backport introduced a synchronization mechanism compatible with the older proxy architecture, ensuring that all proxies had coherent views of the current policies and tags.
3. Policy Evaluation and Enforcement
The evaluation engine was adapted to work with the older protobuf definitions and API constraints. This involved:
- Decoding policy tags correctly with 2.6's protobuf support.
- Validating policies in batches to minimize performance overhead.
- Recovering policy states gracefully after restarts or failures.
The enforcement logic was integrated into query processing, DML operations, and bulk imports, ensuring that row-level policies were consistently applied across all data access paths.
4. Maintaining Parser and Import Mechanisms
To avoid regressions, the backport preserved 2.6’s existing parser and bulk import mechanisms. Instead of replacing these components, the RLS feature was carefully woven into the existing code paths, ensuring seamless integration without disrupting established workflows.
5. Incorporating Fixes from the RLS7 Branch
The backport included critical fixes originally developed in the RLS7 branch, such as tag decoding improvements, batch validation enhancements, and policy recovery strategies. These fixes were adapted to 2.6’s environment, ensuring parity in behavior with the master branch.
Lessons Learned: Backporting Complex Features Across Versions
This backport illustrates several important lessons for engineering teams:
- Decouple core logic from dependencies: Successful backporting requires isolating feature logic from newer APIs and data structures, enabling adaptation to older environments.
- Adapt serialization and parsing carefully: Differences in protobuf versions and import formats can cause subtle bugs, so thorough testing and adjustment are essential.
- Preserve existing functionality: Integrating new features should avoid disrupting established workflows, particularly in widely used components like parsers and importers.
- Synchronize distributed components thoughtfully: Proxy synchronization mechanisms must be compatible with the target version’s architecture to maintain consistency.
By carefully balancing these factors, the Milvus team enabled secure, multi-tenant usage of 2.6 without sacrificing stability or performance.
This article is based on the Milvus PR titled "feat: [2.6] backport row-level security" from the milvus-io/milvus repository. Read the full breakdown on nossletter.tech.
Originally published on Nossletter.
Top comments (0)