Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Provide builder for KBS Protocol Wrapper #278

Conversation

mkulke
Copy link
Contributor

@mkulke mkulke commented Jul 12, 2023

Currently KbsProtocolWrapper bootstraps itself. It detects a TEE and attempts to create an Attester object. There are situations in which it is desirable to inject an Attester implementation from the caller side (e.g. we want to wrap get_evidence() for a given TEE in an IPC call).

(discussion here, for more context)

This is made possible by providing a builder for the wrapper. Existing code should work without modifications. The builder requires a tee and accepts an attester object optionally.

let wrapper = KbsProtocolWrapperBuilder::new().with_tee(Tee::Snp).build();
# or
let wrapper = KbsProtocolWrapperBuilder::new()
  .with_tee(Tee::Tdx)
  .with_attester(brought_my_own)
  .build();

There are some drive-by fixes, as the type KBSProtocolWrapper currently doesn't reflect certain invariants:

  • nonce should not default to an empty string, we want to bail out if it's unset
  • attester and tee_key otoh shouldn't be wrapped in Options, the wrapper will not work w/o them.
  • tee_key can be passed by ref

@mkulke mkulke requested a review from jialez0 as a code owner July 12, 2023 18:08
@mkulke mkulke force-pushed the mkulke/add-builder-for-kbsprotocolwrapper branch 3 times, most recently from b1d98aa to e6e878a Compare July 13, 2023 08:59
http_client: reqwest::Client,
authenticated: bool,
}

struct NoTee;
Copy link
Member

Choose a reason for hiding this comment

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

Why we use generic types here? I saw builder in projects like reqwest and in-toto uses the following, which seems more straightforward

pub struct KbsProtocolWrapperBuilder {
    tee: Option<..>,
}

impl KbsProtocolWrapperBuilder {
    pub fn with_tee(mut self, tee: ..) -> Self {
        self.tee = Some(tee);
        self
    } 
}

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Why we use generic types here? I saw builder in projects like reqwest and in-toto uses the following, which seems more straightforward

pub struct KbsProtocolWrapperBuilder {
    tee: Option<..>,
}

impl KbsProtocolWrapperBuilder {
    pub fn with_tee(mut self, tee: ..) -> Self {
        self.tee = Some(tee);
        self
    } 
}

Yeah, this is a more pragmatic way to create builders. If you call .build() and the builder struct has Option<...> types, you need to perform runtime checks to verify tee is actually set (e.g. return Result<KbsProtocolWrapper> or panic even).

If you use encode the requirements via generics the verification happens at compile time, you are not able to call build() unless you called .with_tee().

The type-level verification is admittedly more verbose, and I just noticed we already need to return a Result<KbsProtocolWrapper> from .build(), so i'd be fine with changing this to a builder w/ Option<...> properties. What do you prefer?

Copy link
Member

Choose a reason for hiding this comment

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

Generic types actually will save runtime. I'm ok with both. However there is a naive question from myside. I think the main aim of the PR is to avoid creating an Attester from the TEE detection heuristic inside new() of KbsProtocolWrapper, but to receive an outer Attester parameter. If so, why do not directly add a function with the signature

pub fn new(attester: BoxedAttester) -> Self {
    Self {
        attester,
        ...
    }
}

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Generic types actually will save runtime. I'm ok with both. However there is a naive question from myside. I think the main aim of the PR is to avoid creating an Attester from the TEE detection heuristic inside new() of KbsProtocolWrapper, but to receive an outer Attester parameter. If so, why do not directly add a function with the signature

pub fn new(attester: BoxedAttester) -> Self {
    Self {
        attester,
        ...
    }
}

you mean pub fn new_with_attester() or simply replacing the existing pub fn new() function?

if we can do that, the builder pattern is too much. it would maybe be useful if we would want to inject an HTTP client (for unit testing or having a generic RPC client), but we can also add it later.

Copy link
Member

@Xynnn007 Xynnn007 left a comment

Choose a reason for hiding this comment

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

LGTM. cc @jialez0 please take a look if it is ok to you

@mkulke mkulke force-pushed the mkulke/add-builder-for-kbsprotocolwrapper branch from e6e878a to bff7053 Compare July 20, 2023 10:26
@Xynnn007
Copy link
Member

Hi @mkulke Can you rebase the PR?

Signed-off-by: Magnus Kulke <magnuskulke@microsoft.com>
Signed-off-by: Magnus Kulke <magnuskulke@microsoft.com>
@mkulke mkulke force-pushed the mkulke/add-builder-for-kbsprotocolwrapper branch from 59fcb52 to b401624 Compare July 27, 2023 07:44
@Xynnn007
Copy link
Member

@mkulke Well. Seems that some coupled code was broken by this PR, s.t. Kbc::new() which is called in a unit test

@mkulke
Copy link
Contributor Author

mkulke commented Jul 27, 2023

@mkulke Well. Seems that some coupled code was broken by this PR, s.t. Kbc::new() which is called in a unit test

oh, that's a surprise. i'll look into that.

Signed-off-by: Magnus Kulke <magnuskulke@microsoft.com>
@mkulke
Copy link
Contributor Author

mkulke commented Jul 27, 2023

@mkulke Well. Seems that some coupled code was broken by this PR, s.t. Kbc::new() which is called in a unit test

oh, that's a surprise. i'll look into that.

Ok, apparently those unit tests construct a kbs protocol wrapper w/ an attester: None as a side effect. The PR removed the Option wrapping, as having an attester in a kbc client is an invariant (i.e. clients w/o attester don't work) => tests fail w/ TEE not supported.

The tests in question are URI parsing tests however, so they don't mind a non-functional kbc client.

Refactoring the tests/code to untangle uri parsing from the kbc client and unit testing just that is probably the right fix long-term. The unit tests atm perform IO side-effects as they detect the tee which adds delay and faux errors to the log.

Short term I see 2 ways to address that:

  • Restoring the Option wrapping for Attester
  • set AA_SAMPLE_ATTESTER_TEST=1 in the unit test, so we'll get a proper a Attester trait object.

I don't particularly like either option, but restoring the Option wrapper is probably the most sensible option in the scope of this PR.

@fitzthum
Copy link
Member

There are situations in which it is desirable to inject an Attester implementation from the caller side (e.g. we want to wrap get_evidence() for a given TEE in an IPC call).

I am missing something at the high level. When would we want to use an Attester other than the one that we would currently create with the TEE detection heuristic?

@mkulke
Copy link
Contributor Author

mkulke commented Jul 27, 2023

There are situations in which it is desirable to inject an Attester implementation from the caller side (e.g. we want to wrap get_evidence() for a given TEE in an IPC call).

I am missing something at the high level. When would we want to use an Attester other than the one that we would currently create with the TEE detection heuristic?

One scenario would be a kbs-client which is not able to access the TEE's evidence-retrieval facilities directly (like in peer-pods where the network is segmented), without having to replicate the kbs client logic. Another scenario would be the injection of a mock attester in a unit test for the RCAR logic.

@Xynnn007 Xynnn007 merged commit f487cdc into confidential-containers:main Jul 28, 2023
9 checks passed
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