Add better logging/Other stuff #3

Merged
ChrisChrome merged 6 commits from dev into main 2026-02-18 13:46:00 -07:00
5 changed files with 101 additions and 5 deletions

View file

@ -55,7 +55,7 @@ pool.getConnection().then((conn) => {
// delete all users (The big scary one lol) // delete all users (The big scary one lol)
conn.query("DELETE FROM users").then(() => { conn.query("DELETE FROM users").then(() => {
// Generate 32 char random string // Generate 32 char random string
const passwd = crypto.randomBytes(32).toString('hex'); const passwd = process.env.SET_ADMIN_PASS || crypto.randomBytes(32).toString('hex');
bcrypt.hash(passwd, 10).then((hash) => { bcrypt.hash(passwd, 10).then((hash) => {
conn.query("INSERT INTO users (id, username, passwordHash) VALUES (1, 'admin', ?)", conn.query("INSERT INTO users (id, username, passwordHash) VALUES (1, 'admin', ?)",
[hash]).then(() => { [hash]).then(() => {
@ -1031,10 +1031,10 @@ app.get("/api/healthcheck", (req, res) => {
}); });
// logCall function (caller, callee) // logCall function (caller, callee)
const logCall = (caller, callee) => { const logCall = (caller, callee, srcIp="none_given", success, reason="none_given") => {
pool.getConnection().then(conn => { pool.getConnection().then(conn => {
conn.query('INSERT INTO callLogs (caller, callee, timestamp) VALUES (?, ?, ?)', conn.query('INSERT INTO callLogs (caller, callee, timestamp, srcIp, success, reason) VALUES (?, ?, ?, ?, ?, ?)',
[caller, callee, Math.floor(Date.now())]).catch(err => { [caller, callee, Math.floor(Date.now()), srcIp, success, reason]).catch(err => {
console.error('Error logging call:', err); console.error('Error logging call:', err);
}).finally(() => { }).finally(() => {
conn.release(); conn.release();
@ -1043,6 +1043,7 @@ const logCall = (caller, callee) => {
} }
const genCall = (req, res, apiKey, ani, number) => { const genCall = (req, res, apiKey, ani, number) => {
const srcIp = process.env.PROXY_HEADER ? req.headers[process.env.PROXY_HEADER] : req.ip;
pool.getConnection().then(conn => { pool.getConnection().then(conn => {
//conn.query("SELECT * FROM routes WHERE apiKey = ? AND block_start <= ? AND block_start + block_length >= ?", [apiKey, ani, ani]).then((rows) => { //conn.query("SELECT * FROM routes WHERE apiKey = ? AND block_start <= ? AND block_start + block_length >= ?", [apiKey, ani, ani]).then((rows) => {
conn.query("SELECT * FROM routes WHERE apiKey = ?", [apiKey]).then((rows) => { // We'll try this Nick, if it doesn't work we'll go back to the original conn.query("SELECT * FROM routes WHERE apiKey = ?", [apiKey]).then((rows) => { // We'll try this Nick, if it doesn't work we'll go back to the original
@ -1055,17 +1056,20 @@ const genCall = (req, res, apiKey, ani, number) => {
// If no row or error, return 401 // If no row or error, return 401
if (!row) { if (!row) {
res.status(401).send(`${process.env.MSG_ROUTE_ADDRESS}/401`) res.status(401).send(`${process.env.MSG_ROUTE_ADDRESS}/401`)
logCall(ani, number, srcIp, false, "invalid_api_key");
return; return;
} }
// Validate the ani and number are 7 digit numbers // Validate the ani and number are 7 digit numbers
if (!ani || ani < 1000000 || ani > 9999999 || !number || number < 1000000 || number > 9999999) { if (!ani || ani < 1000000 || ani > 9999999 || !number || number < 1000000 || number > 9999999) {
res.status(400).send(`${process.env.MSG_ROUTE_ADDRESS}/400`); res.status(400).send(`${process.env.MSG_ROUTE_ADDRESS}/400`);
logCall(ani, number, srcIp, false, "invalid_number");
return; return;
} }
// Validate the ani is owned by the apiKey // Validate the ani is owned by the apiKey
if (ani < row.block_start || ani > row.block_start + row.block_length) { if (ani < row.block_start || ani > row.block_start + row.block_length) {
res.status(403).send(`${process.env.MSG_ROUTE_ADDRESS}/403`); res.status(403).send(`${process.env.MSG_ROUTE_ADDRESS}/403`);
logCall(ani, number, srcIp, false, "ani_not_in_block");
return; return;
} }
@ -1077,6 +1081,7 @@ const genCall = (req, res, apiKey, ani, number) => {
const routeId = row ? row.id : null; const routeId = row ? row.id : null;
if (!routeId) { if (!routeId) {
res.status(404).send(`${process.env.MSG_ROUTE_ADDRESS}/404`); res.status(404).send(`${process.env.MSG_ROUTE_ADDRESS}/404`);
logCall(ani, number, srcIp, false, "no_route");
return; return;
} }
@ -1084,6 +1089,7 @@ const genCall = (req, res, apiKey, ani, number) => {
if (blockRows.length > 0) { if (blockRows.length > 0) {
// ANI is blocked from calling this route // ANI is blocked from calling this route
console.log(`Blocked Call Attempt: ${ani} -> ${number}`); console.log(`Blocked Call Attempt: ${ani} -> ${number}`);
logCall(ani, number, srcIp, false, "blocklist");
res.status(403).send(`${process.env.MSG_ROUTE_ADDRESS}/403`); res.status(403).send(`${process.env.MSG_ROUTE_ADDRESS}/403`);
return; return;
} }
@ -1092,7 +1098,7 @@ const genCall = (req, res, apiKey, ani, number) => {
// Check if the ANI is within the block range // Check if the ANI is within the block range
// If it is, return `local` // If it is, return `local`
console.log(`New Call: ${ani} -> ${number}`); console.log(`New Call: ${ani} -> ${number}`);
logCall(ani, number); logCall(ani, number, srcIp, true);
// incriment estCallsMade analytics // incriment estCallsMade analytics
addAnalytic("estCallsMade"); addAnalytic("estCallsMade");
dailyAnalytic("dailyCallsMade"); dailyAnalytic("dailyCallsMade");
@ -1103,19 +1109,24 @@ const genCall = (req, res, apiKey, ani, number) => {
} }
} else { } else {
res.status(404).send(`${process.env.MSG_ROUTE_ADDRESS}/404`); res.status(404).send(`${process.env.MSG_ROUTE_ADDRESS}/404`);
logCall(ani, number, srcIp, false, "no_route");
return;
} }
}).catch(err => { }).catch(err => {
console.error('Error checking blocklist:', err); console.error('Error checking blocklist:', err);
res.status(500).send(`${process.env.MSG_ROUTE_ADDRESS}/500`); res.status(500).send(`${process.env.MSG_ROUTE_ADDRESS}/500`);
logCall(ani, number, srcIp, false, "blocklist_error");
return; return;
}); });
}).catch(err => { }).catch(err => {
console.error('Error getting route:', err); console.error('Error getting route:', err);
res.status(500).send(`${process.env.MSG_ROUTE_ADDRESS}/500`) res.status(500).send(`${process.env.MSG_ROUTE_ADDRESS}/500`)
}); });
}).catch(err => { }).catch(err => {
console.error(err); console.error(err);
res.status(401).send(`${process.env.MSG_ROUTE_ADDRESS}/401`) res.status(401).send(`${process.env.MSG_ROUTE_ADDRESS}/401`)
logCall(ani, number, srcIp, false, "invalid_api_key");
}).finally(() => { }).finally(() => {
conn.release(); conn.release();
}); });

View file

@ -0,0 +1,4 @@
ALTER TABLE callLogs
ADD COLUMN srcIp VARCHAR(255) NOT NULL DEFAULT 'unknown',
ADD COLUMN success INTEGER NOT NULL DEFAULT 0,
ADD COLUMN reason VARCHAR(255) NOT NULL DEFAULT 'none';

View file

@ -0,0 +1,5 @@
[Unit]
Description=Rotate AstroCom Secret
[Service]
ExecStart=/usr/bin/astrocom_rotate.sh
Type=oneshot

View file

@ -0,0 +1,7 @@
[Unit]
Description=Rotate AstroCom Secret
[Timer]
# This will run daily at 0200 local time
OnCalendar=*-*-* 02:00:00
[Install]
WantedBy=timers.target

View file

@ -0,0 +1,69 @@
#!/bin/bash
# --- USER CONFIG ---
IAXBLOCK="from-astrocom"
CONF="/etc/asterisk/iax_custom.conf"
# Astrocom API endpoint info
ASTROCOM_URL="https://astrocom.tel/api/v1/user/update" # Shouldn't need to change this!
ASTROCOM_TOKEN=""
# -------------------
# Parse flags
DRYRUN=0
if [[ "$1" == "--dry-run" || "$1" == "-n" ]]; then
DRYRUN=1
fi
# Generate a 32-char alphanumeric secret
NEWSECRET=$(tr -dc 'A-Za-z0-9' </dev/urandom | head -c 32)
# Escape block name for sed search
ESC_BLOCK=$(printf '%s\n' "$IAXBLOCK" | sed 's/[]\/$*.^[]/\\&/g')
# Sed script to modify only the first secret= inside the block
SED_SCRIPT="
/^\[$ESC_BLOCK\]/,/^\[/{
/^\[/!{
s/^secret=.*/secret=$NEWSECRET/
t done
}
}
: done
"
if [[ $DRYRUN -eq 1 ]]; then
echo "=== DRY RUN: Showing updated output only ==="
sed "$SED_SCRIPT" "$CONF"
exit 0
fi
#
# REAL UPDATE EXECUTION
#
echo "Updating secret in file..."
sed -i "$SED_SCRIPT" "$CONF" || { echo "Error updating $CONF"; exit 1; }
echo "New secret: $NEWSECRET"
#
# ASTROCOM API REQUEST
#
echo "Sending updated secret to Astrocom..."
# ----- EDIT THIS TO MATCH YOUR REAL API FORMAT -----
curl -X PATCH "$ASTROCOM_URL" \
-H "Authorization: Bearer $ASTROCOM_TOKEN" \
-H "Content-Type: application/json" \
-d "{\"secret\": \"$NEWSECRET\"}" \
|| echo "Warning: Astrocom API call failed"
# ---------------------------------------------------
#
# RELOAD ASTERISK
#
echo "Reloading Asterisk..."
asterisk -x "core reload"
echo "Update complete."