mirror of
https://github.com/ChrispyBacon-dev/DockFlare.git
synced 2026-04-28 03:39:32 +00:00
Bugfix Docker Rule overwrite in UI
This commit is contained in:
parent
4f35fde0c6
commit
74aec141d8
7 changed files with 85 additions and 5 deletions
|
|
@ -105,6 +105,16 @@ def create_app():
|
|||
login_manager.login_view = 'auth.login'
|
||||
login_manager.login_message_category = "info"
|
||||
|
||||
@login_manager.unauthorized_handler
|
||||
def unauthorized():
|
||||
"""Handle unauthorized access - return JSON for API requests, redirect for web requests."""
|
||||
from flask import request, jsonify, redirect, url_for
|
||||
# Check if this is an API request
|
||||
if request.path.startswith('/api/'):
|
||||
return jsonify({"status": "error", "message": "authentication_required"}), 401
|
||||
# For web requests, redirect to login page
|
||||
return redirect(url_for('auth.login'))
|
||||
|
||||
@login_manager.user_loader
|
||||
def load_user(user_id):
|
||||
"""Load user from the config for session management."""
|
||||
|
|
|
|||
|
|
@ -268,6 +268,10 @@ def process_container_start(container_obj):
|
|||
logging.info(f"DOCKER_HANDLER: Rule {rule_key} is manual, skipping for {container_name_val}.")
|
||||
continue
|
||||
|
||||
if existing_rule and existing_rule.get("rule_ui_override", False):
|
||||
logging.info(f"DOCKER_HANDLER: Rule {rule_key} is UI-overridden, skipping Docker updates for {container_name_val}.")
|
||||
continue
|
||||
|
||||
original_existing_rule_for_comparison = copy.deepcopy(existing_rule) if existing_rule else None
|
||||
|
||||
if existing_rule:
|
||||
|
|
@ -337,6 +341,7 @@ def process_container_start(container_obj):
|
|||
"access_policy_type": None,
|
||||
"access_app_config_hash": None,
|
||||
"access_policy_ui_override": False,
|
||||
"rule_ui_override": False,
|
||||
"source": "docker",
|
||||
"access_group_id": None,
|
||||
"tunnel_id": master_tunnel_id,
|
||||
|
|
|
|||
|
|
@ -264,6 +264,7 @@ def reconcile_agent_report(agent_id, reported_containers):
|
|||
"access_policy_type": None,
|
||||
"access_app_config_hash": None,
|
||||
"access_policy_ui_override": False,
|
||||
"rule_ui_override": False,
|
||||
"source": "agent",
|
||||
"agent_id": agent_id,
|
||||
"access_group_id": None,
|
||||
|
|
@ -389,7 +390,7 @@ def _run_reconciliation_logic():
|
|||
"origin_server_name": desired_details.get("origin_server_name"),
|
||||
"http_host_header": desired_details.get("http_host_header"),
|
||||
"access_app_id": None, "access_policy_type": None, "access_app_config_hash": None,
|
||||
"access_policy_ui_override": False, "source": "docker",
|
||||
"access_policy_ui_override": False, "rule_ui_override": False, "source": "docker",
|
||||
"access_group_id": None,
|
||||
"tunnel_name": master_tunnel_name
|
||||
}
|
||||
|
|
|
|||
|
|
@ -128,6 +128,7 @@ def load_state():
|
|||
rule_copy.setdefault("access_policy_type", None)
|
||||
rule_copy.setdefault("access_app_config_hash", None)
|
||||
rule_copy.setdefault("access_policy_ui_override", False)
|
||||
rule_copy.setdefault("rule_ui_override", False)
|
||||
rule_copy.setdefault("source", "docker")
|
||||
rule_copy.setdefault("path", None)
|
||||
rule_copy.setdefault("http_host_header", None)
|
||||
|
|
@ -197,6 +198,7 @@ def save_state():
|
|||
"access_policy_type": rule.get("access_policy_type"),
|
||||
"access_app_config_hash": rule.get("access_app_config_hash"),
|
||||
"access_policy_ui_override": rule.get("access_policy_ui_override", False),
|
||||
"rule_ui_override": rule.get("rule_ui_override", False),
|
||||
"source": rule.get("source", "docker"),
|
||||
"access_group_id": rule.get("access_group_id"),
|
||||
"tunnel_id": rule.get("tunnel_id"),
|
||||
|
|
@ -355,7 +357,8 @@ def serialize_managed_rule(rule_key: str, rule: Dict[str, Any]) -> Dict[str, Any
|
|||
"tunnel_id": rule.get("tunnel_id"),
|
||||
"tunnel_name": rule.get("tunnel_name"),
|
||||
"access_policy_type": rule.get("access_policy_type"),
|
||||
"access_policy_ui_override": rule.get("access_policy_ui_override", False)
|
||||
"access_policy_ui_override": rule.get("access_policy_ui_override", False),
|
||||
"rule_ui_override": rule.get("rule_ui_override", False)
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -189,6 +189,9 @@
|
|||
{% if details.access_policy_ui_override and details.source != 'manual' %}
|
||||
<span class="badge badge-warning badge-xs ml-2 animate-pulse" title="This policy is managed by the UI and overrides container labels.">UI Override</span>
|
||||
{% endif %}
|
||||
{% if details.rule_ui_override and details.source == 'docker' %}
|
||||
<span class="badge badge-error badge-xs ml-2 animate-pulse" title="This Docker rule has been overridden via UI and no longer reflects container labels.">Rule UI Override</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td class="p-3 whitespace-nowrap text-sm policy-actions-cell" style="min-width: 180px;">
|
||||
<div class="flex items-center space-x-1 sm:space-x-2">
|
||||
|
|
@ -203,6 +206,13 @@
|
|||
<button type="submit" class="btn btn-xs btn-warning btn-outline" title="Revert to Label/Default Policy">Revert</button>
|
||||
</form>
|
||||
{% endif %}
|
||||
{% if details.rule_ui_override and details.source == 'docker' %}
|
||||
<form action="{{ url_for('web.ui_revert_docker_rule_route') }}" method="POST" onsubmit="return confirm('Revert rule for {{ display_hostname }} to be managed by Docker labels?');" class="protocol-aware-form inline-block">
|
||||
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
|
||||
<input type="hidden" name="rule_key" value="{{ hostname }}">
|
||||
<button type="submit" class="btn btn-xs btn-error btn-outline" title="Revert to Docker Label Control">Revert to Labels</button>
|
||||
</form>
|
||||
{% endif %}
|
||||
</div>
|
||||
</td>
|
||||
<td class="p-3 whitespace-nowrap text-sm opacity-70" data-role="expires-cell">
|
||||
|
|
|
|||
|
|
@ -75,8 +75,12 @@ def _enforce_master_api_key():
|
|||
return
|
||||
if request.method == 'OPTIONS':
|
||||
return
|
||||
|
||||
# For agent endpoints in allowlist, skip all authentication (including Flask-Login)
|
||||
if endpoint in _AGENT_ENDPOINT_ALLOWLIST:
|
||||
return
|
||||
|
||||
# For all other API endpoints, ensure proper API authentication
|
||||
expected_key = current_app.config.get('MASTER_API_KEY') or config.MASTER_API_KEY
|
||||
if not expected_key:
|
||||
logging.warning("MASTER_AUTH: Master API key not configured; rejecting %s", endpoint)
|
||||
|
|
@ -495,6 +499,7 @@ def create_manual_rule_api():
|
|||
"access_policy_type": None,
|
||||
"access_app_config_hash": None,
|
||||
"access_policy_ui_override": False,
|
||||
"rule_ui_override": False,
|
||||
"source": "manual",
|
||||
"access_group_id": access_groups or None,
|
||||
"tunnel_id": tunnel_id,
|
||||
|
|
@ -723,6 +728,7 @@ def process_agent_container_start(payload, agent_id):
|
|||
"access_policy_type": None,
|
||||
"access_app_config_hash": None,
|
||||
"access_policy_ui_override": False,
|
||||
"rule_ui_override": False,
|
||||
"source": "agent",
|
||||
"agent_id": agent_id,
|
||||
"tunnel_name": assigned_tunnel_name,
|
||||
|
|
|
|||
|
|
@ -1262,6 +1262,7 @@ def ui_add_manual_rule_route():
|
|||
"access_app_config_hash": access_app_config_hash,
|
||||
"access_group_id": access_group_id,
|
||||
"access_policy_ui_override": True,
|
||||
"rule_ui_override": False,
|
||||
"tunnel_id": target_tunnel_id,
|
||||
"tunnel_name": target_tunnel_name
|
||||
}
|
||||
|
|
@ -1280,6 +1281,49 @@ def ui_add_manual_rule_route():
|
|||
|
||||
return redirect(url_for('web.status_page'))
|
||||
|
||||
@bp.route('/ui/docker-rules/revert', methods=['POST'])
|
||||
def ui_revert_docker_rule_route():
|
||||
"""
|
||||
Reverts a UI-overridden Docker rule back to label-driven configuration.
|
||||
"""
|
||||
if not docker_client:
|
||||
cloudflared_agent_state["last_action_status"] = "Error: Docker client unavailable."
|
||||
return redirect(url_for('web.status_page'))
|
||||
|
||||
rule_key = request.form.get('rule_key')
|
||||
if not rule_key:
|
||||
cloudflared_agent_state["last_action_status"] = "Error: Missing rule key for revert."
|
||||
return redirect(url_for('web.status_page'))
|
||||
|
||||
with state_lock:
|
||||
existing = managed_rules.get(rule_key)
|
||||
if not existing:
|
||||
cloudflared_agent_state["last_action_status"] = f"Error: Rule '{rule_key}' not found."
|
||||
return redirect(url_for('web.status_page'))
|
||||
|
||||
if existing.get("source") != "docker":
|
||||
cloudflared_agent_state["last_action_status"] = f"Error: Rule '{rule_key}' is not a Docker rule."
|
||||
return redirect(url_for('web.status_page'))
|
||||
|
||||
if not existing.get("rule_ui_override", False):
|
||||
cloudflared_agent_state["last_action_status"] = f"Info: Rule '{rule_key}' is not UI-overridden."
|
||||
return redirect(url_for('web.status_page'))
|
||||
|
||||
# Revert the rule back to Docker label control
|
||||
existing["rule_ui_override"] = False
|
||||
save_state()
|
||||
|
||||
cloudflared_agent_state["last_action_status"] = f"Success: Rule '{rule_key}' reverted to Docker label control. Reconciliation will update it based on container labels."
|
||||
|
||||
# Trigger reconciliation to pick up Docker labels
|
||||
try:
|
||||
from app.core.reconciler import reconcile_state_threaded
|
||||
reconcile_state_threaded()
|
||||
except Exception as e:
|
||||
logging.error(f"Failed to trigger reconciliation after Docker rule revert: {e}")
|
||||
|
||||
return redirect(url_for('web.status_page'))
|
||||
|
||||
@bp.route('/ui/manual-rules/edit', methods=['POST'])
|
||||
def ui_edit_manual_rule_route():
|
||||
"""
|
||||
|
|
@ -1301,9 +1345,9 @@ def ui_edit_manual_rule_route():
|
|||
if not existing:
|
||||
cloudflared_agent_state["last_action_status"] = f"Error: Rule '{rule_key}' not found."
|
||||
return redirect(url_for('web.status_page'))
|
||||
if existing.get("source") == "docker":
|
||||
cloudflared_agent_state["last_action_status"] = f"Error: Rule '{rule_key}' is Docker-managed and cannot be edited via UI."
|
||||
return redirect(url_for('web.status_page'))
|
||||
|
||||
# Allow editing Docker rules but mark them as UI-overridden
|
||||
is_docker_rule = existing.get("source") == "docker"
|
||||
|
||||
subdomain_input = request.form.get('edit_subdomain', '').strip()
|
||||
domain_name_input = request.form.get('edit_domain_name', '').strip()
|
||||
|
|
@ -1442,6 +1486,7 @@ def ui_edit_manual_rule_route():
|
|||
"access_app_config_hash": access_app_config_hash,
|
||||
"access_group_id": access_group_id,
|
||||
"access_policy_ui_override": True,
|
||||
"rule_ui_override": is_docker_rule,
|
||||
"source": existing.get("source", "manual")
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue