Merge pull request 'Add better logging/Other stuff' (#3) from dev into main
Reviewed-on: #3
This commit is contained in:
commit
d0445d5103
21
index.js
21
index.js
|
|
@ -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();
|
||||||
});
|
});
|
||||||
|
|
|
||||||
4
migrations/011_update_callLogs_add_details.sql
Normal file
4
migrations/011_update_callLogs_add_details.sql
Normal 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';
|
||||||
5
tools/Rotating AstroCom Secret/AstroCom_Rotate.service
Normal file
5
tools/Rotating AstroCom Secret/AstroCom_Rotate.service
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
[Unit]
|
||||||
|
Description=Rotate AstroCom Secret
|
||||||
|
[Service]
|
||||||
|
ExecStart=/usr/bin/astrocom_rotate.sh
|
||||||
|
Type=oneshot
|
||||||
7
tools/Rotating AstroCom Secret/AstroCom_Rotate.timer
Normal file
7
tools/Rotating AstroCom Secret/AstroCom_Rotate.timer
Normal 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
|
||||||
69
tools/Rotating AstroCom Secret/astrocom_rotate.sh
Normal file
69
tools/Rotating AstroCom Secret/astrocom_rotate.sh
Normal 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."
|
||||||
Loading…
Reference in a new issue