Skip to content

External plugin support for KBS#1188

Open
bpradipt wants to merge 8 commits intoconfidential-containers:mainfrom
bpradipt:ext-plugin-v3
Open

External plugin support for KBS#1188
bpradipt wants to merge 8 commits intoconfidential-containers:mainfrom
bpradipt:ext-plugin-v3

Conversation

@bpradipt
Copy link
Member

Creating this as a draft PR to make it easier to discuss and collaborate.

@bpradipt bpradipt force-pushed the ext-plugin-v3 branch 5 times, most recently from ce4183c to b9298bd Compare February 27, 2026 06:47
Copy link
Member

@fitzthum fitzthum left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A couple comments. Generally I think this is a fine approach, but I would try to reduce the scope as much as possible. Probably don't need the example implementations (in this repo at least).

Also, why are we modifying the existing plugins?

@bpradipt bpradipt force-pushed the ext-plugin-v3 branch 2 times, most recently from f500ee6 to 98ee342 Compare February 27, 2026 17:48
@bpradipt
Copy link
Member Author

A couple comments. Generally I think this is a fine approach, but I would try to reduce the scope as much as possible. Probably don't need the example implementations (in this repo at least).

Also, why are we modifying the existing plugins?

The signature for handle() required changing for ext plugin:

  • attestation/session context forwarding
  • custom http status codes and content in response

Would you prefer not touching the built-in plugins at all ?

@bpradipt bpradipt marked this pull request as ready for review February 27, 2026 18:07
@bpradipt bpradipt requested a review from a team as a code owner February 27, 2026 18:07
@fitzthum
Copy link
Member

Would you prefer not touching the built-in plugins at all ?

Hmm. Those are reasonable features, but might be best to start with the simplest possible approach and then improve it if people have the requirement.

@Xynnn007
Copy link
Member

Xynnn007 commented Feb 28, 2026

Hi @bpradipt I am excited to see the work and it's a pity I didn't participate in the community weekly meeting and related discussions.

I have had the similar idea with you that the core value of KBS is 1) RCAR handshake protocol 2)
Supports complex external plugin calls that rely on remote attestation results.

Regarding the second point, I once proposed an RFC to address the issue of unified abstraction of different attestation service tokens/results encountered by different plugins. That thread might change some API but we can perfect them later.

Our original design allowed for custom backend implementations of the plugin, meaning that backend plugins could connect to external services via gRPC, RESTful, or any other communication method (IPC, net, etc.).

Before diving into reviewing the huge PR, at a quick glance. I'm pleased to see the implementation utilizing a sub-plugin (external) as a proprietary method for gRPC calls to external connections.

As @fitzthum points out, we might not need the sdk code to be included in the repo. There are some precedents; we added a Go SDK to CDH, but it was never maintained and was eventually removed. Therefore, it's recommended to just keep the proto in the repo.

Anyway, I will take a review once these "weight loss work" is done for the PR.

@bpradipt bpradipt force-pushed the ext-plugin-v3 branch 3 times, most recently from b327d48 to 03a512e Compare March 4, 2026 17:33
@bpradipt bpradipt requested a review from fitzthum March 5, 2026 02:36
@bpradipt
Copy link
Member Author

bpradipt commented Mar 5, 2026

@fitzthum @Xynnn007 ptal. I tried to keep the changes to existing code at a minimum.

/// from the session store. For Bearer token auth, KBS has already verified
/// the token before reaching the plugin handler, so is_attested is set to
/// true; tee_type is not available without re-parsing the JWT claims.
pub async fn get_plugin_context(&self, request: &HttpRequest) -> crate::plugins::PluginContext {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like the idea of pluginContext as it shares a same idea with mine to transform the complex attestation context into a simple one for different plugins to use.

I want to do an abstraction upon this PluginContext here as a core component working in kbs logic, probably naming to TrustContext.

Currently the is_attested only means it has an attestation token binding to the session, while an attestation token only means the endorsement is verified based on the current CoCoAS blackbox. This can be perfect-ted in future.

SessionStatus::Authed { request: req, .. } => {
let tee_type = serde_json::to_string(&req.tee)
.ok()
.map(|s| s.trim_matches('"').to_owned());
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not related to this PR. We are always trying to do trim options upon the Tee type's serialization. We need to impl a ToString or some trait for Tee someday.


#[cfg(feature = "external-plugin")]
#[derive(Clone, Debug, Deserialize, PartialEq)]
pub struct ExternalPluginConfig {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I saw a lot of feature flags external-plugin. Can we move the definitions into a separate module implementations/external_plugin.rs

Comment on lines +213 to +217
pub enum PluginInstance {
BuiltIn(Arc<dyn ClientPlugin>),
#[cfg(feature = "external-plugin")]
External(Arc<GrpcPluginProxy>),
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I noticed that the user-side toml definitions still follows the previous style, s.t. name item is used to choose external. Thus we can organize the code in a style that external is treated as a same-level plugin as current pkcs11, sample, etc. Thus we might not need this wrapper. Just add a new implementations/external.rs - or if more files are needed, implementations/external/mod.rs to have the external plugins forwarder.

Copy link
Member Author

@bpradipt bpradipt Mar 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is intentional to avoid changing the existing ClientPlugin trait. External plugins need to forward request context (session ID, TEE type, attestation status) as gRPC metadata, which required a separate dispatch path (handle_with_context). Adding &PluginContext toClientPlugin::handle() would have been a breaking change for all existing plugin implementations.

Infact my earlier implementation was around the approach you described but resulted in changing the existing plugins. So after @fitzthum comment I went with the current approach.

Happy to revisit if there's a preferred way to extend the trait without breaking existing plugins.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In fact, in my view, the only difference between external plugin calls and internal integrations is whether or not a gRPC forwarder is involved; they are essentially the same. Both calls need to accept the same parameters, regardless of how the parameters are designed. There is no problems upon the trust relations.

        body: &[u8],
        query: &HashMap<String, String>,
        path: &[&str],
        method: &Method,
        context: &PluginContext,

[workspace]
members = [
"kbs",
"rust-sdk",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if we need this rust-sdk. IMOO, a protobuf is enough. But we can keep this for some time and see if the maintaince is good.

config.plugin_name.clone(),
));

let timeout = config.timeout_ms.map(Duration::from_millis);

Check failure

Code scanning / CodeQL

Cleartext logging of sensitive information High

This operation writes
ensure_auth_key_pair(...)
to a log file.
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like a false positive. Wrongly attributing ensure_auth_key_pair to external_plugin.rs

Comment on lines +29 to +32
use crate::plugins::external::plugin_api::{
kbs_plugin_client::KbsPluginClient, GetCapabilitiesRequest, NeedsEncryptionRequest,
PluginRequest, ValidateAuthRequest,
};
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can also include the code of crate::plugins::external into this file and delete external.rs?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good suggestion @Xynnn007

bpradipt added 5 commits March 9, 2026 11:06
- Add KbsPlugin service with Handle and GetCapabilities RPCs
- Define PluginRequest with body, query, path, method fields
- Define PluginResponse with body, status_code, content_type fields
- Reserve field 5 for future attestation context
- Set up buf tooling for proto linting and CI

Signed-off-by: Pradipta Banerjee <pradipta.banerjee@gmail.com>
Introduces the gRPC proxy backend for external plugins and wires it into
the KBS plugin dispatch layer:

- api_server.rs: switch to async PluginManager::new() (external plugin
  init requires gRPC TLS channel setup); add PUT/DELETE route handlers
  so external plugins can implement full REST APIs; build PluginContext
  from session/attestation state to pass TEE metadata without raw tokens;
  replace plugin.handle() with plugin.dispatch() returning PluginOutput
  (body + optional HTTP status + content type); downcast PluginCallError
  so external plugins can return structured HTTP error responses.

- implementations/mod.rs: feature-gate the grpc_proxy module and
  re-export GrpcPluginProxy under the external-plugin feature flag.

Signed-off-by: Pradipta Banerjee <pradipta.banerjee@gmail.com>
Basic metrics for external plugin

Signed-off-by: Pradipta Banerjee <pradipta.banerjee@gmail.com>
Rust SDK for external plugin

Signed-off-by: Pradipta Banerjee <pradipta.banerjee@gmail.com>
Signed-off-by: Pradipta Banerjee <pradipta.banerjee@gmail.com>
bpradipt added 3 commits March 9, 2026 11:06
Adds an external gRPC resource plugin example (`resource_plugin.rs`) that
replicates the built-in KBS resource plugin behaviour exactly:

- POST (store) is admin-gated (validate_auth = true)
- GET (retrieve) is attestation-gated (validate_auth = false)
- GET responses are JWE-encrypted with the TEE public key (needs_encryption = true)
- POST responses are plain acknowledgements (needs_encryption = false)
- Returns gRPC NotFound on GET for missing keys (mirrors built-in 404)

The plugin maintains an in-memory key-value store using
`Arc<RwLock<HashMap<String, Vec<u8>>>>` and registers itself with the
capabilities name "extstore". The default listen address is 127.0.0.1:50053
and can be overridden via `RESOURCE_PLUGIN_LISTEN_ADDR`.

Also adds a full e2e roundtrip test in `kbs/test/Makefile`:
- Builds the plugin binary and starts it on :50053
- Starts KBS on :8088 with a config that routes requests through the plugin
- POSTs a random 16-byte secret via the admin-gated endpoint
- GETs it back via kbs-client
- Diffs the result against the original

Signed-off-by: Pradipta Banerjee <pradipta.banerjee@gmail.com>
- Introduction, Quick Start, Writing a Plugin, Building and Packaging
- Quick Start examples in Rust, Go, and Python
- Handler implementation guide with request/response field tables
- gRPC metadata access patterns for session context
- Error handling with gRPC-to-HTTP status code mapping
- Containerization and CI/CD integration patterns
- KBS TOML configuration with TLS mode examples (insecure/tls/mtls)
- Standalone and Kubernetes deployment patterns with manifests
- Health check integration with grpc.health.v1.Health
- Per-plugin Prometheus metrics documentation (requests, duration, errors)
- Links to proto file, SDK docs, and echo plugin examples

Signed-off-by: Pradipta Banerjee <pradipta.banerjee@gmail.com>
Runs the `make e2e-ext-plugin` target (plaintext, TLS, attestation,
metrics) on PRs that touch plugin source, config, or test files.

Signed-off-by: Pradipta Banerjee <pradipta.banerjee@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants