(
&self,
headers: &http::HeaderMap,
path: &str,
)
| 91 | #[async_trait] |
| 92 | impl Authenticator for K8sServiceAccountAuthenticator { |
| 93 | async fn authenticate( |
| 94 | &self, |
| 95 | headers: &http::HeaderMap, |
| 96 | path: &str, |
| 97 | ) -> Result<Option<Principal>, Status> { |
| 98 | // Scope: only the bootstrap RPC. Other paths fall through so the |
| 99 | // SandboxJwtAuthenticator (or OIDC) handles them. |
| 100 | if path != ISSUE_SANDBOX_TOKEN_PATH { |
| 101 | return Ok(None); |
| 102 | } |
| 103 | |
| 104 | let Some(token) = headers |
| 105 | .get("authorization") |
| 106 | .and_then(|v| v.to_str().ok()) |
| 107 | .and_then(|v| v.strip_prefix("Bearer ")) |
| 108 | else { |
| 109 | return Ok(None); |
| 110 | }; |
| 111 | |
| 112 | let Some(resolved) = self.resolver.resolve(token).await? else { |
| 113 | debug!("K8s SA token did not authenticate; falling through"); |
| 114 | return Ok(None); |
| 115 | }; |
| 116 | |
| 117 | if resolved.sandbox_id.is_empty() { |
| 118 | warn!( |
| 119 | pod = %resolved.pod_name, |
| 120 | "pod missing openshell.io/sandbox-id annotation; rejecting" |
| 121 | ); |
| 122 | return Err(Status::permission_denied( |
| 123 | "pod is not bound to a sandbox identity", |
| 124 | )); |
| 125 | } |
| 126 | |
| 127 | Ok(Some(Principal::Sandbox(SandboxPrincipal { |
| 128 | sandbox_id: resolved.sandbox_id, |
| 129 | source: SandboxIdentitySource::K8sServiceAccount { |
| 130 | pod_name: resolved.pod_name, |
| 131 | pod_uid: resolved.pod_uid, |
| 132 | }, |
| 133 | trust_domain: Some("openshell".to_string()), |
| 134 | }))) |
| 135 | } |
| 136 | } |
| 137 | |
| 138 | #[derive(Debug)] |
no test coverage detected