unify config
This commit is contained in:
parent
3511f18f9a
commit
689e8c00d4
34
.env.example
Normal file
34
.env.example
Normal file
@ -0,0 +1,34 @@
|
||||
# ===============================================
|
||||
# DNSRecon Environment Variables
|
||||
# ===============================================
|
||||
# Copy this file to .env and fill in your values.
|
||||
|
||||
# --- API Keys ---
|
||||
# Add your Shodan API key for the Shodan provider to be enabled.
|
||||
SHODAN_API_KEY=
|
||||
|
||||
# --- Flask & Session Settings ---
|
||||
# A strong, random secret key is crucial for session security.
|
||||
FLASK_SECRET_KEY=your-very-secret-and-random-key-here
|
||||
FLASK_HOST=127.0.0.1
|
||||
FLASK_PORT=5000
|
||||
FLASK_DEBUG=True
|
||||
# How long a user's session in the browser lasts (in hours).
|
||||
FLASK_PERMANENT_SESSION_LIFETIME_HOURS=2
|
||||
# How long inactive scanner data is stored in Redis (in minutes).
|
||||
SESSION_TIMEOUT_MINUTES=60
|
||||
|
||||
|
||||
# --- Application Core Settings ---
|
||||
# The default number of levels to recurse when scanning.
|
||||
DEFAULT_RECURSION_DEPTH=2
|
||||
# Default timeout for provider API requests in seconds.
|
||||
DEFAULT_TIMEOUT=30
|
||||
# The number of concurrent provider requests to make.
|
||||
MAX_CONCURRENT_REQUESTS=5
|
||||
# The number of results from a provider that triggers the "large entity" grouping.
|
||||
LARGE_ENTITY_THRESHOLD=100
|
||||
# The number of times to retry a target if a provider fails.
|
||||
MAX_RETRIES_PER_TARGET=3
|
||||
# How long cached provider responses are stored (in hours).
|
||||
CACHE_EXPIRY_HOURS=12
|
5
app.py
5
app.py
@ -16,8 +16,9 @@ from config import config
|
||||
|
||||
|
||||
app = Flask(__name__)
|
||||
app.config['SECRET_KEY'] = 'dnsrecon-dev-key-change-in-production'
|
||||
app.config['PERMANENT_SESSION_LIFETIME'] = timedelta(hours=2) # 2 hour session lifetime
|
||||
# Use centralized configuration for Flask settings
|
||||
app.config['SECRET_KEY'] = config.flask_secret_key
|
||||
app.config['PERMANENT_SESSION_LIFETIME'] = timedelta(hours=config.flask_permanent_session_lifetime_hours)
|
||||
|
||||
def get_user_scanner():
|
||||
"""
|
||||
|
128
config.py
128
config.py
@ -5,111 +5,93 @@ Handles API key storage, rate limiting, and default settings.
|
||||
|
||||
import os
|
||||
from typing import Dict, Optional
|
||||
from dotenv import load_dotenv
|
||||
|
||||
# Load environment variables from .env file
|
||||
load_dotenv()
|
||||
|
||||
class Config:
|
||||
"""Configuration manager for DNSRecon application."""
|
||||
|
||||
def __init__(self):
|
||||
"""Initialize configuration with default values."""
|
||||
self.api_keys: Dict[str, Optional[str]] = {
|
||||
'shodan': None
|
||||
}
|
||||
self.api_keys: Dict[str, Optional[str]] = {}
|
||||
|
||||
# Default settings
|
||||
# --- General Settings ---
|
||||
self.default_recursion_depth = 2
|
||||
self.default_timeout = 10
|
||||
self.max_concurrent_requests = 5
|
||||
self.large_entity_threshold = 100
|
||||
self.max_retries_per_target = 3
|
||||
self.cache_expiry_hours = 12
|
||||
|
||||
# Rate limiting settings (requests per minute)
|
||||
# --- Rate Limiting (requests per minute) ---
|
||||
self.rate_limits = {
|
||||
'crtsh': 60, # Free service, be respectful
|
||||
'shodan': 60, # API dependent
|
||||
'dns': 100 # Local DNS queries
|
||||
'crtsh': 60,
|
||||
'shodan': 60,
|
||||
'dns': 100
|
||||
}
|
||||
|
||||
# Provider settings
|
||||
# --- Provider Settings ---
|
||||
self.enabled_providers = {
|
||||
'crtsh': True, # Always enabled (free)
|
||||
'dns': True, # Always enabled (free)
|
||||
'shodan': False # Requires API key
|
||||
'crtsh': True,
|
||||
'dns': True,
|
||||
'shodan': False
|
||||
}
|
||||
|
||||
# Logging configuration
|
||||
# --- Logging ---
|
||||
self.log_level = 'INFO'
|
||||
self.log_format = '%(asctime)s - %(name)s - %(levelname)s - %(message)s'
|
||||
|
||||
# Flask configuration
|
||||
# --- Flask & Session Settings ---
|
||||
self.flask_host = '127.0.0.1'
|
||||
self.flask_port = 5000
|
||||
self.flask_debug = True
|
||||
self.flask_secret_key = 'default-secret-key-change-me'
|
||||
self.flask_permanent_session_lifetime_hours = 2
|
||||
self.session_timeout_minutes = 60
|
||||
|
||||
def set_api_key(self, provider: str, api_key: str) -> bool:
|
||||
"""
|
||||
Set API key for a provider.
|
||||
|
||||
Args:
|
||||
provider: Provider name (shodan, etc)
|
||||
api_key: API key string
|
||||
|
||||
Returns:
|
||||
bool: True if key was set successfully
|
||||
"""
|
||||
if provider in self.api_keys:
|
||||
self.api_keys[provider] = api_key
|
||||
self.enabled_providers[provider] = True if api_key else False
|
||||
return True
|
||||
return False
|
||||
|
||||
def get_api_key(self, provider: str) -> Optional[str]:
|
||||
"""
|
||||
Get API key for a provider.
|
||||
|
||||
Args:
|
||||
provider: Provider name
|
||||
|
||||
Returns:
|
||||
API key or None if not set
|
||||
"""
|
||||
return self.api_keys.get(provider)
|
||||
|
||||
def is_provider_enabled(self, provider: str) -> bool:
|
||||
"""
|
||||
Check if a provider is enabled.
|
||||
|
||||
Args:
|
||||
provider: Provider name
|
||||
|
||||
Returns:
|
||||
bool: True if provider is enabled
|
||||
"""
|
||||
return self.enabled_providers.get(provider, False)
|
||||
|
||||
def get_rate_limit(self, provider: str) -> int:
|
||||
"""
|
||||
Get rate limit for a provider.
|
||||
|
||||
Args:
|
||||
provider: Provider name
|
||||
|
||||
Returns:
|
||||
Rate limit in requests per minute
|
||||
"""
|
||||
return self.rate_limits.get(provider, 60)
|
||||
# Load environment variables to override defaults
|
||||
self.load_from_env()
|
||||
|
||||
def load_from_env(self):
|
||||
"""Load configuration from environment variables."""
|
||||
if os.getenv('SHODAN_API_KEY'):
|
||||
self.set_api_key('shodan', os.getenv('SHODAN_API_KEY'))
|
||||
self.set_api_key('shodan', os.getenv('SHODAN_API_KEY'))
|
||||
|
||||
# Override default settings from environment
|
||||
self.default_recursion_depth = int(os.getenv('DEFAULT_RECURSION_DEPTH', '2'))
|
||||
self.flask_debug = os.getenv('FLASK_DEBUG', 'True').lower() == 'true'
|
||||
self.default_timeout = 30
|
||||
self.max_concurrent_requests = 5
|
||||
# Override settings from environment
|
||||
self.default_recursion_depth = int(os.getenv('DEFAULT_RECURSION_DEPTH', self.default_recursion_depth))
|
||||
self.default_timeout = int(os.getenv('DEFAULT_TIMEOUT', self.default_timeout))
|
||||
self.max_concurrent_requests = int(os.getenv('MAX_CONCURRENT_REQUESTS', self.max_concurrent_requests))
|
||||
self.large_entity_threshold = int(os.getenv('LARGE_ENTITY_THRESHOLD', self.large_entity_threshold))
|
||||
self.max_retries_per_target = int(os.getenv('MAX_RETRIES_PER_TARGET', self.max_retries_per_target))
|
||||
self.cache_expiry_hours = int(os.getenv('CACHE_EXPIRY_HOURS', self.cache_expiry_hours))
|
||||
|
||||
# Override Flask and session settings
|
||||
self.flask_host = os.getenv('FLASK_HOST', self.flask_host)
|
||||
self.flask_port = int(os.getenv('FLASK_PORT', self.flask_port))
|
||||
self.flask_debug = os.getenv('FLASK_DEBUG', str(self.flask_debug)).lower() == 'true'
|
||||
self.flask_secret_key = os.getenv('FLASK_SECRET_KEY', self.flask_secret_key)
|
||||
self.flask_permanent_session_lifetime_hours = int(os.getenv('FLASK_PERMANENT_SESSION_LIFETIME_HOURS', self.flask_permanent_session_lifetime_hours))
|
||||
self.session_timeout_minutes = int(os.getenv('SESSION_TIMEOUT_MINUTES', self.session_timeout_minutes))
|
||||
|
||||
def set_api_key(self, provider: str, api_key: Optional[str]) -> bool:
|
||||
"""Set API key for a provider."""
|
||||
self.api_keys[provider] = api_key
|
||||
if api_key:
|
||||
self.enabled_providers[provider] = True
|
||||
return True
|
||||
|
||||
def get_api_key(self, provider: str) -> Optional[str]:
|
||||
"""Get API key for a provider."""
|
||||
return self.api_keys.get(provider)
|
||||
|
||||
def is_provider_enabled(self, provider: str) -> bool:
|
||||
"""Check if a provider is enabled."""
|
||||
return self.enabled_providers.get(provider, False)
|
||||
|
||||
def get_rate_limit(self, provider: str) -> int:
|
||||
"""Get rate limit for a provider."""
|
||||
return self.rate_limits.get(provider, 60)
|
||||
|
||||
# Global configuration instance
|
||||
config = Config()
|
@ -3,11 +3,9 @@ Per-session configuration management for DNSRecon.
|
||||
Provides isolated configuration instances for each user session.
|
||||
"""
|
||||
|
||||
import os
|
||||
from typing import Dict, Optional
|
||||
from config import Config
|
||||
|
||||
|
||||
class SessionConfig:
|
||||
class SessionConfig(Config):
|
||||
"""
|
||||
Session-specific configuration that inherits from global config
|
||||
but maintains isolated API keys and provider settings.
|
||||
@ -15,106 +13,8 @@ class SessionConfig:
|
||||
|
||||
def __init__(self):
|
||||
"""Initialize session config with global defaults."""
|
||||
# Copy all attributes from global config
|
||||
self.api_keys: Dict[str, Optional[str]] = {
|
||||
'shodan': None
|
||||
}
|
||||
super().__init__()
|
||||
|
||||
# Default settings (copied from global config)
|
||||
self.default_recursion_depth = 2
|
||||
self.default_timeout = 30
|
||||
self.max_concurrent_requests = 5
|
||||
self.large_entity_threshold = 100
|
||||
|
||||
# Rate limiting settings (per session)
|
||||
self.rate_limits = {
|
||||
'crtsh': 60,
|
||||
'shodan': 60,
|
||||
'dns': 100
|
||||
}
|
||||
|
||||
# Provider settings (per session)
|
||||
self.enabled_providers = {
|
||||
'crtsh': True,
|
||||
'dns': True,
|
||||
'shodan': False
|
||||
}
|
||||
|
||||
# Logging configuration
|
||||
self.log_level = 'INFO'
|
||||
self.log_format = '%(asctime)s - %(name)s - %(levelname)s - %(message)s'
|
||||
|
||||
# Flask configuration (shared)
|
||||
self.flask_host = '127.0.0.1'
|
||||
self.flask_port = 5000
|
||||
self.flask_debug = True
|
||||
|
||||
def set_api_key(self, provider: str, api_key: str) -> bool:
|
||||
"""
|
||||
Set API key for a provider in this session.
|
||||
|
||||
Args:
|
||||
provider: Provider name (shodan, etc)
|
||||
api_key: API key string
|
||||
|
||||
Returns:
|
||||
bool: True if key was set successfully
|
||||
"""
|
||||
if provider in self.api_keys:
|
||||
self.api_keys[provider] = api_key
|
||||
self.enabled_providers[provider] = True if api_key else False
|
||||
return True
|
||||
return False
|
||||
|
||||
def get_api_key(self, provider: str) -> Optional[str]:
|
||||
"""
|
||||
Get API key for a provider in this session.
|
||||
|
||||
Args:
|
||||
provider: Provider name
|
||||
|
||||
Returns:
|
||||
API key or None if not set
|
||||
"""
|
||||
return self.api_keys.get(provider)
|
||||
|
||||
def is_provider_enabled(self, provider: str) -> bool:
|
||||
"""
|
||||
Check if a provider is enabled in this session.
|
||||
|
||||
Args:
|
||||
provider: Provider name
|
||||
|
||||
Returns:
|
||||
bool: True if provider is enabled
|
||||
"""
|
||||
return self.enabled_providers.get(provider, False)
|
||||
|
||||
def get_rate_limit(self, provider: str) -> int:
|
||||
"""
|
||||
Get rate limit for a provider in this session.
|
||||
|
||||
Args:
|
||||
provider: Provider name
|
||||
|
||||
Returns:
|
||||
Rate limit in requests per minute
|
||||
"""
|
||||
return self.rate_limits.get(provider, 60)
|
||||
|
||||
def load_from_env(self):
|
||||
"""Load configuration from environment variables (only if not already set)."""
|
||||
if os.getenv('SHODAN_API_KEY') and not self.api_keys['shodan']:
|
||||
self.set_api_key('shodan', os.getenv('SHODAN_API_KEY'))
|
||||
|
||||
# Override default settings from environment
|
||||
self.default_recursion_depth = int(os.getenv('DEFAULT_RECURSION_DEPTH', '2'))
|
||||
self.default_timeout = 30
|
||||
self.max_concurrent_requests = 5
|
||||
|
||||
|
||||
def create_session_config() -> SessionConfig:
|
||||
def create_session_config() -> 'SessionConfig':
|
||||
"""Create a new session configuration instance."""
|
||||
session_config = SessionConfig()
|
||||
session_config.load_from_env()
|
||||
return session_config
|
||||
return SessionConfig()
|
@ -8,6 +8,7 @@ import pickle
|
||||
from typing import Dict, Optional, Any, List
|
||||
|
||||
from core.scanner import Scanner
|
||||
from config import config
|
||||
|
||||
# WARNING: Using pickle can be a security risk if the data source is not trusted.
|
||||
# In this case, we are only serializing/deserializing our own trusted Scanner objects,
|
||||
@ -18,10 +19,13 @@ class SessionManager:
|
||||
Manages multiple scanner instances for concurrent user sessions using Redis.
|
||||
"""
|
||||
|
||||
def __init__(self, session_timeout_minutes: int = 60):
|
||||
def __init__(self, session_timeout_minutes: int = 0):
|
||||
"""
|
||||
Initialize session manager with a Redis backend.
|
||||
"""
|
||||
if session_timeout_minutes is None:
|
||||
session_timeout_minutes = config.session_timeout_minutes
|
||||
|
||||
self.redis_client = redis.StrictRedis(db=0, decode_responses=False)
|
||||
self.session_timeout = session_timeout_minutes * 60 # Convert to seconds
|
||||
self.lock = threading.Lock() # Lock for local operations, Redis handles atomic ops
|
||||
|
@ -82,7 +82,7 @@ class BaseProvider(ABC):
|
||||
|
||||
# Caching configuration (per session)
|
||||
self.cache_dir = f'.cache/{id(self.config)}' # Unique cache per session config
|
||||
self.cache_expiry = 12 * 3600 # 12 hours in seconds
|
||||
self.cache_expiry = self.config.cache_expiry_hours * 3600
|
||||
if not os.path.exists(self.cache_dir):
|
||||
os.makedirs(self.cache_dir)
|
||||
|
||||
|
@ -7,3 +7,4 @@ urllib3>=2.0.0
|
||||
dnspython>=2.4.2
|
||||
gunicorn
|
||||
redis
|
||||
python-dotenv
|
Loading…
x
Reference in New Issue
Block a user