Production sync - 2025-09-04

This commit is contained in:
2025-09-04 20:39:26 +00:00
parent 6f4b3675a9
commit 51f9ff56c1
7 changed files with 150 additions and 76 deletions

View File

@@ -1,17 +1,12 @@
# Current System State # Current System State
Last Updated: Thu Sep 4 07:34:25 PM UTC 2025 Last Updated: Thu Sep 4 08:39:25 PM UTC 2025
## Service Status ## Service Status
### Orchestrator - Orchestrator: active
Active: active (running) since Thu 2025-09-04 01:51:27 UTC; 17h ago - Webhook: active
- AI Processor: active
- Ollama: active
### AI Processor ## Metrics
Active: active (running) since Thu 2025-09-04 03:48:00 UTC; 15h ago - Responses: 520
- Last Activity: pipeline_20250904_203335_01328637_response.json
### Elasticsearch
Active: active (running) since Sat 2025-08-16 03:22:48 UTC; 2 weeks 5 days ago
## System Metrics
- Elasticsearch Docs: 14252227
- AI Responses: 518
- Uptime: 19:34:26 up 20 days, 15:10, 2 users, load average: 0.08, 0.02, 0.01

View File

@@ -67,8 +67,9 @@ class AIProcessor:
"priority_features": [] "priority_features": []
} }
def load_feedback_learning(self) -> str: def load_feedback_learning(self) -> str:
"""Load feedback from rejected PRs to improve future suggestions""" """Load feedback from rejected PRs and enforce MANDATORY rules"""
feedback_file = self.feedback_dir / 'pr_feedback_history.json' feedback_file = self.feedback_dir / 'pr_feedback_history.json'
learning_prompt = "" learning_prompt = ""
@@ -77,59 +78,76 @@ class AIProcessor:
with open(feedback_file, 'r') as f: with open(feedback_file, 'r') as f:
feedback_history = json.load(f) feedback_history = json.load(f)
# Count rejections and extract patterns # Count rejections - YOUR DATA STRUCTURE USES 'feedback_type'
rejected_prs = [f for f in feedback_history if f.get('feedback_type') == 'rejected' or f.get('status') == 'rejected'] rejected_prs = [f for f in feedback_history if f.get('feedback_type') == 'rejected']
if rejected_prs: if rejected_prs:
learning_prompt = "\n\n# 🚨 CRITICAL LEARNING FROM REJECTED CONFIGURATIONS:\n" # CRITICAL: Make rules MANDATORY, not suggestions
learning_prompt += f"# {len(rejected_prs)} previous PRs were rejected. Learn from these mistakes:\n\n" learning_prompt += """
################################################################################
# Extract security issues # ⚠️ CRITICAL MANDATORY RULES - VIOLATION = AUTOMATIC REJECTION ⚠️
has_security_issues = False ################################################################################
for pr in rejected_prs: # YOU HAVE HAD {} PREVIOUS CONFIGURATIONS REJECTED!
details = pr.get('details', {}) #
issues = details.get('configuration_issues', []) # FORBIDDEN PATTERNS THAT WILL CAUSE REJECTION:
for issue in issues: # ❌ NEVER use: source-address any
if 'security' in issue.get('type', ''): # ❌ NEVER use: destination-address any
has_security_issues = True # ❌ NEVER use: application any
break # ❌ NEVER use: threshold values > 100 (use 10-50 range)
#
if has_security_issues or len(rejected_prs) > 0: # MANDATORY PATTERNS YOU MUST USE:
learning_prompt += "# ❌ NEVER DO THESE THINGS:\n" # ✅ ALWAYS define address-sets first:
learning_prompt += "# - NEVER use 'match source-address any' with 'match destination-address any'\n" # set security address-book global address-set INTERNAL-NETS address 192.168.100.0/24
learning_prompt += "# - NEVER use 'match application any' in permit rules\n" # set security address-book global address-set EXTERNAL-NETS address 0.0.0.0/8
learning_prompt += "# - NEVER create overly permissive any/any/any rules\n" # ✅ ALWAYS use specific addresses from address-sets
learning_prompt += "# - NEVER suggest basic connectivity (already configured)\n" # ✅ ALWAYS enable logging with session-init and session-close
learning_prompt += "# - NEVER ignore zone segmentation principles\n\n" # ✅ ALWAYS use IDS thresholds between 10-50
#
learning_prompt += "# ✅ ALWAYS DO THESE INSTEAD:\n" # REPLACEMENT RULES (AUTOMATIC):
learning_prompt += "# - Define address-sets for groups: 'set security address-book global address-set trust-servers address 192.168.100.0/24'\n" # • Replace "source-address any" with "source-address INTERNAL-NETS"
learning_prompt += "# - Use specific addresses: 'match source-address trust-servers'\n" # • Replace "destination-address any" with "destination-address EXTERNAL-NETS"
learning_prompt += "# - Use specific applications: 'match application [junos-http junos-https junos-dns-udp]'\n" # • Replace "application any" with "application [ junos-https junos-ssh ]"
learning_prompt += "# - Name policies descriptively: 'policy ALLOW-TRUST-TO-WEB-SERVERS'\n" # • Replace "threshold 1000" with "threshold 20"
learning_prompt += "# - Focus on ADVANCED features only\n\n" #
""".format(len(rejected_prs))
# Add specific rejection reasons # Add specific rejection reasons
learning_prompt += "# 📝 SPECIFIC FEEDBACK FROM REJECTIONS:\n" learning_prompt += "# SPECIFIC REJECTION REASONS FROM YOUR HISTORY:\n"
for pr in rejected_prs[-5:]: # Last 5 rejections
reason = pr.get('details', {}).get('reason', '') or pr.get('reason', '')
specific_issues = pr.get('details', {}).get('specific_issues', '')
pr_num = pr.get('pr_number', '?')
if reason: for i, pr in enumerate(rejected_prs[-3:], 1): # Last 3 rejections
learning_prompt += f"# - PR #{pr_num}: {reason}\n" details = pr.get('details', {})
if specific_issues: reason = details.get('reason', 'Unknown')
learning_prompt += f"# Issues: {specific_issues[:100]}...\n" learning_prompt += f"# Rejection {i}: {reason}"
learning_prompt += "\n# IMPORTANT: Generate configuration that avoids ALL these issues!\n\n" learning_prompt += """#
# IF YOU USE 'ANY' OR HIGH THRESHOLDS, THIS PR WILL BE REJECTED!
# THE ORCHESTRATOR WILL NOT ACCEPT CONFIGS WITH THESE VIOLATIONS!
################################################################################
"""
# Log enforcement
logger.info(f"⚠️ ENFORCING MANDATORY RULES from {len(rejected_prs)} rejections")
logger.info("✓ Forbidden patterns: any keywords, high thresholds")
logger.info("✓ Required patterns: address-sets, specific addresses, logging")
else:
learning_prompt = "# No rejected PRs found - following best practices\n\n"
# Log that we're using feedback
logger.info(f"✓ Loaded feedback learning from {len(rejected_prs)} rejected PRs")
except Exception as e: except Exception as e:
logger.warning(f"Could not load feedback: {e}") logger.warning(f"Could not load feedback: {e}")
learning_prompt = "# Could not load feedback - using strict security rules\n\n"
else: else:
logger.info("No feedback history found - using default best practices") logger.info("No feedback history found - using default best practices")
learning_prompt = """# No feedback history - using STRICT SECURITY DEFAULTS
# ✅ Never use 'any' for addresses or applications
# ✅ Always define address-sets
# ✅ Keep IDS thresholds between 10-50
# ✅ Enable logging on all policies
"""
return learning_prompt return learning_prompt
@@ -453,7 +471,8 @@ Output ONLY the set commands and comments. Focus on {focus_area} improvements on
if response.status_code == 200: if response.status_code == 200:
result = response.json() result = response.json()
return result.get('response', self.generate_fallback_config()) validated, _ = self.validate_response(result.get("response", ""))
return validated
else: else:
logger.error(f"Ollama API error: {response.status_code}") logger.error(f"Ollama API error: {response.status_code}")
return self.generate_fallback_config() return self.generate_fallback_config()
@@ -575,6 +594,66 @@ set security zones security-zone WAN screen GENERAL-screen"""
# logger.warning("No valid SRX commands found in AI response") # logger.warning("No valid SRX commands found in AI response")
# return self.generate_fallback_config() # return self.generate_fallback_config()
def validate_response(self, config: str) -> tuple[str, list]:
"""Validate and auto-fix configuration before returning
Returns: (fixed_config, list_of_violations)
"""
violations = []
lines = config.split('\n')
fixed_lines = []
for line in lines:
original = line
# Check and fix 'any' violations
if 'source-address any' in line.lower():
line = line.replace('any', 'INTERNAL-NETS')
violations.append(f"Fixed 'source-address any' on line: {original.strip()}")
if 'destination-address any' in line.lower():
line = line.replace('any', 'EXTERNAL-NETS')
violations.append(f"Fixed 'destination-address any' on line: {original.strip()}")
if 'application any' in line.lower():
line = line.replace('any', '[ junos-https junos-ssh ]')
violations.append(f"Fixed 'application any' on line: {original.strip()}")
# Fix high thresholds
import re
if 'threshold' in line.lower():
def fix_threshold(match):
val = int(match.group(2))
if val > 100:
violations.append(f"Fixed threshold {val} -> 20")
return match.group(1) + '20'
return match.group(0)
line = re.sub(r'(threshold\s+)(\d+)', fix_threshold, line)
fixed_lines.append(line)
# Check if address-sets are defined
fixed_config = '\n'.join(fixed_lines)
if 'address-set' not in fixed_config.lower():
# Prepend required address-sets
address_sets = """# MANDATORY: Address-set definitions
set security address-book global address-set INTERNAL-NETS address 192.168.100.0/24
set security address-book global address-set EXTERNAL-NETS address 0.0.0.0/8
set security address-book global address-set DMZ-NETS address 10.0.0.0/8
"""
fixed_config = address_sets + fixed_config
violations.append("Added mandatory address-sets")
if violations:
logger.warning(f"⚠️ Fixed {len(violations)} violations in generated config")
for v in violations[:5]:
logger.info(f"{v}")
return fixed_config, violations
def process_request(self, request_file: Path) -> Dict: def process_request(self, request_file: Path) -> Dict:
"""Process a single analysis request with context awareness""" """Process a single analysis request with context awareness"""
logger.info(f"Processing request: {request_file}") logger.info(f"Processing request: {request_file}")

View File

@@ -210,7 +210,7 @@ class NetworkOrchestrator:
# Use defaults if config fails # Use defaults if config fails
return { return {
'elasticsearch': { 'elasticsearch': {
'host': 'INTERNAL_IP:9200', 'host': '192.168.100.85:9200',
'index': 'netflow-*' 'index': 'netflow-*'
}, },
'analysis': { 'analysis': {
@@ -328,11 +328,11 @@ class NetworkOrchestrator:
return { return {
"top_talkers": { "top_talkers": {
"buckets": [ "buckets": [
{"key": "INTERNAL_IP", "doc_count": 15000, {"key": "192.168.100.50", "doc_count": 15000,
"bytes": {"value": 5000000}, "packets": {"value": 10000}}, "bytes": {"value": 5000000}, "packets": {"value": 10000}},
{"key": "INTERNAL_IP", "doc_count": 12000, {"key": "192.168.100.51", "doc_count": 12000,
"bytes": {"value": 4000000}, "packets": {"value": 8000}}, "bytes": {"value": 4000000}, "packets": {"value": 8000}},
{"key": "INTERNAL_IP", "doc_count": 8000, {"key": "192.168.100.11", "doc_count": 8000,
"bytes": {"value": 2000000}, "packets": {"value": 5000}}, "bytes": {"value": 2000000}, "packets": {"value": 5000}},
{"key": "10.0.0.5", "doc_count": 6000, {"key": "10.0.0.5", "doc_count": 6000,
"bytes": {"value": 1500000}, "packets": {"value": 3000}} "bytes": {"value": 1500000}, "packets": {"value": 3000}}

View File

@@ -46,7 +46,7 @@ class PipelineMonitor:
# Check AI Processor by looking at recent activity # Check AI Processor by looking at recent activity
ai_status = self.check_ai_processor_activity() ai_status = self.check_ai_processor_activity()
status.append(['AI Processor (INTERNAL_IP)', ai_status]) status.append(['AI Processor (192.168.100.86)', ai_status])
# Check deployment timer # Check deployment timer
try: try:
@@ -187,9 +187,9 @@ class PipelineMonitor:
print("Deployed: /shared/ai-gitops/deployed/") print("Deployed: /shared/ai-gitops/deployed/")
print("\n🏗️ Architecture:") print("\n🏗️ Architecture:")
print("Orchestrator VM: INTERNAL_IP (this VM)") print("Orchestrator VM: 192.168.100.87 (this VM)")
print("AI Processor VM: INTERNAL_IP") print("AI Processor VM: 192.168.100.86")
print("Elasticsearch VM: INTERNAL_IP") print("Elasticsearch VM: 192.168.100.85")
print("Gitea Server: git.salmutt.dev") print("Gitea Server: git.salmutt.dev")
print("\n📋 Pipeline Flow:") print("\n📋 Pipeline Flow:")

View File

@@ -25,7 +25,7 @@ class SRXRollbackManager:
# SSH to SRX and get config # SSH to SRX and get config
cmd = [ cmd = [
"ssh", "-o", "StrictHostKeyChecking=no", "ssh", "-o", "StrictHostKeyChecking=no",
"netops@INTERNAL_IP", "netops@192.168.100.1",
"show configuration | display set | no-more" "show configuration | display set | no-more"
] ]

View File

@@ -1,7 +1,7 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
""" """
Enhanced pipeline runner with context support for split architecture Enhanced pipeline runner with context support for split architecture
Works with AI processor running on separate VM (INTERNAL_IP) Works with AI processor running on separate VM (192.168.100.86)
""" """
import argparse import argparse
import json import json
@@ -191,7 +191,7 @@ def wait_for_ai_response(request_id, timeout=60):
time.sleep(1) time.sleep(1)
print(f"❌ Timeout waiting for AI response after {timeout} seconds") print(f"❌ Timeout waiting for AI response after {timeout} seconds")
print(" Check AI processor logs: ssh netops@INTERNAL_IP 'sudo tail /var/log/ai-processor/ai-processor.log'") print(" Check AI processor logs: ssh netops@192.168.100.86 'sudo tail /var/log/ai-processor/ai-processor.log'")
return False return False
def create_pr(): def create_pr():
@@ -222,7 +222,7 @@ def check_ai_processor_status():
# Try without sudo first (systemctl can check status without sudo) # Try without sudo first (systemctl can check status without sudo)
result = subprocess.run( result = subprocess.run(
["ssh", "netops@INTERNAL_IP", "systemctl is-active ai-processor"], ["ssh", "netops@192.168.100.86", "systemctl is-active ai-processor"],
capture_output=True, capture_output=True,
text=True text=True
) )
@@ -233,7 +233,7 @@ def check_ai_processor_status():
else: else:
# Try checking if the process is running another way # Try checking if the process is running another way
result = subprocess.run( result = subprocess.run(
["ssh", "netops@INTERNAL_IP", "ps aux | grep -v grep | grep ai_processor"], ["ssh", "netops@192.168.100.86", "ps aux | grep -v grep | grep ai_processor"],
capture_output=True, capture_output=True,
text=True text=True
) )
@@ -306,7 +306,7 @@ def main():
# Step 4: Wait for AI processor to complete # Step 4: Wait for AI processor to complete
if not wait_for_ai_response(request_id, args.timeout): if not wait_for_ai_response(request_id, args.timeout):
print("\n⚠️ AI processor may be busy or not running properly") print("\n⚠️ AI processor may be busy or not running properly")
print(" Check status: ssh netops@INTERNAL_IP 'sudo systemctl status ai-processor'") print(" Check status: ssh netops@192.168.100.86 'sudo systemctl status ai-processor'")
sys.exit(1) sys.exit(1)
# Step 5: Create PR (unless dry-run) # Step 5: Create PR (unless dry-run)

View File

@@ -285,7 +285,7 @@ if __name__ == "__main__":
# Test the SRX Manager # Test the SRX Manager
srx = SRXManager( srx = SRXManager(
host="INTERNAL_IP", host="192.168.100.1",
user="netops", user="netops",
ssh_key="/home/netops/.ssh/srx_key" ssh_key="/home/netops/.ssh/srx_key"
) )