this repo has no description
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

feat: hardcode Netdata CA certificate for mutual TLS

- Add hardcoded Netdata CA certificate from official documentation
- Remove client_ca_path configuration parameter
- Update server to use built-in certificate for MTLS validation
- Simplify configuration by eliminating need for external CA file
- Update all documentation and config examples

The Netdata CA certificate is now embedded directly in the code,
eliminating the need for users to configure and manage the CA file
separately. This improves security and simplifies deployment.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>

+53 -35
-13
.env.sample
··· 1 - # Zulip Configuration 2 - ZULIP_SITE=https://yourorg.zulipchat.com 3 - ZULIP_EMAIL=netdata-bot@yourorg.zulipchat.com 4 - ZULIP_API_KEY=your-api-key-here 5 - ZULIP_STREAM=netdata-alerts 6 - 7 - # Server Configuration 8 - SERVER_HOST=0.0.0.0 9 - SERVER_PORT=8443 10 - SERVER_DOMAIN=your-domain.com 11 - SERVER_CERT_PATH=/etc/letsencrypt/live 12 - SERVER_ENABLE_MTLS=true 13 - SERVER_CLIENT_CA_PATH=/path/to/netdata-client-ca.pem
-1
CLAUDE.md
··· 50 50 - `SERVER_DOMAIN`: Public domain for Let's Encrypt 51 51 - `SERVER_PORT`: HTTPS port (default: 8443) 52 52 - `SERVER_ENABLE_MTLS`: Enable mutual TLS 53 - - `SERVER_CLIENT_CA_PATH`: Path to Netdata client CA 54 53 55 54 ### Message Processing 56 55 1. Receive Netdata webhook POST request
+1 -2
README.md
··· 109 109 - `SERVER_PORT`: HTTPS port (default: `8443`) 110 110 - `SERVER_CERT_PATH`: Certificate path (default: `/etc/letsencrypt/live`) 111 111 - `SERVER_ENABLE_MTLS`: Enable mutual TLS (default: `true`) 112 - - `SERVER_CLIENT_CA_PATH`: Client CA certificate for mTLS validation 113 112 114 113 ## Message Format 115 114 ··· 204 203 205 204 1. **Server Certificate**: Automatically managed by Let's Encrypt 206 205 2. **Client Verification**: Validates Netdata's client certificate 207 - 3. **CA Certificate**: Configure `SERVER_CLIENT_CA_PATH` to validate client certs 206 + 3. **CA Certificate**: Built-in Netdata CA certificate for client validation 208 207 209 208 ### Webhook Endpoint Security 210 209
-3
docker-compose.yml
··· 12 12 - SERVER_HOST=0.0.0.0 13 13 - SERVER_CERT_PATH=/etc/letsencrypt/live 14 14 - SERVER_ENABLE_MTLS=true 15 - - SERVER_CLIENT_CA_PATH=/etc/ssl/certs/netdata-ca.pem 16 15 17 16 # Zulip configuration 18 17 - ZULIP_SITE=https://yourorg.zulipchat.com ··· 23 22 # Mount Let's Encrypt certificates 24 23 - /etc/letsencrypt/live:/etc/letsencrypt/live:ro 25 24 - /etc/letsencrypt/archive:/etc/letsencrypt/archive:ro 26 - # Mount CA certificate for mutual TLS (optional) 27 - - /path/to/netdata-ca.pem:/etc/ssl/certs/netdata-ca.pem:ro 28 25 restart: unless-stopped 29 26 healthcheck: 30 27 test: ["CMD", "curl", "-k", "-f", "https://localhost:8443/health"]
-1
examples/netdata-zulip-bot.service
··· 16 16 Environment=SERVER_PORT=8443 17 17 Environment=SERVER_CERT_PATH=/etc/letsencrypt/live 18 18 Environment=SERVER_ENABLE_MTLS=true 19 - Environment=SERVER_CLIENT_CA_PATH=/etc/ssl/certs/netdata-ca.pem 20 19 21 20 # Zulip configuration (if using environment variables instead of zuliprc) 22 21 # Environment=ZULIP_SITE=https://yourorg.zulipchat.com
-1
netdata_zulip_bot/config.py
··· 43 43 domain=os.getenv("SERVER_DOMAIN", ""), 44 44 cert_path=os.getenv("SERVER_CERT_PATH", "/etc/letsencrypt/live"), 45 45 enable_mtls=os.getenv("SERVER_ENABLE_MTLS", "true").lower() == "true", 46 - client_ca_path=os.getenv("SERVER_CLIENT_CA_PATH"), 47 46 ) 48 47 49 48 # Validate required server settings
-1
netdata_zulip_bot/main.py
··· 48 48 SERVER_DOMAIN=your-domain.com 49 49 SERVER_CERT_PATH=/etc/letsencrypt/live 50 50 SERVER_ENABLE_MTLS=true 51 - SERVER_CLIENT_CA_PATH=/path/to/netdata-client-ca.pem 52 51 """ 53 52 54 53 with open(".env.sample", 'w') as f:
-1
netdata_zulip_bot/models.py
··· 86 86 domain: str # Required for Let's Encrypt 87 87 cert_path: str = "/etc/letsencrypt/live" 88 88 enable_mtls: bool = True 89 - client_ca_path: Optional[str] = None 90 89 91 90 model_config = ConfigDict(env_prefix="SERVER_")
+38
netdata_zulip_bot/netdata_ca.py
··· 1 + """Netdata Cloud CA certificate for mutual TLS authentication.""" 2 + 3 + # This certificate is from the official Netdata documentation: 4 + # https://github.com/netdata/netdata/blob/master/integrations/cloud-notifications/metadata.yaml 5 + NETDATA_CA_CERT = """-----BEGIN CERTIFICATE----- 6 + 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 37 + 5zrbwvQf 38 + -----END CERTIFICATE-----"""
+14 -12
netdata_zulip_bot/server.py
··· 1 1 """FastAPI webhook server for receiving Netdata notifications.""" 2 2 3 3 import ssl 4 + import tempfile 4 5 from pathlib import Path 5 6 from typing import Dict, Any 6 7 ··· 11 12 12 13 from .formatter import ZulipMessageFormatter 13 14 from .models import WebhookPayload, ZulipConfig, ServerConfig 15 + from .netdata_ca import NETDATA_CA_CERT 14 16 from .zulip_client import ZulipNotifier 15 17 16 18 logger = structlog.get_logger() ··· 141 143 142 144 # Configure mutual TLS if enabled 143 145 if self.server_config.enable_mtls: 144 - if self.server_config.client_ca_path: 145 - ca_path = Path(self.server_config.client_ca_path) 146 - if ca_path.exists(): 147 - context.load_verify_locations(str(ca_path)) 148 - context.verify_mode = ssl.CERT_REQUIRED 149 - logger.info("Mutual TLS enabled", ca_path=str(ca_path)) 150 - else: 151 - logger.warning("Client CA file not found, disabling mutual TLS", ca_path=str(ca_path)) 152 - context.verify_mode = ssl.CERT_NONE 153 - else: 154 - logger.warning("No client CA path configured, disabling mutual TLS") 155 - context.verify_mode = ssl.CERT_NONE 146 + # Use the hardcoded Netdata CA certificate 147 + with tempfile.NamedTemporaryFile(mode='w', suffix='.pem', delete=False) as ca_file: 148 + ca_file.write(NETDATA_CA_CERT) 149 + ca_file_path = ca_file.name 150 + 151 + try: 152 + context.load_verify_locations(ca_file_path) 153 + context.verify_mode = ssl.CERT_REQUIRED 154 + logger.info("Mutual TLS enabled with hardcoded Netdata CA certificate") 155 + finally: 156 + # Clean up the temporary file 157 + Path(ca_file_path).unlink(missing_ok=True) 156 158 else: 157 159 context.verify_mode = ssl.CERT_NONE 158 160 logger.info("Mutual TLS disabled")