Spaces:
Sleeping
Sleeping
| import shodan | |
| import gradio as gr | |
| import logging | |
| from abc import ABC, abstractmethod | |
| from typing import List, Optional | |
| import json | |
| import asyncio | |
| # Logging setup | |
| logging.basicConfig( | |
| level=logging.INFO, | |
| format="%(asctime)s [%(levelname)s] %(message)s", | |
| handlers=[logging.StreamHandler()] | |
| ) | |
| logger = logging.getLogger(__name__) | |
| # Custom exceptions | |
| class ScannerException(Exception): | |
| """Base exception for scanner errors.""" | |
| pass | |
| # Instance class (assuming a basic structure since it wasn’t provided) | |
| class Instance: | |
| """Represents an Ollama instance.""" | |
| def __init__(self, ip: str, port: int, owner_id: Optional[str] = None): | |
| self.ip = ip | |
| self.port = port | |
| self.owner_id = owner_id | |
| def from_shodan(cls, result: dict, owner_id: Optional[str] = None) -> 'Instance': | |
| """Create Instance from Shodan result.""" | |
| return cls(ip=result['ip_str'], port=result['port'], owner_id=owner_id) | |
| def to_dict(self) -> dict: | |
| """Convert to dict for JSON output.""" | |
| return {"ip": self.ip, "port": self.port, "owner_id": self.owner_id} | |
| # Scanner interface | |
| class IScanner(ABC): | |
| """Scanner interface for finding Ollama instances.""" | |
| async def scan_instances(self, limit: int = 100) -> List[Instance]: | |
| pass | |
| # ShodanScanner implementation | |
| class ShodanScanner(IScanner): | |
| """Scanner for finding public Ollama instances using Shodan.""" | |
| def __init__(self, api_key: str, owner_id: Optional[str] = None): | |
| self.api_key = api_key | |
| self.owner_id = owner_id | |
| async def scan_instances(self, limit: int = 100) -> List[Instance]: | |
| """Scan for Ollama instances using Shodan.""" | |
| try: | |
| api = shodan.Shodan(self.api_key) | |
| results = api.search('port:11434 "Ollama"', limit=limit) | |
| instances = [Instance.from_shodan(result, self.owner_id) for result in results['matches']] | |
| return instances | |
| except shodan.APIError as e: | |
| raise ScannerException(f"Shodan API error: {str(e)}") | |
| except Exception as e: | |
| raise ScannerException(f"Error during Shodan scan: {str(e)}") | |
| # Gradio processing function | |
| async def scan_ollama_instances(api_key: str, limit: int, owner_id: str = "") -> str: | |
| """Process Shodan scan and return results.""" | |
| try: | |
| if not api_key: | |
| return "Yo, drop a Shodan API key, fam!" | |
| scanner = ShodanScanner(api_key, owner_id if owner_id else None) | |
| instances = await scanner.scan_instances(limit=limit) | |
| if not instances: | |
| return "No Ollama instances found, dawg!" | |
| output = ["Found Ollama Instances:"] | |
| for i, instance in enumerate(instances, 1): | |
| output.append(f"{i}. {json.dumps(instance.to_dict(), indent=2)}") | |
| logger.info(f"Scanned {len(instances)} Ollama instances") | |
| return "\n\n".join(output) | |
| except ScannerException as e: | |
| logger.error(f"Scan failed: {str(e)}") | |
| return f"Scan crashed: {str(e)}" | |
| except Exception as e: | |
| logger.error(f"Unexpected error: {str(e)}") | |
| return f"Shit hit the fan, fam: {str(e)}" | |
| # Gradio wrapper to run async function | |
| def gradio_scan(api_key: str, limit: int, owner_id: str) -> str: | |
| """Wrapper to run async scan in Gradio.""" | |
| return asyncio.run(scan_ollama_instances(api_key, limit, owner_id)) | |
| # Gradio interface | |
| demo = gr.Interface( | |
| fn=gradio_scan, | |
| inputs=[ | |
| gr.Textbox(label="Shodan API Key", placeholder="Enter your Shodan API key here", type="password"), | |
| gr.Slider(label="Max Results", minimum=1, maximum=1000, value=100, step=1), | |
| gr.Textbox(label="Owner ID (Optional)", placeholder="e.g., your_user_id", value="") | |
| ], | |
| outputs=gr.Textbox(label="Ollama Instances Found"), | |
| title="Ollama Instance Scanner", | |
| description="Scan for public Ollama instances using Shodan, Bay Area style! Drop your API key and let’s roll.", | |
| allow_flagging="never" | |
| ) | |
| if __name__ == "__main__": | |
| logger.info("Firin’ up the Ollama scanner on Gradio...") | |
| demo.launch(server_name="0.0.0.0", server_port=7860) |