Compare commits
3
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
02c7552725 | ||
|
|
3e233da128 | ||
|
|
a72f79e557 |
+19
-8
@@ -7,6 +7,13 @@ on:
|
|||||||
pull_request:
|
pull_request:
|
||||||
|
|
||||||
env:
|
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
|
CARGO_TERM_COLOR: always
|
||||||
RUSTFLAGS: "-D warnings"
|
RUSTFLAGS: "-D warnings"
|
||||||
# Compile cache: sccache -> Hetzner S3 (breakpilot-sccache), runner-independent
|
# 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 init && git remote add origin "${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}.git"
|
||||||
git fetch --depth=1 origin "${GITHUB_SHA}" && git checkout FETCH_HEAD
|
git fetch --depth=1 origin "${GITHUB_SHA}" && git checkout FETCH_HEAD
|
||||||
IMAGE=repo.meghsakha.com/certifai/compliance-agent
|
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 \
|
DOCKER_BUILDKIT=1 docker build --secret id=tramiton_token,env=TRAMITON_FETCH_TOKEN \
|
||||||
-f Dockerfile.agent -t "$IMAGE:latest" -t "$IMAGE:${GITHUB_SHA}" .
|
-f Dockerfile.agent -t "$IMAGE:latest" -t "$IMAGE:${GITHUB_SHA}" .
|
||||||
docker push "$IMAGE:latest" && docker push "$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"
|
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}")
|
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}')
|
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 init && git remote add origin "${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}.git"
|
||||||
git fetch --depth=1 origin "${GITHUB_SHA}" && git checkout FETCH_HEAD
|
git fetch --depth=1 origin "${GITHUB_SHA}" && git checkout FETCH_HEAD
|
||||||
IMAGE=repo.meghsakha.com/certifai/compliance-dashboard
|
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 \
|
DOCKER_BUILDKIT=1 docker build --secret id=tramiton_token,env=TRAMITON_FETCH_TOKEN \
|
||||||
-f Dockerfile.dashboard -t "$IMAGE:latest" -t "$IMAGE:${GITHUB_SHA}" .
|
-f Dockerfile.dashboard -t "$IMAGE:latest" -t "$IMAGE:${GITHUB_SHA}" .
|
||||||
docker push "$IMAGE:latest" && docker push "$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"
|
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}")
|
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}')
|
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 init && git remote add origin "${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}.git"
|
||||||
git fetch --depth=1 origin "${GITHUB_SHA}" && git checkout FETCH_HEAD
|
git fetch --depth=1 origin "${GITHUB_SHA}" && git checkout FETCH_HEAD
|
||||||
IMAGE=repo.meghsakha.com/certifai/compliance-docs
|
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 build -f Dockerfile.docs -t "$IMAGE:latest" -t "$IMAGE:${GITHUB_SHA}" .
|
||||||
docker push "$IMAGE:latest" && docker push "$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"
|
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}")
|
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}')
|
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 init && git remote add origin "${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}.git"
|
||||||
git fetch --depth=1 origin "${GITHUB_SHA}" && git checkout FETCH_HEAD
|
git fetch --depth=1 origin "${GITHUB_SHA}" && git checkout FETCH_HEAD
|
||||||
IMAGE=repo.meghsakha.com/certifai/compliance-mcp
|
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 \
|
DOCKER_BUILDKIT=1 docker build --secret id=tramiton_token,env=TRAMITON_FETCH_TOKEN \
|
||||||
-f Dockerfile.mcp -t "$IMAGE:latest" -t "$IMAGE:${GITHUB_SHA}" .
|
-f Dockerfile.mcp -t "$IMAGE:latest" -t "$IMAGE:${GITHUB_SHA}" .
|
||||||
docker push "$IMAGE:latest" && docker push "$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"
|
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}")
|
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}')
|
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 pool_for_factory = pool.clone();
|
||||||
let service = StreamableHttpService::new(
|
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()),
|
Arc::new(LocalSessionManager::default()),
|
||||||
StreamableHttpServerConfig::default(),
|
StreamableHttpServerConfig::default(),
|
||||||
);
|
);
|
||||||
@@ -69,16 +81,11 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|||||||
tenant_id = %synth_tenant,
|
tenant_id = %synth_tenant,
|
||||||
"stdio transport — using synthetic tenant id; DO NOT use in production"
|
"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();
|
let transport = rmcp::transport::stdio();
|
||||||
use rmcp::ServiceExt;
|
use rmcp::ServiceExt;
|
||||||
auth::TENANT_ID
|
let handle = server.serve(transport).await?;
|
||||||
.scope(synth_tenant, async {
|
handle.waiting().await?;
|
||||||
let handle = server.serve(transport).await?;
|
|
||||||
handle.waiting().await?;
|
|
||||||
Ok::<_, Box<dyn std::error::Error>>(())
|
|
||||||
})
|
|
||||||
.await?;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|||||||
@@ -2,37 +2,33 @@ use rmcp::{
|
|||||||
handler::server::wrapper::Parameters, model::*, tool, tool_handler, tool_router, ServerHandler,
|
handler::server::wrapper::Parameters, model::*, tool, tool_handler, tool_router, ServerHandler,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::auth::current_tenant_id;
|
|
||||||
use crate::database::{Database, DatabasePool};
|
use crate::database::{Database, DatabasePool};
|
||||||
use crate::tools::{dast, findings, oscal, pentest, sbom};
|
use crate::tools::{dast, findings, oscal, pentest, sbom};
|
||||||
|
|
||||||
pub struct ComplianceMcpServer {
|
pub struct ComplianceMcpServer {
|
||||||
pool: DatabasePool,
|
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)]
|
#[allow(dead_code)]
|
||||||
tool_router: rmcp::handler::server::router::tool::ToolRouter<Self>,
|
tool_router: rmcp::handler::server::router::tool::ToolRouter<Self>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ComplianceMcpServer {
|
impl ComplianceMcpServer {
|
||||||
/// Resolve the per-tenant `Database` from the bearer-set
|
/// The per-tenant `Database` for this session.
|
||||||
/// `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).
|
|
||||||
fn tenant_db(&self) -> Result<Database, rmcp::ErrorData> {
|
fn tenant_db(&self) -> Result<Database, rmcp::ErrorData> {
|
||||||
let tenant_id = current_tenant_id().ok_or_else(|| {
|
Ok(self.pool.for_tenant_id(&self.tenant_id))
|
||||||
rmcp::ErrorData::internal_error(
|
|
||||||
"no tenant context — bearer middleware not in chain".to_string(),
|
|
||||||
None,
|
|
||||||
)
|
|
||||||
})?;
|
|
||||||
Ok(self.pool.for_tenant_id(&tenant_id))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tool_router]
|
#[tool_router]
|
||||||
impl ComplianceMcpServer {
|
impl ComplianceMcpServer {
|
||||||
pub fn new(pool: DatabasePool) -> Self {
|
pub fn new(pool: DatabasePool, tenant_id: String) -> Self {
|
||||||
Self {
|
Self {
|
||||||
pool,
|
pool,
|
||||||
|
tenant_id,
|
||||||
tool_router: Self::tool_router(),
|
tool_router: Self::tool_router(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user