InFlask app update and fix

This commit is contained in:
root 2024-12-11 09:05:26 +00:00
parent e4ca0ef8bf
commit 214841bdae
4 changed files with 59 additions and 56 deletions

2
.flaskenv Normal file
View File

@ -0,0 +1,2 @@
FLASK_APP=app
FLASK_ENV=development

View File

@ -0,0 +1,19 @@
from flask import Flask, request, jsonify
import logging
from .handlers.webhook_handler import handle_payment_webhook
# Set up logging
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)
logger = logging.getLogger(__name__)
app = Flask(__name__)
@app.route('/webhook/vpn', methods=['POST'])
def handle_payment():
return handle_payment_webhook(request)
if __name__ == '__main__':
app.run(host='0.0.0.0', port=5000)

View File

@ -1,55 +1,56 @@
# app/handlers/webhook_handler.py
import sys
from pathlib import Path
sys.path.append(str(Path(__file__).resolve().parent.parent.parent))
from flask import Flask, request, jsonify
import tempfile
from flask import jsonify
import subprocess
import os
import logging
import json
from pathlib import Path
import hmac
import hashlib
import yaml
import traceback
import tempfile
from pathlib import Path
from dotenv import load_dotenv
from app.utils.vault_helper import decrypt_vault_file
# Load environment variables from .env file
load_dotenv()
# Configure logging
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)
logger = logging.getLogger(__name__)
app = Flask(__name__)
# Update paths for new structure
BASE_DIR = Path(__file__).resolve().parent.parent.parent
PLAYBOOK_PATH = BASE_DIR / 'ansible' / 'playbooks' / 'vpn_provision.yml'
VAULT_PATH = BASE_DIR / 'ansible' / 'group_vars' / 'vpn_servers' / 'vault.yml'
INVENTORY_PATH = BASE_DIR / 'inventory.ini'
def get_vault_values():
"""Get decrypted values from Ansible vault"""
try:
# Use vault helper to decrypt contents
vault_contents = decrypt_vault_file(VAULT_PATH)
vault_data = yaml.safe_load(vault_contents)
vault_pass = os.getenv('ANSIBLE_VAULT_PASSWORD', '')
if not vault_pass:
raise Exception("Vault password not found in environment variables")
# Construct full webhook URL
vault_data['webhook_full_url'] = (
f"{vault_data['btcpay_base_url']}"
f"{vault_data['btcpay_webhook_path']}"
with tempfile.NamedTemporaryFile(mode='w', delete=False) as vault_pass_file:
vault_pass_file.write(vault_pass)
vault_pass_file.flush()
result = subprocess.run(
['ansible-vault', 'view', str(BASE_DIR / 'ansible/group_vars/vpn_servers/vault.yml')],
capture_output=True,
text=True,
env={**os.environ, 'ANSIBLE_VAULT_PASSWORD_FILE': vault_pass_file.name}
)
os.unlink(vault_pass_file.name)
if result.returncode != 0:
raise Exception(f"Failed to decrypt vault: {result.stderr}")
vault_contents = yaml.safe_load(result.stdout)
vault_contents['webhook_full_url'] = (
f"{vault_contents['btcpay_base_url']}"
f"{vault_contents['btcpay_webhook_path']}"
)
return vault_data
return vault_contents
except Exception as e:
logger.error(f"Error reading vault: {str(e)}")
@ -61,8 +62,6 @@ def verify_signature(payload_body, signature_header):
vault_values = get_vault_values()
secret = vault_values['webhook_secret']
logger.debug(f"Secret type in verify_signature: {type(secret)}")
expected_signature = hmac.new(
secret.encode('utf-8'),
payload_body,
@ -77,20 +76,17 @@ def verify_signature(payload_body, signature_header):
logger.error(f"Signature verification failed: {str(e)}")
return False
@app.route('/webhook/vpn', methods=['POST'])
def handle_payment():
def handle_payment_webhook(request):
"""Handle BTCPay Server webhook for VPN provisioning"""
try:
# Get vault values first to ensure we can access them
vault_values = get_vault_values()
logger.info(f"Processing webhook on endpoint: {vault_values['webhook_full_url']}")
# Get the signature from headers
signature = request.headers.get('BTCPay-Sig')
if not signature:
logger.error("Missing BTCPay-Sig header")
return jsonify({"error": "Missing signature"}), 401
# Verify signature
is_valid = verify_signature(request.get_data(), signature)
if not is_valid:
logger.error("Invalid signature")
@ -104,17 +100,14 @@ def handle_payment():
logger.error("Missing invoiceId in webhook data")
return jsonify({"error": "Missing invoiceId"}), 400
# Remove __test__ prefix/suffix for test webhooks
if invoice_id.startswith('__test__') and invoice_id.endswith('__test__'):
invoice_id = invoice_id[8:-8] # Strip the test markers
invoice_id = invoice_id[8:-8]
logger.info(f"Stripped test markers from invoice ID: {invoice_id}")
# Get vault password from environment
vault_pass = os.getenv('ANSIBLE_VAULT_PASSWORD')
vault_pass = os.getenv('ANSIBLE_VAULT_PASSWORD', '')
if not vault_pass:
raise Exception("Vault password not found in environment variables")
# Create temporary vault password file for ansible-playbook
with tempfile.NamedTemporaryFile(mode='w', delete=False) as vault_pass_file:
vault_pass_file.write(vault_pass)
vault_pass_file.flush()
@ -122,10 +115,10 @@ def handle_payment():
cmd = [
'ansible-playbook',
str(PLAYBOOK_PATH),
'-i', str(INVENTORY_PATH),
'-i', str(BASE_DIR / 'inventory.ini'),
'-e', f'invoice_id={invoice_id}',
'--vault-password-file', vault_pass_file.name,
'-vvv' # Add verbose output
'-vvv'
]
logger.info(f"Running ansible-playbook command: {' '.join(cmd)}")
@ -136,10 +129,8 @@ def handle_payment():
text=True
)
# Clean up the temporary file
os.unlink(vault_pass_file.name)
# Log the complete output regardless of success/failure
logger.info(f"Ansible playbook stdout: {result.stdout}")
if result.stderr:
logger.error(f"Ansible playbook stderr: {result.stderr}")
@ -167,16 +158,3 @@ def handle_payment():
"error": str(e),
"traceback": traceback.format_exc()
}), 500
if __name__ == '__main__':
# Verify we can read the vault and construct URL before starting
try:
vault_values = get_vault_values()
logger.info(f"Successfully loaded vault values")
logger.info(f"Configured webhook URL: {vault_values['webhook_full_url']}")
except Exception as e:
logger.error(f"Failed to load vault values: {str(e)}")
exit(1)
logger.info(f"Starting webhook handler, watching for playbook at {PLAYBOOK_PATH}")
app.run(host='0.0.0.0', port=5000)

4
run.py Normal file
View File

@ -0,0 +1,4 @@
from app import app
if __name__ == '__main__':
app.run(host='0.0.0.0', port=5000)