added CLOUDFLARED METRICS support

This commit is contained in:
ChrispyBacon-dev 2025-06-23 15:55:48 +02:00
parent 967e115451
commit f75470c7f8
4 changed files with 51 additions and 28 deletions

View file

@ -78,6 +78,19 @@ TUNNEL_DNS_SCAN_ZONE_NAMES = [name.strip() for name in TUNNEL_DNS_SCAN_ZONE_NAME
REQUIRED_VARS_BASE = ["CF_API_TOKEN", "CF_ACCOUNT_ID"]
missing_vars = []
# If set, enables the Prometheus metrics endpoint on the specified port.
# The IP is hardcoded to 0.0.0.0 to be accessible within Docker networks.
CLOUDFLARED_METRICS_PORT = os.getenv('CLOUDFLARED_METRICS_PORT')
if CLOUDFLARED_METRICS_PORT:
try:
port = int(CLOUDFLARED_METRICS_PORT)
if not (1 <= port <= 65535):
logging.warning(f"Metrics port {port} is outside the valid range (1-65535). Disabling.")
CLOUDFLARED_METRICS_PORT = None
except ValueError:
logging.warning(f"Invalid value for CLOUDFLARED_METRICS_PORT: '{CLOUDFLARED_METRICS_PORT}'. Must be a number. Disabling.")
CLOUDFLARED_METRICS_PORT = None
if not USE_EXTERNAL_CLOUDFLARED:
if not TUNNEL_NAME:
REQUIRED_VARS_BASE.append("TUNNEL_NAME")

View file

@ -427,17 +427,10 @@ def start_cloudflared_container():
cloudflared_agent_state["last_action_status"] = "Starting/Reconciling..."
if not docker_client:
msg = "Docker client not available."
logging.error(msg)
cloudflared_agent_state["last_action_status"] = f"Error: {msg}"
return False
if not tunnel_state.get("token"):
msg = "Tunnel token not available."
logging.error(msg)
cloudflared_agent_state["last_action_status"] = f"Error: {msg}"
return False
if not config.CLOUDFLARED_NETWORK_NAME or not ensure_docker_network_exists(config.CLOUDFLARED_NETWORK_NAME):
logging.error(f"Failed network check/create for '{config.CLOUDFLARED_NETWORK_NAME}'. Cannot start agent.")
return False
token = tunnel_state["token"]
@ -451,7 +444,6 @@ def start_cloudflared_container():
if network_mode != config.CLOUDFLARED_NETWORK_NAME:
logging.warning(f"Network mismatch for managed agent. Desired: '{config.CLOUDFLARED_NETWORK_NAME}', Actual: '{network_mode}'. Recreation required.")
needs_recreate = True
try:
current_image = container.image.tags[0] if container.image.tags else None
if current_image != config.CLOUDFLARED_IMAGE:
@ -459,12 +451,29 @@ def start_cloudflared_container():
needs_recreate = True
except Exception as img_err:
logging.warning(f"Could not reliably determine image for running agent container: {img_err}")
desired_metrics_port = config.CLOUDFLARED_METRICS_PORT
port_bindings = container.attrs.get('HostConfig', {}).get('PortBindings', {})
actual_metrics_port = None
if port_bindings:
for port_key in port_bindings:
if port_key.endswith('/tcp'):
actual_metrics_port = port_key[:-4]
break
if desired_metrics_port and actual_metrics_port != desired_metrics_port:
logging.warning(f"Metrics port mismatch. Desired: '{desired_metrics_port}', Actual: '{actual_metrics_port}'. Recreation required.")
needs_recreate = True
elif not desired_metrics_port and actual_metrics_port:
logging.warning(f"Metrics port should be disabled, but found port '{actual_metrics_port}' exposed. Recreation required.")
needs_recreate = True
if needs_recreate:
logging.info(f"Removing misconfigured agent container '{container.name}' before recreation...")
try:
container.remove(force=True)
container = None
container = None
except (APIError, requests.exceptions.ConnectionError) as rm_err:
logging.error(f"Failed to remove misconfigured agent '{container.name}': {rm_err}. Cannot proceed.")
cloudflared_agent_state["last_action_status"] = f"Error: Failed to remove old agent: {rm_err}"
@ -487,37 +496,38 @@ def start_cloudflared_container():
logging.info(f"Pulling image {config.CLOUDFLARED_IMAGE}...");
docker_client.images.pull(config.CLOUDFLARED_IMAGE)
logging.info("Image pull complete.")
except APIError as img_err:
except Exception as img_err:
logging.warning(f"Could not pull image {config.CLOUDFLARED_IMAGE}: {img_err}. Will attempt using local if available.")
except requests.exceptions.ConnectionError as e_conn_pull:
logging.error(f"Docker connection failed during image pull: {e_conn_pull}")
cloudflared_agent_state["last_action_status"] = f"Error: Docker connect pull image."
return False
command_parts = ["tunnel"]
ports_mapping = {}
if config.CLOUDFLARED_METRICS_PORT:
metrics_address = f"0.0.0.0:{config.CLOUDFLARED_METRICS_PORT}"
command_parts.extend(["--metrics", metrics_address])
ports_mapping[f"{config.CLOUDFLARED_METRICS_PORT}/tcp"] = int(config.CLOUDFLARED_METRICS_PORT)
logging.info(f"Metrics endpoint will be enabled on {metrics_address}")
command_parts.extend(["--no-autoupdate", "run", "--token", token])
try:
container_params = {
"image": config.CLOUDFLARED_IMAGE,
"command": f"tunnel --no-autoupdate run --token {token}",
"command": command_parts,
"name": config.CLOUDFLARED_CONTAINER_NAME,
"network": config.CLOUDFLARED_NETWORK_NAME,
"restart_policy": {"Name": "unless-stopped"},
"detach": True,
"remove": False,
"labels": {"managed-by": "dockflare"}
"labels": {"managed-by": "dockflare"},
"ports": ports_mapping
}
new_container = docker_client.containers.run(**container_params)
msg = f"Successfully created and started agent container '{new_container.name}' ({new_container.id[:12]})."
cloudflared_agent_state["last_action_status"] = msg
logging.info(msg)
except APIError as create_err:
msg = f"Docker API error creating agent container: {create_err}"
logging.error(msg, exc_info=True)
cloudflared_agent_state["last_action_status"] = msg
return False
return False
except requests.exceptions.ConnectionError as e_conn_run:
logging.error(f"Docker connection failed running agent container: {e_conn_run}")
cloudflared_agent_state["last_action_status"] = f"Error: Docker connect run agent."
return False
return False
time.sleep(2)
update_cloudflared_container_status()

View file

@ -174,7 +174,7 @@ def main_application_entrypoint():
logging.info("-" * 52)
logging.info("--- DockFlare Starting ---")
logging.info(f"--- Version: 1.9.0 ---")
logging.info(f"--- Version: 1.9.1 ---")
logging.info("-" * 52)
load_state()

View file

@ -21,7 +21,7 @@
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>DockFlare v1.9.0 - Cloudflare Tunnel ingress Manager</title>
<title>DockFlare v1.9.1 - Cloudflare Tunnel ingress Manager</title>
<link rel="stylesheet" href="{{ url_for('static', filename='css/output.css') }}">
<link rel="preconnect" href="https://rsms.me" crossorigin>
@ -43,7 +43,7 @@
<body class="min_h-screen flex flex-col font-sans transition-colors duration-300">
<div class="fixed bottom-6 right-[-38px] sm:bottom-7 sm:right-[-35px] z-50 transform -rotate-45 bg-indigo-600 text-white text-xs font-semibold tracking-wider text-center py-1 w-36 shadow-md"
style="pointer-events: none;">
version 1.9.0
version 1.9.1
</div>
<header class="sticky top-0 z-40 w-full backdrop-blur-sm shadow-sm bg-base-100/90">
@ -446,7 +446,7 @@
<p class="mb-1"><a href="https://github.com/ChrispyBacon-dev/DockFlare/wiki" target="_blank" rel="noopener noreferrer" class="link link-hover link-primary">DockFlare Documentation</a></p>
<p class="mb-1">Enjoying DockFlare? Consider supporting the project!</p>
<p><a href="https://github.com/sponsors/ChrispyBacon-dev" target="_blank" rel="noopener noreferrer" class="inline-flex items-center link link-hover"><svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4 mr-1 text-pink-500" viewBox="0 0 20 20" fill="currentColor"><path fill-rule="evenodd" d="M3.172 5.172a4 4 0 015.656 0L10 6.343l1.172-1.171a4 4 0 115.656 5.656L10 17.657l-6.828-6.829a4 4 0 010-5.656z" clip-rule="evenodd" /></svg>Sponsor ChrispyBacon-dev on GitHub</a></p>
<p class="mt-2 text-xs opacity-60">DockFlare v1.9.0</p>
<p class="mt-2 text-xs opacity-60">DockFlare v1.9.1</p>
</div>
</footer>
<dialog id="add_manual_rule_modal" class="modal">