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 = Flask(__name__)
|
||||||
app.config['SECRET_KEY'] = 'dnsrecon-dev-key-change-in-production'
|
# Use centralized configuration for Flask settings
|
||||||
app.config['PERMANENT_SESSION_LIFETIME'] = timedelta(hours=2) # 2 hour session lifetime
|
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():
|
def get_user_scanner():
|
||||||
"""
|
"""
|
||||||
|
118
config.py
118
config.py
@ -5,111 +5,93 @@ Handles API key storage, rate limiting, and default settings.
|
|||||||
|
|
||||||
import os
|
import os
|
||||||
from typing import Dict, Optional
|
from typing import Dict, Optional
|
||||||
|
from dotenv import load_dotenv
|
||||||
|
|
||||||
|
# Load environment variables from .env file
|
||||||
|
load_dotenv()
|
||||||
|
|
||||||
class Config:
|
class Config:
|
||||||
"""Configuration manager for DNSRecon application."""
|
"""Configuration manager for DNSRecon application."""
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
"""Initialize configuration with default values."""
|
"""Initialize configuration with default values."""
|
||||||
self.api_keys: Dict[str, Optional[str]] = {
|
self.api_keys: Dict[str, Optional[str]] = {}
|
||||||
'shodan': None
|
|
||||||
}
|
|
||||||
|
|
||||||
# Default settings
|
# --- General Settings ---
|
||||||
self.default_recursion_depth = 2
|
self.default_recursion_depth = 2
|
||||||
self.default_timeout = 10
|
self.default_timeout = 10
|
||||||
self.max_concurrent_requests = 5
|
self.max_concurrent_requests = 5
|
||||||
self.large_entity_threshold = 100
|
self.large_entity_threshold = 100
|
||||||
self.max_retries_per_target = 3
|
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 = {
|
self.rate_limits = {
|
||||||
'crtsh': 60, # Free service, be respectful
|
'crtsh': 60,
|
||||||
'shodan': 60, # API dependent
|
'shodan': 60,
|
||||||
'dns': 100 # Local DNS queries
|
'dns': 100
|
||||||
}
|
}
|
||||||
|
|
||||||
# Provider settings
|
# --- Provider Settings ---
|
||||||
self.enabled_providers = {
|
self.enabled_providers = {
|
||||||
'crtsh': True, # Always enabled (free)
|
'crtsh': True,
|
||||||
'dns': True, # Always enabled (free)
|
'dns': True,
|
||||||
'shodan': False # Requires API key
|
'shodan': False
|
||||||
}
|
}
|
||||||
|
|
||||||
# Logging configuration
|
# --- Logging ---
|
||||||
self.log_level = 'INFO'
|
self.log_level = 'INFO'
|
||||||
self.log_format = '%(asctime)s - %(name)s - %(levelname)s - %(message)s'
|
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_host = '127.0.0.1'
|
||||||
self.flask_port = 5000
|
self.flask_port = 5000
|
||||||
self.flask_debug = True
|
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:
|
# Load environment variables to override defaults
|
||||||
"""
|
self.load_from_env()
|
||||||
Set API key for a provider.
|
|
||||||
|
|
||||||
Args:
|
def load_from_env(self):
|
||||||
provider: Provider name (shodan, etc)
|
"""Load configuration from environment variables."""
|
||||||
api_key: API key string
|
self.set_api_key('shodan', os.getenv('SHODAN_API_KEY'))
|
||||||
|
|
||||||
Returns:
|
# Override settings from environment
|
||||||
bool: True if key was set successfully
|
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))
|
||||||
if provider in self.api_keys:
|
self.max_concurrent_requests = int(os.getenv('MAX_CONCURRENT_REQUESTS', self.max_concurrent_requests))
|
||||||
self.api_keys[provider] = api_key
|
self.large_entity_threshold = int(os.getenv('LARGE_ENTITY_THRESHOLD', self.large_entity_threshold))
|
||||||
self.enabled_providers[provider] = True if api_key else False
|
self.max_retries_per_target = int(os.getenv('MAX_RETRIES_PER_TARGET', self.max_retries_per_target))
|
||||||
return True
|
self.cache_expiry_hours = int(os.getenv('CACHE_EXPIRY_HOURS', self.cache_expiry_hours))
|
||||||
return False
|
|
||||||
|
# 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]:
|
def get_api_key(self, provider: str) -> Optional[str]:
|
||||||
"""
|
"""Get API key for a provider."""
|
||||||
Get API key for a provider.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
provider: Provider name
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
API key or None if not set
|
|
||||||
"""
|
|
||||||
return self.api_keys.get(provider)
|
return self.api_keys.get(provider)
|
||||||
|
|
||||||
def is_provider_enabled(self, provider: str) -> bool:
|
def is_provider_enabled(self, provider: str) -> bool:
|
||||||
"""
|
"""Check if a provider is enabled."""
|
||||||
Check if a provider is enabled.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
provider: Provider name
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
bool: True if provider is enabled
|
|
||||||
"""
|
|
||||||
return self.enabled_providers.get(provider, False)
|
return self.enabled_providers.get(provider, False)
|
||||||
|
|
||||||
def get_rate_limit(self, provider: str) -> int:
|
def get_rate_limit(self, provider: str) -> int:
|
||||||
"""
|
"""Get rate limit for a provider."""
|
||||||
Get rate limit for a provider.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
provider: Provider name
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
Rate limit in requests per minute
|
|
||||||
"""
|
|
||||||
return self.rate_limits.get(provider, 60)
|
return self.rate_limits.get(provider, 60)
|
||||||
|
|
||||||
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'))
|
|
||||||
|
|
||||||
# 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
|
|
||||||
|
|
||||||
|
|
||||||
# Global configuration instance
|
# Global configuration instance
|
||||||
config = Config()
|
config = Config()
|
@ -3,11 +3,9 @@ Per-session configuration management for DNSRecon.
|
|||||||
Provides isolated configuration instances for each user session.
|
Provides isolated configuration instances for each user session.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import os
|
from config import Config
|
||||||
from typing import Dict, Optional
|
|
||||||
|
|
||||||
|
class SessionConfig(Config):
|
||||||
class SessionConfig:
|
|
||||||
"""
|
"""
|
||||||
Session-specific configuration that inherits from global config
|
Session-specific configuration that inherits from global config
|
||||||
but maintains isolated API keys and provider settings.
|
but maintains isolated API keys and provider settings.
|
||||||
@ -15,106 +13,8 @@ class SessionConfig:
|
|||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
"""Initialize session config with global defaults."""
|
"""Initialize session config with global defaults."""
|
||||||
# Copy all attributes from global config
|
super().__init__()
|
||||||
self.api_keys: Dict[str, Optional[str]] = {
|
|
||||||
'shodan': None
|
|
||||||
}
|
|
||||||
|
|
||||||
# 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."""
|
"""Create a new session configuration instance."""
|
||||||
session_config = SessionConfig()
|
return SessionConfig()
|
||||||
session_config.load_from_env()
|
|
||||||
return session_config
|
|
@ -8,6 +8,7 @@ import pickle
|
|||||||
from typing import Dict, Optional, Any, List
|
from typing import Dict, Optional, Any, List
|
||||||
|
|
||||||
from core.scanner import Scanner
|
from core.scanner import Scanner
|
||||||
|
from config import config
|
||||||
|
|
||||||
# WARNING: Using pickle can be a security risk if the data source is not trusted.
|
# 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,
|
# 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.
|
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.
|
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.redis_client = redis.StrictRedis(db=0, decode_responses=False)
|
||||||
self.session_timeout = session_timeout_minutes * 60 # Convert to seconds
|
self.session_timeout = session_timeout_minutes * 60 # Convert to seconds
|
||||||
self.lock = threading.Lock() # Lock for local operations, Redis handles atomic ops
|
self.lock = threading.Lock() # Lock for local operations, Redis handles atomic ops
|
||||||
|
@ -82,7 +82,7 @@ class BaseProvider(ABC):
|
|||||||
|
|
||||||
# Caching configuration (per session)
|
# Caching configuration (per session)
|
||||||
self.cache_dir = f'.cache/{id(self.config)}' # Unique cache per session config
|
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):
|
if not os.path.exists(self.cache_dir):
|
||||||
os.makedirs(self.cache_dir)
|
os.makedirs(self.cache_dir)
|
||||||
|
|
||||||
|
@ -6,4 +6,5 @@ Werkzeug>=2.3.7
|
|||||||
urllib3>=2.0.0
|
urllib3>=2.0.0
|
||||||
dnspython>=2.4.2
|
dnspython>=2.4.2
|
||||||
gunicorn
|
gunicorn
|
||||||
redis
|
redis
|
||||||
|
python-dotenv
|
Loading…
x
Reference in New Issue
Block a user