Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
3c6739d
feat: allow free-form cloud regions and add cluster discovery metadata
JLCode-tech Aug 31, 2026
65fc3e3
feat: enrich BNK Health dashboard with node/AZ placement metadata
JLCode-tech Aug 31, 2026
9b1c608
refactor: DRY operator connectivity, node enrichment, and region sele…
JLCode-tech Aug 31, 2026
a06eea4
feat: BNK Health dashboard connectivity and integration status
JLCode-tech Aug 31, 2026
0bae072
feat: credential-template-driven cluster auto-detection
JLCode-tech Aug 31, 2026
3123580
feat: add BNK Resources fleet consumption dashboard to System page
JLCode-tech Aug 31, 2026
6f2ef28
refactor: relocate BNK Resources, MCP, and Benchmarks navigation
JLCode-tech Sep 1, 2026
81b67f3
feat: add TMM traffic stats to BNK gateway/listener/egress views
JLCode-tech Sep 1, 2026
6d9f5ca
feat: add operational-state polish to App Delivery (Slice 7)
JLCode-tech Sep 1, 2026
14eaaf3
feat: support custom TLS CA certificates for corporate SSL inspection…
JLCode-tech Sep 1, 2026
b36e82a
fix(module-library): honor git_ref tag refs and reconcile official so…
JLCode-tech Sep 1, 2026
5248152
perf(f5bnk): stop blocking health dashboard on unified data; skip con…
JLCode-tech Sep 1, 2026
2f17cfb
perf(system): parallelize bnk-consumption cluster collection with 30s…
JLCode-tech Sep 1, 2026
aad6c0d
fix(f5bnk): evaluate BNK 2.3 policy refs, route parent conditions, an…
JLCode-tech Sep 1, 2026
22ed12c
perf(bnk): cap thread pools and cache expensive cloud/BNK/license work
JLCode-tech Sep 1, 2026
20794c8
feat(k8s): expose cluster metadata fields end-to-end
JLCode-tech Sep 1, 2026
c0cb632
test(f5bnk): return real cluster from mocked KubernetesService.get_cl…
JLCode-tech Sep 1, 2026
c93da55
perf(bnk): cache shared BNK state across tabs, diagnostics, and licen…
JLCode-tech Sep 1, 2026
35c8ac9
feat(f5bnk): show per-listener connection counters in Traffic Flow
JLCode-tech Sep 1, 2026
ebe1d39
feat(system): add node capacity fallback for bnk resources and polish…
JLCode-tech Sep 2, 2026
9e8d5b7
perf(k8s,f5bnk,fleet): eliminate blocking spinners and redundant fetc…
JLCode-tech Sep 2, 2026
90f519d
perf: optimize F5 BNK diagnostics data caching and cross-tab navigation
JLCode-tech Sep 2, 2026
213a5b7
fix(ci): suppress upstream CVE-2026-56854 in bundled Go binaries
JLCode-tech Sep 2, 2026
2a6d52f
fix(system): isolate thread db sessions in bnk consumption and harden…
JLCode-tech Sep 6, 2026
0be1426
refactor(bnk): consolidate bnk version extraction and coordinate quer…
JLCode-tech Sep 7, 2026
01b0bb8
fix(fleet): remove unused re import
JLCode-tech Sep 7, 2026
6c1ffe4
fix(#205 r*): assert kubeconfig encryption-at-rest (M1) + normalize A…
jgruber Sep 8, 2026
59a84f6
fix(frontend): fail closed when blueprint template load errors in dep…
JLCode-tech Sep 11, 2026
9d4f83d
feat(auth): add Microsoft Azure Service Principal and Entra ID SSO cr…
JLCode-tech Sep 3, 2026
763cb8f
fix(auth): rebase migration to v2_157 and harden azure sso token refresh
JLCode-tech Sep 6, 2026
f6a9ba4
fix(auth): set migration revision to v2_156 for independent branch on…
JLCode-tech Sep 6, 2026
ead0e2a
fix(auth): deduplicate 24-hour credential expiry notification and ali…
JLCode-tech Sep 7, 2026
827b0c6
fix(#207): self-review — tz-naive expiry crash, drop token-leaking SS…
jgruber Sep 8, 2026
025330c
fix(auth): address review findings for azure auth templates
JLCode-tech Sep 11, 2026
cc748e2
fix(auth): update openapi spec and frontend api types for azure auth …
JLCode-tech Sep 11, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,14 @@ docker-compose.override.yml
# OPT-001: Smart build script stores last-build commit hash
.build-hash

# Custom TLS CA certificates (corporate proxy certs, etc.)
# The certs/ directory itself is tracked (see certs/.gitkeep), but certificate
# files should never be committed.
/certs/*.crt
/certs/*.pem
/certs/*.cer
/certs/*.der

# Environment variables
.env
.env.local
Expand Down Expand Up @@ -123,7 +131,9 @@ configs/.module_catalog/
*.backup
backend/venv/

frontend-v2/node_modules
frontend-v2/node_modules/
**/node_modules
**/node_modules/
frontend-v2/dist/
frontend-v2/.vite/
Expand Down
10 changes: 10 additions & 0 deletions .trivyignore
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,16 @@ CVE-2026-56854 exp:2026-11-30
# Added: 2026-04-15
CVE-2026-33186 exp:2026-11-30

# CVE-2026-56854: golang.org/x/crypto/ssh - Authentication bypass due to unenforced source-address restrictions
# Affects: helm, oras, and tofu binaries bundled into Docker images (golang.org/x/crypto v0.31.0 - v0.46.0)
# Fixed in: golang.org/x/crypto >= 0.55.0
# Not exploitable in our context: Forge invokes client CLI commands against Kubernetes APIs and
# OCI/container registries. No container runs an SSH server or accepts incoming SSH connections.
# Status: Waiting for upstream helm, oras, and opentofu releases built with patched golang.org/x/crypto.
# Added: 2026-09-02
CVE-2026-56854 exp:2026-11-30


# CVE-2026-7598: libssh2 — integer overflow via large username/password
# Affects: libssh2-1t64 1.11.1-1 in Debian trixie base image
# Pulled in transitively (git/curl/apt deps); not directly used by Forge —
Expand Down
34 changes: 34 additions & 0 deletions backend/alembic/versions/v2_156_add_cluster_discovery_metadata.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
"""Add account_id and discovery_status to kubernetes_clusters.

Revision ID: v2_156
Revises: v2_155

Adds cloud-account metadata and a coarse discovery status to the
KubernetesCluster table so that fleet-health and cluster-list views can
surface per-cluster cloud context (account/subscription) and discovery
state without extra joins.
"""
import sqlalchemy as sa

from alembic import op

revision = "v2_156"
down_revision = "v2_155"
branch_labels = None
depends_on = None


def upgrade() -> None:
op.add_column(
"kubernetes_clusters",
sa.Column("account_id", sa.String(length=100), nullable=True),
)
op.add_column(
"kubernetes_clusters",
sa.Column("discovery_status", sa.String(length=50), nullable=True),
)


def downgrade() -> None:
op.drop_column("kubernetes_clusters", "discovery_status")
op.drop_column("kubernetes_clusters", "account_id")
48 changes: 48 additions & 0 deletions backend/alembic/versions/v2_157_add_cluster_metadata_fields.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
"""Add cluster metadata fields to kubernetes_clusters.

Revision ID: v2_157
Revises: v2_156

Adds node_count, connectivity_status, integration_status, zones, and
access_method so cluster list/detail views can surface per-cluster
metadata without extra joins or probes.
"""
import sqlalchemy as sa

from alembic import op

revision = "v2_157"
down_revision = "v2_156"
branch_labels = None
depends_on = None


def upgrade() -> None:
op.add_column(
"kubernetes_clusters",
sa.Column("node_count", sa.Integer(), nullable=True),
)
op.add_column(
"kubernetes_clusters",
sa.Column("connectivity_status", sa.String(length=50), nullable=True),
)
op.add_column(
"kubernetes_clusters",
sa.Column("integration_status", sa.String(length=50), nullable=True),
)
op.add_column(
"kubernetes_clusters",
sa.Column("zones", sa.JSON(), nullable=True),
)
op.add_column(
"kubernetes_clusters",
sa.Column("access_method", sa.String(length=50), nullable=True),
)


def downgrade() -> None:
op.drop_column("kubernetes_clusters", "access_method")
op.drop_column("kubernetes_clusters", "zones")
op.drop_column("kubernetes_clusters", "integration_status")
op.drop_column("kubernetes_clusters", "connectivity_status")
op.drop_column("kubernetes_clusters", "node_count")
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
"""Add Azure credential fields to cloud_credential_templates.

Revision ID: v2_158
Revises: v2_157
"""
import sqlalchemy as sa

from alembic import op

revision = "v2_158"
down_revision = "v2_157"
branch_labels = None
depends_on = None


def upgrade() -> None:
with op.batch_alter_table("cloud_credential_templates") as batch:
batch.add_column(sa.Column("azure_auth_method", sa.String(50), nullable=True))
batch.add_column(sa.Column("azure_client_id", sa.String(255), nullable=True))
batch.add_column(sa.Column("azure_client_secret_encrypted", sa.Text(), nullable=True))
batch.add_column(sa.Column("azure_sso_access_token_encrypted", sa.Text(), nullable=True))
batch.add_column(sa.Column("azure_sso_refresh_token_encrypted", sa.Text(), nullable=True))
batch.add_column(sa.Column("azure_sso_token_expiry", sa.DateTime(timezone=True), nullable=True))
batch.add_column(sa.Column("azure_sso_authenticated_at", sa.DateTime(timezone=True), nullable=True))


def downgrade() -> None:
with op.batch_alter_table("cloud_credential_templates") as batch:
batch.drop_column("azure_sso_authenticated_at")
batch.drop_column("azure_sso_token_expiry")
batch.drop_column("azure_sso_refresh_token_encrypted")
batch.drop_column("azure_sso_access_token_encrypted")
batch.drop_column("azure_client_secret_encrypted")
batch.drop_column("azure_client_id")
batch.drop_column("azure_auth_method")
42 changes: 42 additions & 0 deletions backend/entrypoint.sh
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,48 @@ echo "================================================"
echo "BNK-Forge Backend Startup"
echo "================================================"

# Install user-supplied TLS CA certificates (e.g. corporate SSL inspection proxy).
# Corporate proxies re-sign outbound TLS (GitHub, Docker Hub, cloud APIs) with an
# internal CA that is not shipped in the public ca-certificates package. Mounting
# those CA files into /app/certs lets Forge trust them without baking them into
# the image or disabling certificate verification.
#
# The entrypoint runs as the non-root bnkforge user, so we cannot update the
# system-wide store. Instead we build a per-user bundle and export the standard
# environment variables that git, Python requests/urllib3, curl, Go binaries
# (Helm, OpenTofu), and the AWS CLI honor.
CUSTOM_CERT_DIR="/app/certs"
CUSTOM_BUNDLE="/home/bnkforge/.bnk-forge-ca-bundle.crt"
if [ -d "$CUSTOM_CERT_DIR" ]; then
installed_count=0
for cert in "$CUSTOM_CERT_DIR"/*.crt "$CUSTOM_CERT_DIR"/*.pem "$CUSTOM_CERT_DIR"/*.cer "$CUSTOM_CERT_DIR"/*.der; do
[ -e "$cert" ] || continue
installed_count=$((installed_count + 1))
done
if [ "$installed_count" -gt 0 ]; then
echo "Installing $installed_count custom CA certificate(s) from $CUSTOM_CERT_DIR"
# Start from the current system bundle, then append custom certs.
cp /etc/ssl/certs/ca-certificates.crt "$CUSTOM_BUNDLE"
for cert in "$CUSTOM_CERT_DIR"/*.crt "$CUSTOM_CERT_DIR"/*.pem "$CUSTOM_CERT_DIR"/*.cer "$CUSTOM_CERT_DIR"/*.der; do
[ -e "$cert" ] || continue
case "$cert" in
*.der)
openssl x509 -inform DER -in "$cert" -out - >> "$CUSTOM_BUNDLE" 2>/dev/null || cat "$cert" >> "$CUSTOM_BUNDLE"
;;
*)
cat "$cert" >> "$CUSTOM_BUNDLE"
;;
esac
done
# Make the bundle available to common TLS consumers.
export SSL_CERT_FILE="$CUSTOM_BUNDLE"
export GIT_SSL_CAINFO="$CUSTOM_BUNDLE"
# curl and Node/Go tooling may also honor these.
export CURL_CA_BUNDLE="$CUSTOM_BUNDLE"
export REQUESTS_CA_BUNDLE="$CUSTOM_BUNDLE"
fi
fi

# Fix volume permissions on first run
# Docker volumes are created as root, but we run as bnkforge (uid 1000)
# The Makefile install target handles permissions, but we also check here
Expand Down
7 changes: 7 additions & 0 deletions backend/models/kubernetes.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,13 @@ class KubernetesCluster(Base):
kubeconfig_encrypted = Column(Text, nullable=True) # Base64 encoded encrypted kubeconfig
cloud_provider = Column(String(50)) # aws, azure, gcp, on-prem
region = Column(String(100)) # Cloud region
account_id = Column(String(100), nullable=True) # Cloud account / subscription ID
discovery_status = Column(String(50), nullable=True) # pending/probing/completed/failed
node_count = Column(Integer, nullable=True)
connectivity_status = Column(String(50), nullable=True) # connected/reachable/partial/unreachable/unknown
integration_status = Column(String(50), nullable=True) # agent_connected/agent_disconnected/direct
zones = Column(JSON, nullable=True) # List of availability zones from nodes
access_method = Column(String(50), nullable=True) # kubeconfig/ssh_tunnel/operator
default_namespace = Column(String(255), default="default")

# PLATFORM-CONTEXT-002: detected cluster platform context (additive)
Expand Down
10 changes: 9 additions & 1 deletion backend/models/system.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,10 +85,18 @@ class CloudCredentialTemplate(Base):
gcp_credentials_encrypted = Column(Text)
gcp_project_id = Column(String(255))

# Azure credentials (future)
# Azure credentials
azure_auth_method = Column(String(50), nullable=True) # 'service_principal' or 'sso'
azure_subscription_id = Column(String(255))
azure_tenant_id = Column(String(255))
azure_client_id = Column(String(255))
azure_client_secret_encrypted = Column(Text)
azure_credentials_encrypted = Column(Text)
# Azure SSO / Device Flow Session
azure_sso_access_token_encrypted = Column(Text)
azure_sso_refresh_token_encrypted = Column(Text)
azure_sso_token_expiry = Column(DateTime(timezone=True))
azure_sso_authenticated_at = Column(DateTime(timezone=True))

# IBM Cloud credentials
ibmcloud_api_key_encrypted = Column(Text)
Expand Down
Loading
Loading