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

Rework the attestation facade #54

Merged
merged 3 commits into from
Oct 24, 2024
Merged

Rework the attestation facade #54

merged 3 commits into from
Oct 24, 2024

Conversation

bojidar-bg
Copy link
Contributor

This PR reworks tpod-proxy/tpodproxy to be a proxy for the actual underlying HTTP pod, and not a proxy for the tpodserver. The main benefit of doing this is that this way users can connect to a specific version of the target pod, and know that the request will fail if the publisher updates the pod later on—thus eliminating a possible TOCTOU (time to check to time to use) vulnerability where a user checks a pod before sending a request, but the publisher swaps the pod out in the meantime. This also happens to bring the implementation closer to the design in #35.

Architecturally the proxy implemented by this PR is part of the the same auto-scaled Pod as the rest of the user's code, just like the earlier proxy. However, the new version takes all requests from the Keda HTTP scaler, and then routes them to the appropriate version of the user pod, in case it's available, or returns an error if the user pod's version has changed (by making use of the fact that it should fail to connect to the old version). It accomplishes that by using an extra service for each versions, which seemed like the cleanest Kubernetes way to achieve the result, though it might be a bit unorthodox.

---
config:
    layout: elk
---
flowchart LR
    HTTPSO---|routing|ProxyService
    HTTPSO---|scaling|Deployment
    subgraph Deployment
        subgraph PodV1
            Proxy
            UserContainerV1
        end
        subgraph PodV2
            Proxy2[Proxy]
            UserContainerV2
        end
    end
    ProxyService-->Proxy
    ProxyService-->Proxy2
    Proxy---|v1|v1Service
    Proxy2---|v1|v1Service
    v1Service-->UserContainerV1
    Proxy---|v2|v2Service
    Proxy2---|v2|v2Service
    v2Service-->UserContainerV2
Loading

Operationally, this PR's proxy works as follows:

sequenceDiagram
    User->>+Ingress/KEDA/HSO: GET http://host/.well-known<br/>/network.apocryph.attest<br/>?nonce=...
    Ingress/KEDA/HSO-->>Proxy: 
    Ingress/KEDA/HSO-->>Other pod Containers: 
    Ingress/KEDA/HSO->>+Proxy: GET ...?
    participant Verification Service as Verification Service<br>(Constellation)
    Proxy->>+Verification Service: GET /?nonce=...
    Verification Service->>-Proxy: 
    Proxy->>-Ingress/KEDA/HSO: Attestation <br>+ Expected value for header
    Ingress/KEDA/HSO->>-User: 
    User->>User: Check attestation
    
    User->>+Ingress/KEDA/HSO: GET http://host/path<br/>X-Apocryph-Expected: XX
    box Pod
    participant Proxy
    participant Other pod Containers
    end
    Ingress/KEDA/HSO-->>Proxy:  Scale up
    Ingress/KEDA/HSO-->>Other pod Containers: 
    Ingress/KEDA/HSO->>+Proxy: GET ...
    Proxy->>Proxy: Check version
    Proxy->>+Other pod Containers: GET ...
    Other pod Containers->>-Proxy: 
    Proxy->>-Ingress/KEDA/HSO: 
    Ingress/KEDA/HSO->>-User: 
Loading

From the perspective of the user, there are five steps to using an Apocryph app with attestation:

  • Connect to the node running the app in a TEE. - Not covered by this PR; we assume the user already managed to send the HTTP/S request to the Ingress.
  • Request the node's attestation and the current version of the software. - Here, we do this by issuing a request to the "magic" URL, /.well-known/network.apocryph.attest.
  • Confirm that the attestation is correct, signed with the appropriate keys, and that the software version reported is correct. - Here, we assume that checking all the images configured by the publisher is sufficient; for this to be actually acceptable to third-parties, the publisher should submit image URLs with full hashes. - Not covered by this PR is client-side part of checking the attestation itself, this will come later.
  • Confirm that the certificate used is actually generated by the TEE (to prevent MitM). - Not covered by this PR; we just use HTTP at this point.
  • Send requests to the now-confirmed application. - Here, we use the "magic" header X-Apocryph-Expected, which holds an internal Apocryph value that is based on hashing all the image URLs together. The proxy's main job is ensuring that the request is routed to the pod only if the header matches.

Something that might require deeper thinking later on is figuring out a way for Web-based clients to pin the later requests to the same certificate that was attested earlier. Probably new WebTransport({serverCertificateHashes: ...}) could work for that, especially as it's now supported by both Chrome and Firefox.

In terms of security, I'm a bit worried about having both the proxy and user application in the same pod; for sandboxing, I'd like for them to be separate. Doing this would require reaching deeper into KEDA HTTP's internals, and managing the ScaledObjects manually, which is why I opted to leave the two together for now.

This PR builds on top of #53.

@bojidar-bg bojidar-bg force-pushed the xx-attestation-facade branch from 5cf9a22 to 0e0f40f Compare October 24, 2024 14:30
@bojidar-bg bojidar-bg merged commit c144994 into master Oct 24, 2024
2 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.

1 participant