Version: 1.3.7 Last Updated: 2026-07-30
This is a distributable package of the GCM (Guardium Cryptography Manager) Web UI application. It provides a complete web-based interface for managing GCM operations including certificate management, IT asset management, profile management, authentication, disconnected SSL certificate scanning, and GCM user management.
This package contains:
POST /api/v1/user-management/profiles/{profile_id}/usersBefore installing, ensure you have:
Extract this distribution package to your desired location:
cd /path/to/installation
unzip gcm-webui-dist.zip
cd dist
Navigate to the backend directory and run the setup script:
cd backend
chmod +x setup.sh
./setup.sh
The setup script will:
.env configuration fileManual Setup (if setup.sh fails):
# Create virtual environment
python3 -m venv .venv
# Activate virtual environment
source .venv/bin/activate # On Linux/macOS
# OR
.venv\Scripts\activate # On Windows
# Install dependencies
pip install -r requirements.txt
# Generate encryption key
python -c "from cryptography.fernet import Fernet; print(Fernet.generate_key().decode())"
# Create .env file
cp .env.example .env
# Edit .env and add the generated key to SECRET_KEY
Edit the config.toml file in the root directory:
cd ..
cp config.toml.template config.toml
nano config.toml # or use your preferred editor
Update the following settings:
For OIDC authentication (default):
[connection]
app_uri = "https://your-gcm-host:31443"
oidc_uri = "https://your-gcm-host:30443"
realm = "gcmrealm"
[authentication]
auth_method = "oidc"
client_id = "" # auto-selected if empty
client_secret = ""
# username = ""
# password = ""
[http]
timeout = 30.0
insecure = false # Set to true only for development/test
For API key authentication:
[connection]
app_uri = "https://your-gcm-host:31443"
# oidc_uri is not required for api_key authentication
[authentication]
auth_method = "api_key"
# api_key = "" # set via GCM_API_KEY env var (recommended)
[http]
timeout = 30.0
insecure = false
Edit the backend/.env file:
cd backend
nano .env
Ensure these variables are set:
SECRET_KEY=your-generated-fernet-key
DATABASE_URL=sqlite:///./gcm_webui.db
CORS_ORIGINS=http://localhost:8000,http://127.0.0.1:8000
From the backend directory:
# Activate virtual environment
source .venv/bin/activate # On Linux/macOS
# OR
.venv\Scripts\activate # On Windows
# Start the server
uvicorn app.main:app --host 0.0.0.0 --port 8000 --reload
The server will start on http://localhost:8000
Open your web browser and navigate to:
http://localhost:8000
You should see the GCM Web UI dashboard.
Interactive API documentation is available at:
Before using the application, you need to create a GCM connection profile:
gcmrealm)oidc (default) or api_key β see belowThe application supports two mutually exclusive authentication methods per profile. Set the auth_method field when creating or updating a profile.
Used when auth_method = oidc. The application performs a standard OIDC password grant flow against Keycloak and exchanges the resulting token with GCM. After activating an OIDC profile:
Used when auth_method = api_key. No OIDC token exchange takes place. The API key is sent verbatim in every outgoing request using two headers:
Authorization: <your-api-key>
token_type: api_key
When an API key profile is active, the Login & Authorize step is not required β the key is used automatically on each request. The /api/v1/auth/login endpoint will return a 400 error if called with an API key profile active, since no login step is needed.
Security note: API keys are encrypted at rest using Fernet encryption (same as passwords and tokens).
Youβre now ready to use all features!
Sync Certificates from GCM:
Sync keeps local data in step with GCM. After every full sync, any certificate that no longer exists on the remote server is automatically removed from the local database. The sync response includes a
deletedcount alongsidesyncedandupdated.
Upload a Certificate:
View Certificate Details:
Sync Assets from GCM:
Sync keeps local data in step with GCM. After every full sync, any asset that no longer exists on the remote server is automatically removed from the local database. The sync response includes a
deleted_countalongsidesynced_count,created_count, andupdated_count.
The sync captures all GCM asset fields using the confirmed payload:
| Section | Fields |
|---|---|
| Identity | uri, ip, hostname, port, asset_id |
| Protocol | protocol, protocol_version (list, e.g. ["TLSv1.3","TLSv1.2"]) |
| Classification | asset_type, asset_sub_type |
| Service/DB | servicename, databasename, databasetype, version, applicationID, patch |
| Organisation | owner, environment, location, network, tech_contacts, discovery_sources |
| Security | mission_criticality, internet_facing, total_violation, pqc_readiness_flag, exploitability_score, is_exception |
| Timestamps | first_seen, last_seen |
Create a New Asset:
View Asset Details:
The scanner page provides a guided three-step workflow for discovering and importing crypto objects (TLS certificates, SSH host keys, and TLS protocol metadata) from hosts that may not be directly reachable from GCM. Scans run as a real-time streaming operation β progress is shown target-by-target and can be stopped at any time.
Step 1 β Generate Targets (Optional)
192.168.1.0/24, 10.0.0.*), hostnames, and ports.Alias, URI CSV.Step 2 β Scan Targets
Alias and URI columns) using the Targets CSV File file picker. Leave it empty to reuse the CSV generated in Step 1.host:port) being probed in real time.Alias, Certdata, URI CSV for offline use.Protocol detection per target:
| Detected service | How | What is captured |
|---|---|---|
| TLS / HTTPS | Full TLS handshake (strict, with automatic legacy-TLS fallback) | DER certificate, TLS version, cipher suite, subject/issuer, expiry |
| SSH | SSH binary protocol (RFC 4253 KEXINIT) | Server banner, host-key type (e.g. ssh-ed25519), advertised key algorithms |
| Plain-text (FTP, SMTP, POP3, IMAP, β¦) | TCP banner read | Service label, raw banner |
SSH is detected automatically on standard port 22 and common alternatives 2222 / 22222, as well as any other port whose banner starts with SSH-.
Security findings reported per target:
| Finding badge | Meaning |
|---|---|
EXPIRED |
Certificate is past its notAfter date |
EXPIRING SOON |
Certificate expires within 30 days |
LEGACY TLS |
Server negotiated TLSv1.0 or TLSv1.1 |
WEAK CIPHER |
Cipher uses RC4, DES, 3DES, CBC mode, static RSA key exchange, or similar |
WEAK KEY |
RSA/DSA key < 2048 bits or EC key < 224 bits |
SELF-SIGNED |
Certificate subject equals issuer |
SHA-1 CERT |
Certificate is signed with SHA-1 |
WEAK SSH KEY |
Serverβs preferred host key is ssh-rsa (SHA-1 based, deprecated) |
LEGACY SSH KEX |
Server advertises ssh-rsa but no rsa-sha2-* variants |
Step 3 β Import Scan Results into GCM
When a scan was run in Step 2, a summary shows the number of each object type ready to import. Click π€ Import All to GCM to send all three object types to GCM in one operation:
| Object type | GCM API | What is sent |
|---|---|---|
| Certificates | POST /v1/β¦/crypto_objects/certificate_from_file |
Base64 DER certificate, alias, IT asset URI |
| SSH Host Keys | POST /v2/β¦/crypto_objects/keys |
Key algorithm, estimated key length, IT asset URI, relationship_type: HOSTED_ON, relationships block |
| TLS Protocols | POST /v2/β¦/crypto_objects/protocols |
TLS version, cipher suite, IT asset URI, relationship_type: HOSTED_ON |
Before each crypto-object POST, the importer automatically upserts a minimal IT asset record for the target URI (POST /v2/β¦/ingest/it_assets) so that GCM can resolve the it_asset_uri reference. Assets are always stored with https:// URIs regardless of port β GCM does not support ssh:// URI schemes. The SSH protocol is conveyed via the protocol: "SSH" field in the asset body. Key and protocol payloads carry an explicit relationships block (relationship_type: HOSTED_ON) so GCM can link the crypto object to the IT asset, preventing "Unique Identifier not found" skips.
After import, a results table shows Imported and Failed counts per object type. Expand the GCM responses collapsible section to see the raw GCM response body for every per-object API call β useful for diagnosing any remaining issues.
Alternatively, expand Or upload a certificates CSV manually to import only TLS certificates from a custom CSV file (useful when scanning was done offline with convert_certs_into_csv.py).
CSV formats:
| Step | Required columns | Optional columns |
|---|---|---|
| Targets (input to Step 2) | Alias, URI |
β |
| Certificates (output of Step 2 / input to Step 3 manual upload) | Alias, Certdata |
URI (optional) |
Tip: Use
disconnected-scanner/convert_certs_into_csv.pyto convert local PEM/DER certificate files into the certificates CSV format instead of running a live scan.
Backend API endpoints:
| Method | Path | Description |
|---|---|---|
POST |
/api/v1/scanner/generate-targets |
Expand IP/host/port inputs into a targets CSV |
POST |
/api/v1/scanner/run-scan-stream |
Stream scan progress as Server-Sent Events (SSE) β used by the UI |
DELETE |
/api/v1/scanner/stop-scan/{scan_id} |
Signal a running stream scan to stop after the current target |
POST |
/api/v1/scanner/run-scan |
Non-streaming batch scan (kept for scripting / API compatibility) |
POST |
/api/v1/scanner/validate-csv |
Validate a certificates CSV before import |
POST |
/api/v1/scanner/import-csv |
Import TLS certificates from CSV into GCM |
POST |
/api/v1/scanner/ingest-scan-results |
Ingest SSH host keys and TLS protocol metadata from scan results into GCM |
The easiest way to run GCM Web UI is as a Docker container. The image bundles the backend and frontend into a single container β no separate web server is required.
The container runs as a non-root user (appuser) and all Linux capabilities are dropped, making it safe for production and regulated environments.
docker-compose v1.29+)backend/.env with valid SECRET_KEY, ENCRYPTION_KEY, and β if needed β ENABLE_ASSET_SYNC.# 1. Clone / extract the repo and enter the project root
cd /path/to/GCM_External_data_UI
# 2. Create .env from the template (generates keys automatically)
cp backend/.env.example backend/.env
# Edit backend/.env and set SECRET_KEY, ENCRYPTION_KEY (use backend/setup.sh to auto-generate)
# 3. Build and start
docker compose up -d --build
# 4. Open the UI
open http://localhost:8000
The SQLite database is persisted in a named Docker volume (gcm-data), so your profiles and synced data survive container restarts and image rebuilds.
Security note: The published port is bound to
127.0.0.1(loopback) by default so it is not reachable from external hosts. To expose it on all interfaces, overridePORTor edit theportsmapping indocker-compose.yml.
By default only the Profiles, Scanner, and Authentication tabs are visible. To also show the Crypto Objects and IT Assets tabs with their sync actions, set the feature flag in backend/.env:
ENABLE_ASSET_SYNC=true
Then restart the container:
docker compose restart gcm-webui
The frontend reads the flag from /api/v1/config/features on every page load and reveals (or hides) the tabs and sync buttons accordingly β no rebuild required.
| Variable | Default | Description |
|---|---|---|
SECRET_KEY |
(required) | JWT / session signing key |
ENCRYPTION_KEY |
(required) | Fernet key for encrypting credentials at rest |
ENABLE_ASSET_SYNC |
false |
Show IT Assets and Crypto Objects tabs |
HOST |
0.0.0.0 |
Bind address |
PORT |
8000 |
Bind port |
LOG_LEVEL |
INFO |
Uvicorn / app log level |
CORS_ORIGINS |
http://localhost:3000 |
Comma-separated allowed CORS origins |
docker build -t gcm-webui .
docker run -d \
--name gcm-webui \
-p 127.0.0.1:8000:8000 \
--env-file backend/.env \
-v gcm-data:/app/backend \
--cap-drop ALL \
--security-opt no-new-privileges \
gcm-webui
# Pull latest code / rebuild
docker compose build --no-cache
# Restart (database volume is preserved)
docker compose up -d
The application runs zero-downtime column migrations on every startup via migrate_db() in backend/app/database.py. New columns are added with ALTER TABLE β¦ ADD COLUMN and silently skipped if they already exist. No manual migration steps are required when upgrading.
dist/
βββ README.md # This file
βββ config.toml.template # Configuration template
βββ backend/ # Backend application
β βββ app/ # Application code
β β βββ api/ # API endpoints
β β βββ models/ # Database models
β β βββ schemas/ # Data schemas
β β βββ services/ # Business logic
β β βββ main.py # FastAPI application
β β βββ config.py # Configuration
β β βββ database.py # Database setup
β β βββ security.py # Encryption utilities
β βββ requirements.txt # Python dependencies
β βββ setup.sh # Setup script
β βββ .env.example # Environment template
βββ frontend/ # Web interface
β βββ index.html # Main page
β βββ css/ # Stylesheets
β βββ js/ # JavaScript modules
βββ common/ # Shared GCM modules
β βββ config_loader.py # Config file support
β βββ oidc_authz_client.py # Authentication client
β βββ get_authz_token.py # Token management
βββ certificates/ # Certificate operations
β βββ get_certificate_inventory.py
β βββ post_certificate.py
β βββ delete_certificate_from_inventory.py
βββ it_assets/ # IT asset operations
β βββ get_it_assets_by_type.py
β βββ post_it_asset.py
β βββ delete_it_asset_from_inventory.py
βββ user_management/ # User management
β βββ create_keycloak_user.py
β βββ register_oidc_user.py
β βββ list_users.py
βββ disconnected-scanner/ # Scanner utilities
βββ gen_target_list.py # CLI: generate scan targets CSV
βββ get_certificates.py # CLI: fetch SSL certs from target list
βββ post_certificates_from_csv.py # CLI: post certificates CSV to GCM
βββ convert_certs_into_csv.py # CLI: convert PEM/DER files to CSV
.env file.env or config.toml files to version controlProfile URIs (app_uri and oidc_uri) are validated on create and update to prevent Server-Side Request Forgery (SSRF):
http:// or https://127.x.x.x, 10.x.x.x, 172.16β31.x.x, 192.168.x.x, 169.254.x.x, 100.64.x.x, ::1, ULA (fc00::/7), and IPv6 link-local (fe80::/10)localhost is always rejected regardless of caseThis protection is enforced by _assert_public_host() called from the ProfileBase, ProfileCreate, and ProfileUpdate validators on create/update, and re-validated at request time in user_management.py before any outbound call β preventing SSRF via a stale or tampered database value.
insecure = false in productionchmod 600 config.toml
chmod 600 backend/.env
For production deployments, use a reverse proxy (nginx, Apache) with SSL/TLS:
server {
listen 443 ssl;
server_name gcm-ui.example.com;
ssl_certificate /path/to/cert.pem;
ssl_certificate_key /path/to/key.pem;
location / {
proxy_pass http://localhost:8000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}
Problem: Import errors or module not found
Solution:
cd backend
source .venv/bin/activate
pip install -r requirements.txt
Problem: Connection refused or timeout errors
Solution:
config.tomlcurl -k https://your-gcm-host:31443insecure = true for testing (development only)Problem: Login or authorization errors (OIDC)
Solution:
Problem: API key requests return 401 Unauthorized
Solution:
api_key field is set on the active profileauth_method = api_key/api/v1/auth/login endpoint returns 400 for API key profiles β this is expectedProblem: Database locked or corruption errors
Solution:
cd backend
rm gcm_webui.db
uvicorn app.main:app --reload # Will recreate database
Problem: After clicking π€ Import All to GCM the GCM responses panel shows:
{"message":"No Data to Ingest","it_assets":{},"crypto_object_keys":{"skipped":[{"index":1,"message":"Unique Identifier not found"}]}}
Cause (fixed in v1.3.4): The importer was rewriting SSH target URIs from https://host:22 to ssh://host:22 before storing the IT asset in GCM. GCM silently discards IT asset records with ssh:// URIs (returns HTTP 200 with it_assets:{}), so the subsequent key ingest could not find the asset and skipped every record. The fix keeps https:// URIs throughout β GCM stores and resolves all server assets under https://, with the SSH protocol indicated by the protocol: "SSH" field in the asset body.
If it still happens:
[asset]: prefixed lines that indicate the IT asset pre-creation failed.POST /v2/assets/ingest/it_assets).https://host:port URIs β ssh:// URIs are not supported by GCMβs asset inventory.Problem: After a scan completes, the π€ Import All to GCM button does not appear above the results table.
Cause (fixed in v1.3.3): The button was only shown when at least one result matched a strict service-type check (service === 'ssh' or service === 'tls' with a version). Results arriving via the banner-detection fallback path could have a slightly different shape that skipped the check. The condition now shows the button whenever any probe returned success: true.
If the button still does not appear: all probed targets timed out or were unreachable. Verify the targets are reachable from the host running the backend and increase the timeout if needed.
Problem: Port 8000 is already in use
Solution:
# Use a different port
uvicorn app.main:app --host 0.0.0.0 --port 8080
# Or find and kill the process using port 8000
lsof -ti:8000 | xargs kill -9 # On Linux/macOS
Known issues and limitations in the current release.
Status: Open β behaviour is by design but feedback is missing.
Symptoms:
Workaround: Use the Import Certificates tab (Step 3) instead:
Note: TLS protocol metadata and SSH host keys are only imported by the Step 2 Import All to GCM button, not by the Step 3 certificate import. If those object types matter, click the Step 2 button and then verify the results by syncing the relevant section in GCM directly. The import runs even without visible feedback.
Status: Open β no known workaround at this time.
Affected versions: All (including v1.3.4)
Scope: SSH host-key crypto objects only. TLS certificates and TLS protocol objects ingest correctly under the same conditions.
SSH.POST /v2/assets/ingest/it_assets) completes with HTTP 200 and a non-empty it_assets block in the GCM response.Clicking π€ Import All to GCM posts each SSH host key to:
POST /v2/assets/ingest/crypto_objects/keys
GCM returns HTTP 200 but the response body always contains:
{
"message": "No Data to Ingest",
"crypto_object_keys": {
"skipped": [
{ "index": 1, "message": "Unique Identifier not found" }
]
}
}
Every SSH key record is skipped regardless of how the request payload is constructed.
| Attempt | Outcome |
|---|---|
Added relationships block with relationship_type: HOSTED_ON to the key payload |
Still skipped |
Pre-created the IT asset via /v2/assets/ingest/it_assets before posting the key |
Still skipped |
Added mandatory ip field to the IT asset upsert body |
Still skipped |
Changed the target URI scheme from ssh://host:22 to https://host:22 |
Still skipped (GCM silently discards assets stored with ssh:// URIs) |
| Verified authentication, network path, and tenant permissions against a TLS target on the same host | TLS objects ingest correctly β confirming the issue is endpoint-specific |
The GCM /v2/assets/ingest/crypto_objects/keys endpoint appears to require the target IT asset to have been discovered and indexed by GCMβs own internal discovery mechanism before it will accept an externally ingested key relationship. Assets created solely via the ingest API are not sufficient. Alternatively, there may be an undocumented required field or a tenant-level entitlement missing from the key payload β GCM does not surface a useful error beyond "Unique Identifier not found".
If you have access to GCM API documentation for /v2/assets/ingest/crypto_objects/keys or can obtain a working example payload from IBM support, please open an issue β the fix would be a targeted change to the key payload builder in backend/app/services/scanner_service.py.
cd backend
source .venv/bin/activate
pip install gunicorn
# Start with multiple workers
gunicorn app.main:app \
-w 4 \
-k uvicorn.workers.UvicornWorker \
--bind 0.0.0.0:8000 \
--access-logfile - \
--error-logfile -
Create /etc/systemd/system/gcm-webui.service:
[Unit]
Description=GCM Web UI
After=network.target
[Service]
Type=notify
User=gcm-user
WorkingDirectory=/path/to/dist/backend
Environment="PATH=/path/to/dist/backend/.venv/bin"
ExecStart=/path/to/dist/backend/.venv/bin/gunicorn app.main:app \
-w 4 \
-k uvicorn.workers.UvicornWorker \
--bind 0.0.0.0:8000
Restart=always
[Install]
WantedBy=multi-user.target
Enable and start:
sudo systemctl enable gcm-webui
sudo systemctl start gcm-webui
sudo systemctl status gcm-webui
To upgrade to a newer version:
cp backend/gcm_webui.db backend/gcm_webui.db.backup
cp config.toml config.toml.backup
cp backend/.env backend/.env.backup
Extract new version to a temporary location
cp config.toml.backup new-dist/config.toml
cp backend/.env.backup new-dist/backend/.env
cp backend/gcm_webui.db.backup new-dist/backend/gcm_webui.db
cd new-dist/backend
source .venv/bin/activate
pip install -r requirements.txt --upgrade
To remove the application:
# Stop the service (if using systemd)
sudo systemctl stop gcm-webui
sudo systemctl disable gcm-webui
# Remove the application directory
rm -rf /path/to/dist
# Remove systemd service file (if created)
sudo rm /etc/systemd/system/gcm-webui.service
sudo systemctl daemon-reload
For issues or questions:
backend/logs/ (if configured)The following features are planned for future releases:
This software is provided as-is for use with Guardium Cryptography Manager. Refer to your GCM license agreement for terms and conditions.
This application integrates with:
Note: This is experimental software. Review and adapt the code and configurations to your organizationβs security and operational standards before production use.