Compare commits
3
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
02c7552725 | ||
|
|
3e233da128 | ||
|
|
a72f79e557 |
+19
-8
@@ -7,6 +7,13 @@ on:
|
||||
pull_request:
|
||||
|
||||
env:
|
||||
# registry + cosign creds via env, NOT inline ${{ }}: the Harbor robot
|
||||
# username contains '$', which sh expands when interpolated into the
|
||||
# script (robot$ci-push -> robot-push) => docker login unauthorized.
|
||||
REGISTRY_USERNAME: ${{ secrets.REGISTRY_USERNAME }}
|
||||
REGISTRY_PASSWORD: ${{ secrets.REGISTRY_PASSWORD }}
|
||||
COSIGN_KEY: ${{ secrets.COSIGN_KEY }}
|
||||
COSIGN_PASSWORD: ${{ secrets.COSIGN_PASSWORD }}
|
||||
CARGO_TERM_COLOR: always
|
||||
RUSTFLAGS: "-D warnings"
|
||||
# Compile cache: sccache -> Hetzner S3 (breakpilot-sccache), runner-independent
|
||||
@@ -207,11 +214,12 @@ jobs:
|
||||
git init && git remote add origin "${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}.git"
|
||||
git fetch --depth=1 origin "${GITHUB_SHA}" && git checkout FETCH_HEAD
|
||||
IMAGE=repo.meghsakha.com/certifai/compliance-agent
|
||||
echo "${{ secrets.REGISTRY_PASSWORD }}" | docker login repo.meghsakha.com -u "${{ secrets.REGISTRY_USERNAME }}" --password-stdin
|
||||
echo "$REGISTRY_PASSWORD" | docker login repo.meghsakha.com -u "$REGISTRY_USERNAME" --password-stdin
|
||||
DOCKER_BUILDKIT=1 docker build --secret id=tramiton_token,env=TRAMITON_FETCH_TOKEN \
|
||||
-f Dockerfile.agent -t "$IMAGE:latest" -t "$IMAGE:${GITHUB_SHA}" .
|
||||
docker push "$IMAGE:latest" && docker push "$IMAGE:${GITHUB_SHA}"
|
||||
command -v cosign >/dev/null 2>&1 || { curl -sSfLo /usr/local/bin/cosign https://github.com/sigstore/cosign/releases/download/v2.4.3/cosign-linux-amd64 && chmod +x /usr/local/bin/cosign; }
|
||||
{ command -v cosign >/dev/null 2>&1 || curl -sSfLo /usr/local/bin/cosign https://github.com/sigstore/cosign/releases/download/v2.4.3/cosign-linux-amd64 || wget -qO /usr/local/bin/cosign https://github.com/sigstore/cosign/releases/download/v2.4.3/cosign-linux-amd64; } || echo "::warning::cosign fetch failed"
|
||||
chmod +x /usr/local/bin/cosign 2>/dev/null || true
|
||||
cosign sign --yes --key env://COSIGN_KEY "$IMAGE:latest" || echo "::warning::cosign failed"
|
||||
PAYLOAD=$(printf '{"ref":"refs/heads/main","repository":{"full_name":"sharang/compliance-scanner-agent"},"head_commit":{"id":"%s","message":"deploy agent"}}' "${GITHUB_SHA}")
|
||||
SIG=$(printf '%s' "$PAYLOAD" | openssl dgst -sha256 -hmac "${{ secrets.ORCA_WEBHOOK_SECRET }}" | awk '{print $2}')
|
||||
@@ -233,11 +241,12 @@ jobs:
|
||||
git init && git remote add origin "${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}.git"
|
||||
git fetch --depth=1 origin "${GITHUB_SHA}" && git checkout FETCH_HEAD
|
||||
IMAGE=repo.meghsakha.com/certifai/compliance-dashboard
|
||||
echo "${{ secrets.REGISTRY_PASSWORD }}" | docker login repo.meghsakha.com -u "${{ secrets.REGISTRY_USERNAME }}" --password-stdin
|
||||
echo "$REGISTRY_PASSWORD" | docker login repo.meghsakha.com -u "$REGISTRY_USERNAME" --password-stdin
|
||||
DOCKER_BUILDKIT=1 docker build --secret id=tramiton_token,env=TRAMITON_FETCH_TOKEN \
|
||||
-f Dockerfile.dashboard -t "$IMAGE:latest" -t "$IMAGE:${GITHUB_SHA}" .
|
||||
docker push "$IMAGE:latest" && docker push "$IMAGE:${GITHUB_SHA}"
|
||||
command -v cosign >/dev/null 2>&1 || { curl -sSfLo /usr/local/bin/cosign https://github.com/sigstore/cosign/releases/download/v2.4.3/cosign-linux-amd64 && chmod +x /usr/local/bin/cosign; }
|
||||
{ command -v cosign >/dev/null 2>&1 || curl -sSfLo /usr/local/bin/cosign https://github.com/sigstore/cosign/releases/download/v2.4.3/cosign-linux-amd64 || wget -qO /usr/local/bin/cosign https://github.com/sigstore/cosign/releases/download/v2.4.3/cosign-linux-amd64; } || echo "::warning::cosign fetch failed"
|
||||
chmod +x /usr/local/bin/cosign 2>/dev/null || true
|
||||
cosign sign --yes --key env://COSIGN_KEY "$IMAGE:latest" || echo "::warning::cosign failed"
|
||||
PAYLOAD=$(printf '{"ref":"refs/heads/main","repository":{"full_name":"sharang/compliance-scanner-agent"},"head_commit":{"id":"%s","message":"deploy dashboard"}}' "${GITHUB_SHA}")
|
||||
SIG=$(printf '%s' "$PAYLOAD" | openssl dgst -sha256 -hmac "${{ secrets.ORCA_WEBHOOK_SECRET }}" | awk '{print $2}')
|
||||
@@ -257,10 +266,11 @@ jobs:
|
||||
git init && git remote add origin "${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}.git"
|
||||
git fetch --depth=1 origin "${GITHUB_SHA}" && git checkout FETCH_HEAD
|
||||
IMAGE=repo.meghsakha.com/certifai/compliance-docs
|
||||
echo "${{ secrets.REGISTRY_PASSWORD }}" | docker login repo.meghsakha.com -u "${{ secrets.REGISTRY_USERNAME }}" --password-stdin
|
||||
echo "$REGISTRY_PASSWORD" | docker login repo.meghsakha.com -u "$REGISTRY_USERNAME" --password-stdin
|
||||
docker build -f Dockerfile.docs -t "$IMAGE:latest" -t "$IMAGE:${GITHUB_SHA}" .
|
||||
docker push "$IMAGE:latest" && docker push "$IMAGE:${GITHUB_SHA}"
|
||||
command -v cosign >/dev/null 2>&1 || { curl -sSfLo /usr/local/bin/cosign https://github.com/sigstore/cosign/releases/download/v2.4.3/cosign-linux-amd64 && chmod +x /usr/local/bin/cosign; }
|
||||
{ command -v cosign >/dev/null 2>&1 || curl -sSfLo /usr/local/bin/cosign https://github.com/sigstore/cosign/releases/download/v2.4.3/cosign-linux-amd64 || wget -qO /usr/local/bin/cosign https://github.com/sigstore/cosign/releases/download/v2.4.3/cosign-linux-amd64; } || echo "::warning::cosign fetch failed"
|
||||
chmod +x /usr/local/bin/cosign 2>/dev/null || true
|
||||
cosign sign --yes --key env://COSIGN_KEY "$IMAGE:latest" || echo "::warning::cosign failed"
|
||||
PAYLOAD=$(printf '{"ref":"refs/heads/main","repository":{"full_name":"sharang/compliance-scanner-agent"},"head_commit":{"id":"%s","message":"deploy docs"}}' "${GITHUB_SHA}")
|
||||
SIG=$(printf '%s' "$PAYLOAD" | openssl dgst -sha256 -hmac "${{ secrets.ORCA_WEBHOOK_SECRET }}" | awk '{print $2}')
|
||||
@@ -282,11 +292,12 @@ jobs:
|
||||
git init && git remote add origin "${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}.git"
|
||||
git fetch --depth=1 origin "${GITHUB_SHA}" && git checkout FETCH_HEAD
|
||||
IMAGE=repo.meghsakha.com/certifai/compliance-mcp
|
||||
echo "${{ secrets.REGISTRY_PASSWORD }}" | docker login repo.meghsakha.com -u "${{ secrets.REGISTRY_USERNAME }}" --password-stdin
|
||||
echo "$REGISTRY_PASSWORD" | docker login repo.meghsakha.com -u "$REGISTRY_USERNAME" --password-stdin
|
||||
DOCKER_BUILDKIT=1 docker build --secret id=tramiton_token,env=TRAMITON_FETCH_TOKEN \
|
||||
-f Dockerfile.mcp -t "$IMAGE:latest" -t "$IMAGE:${GITHUB_SHA}" .
|
||||
docker push "$IMAGE:latest" && docker push "$IMAGE:${GITHUB_SHA}"
|
||||
command -v cosign >/dev/null 2>&1 || { curl -sSfLo /usr/local/bin/cosign https://github.com/sigstore/cosign/releases/download/v2.4.3/cosign-linux-amd64 && chmod +x /usr/local/bin/cosign; }
|
||||
{ command -v cosign >/dev/null 2>&1 || curl -sSfLo /usr/local/bin/cosign https://github.com/sigstore/cosign/releases/download/v2.4.3/cosign-linux-amd64 || wget -qO /usr/local/bin/cosign https://github.com/sigstore/cosign/releases/download/v2.4.3/cosign-linux-amd64; } || echo "::warning::cosign fetch failed"
|
||||
chmod +x /usr/local/bin/cosign 2>/dev/null || true
|
||||
cosign sign --yes --key env://COSIGN_KEY "$IMAGE:latest" || echo "::warning::cosign failed"
|
||||
PAYLOAD=$(printf '{"ref":"refs/heads/main","repository":{"full_name":"sharang/compliance-scanner-agent"},"head_commit":{"id":"%s","message":"deploy mcp"}}' "${GITHUB_SHA}")
|
||||
SIG=$(printf '%s' "$PAYLOAD" | openssl dgst -sha256 -hmac "${{ secrets.ORCA_WEBHOOK_SECRET }}" | awk '{print $2}')
|
||||
|
||||
@@ -42,7 +42,19 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
|
||||
let pool_for_factory = pool.clone();
|
||||
let service = StreamableHttpService::new(
|
||||
move || Ok(ComplianceMcpServer::new(pool_for_factory.clone())),
|
||||
move || {
|
||||
// The factory runs in the request task, still inside the bearer
|
||||
// middleware's `TENANT_ID` scope, and BEFORE rmcp spawns the
|
||||
// session task (which would lose the task_local). So bind the
|
||||
// tenant into the session's server instance here, once.
|
||||
let tenant_id = auth::current_tenant_id().ok_or_else(|| {
|
||||
std::io::Error::other("no tenant context when creating MCP session")
|
||||
})?;
|
||||
Ok(ComplianceMcpServer::new(
|
||||
pool_for_factory.clone(),
|
||||
tenant_id,
|
||||
))
|
||||
},
|
||||
Arc::new(LocalSessionManager::default()),
|
||||
StreamableHttpServerConfig::default(),
|
||||
);
|
||||
@@ -69,16 +81,11 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
tenant_id = %synth_tenant,
|
||||
"stdio transport — using synthetic tenant id; DO NOT use in production"
|
||||
);
|
||||
let server = ComplianceMcpServer::new(pool);
|
||||
let server = ComplianceMcpServer::new(pool, synth_tenant);
|
||||
let transport = rmcp::transport::stdio();
|
||||
use rmcp::ServiceExt;
|
||||
auth::TENANT_ID
|
||||
.scope(synth_tenant, async {
|
||||
let handle = server.serve(transport).await?;
|
||||
handle.waiting().await?;
|
||||
Ok::<_, Box<dyn std::error::Error>>(())
|
||||
})
|
||||
.await?;
|
||||
let handle = server.serve(transport).await?;
|
||||
handle.waiting().await?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
||||
@@ -2,37 +2,33 @@ use rmcp::{
|
||||
handler::server::wrapper::Parameters, model::*, tool, tool_handler, tool_router, ServerHandler,
|
||||
};
|
||||
|
||||
use crate::auth::current_tenant_id;
|
||||
use crate::database::{Database, DatabasePool};
|
||||
use crate::tools::{dast, findings, oscal, pentest, sbom};
|
||||
|
||||
pub struct ComplianceMcpServer {
|
||||
pool: DatabasePool,
|
||||
/// Tenant this session serves. Bound once at session creation (the HTTP
|
||||
/// factory reads the bearer-set tenant while still in the request scope;
|
||||
/// stdio passes a synthetic id) — NOT a per-request `task_local`, which is
|
||||
/// lost across the `tokio::spawn` that runs the Streamable-HTTP session.
|
||||
tenant_id: String,
|
||||
#[allow(dead_code)]
|
||||
tool_router: rmcp::handler::server::router::tool::ToolRouter<Self>,
|
||||
}
|
||||
|
||||
impl ComplianceMcpServer {
|
||||
/// Resolve the per-tenant `Database` from the bearer-set
|
||||
/// `task_local`. Every tool handler calls this; missing context
|
||||
/// surfaces as `internal_error` because it means the auth
|
||||
/// middleware was misconfigured (handler ran without scope).
|
||||
/// The per-tenant `Database` for this session.
|
||||
fn tenant_db(&self) -> Result<Database, rmcp::ErrorData> {
|
||||
let tenant_id = current_tenant_id().ok_or_else(|| {
|
||||
rmcp::ErrorData::internal_error(
|
||||
"no tenant context — bearer middleware not in chain".to_string(),
|
||||
None,
|
||||
)
|
||||
})?;
|
||||
Ok(self.pool.for_tenant_id(&tenant_id))
|
||||
Ok(self.pool.for_tenant_id(&self.tenant_id))
|
||||
}
|
||||
}
|
||||
|
||||
#[tool_router]
|
||||
impl ComplianceMcpServer {
|
||||
pub fn new(pool: DatabasePool) -> Self {
|
||||
pub fn new(pool: DatabasePool, tenant_id: String) -> Self {
|
||||
Self {
|
||||
pool,
|
||||
tenant_id,
|
||||
tool_router: Self::tool_router(),
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user