diff --git a/CONFIGURATION.md b/CONFIGURATION.md index b66bf8f..618d0f2 100644 --- a/CONFIGURATION.md +++ b/CONFIGURATION.md @@ -13,7 +13,7 @@ services: # - ./instance/images:/app/static/images # Commented out by default to use default logos ``` -### instance/config.yaml +### instance/site.yaml ``` header: title: "photonglass" @@ -23,7 +23,10 @@ footer: text: "photonglass" peeringdb_href: "https://www.peeringdb.com" github_href: "https://github.com/alimickey" +``` +### instance/config.yaml +``` webhook: url: "https://hooks.slack.com/###" ``` @@ -62,15 +65,14 @@ sydney1: subtext: "Equinix SY3" country_code: "AU" type: "linux" - host: "IP_ADDRESS" - port: PORT - username: "USERNAME" - password: "PASSWORD" - ssh_key: "id_rsa" # Optional commands: - ping - traceroute - mtr + credentials: + host: "IP_ADDRESS" + port: PORT + username: "USERNAME" + password: "PASSWORD" + ssh_key: "id_rsa" # Optional ``` - - diff --git a/README.md b/README.md index 962ec39..314c4e9 100644 --- a/README.md +++ b/README.md @@ -6,6 +6,7 @@ A modern, distributed looking glass application that provides network insight fo ## Features - **Multi Device Support**: Connect to multiple devices from one single interface. +- **Custom Command Support**: Built dynamically to support any custom command. Defaults are [ping, traceroute, mtr] - **Easy Deployment**: Extremely easy to deploy and scale with multiple devices. - **Webhook Logging**: Log queries to a webhook channel (optional). - **Rate Limiting**: Reduce service abuse by rate limiting users, 100 requests per day and 10 requests per minute by default. @@ -32,9 +33,9 @@ If you wish to list your instance on this list, please open a Github issue. - Refer to [CONFIGURATION.md](CONFIGURATION.md) 4. Create `docker-compose.yml` - Refer to [CONFIGURATION.md](CONFIGURATION.md) -4. Build and deploy the container (inital build may take a minute) +5. Build and deploy the container (inital build may take a minute) - `docker compose up -d --build` -5. View the app at `http://IP_ADDRESS:5000`, recommend using a reverse proxy (traefik) for production use. +6. View the app at `http://IP_ADDRESS:5000`, recommend using a reverse proxy (traefik) for production use. ## Attribution diff --git a/app/__init__.py b/app/__init__.py index 5140f18..f097c0c 100644 --- a/app/__init__.py +++ b/app/__init__.py @@ -1,4 +1,4 @@ -import logging +import logging, yaml, os from flask import Flask from flask_limiter import Limiter @@ -17,9 +17,25 @@ def create_app(): app = Flask(__name__, instance_path="/instance") limiter.init_app(app) + + config_files = ['config.yaml', 'site.yaml', 'devices.yaml', 'commands.yaml'] + + for config_file in config_files: + config_path = os.path.join("/instance", config_file) + + # Create empty config files if they don't exist + if not os.path.exists(config_path): + with open(config_path, "w") as f: + pass + + # Load the config files into the app config + with open(config_path, 'r') as file: + config_yaml = yaml.safe_load(file) or {} + app.config[config_file.split('.')[0].upper()] = config_yaml + from app.views import main app.register_blueprint(main.bp) app.add_url_rule('/', endpoint='index') - return app + return app \ No newline at end of file diff --git a/app/functions/netmiko.py b/app/functions/netmiko.py index a570d31..a5f6c9a 100644 --- a/app/functions/netmiko.py +++ b/app/functions/netmiko.py @@ -18,11 +18,13 @@ def establish_connection(device_config): # Execute command on network device def execute_command(device, command_format, target, ip_version): + device_credentials = device['credentials'] + device_config = { 'device_type': device['type'], - 'host': device['host'], - 'port': device['port'], - 'username': device['username'], + 'host': device_credentials['host'], + 'port': device_credentials['port'], + 'username': device_credentials['username'], 'timeout': 10, 'session_timeout': 60, 'conn_timeout': 10, @@ -30,22 +32,22 @@ def execute_command(device, command_format, target, ip_version): } # Use SSH key if provided - if "ssh_key" in device: - key_path = os.path.join("/instance/ssh-keys", device['ssh_key']) + if "ssh_key" in device_credentials: + key_path = os.path.join("/instance/ssh-keys", device_credentials['ssh_key']) if not os.path.exists(key_path): - logger.error(f"SSH file not found: {key_path} for {device['host']}") + logger.error(f"SSH file not found: {key_path} for {device_credentials['host']}") return {'error': True, 'message': 'Authentication failed'} device_config['use_keys'] = True - device_config['key_file'] = os.path.join("/instance/ssh-keys", device['ssh_key']) + device_config['key_file'] = os.path.join("/instance/ssh-keys", device_credentials['ssh_key']) else: - device_config['password'] = device['password'] + device_config['password'] = device_credentials['password'] - command_timeout = 30 try: + command_timeout = 30 with establish_connection(device_config) as connection: # Format the command command = str(command_format.format(ip_version=ip_version, target=target).strip()) diff --git a/app/functions/utils.py b/app/functions/utils.py index f90606b..f9ffba4 100644 --- a/app/functions/utils.py +++ b/app/functions/utils.py @@ -1,4 +1,4 @@ -import os, yaml, logging, requests +import logging, requests from functools import wraps from flask import request @@ -16,39 +16,6 @@ def exception_handler(func): logging.exception(f"Exception occurred in {func.__name__}") return "An error occurred", 500 return wrapper - - -# Load YAML configuration file -@exception_handler -def load_yaml(filename, key=None): - if not filename.endswith('.yaml'): - filename = f"{filename}.yaml" - - config_path = os.path.join("/instance", filename) - - if not os.path.exists(config_path): - logger.error(f"Configuration file not found: {config_path}") - return {} - - try: - with open(config_path, 'r') as f: - data = yaml.safe_load(f) - if data is None: - logger.error(f"Empty configuration file: {filename}") - return {} - - # Return specific key if provided - if key: - return data.get(key, None) - - return data - - except yaml.YAMLError as e: - logger.error(f"YAML parsing error in {filename}: {e}") - return {} - except Exception as e: - logger.error(f"Unexpected error loading {filename}: {e}") - return {} # Send data to a webhook URL diff --git a/app/templates/base.html b/app/templates/base.html index 775bb7b..7723095 100644 --- a/app/templates/base.html +++ b/app/templates/base.html @@ -5,16 +5,16 @@ -