Initial LisgloSIPS V2 implementation
This commit is contained in:
@@ -0,0 +1,2 @@
|
||||
server ntp.aliyun.com iburst prefer minpoll 4 maxpoll 10
|
||||
server time1.cloud.tencent.com iburst minpoll 4 maxpoll 10
|
||||
@@ -0,0 +1,11 @@
|
||||
[DEFAULT]
|
||||
backend = systemd
|
||||
bantime = 1h
|
||||
findtime = 10m
|
||||
maxretry = 5
|
||||
|
||||
[sshd]
|
||||
enabled = true
|
||||
port = 22
|
||||
banaction = nftables-multiport
|
||||
ignoreip = 127.0.0.1/8 ::1 100.91.249.119
|
||||
@@ -0,0 +1,27 @@
|
||||
destroy table inet lisglosips_filter
|
||||
|
||||
table inet lisglosips_filter {
|
||||
set admin_ipv4 {
|
||||
type ipv4_addr
|
||||
elements = { 100.91.249.119, 100.98.167.119 }
|
||||
}
|
||||
|
||||
chain input {
|
||||
type filter hook input priority 10; policy drop;
|
||||
|
||||
ct state invalid counter drop
|
||||
ct state established,related counter accept
|
||||
iifname "lo" counter accept
|
||||
|
||||
ip protocol icmp counter accept
|
||||
ip6 nexthdr ipv6-icmp counter accept
|
||||
|
||||
udp dport 41641 counter accept comment "Tailscale direct transport"
|
||||
|
||||
iifname "tailscale0" ip saddr @admin_ipv4 tcp dport { 22, 443 } counter accept comment "Admin SSH and HTTPS"
|
||||
iifname "tailscale0" ip saddr 100.90.90.90 tcp dport 6379 counter accept comment "Server A to Redis"
|
||||
iifname "tailscale0" ip saddr 100.90.90.90 udp dport 9060 counter accept comment "Server A to HEP"
|
||||
|
||||
counter drop
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
#!/usr/sbin/nft -f
|
||||
include "/etc/nftables.d/lisglosips.nft"
|
||||
@@ -0,0 +1,3 @@
|
||||
d /run/lisglosips 0750 lisglosips lisglosips -
|
||||
d /run/lisglo-recorder 0750 lisglo-recorder lisglosips -
|
||||
d /run/lisglo-monitor 0750 lisglo-monitor lisglosips -
|
||||
@@ -0,0 +1,3 @@
|
||||
# LisgloSIPS S04 data bind mounts
|
||||
/data/mysql /var/lib/mysql none bind 0 0
|
||||
/data/redis /var/lib/redis none bind 0 0
|
||||
@@ -0,0 +1,19 @@
|
||||
[mysqld]
|
||||
bind-address = 127.0.0.1
|
||||
mysqlx-bind-address = 127.0.0.1
|
||||
skip_name_resolve = ON
|
||||
local_infile = OFF
|
||||
server_id = 1
|
||||
log_bin = /var/log/mysql/mysql-bin
|
||||
binlog_format = ROW
|
||||
binlog_expire_logs_seconds = 604800
|
||||
sync_binlog = 1
|
||||
innodb_flush_log_at_trx_commit = 1
|
||||
innodb_buffer_pool_size = 1G
|
||||
max_connections = 200
|
||||
default_time_zone = +00:00
|
||||
character_set_server = utf8mb4
|
||||
collation_server = utf8mb4_0900_ai_ci
|
||||
slow_query_log = ON
|
||||
long_query_time = 1
|
||||
slow_query_log_file = /var/log/mysql/mysql-slow.log
|
||||
@@ -0,0 +1,20 @@
|
||||
bind 127.0.0.1 100.90.90.91
|
||||
protected-mode yes
|
||||
port 6379
|
||||
unixsocket /run/redis/redis-server.sock
|
||||
unixsocketperm 770
|
||||
dir /var/lib/redis
|
||||
dbfilename dump.rdb
|
||||
appendonly yes
|
||||
appendfilename "appendonly.aof"
|
||||
appenddirname "appendonlydir"
|
||||
appendfsync everysec
|
||||
save ""
|
||||
save 900 1
|
||||
save 300 10
|
||||
save 60 10000
|
||||
stop-writes-on-bgsave-error yes
|
||||
maxmemory 1536mb
|
||||
maxmemory-policy noeviction
|
||||
tcp-keepalive 60
|
||||
aclfile /etc/redis/users.acl
|
||||
@@ -0,0 +1,5 @@
|
||||
# Do not deploy this file as-is. Replace both placeholders with independent
|
||||
# generated secrets and install the result as /etc/redis/users.acl, mode 0640.
|
||||
user default off
|
||||
user lisglosips on >GENERATE_APP_SECRET ~* &* +@all -@dangerous
|
||||
user lisglo-backup on >GENERATE_BACKUP_SECRET ~* &* +ping +info +bgsave +lastsave
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
#!/bin/bash
|
||||
set -euo pipefail
|
||||
umask 077
|
||||
dest=/data/backups/mysql
|
||||
install -d -o root -g root -m 0700 "$dest"
|
||||
[ "$(realpath "$dest")" = "/data/backups/mysql" ] || exit 2
|
||||
exec 9>/run/lock/lisglosips-mysql-backup.lock
|
||||
flock -n 9 || { echo "MySQL backup already running" >&2; exit 3; }
|
||||
stamp=$(date -u +%Y%m%dT%H%M%SZ)
|
||||
tmp=$(mktemp -d "$dest/.tmp-$stamp.XXXXXX")
|
||||
trap 'rm -rf "$tmp"' EXIT
|
||||
mysqladmin --protocol=socket ping --silent
|
||||
mysqldump --protocol=socket --all-databases --single-transaction --routines --events --triggers --hex-blob --set-gtid-purged=OFF --no-tablespaces | gzip -9 > "$tmp/all-databases.sql.gz"
|
||||
mysqldump --protocol=socket --single-transaction --routines --events --triggers --hex-blob --set-gtid-purged=OFF --no-tablespaces lisglosips | gzip -9 > "$tmp/lisglosips.sql.gz"
|
||||
mysql --protocol=socket -N -e "SELECT @@version, @@global.gtid_executed, @@global.binlog_format, UTC_TIMESTAMP();" > "$tmp/metadata.tsv"
|
||||
(cd "$tmp" && sha256sum *.gz metadata.tsv > SHA256SUMS)
|
||||
chmod 0600 "$tmp"/*
|
||||
final="$dest/$stamp"
|
||||
mv "$tmp" "$final"
|
||||
trap - EXIT
|
||||
find "$dest" -mindepth 1 -maxdepth 1 -type d -name '20*T*Z' -mtime +14 -exec rm -rf -- {} +
|
||||
echo "$final"
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
#!/bin/bash
|
||||
set -euo pipefail
|
||||
umask 077
|
||||
dest=/data/backups/redis
|
||||
install -d -o root -g root -m 0700 "$dest"
|
||||
[ "$(realpath "$dest")" = "/data/backups/redis" ] || exit 2
|
||||
exec 9>/run/lock/lisglosips-redis-backup.lock
|
||||
flock -n 9 || { echo "Redis backup already running" >&2; exit 3; }
|
||||
set -a
|
||||
. /etc/lisglosips/secrets/redis-backup.env
|
||||
set +a
|
||||
export REDISCLI_AUTH="$REDIS_PASSWORD"
|
||||
stamp=$(date -u +%Y%m%dT%H%M%SZ)
|
||||
tmp=$(mktemp -d "$dest/.tmp-$stamp.XXXXXX")
|
||||
trap 'rm -rf "$tmp"' EXIT
|
||||
redis_cmd=(redis-cli -h "$REDIS_HOST" -p "$REDIS_PORT" --user "$REDIS_USER" --no-auth-warning)
|
||||
"${redis_cmd[@]}" PING | grep -qx PONG
|
||||
"${redis_cmd[@]}" BGSAVE >/dev/null
|
||||
for _ in $(seq 1 60); do
|
||||
info=$("${redis_cmd[@]}" INFO persistence | tr -d '\r')
|
||||
in_progress=$(printf '%s\n' "$info" | awk -F: '/^rdb_bgsave_in_progress:/{print $2}')
|
||||
status=$(printf '%s\n' "$info" | awk -F: '/^rdb_last_bgsave_status:/{print $2}')
|
||||
[ "$in_progress" = "0" ] && [ "$status" = "ok" ] && break
|
||||
sleep 1
|
||||
done
|
||||
[ "$in_progress" = "0" ] && [ "$status" = "ok" ]
|
||||
install -o root -g root -m 0600 /var/lib/redis/dump.rdb "$tmp/dump.rdb"
|
||||
redis-check-rdb "$tmp/dump.rdb" > "$tmp/redis-check-rdb.txt"
|
||||
"${redis_cmd[@]}" INFO server | tr -d '\r' | grep -E '^(redis_version|os|arch_bits|process_id|tcp_port)' > "$tmp/metadata.txt"
|
||||
(cd "$tmp" && sha256sum dump.rdb metadata.txt > SHA256SUMS)
|
||||
chmod 0600 "$tmp"/*
|
||||
final="$dest/$stamp"
|
||||
mv "$tmp" "$final"
|
||||
trap - EXIT
|
||||
find "$dest" -mindepth 1 -maxdepth 1 -type d -name '20*T*Z' -mtime +14 -exec rm -rf -- {} +
|
||||
echo "$final"
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
#!/bin/sh
|
||||
set -eu
|
||||
for _ in $(seq 1 60); do
|
||||
if /usr/sbin/ip -4 -o addr show dev tailscale0 2>/dev/null | /usr/bin/grep -q '100.90.90.91/32'; then
|
||||
exit 0
|
||||
fi
|
||||
/usr/bin/sleep 1
|
||||
done
|
||||
echo 'Tailscale address 100.90.90.91 not ready' >&2
|
||||
exit 1
|
||||
@@ -0,0 +1,16 @@
|
||||
[Unit]
|
||||
Description=LisgloSIPS MySQL and Redis backup
|
||||
Requires=mysql.service redis-server.service
|
||||
After=mysql.service redis-server.service
|
||||
ConditionPathIsMountPoint=/var/lib/mysql
|
||||
ConditionPathIsMountPoint=/var/lib/redis
|
||||
|
||||
[Service]
|
||||
Type=oneshot
|
||||
User=root
|
||||
Group=root
|
||||
UMask=0077
|
||||
Nice=10
|
||||
IOSchedulingClass=idle
|
||||
ExecStart=/usr/local/sbin/lisglosips-mysql-backup
|
||||
ExecStart=/usr/local/sbin/lisglosips-redis-backup
|
||||
@@ -0,0 +1,11 @@
|
||||
[Unit]
|
||||
Description=Daily LisgloSIPS database backup
|
||||
|
||||
[Timer]
|
||||
OnCalendar=*-*-* 03:15:00
|
||||
RandomizedDelaySec=15m
|
||||
Persistent=true
|
||||
Unit=lisglosips-backup.service
|
||||
|
||||
[Install]
|
||||
WantedBy=timers.target
|
||||
@@ -0,0 +1,9 @@
|
||||
[Unit]
|
||||
Description=Wait for Server B Tailscale address
|
||||
Requires=tailscaled.service
|
||||
After=tailscaled.service
|
||||
|
||||
[Service]
|
||||
Type=oneshot
|
||||
ExecStart=/usr/local/sbin/lisglosips-wait-tailscale-b
|
||||
RemainAfterExit=yes
|
||||
@@ -0,0 +1,3 @@
|
||||
[Unit]
|
||||
Requires=var-lib-mysql.mount
|
||||
After=var-lib-mysql.mount
|
||||
@@ -0,0 +1,3 @@
|
||||
[Unit]
|
||||
Requires=var-lib-redis.mount lisglosips-tailscale-ready.service
|
||||
After=var-lib-redis.mount lisglosips-tailscale-ready.service
|
||||
@@ -0,0 +1,2 @@
|
||||
MYSQL_VERSION=8.0.46-0ubuntu0.24.04.2
|
||||
REDIS_VERSION=7.0.15-1ubuntu0.24.04.4
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
LISGLOSIPS_ENTRYPOINT=/opt/lisglosips/current/server.mjs
|
||||
HOST=127.0.0.1
|
||||
PORT=3000
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
server_tokens off;
|
||||
|
||||
limit_req_zone $binary_remote_addr zone=lisglosips_api_per_ip:10m rate=20r/s;
|
||||
limit_req_zone $binary_remote_addr zone=lisglosips_auth_per_ip:10m rate=5r/m;
|
||||
limit_conn_zone $binary_remote_addr zone=lisglosips_conn_per_ip:10m;
|
||||
limit_req_status 429;
|
||||
limit_conn_status 429;
|
||||
|
||||
map $http_upgrade $lisglosips_connection_upgrade {
|
||||
default upgrade;
|
||||
'' close;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_set_header X-Request-ID $request_id;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection $lisglosips_connection_upgrade;
|
||||
proxy_connect_timeout 3s;
|
||||
proxy_send_timeout 30s;
|
||||
proxy_read_timeout 30s;
|
||||
proxy_buffering on;
|
||||
proxy_buffer_size 8k;
|
||||
proxy_buffers 8 16k;
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
add_header Strict-Transport-Security "max-age=300" always;
|
||||
add_header X-Content-Type-Options "nosniff" always;
|
||||
add_header X-Frame-Options "DENY" always;
|
||||
add_header Referrer-Policy "same-origin" always;
|
||||
add_header Permissions-Policy "camera=(), microphone=(), geolocation=()" always;
|
||||
add_header Content-Security-Policy "default-src 'self'; base-uri 'self'; frame-ancestors 'none'; form-action 'self'; object-src 'none'" always;
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
ssl_certificate /etc/lisglosips/pki/certs/server.crt;
|
||||
ssl_certificate_key /etc/lisglosips/pki/private/server.key;
|
||||
ssl_protocols TLSv1.2 TLSv1.3;
|
||||
ssl_prefer_server_ciphers off;
|
||||
ssl_session_cache shared:LisgloSIPSTLS:10m;
|
||||
ssl_session_timeout 1d;
|
||||
ssl_session_tickets off;
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
upstream lisglosips_api {
|
||||
server 127.0.0.1:3000 max_fails=3 fail_timeout=5s;
|
||||
keepalive 32;
|
||||
}
|
||||
|
||||
server {
|
||||
listen 80 default_server;
|
||||
listen [::]:80 default_server;
|
||||
server_name _;
|
||||
return 308 https://$host$request_uri;
|
||||
}
|
||||
|
||||
server {
|
||||
listen 443 ssl default_server;
|
||||
listen [::]:443 ssl default_server;
|
||||
server_name _;
|
||||
|
||||
include /etc/nginx/snippets/lisglosips-tls.conf;
|
||||
|
||||
root /opt/lisglosips/current/public;
|
||||
index index.html;
|
||||
client_max_body_size 2m;
|
||||
max_ranges 1;
|
||||
|
||||
include /etc/nginx/snippets/lisglosips-security-headers.conf;
|
||||
|
||||
limit_conn lisglosips_conn_per_ip 30;
|
||||
|
||||
location = /healthz {
|
||||
include /etc/nginx/snippets/lisglosips-proxy.conf;
|
||||
proxy_pass http://lisglosips_api;
|
||||
access_log off;
|
||||
}
|
||||
|
||||
location = /api/auth/login {
|
||||
include /etc/nginx/snippets/lisglosips-proxy.conf;
|
||||
limit_req zone=lisglosips_auth_per_ip burst=3 nodelay;
|
||||
proxy_pass http://lisglosips_api;
|
||||
}
|
||||
|
||||
location /api/ {
|
||||
include /etc/nginx/snippets/lisglosips-proxy.conf;
|
||||
limit_req zone=lisglosips_api_per_ip burst=40 nodelay;
|
||||
proxy_pass http://lisglosips_api;
|
||||
}
|
||||
|
||||
location /_recordings/ {
|
||||
internal;
|
||||
alias /data/recordings/;
|
||||
autoindex off;
|
||||
include /etc/nginx/snippets/lisglosips-security-headers.conf;
|
||||
add_header Accept-Ranges bytes always;
|
||||
}
|
||||
|
||||
location /assets/ {
|
||||
try_files $uri =404;
|
||||
expires 7d;
|
||||
include /etc/nginx/snippets/lisglosips-security-headers.conf;
|
||||
add_header Cache-Control "public, immutable";
|
||||
}
|
||||
|
||||
location / {
|
||||
try_files $uri $uri/ /index.html;
|
||||
expires -1;
|
||||
include /etc/nginx/snippets/lisglosips-security-headers.conf;
|
||||
add_header Cache-Control "no-store";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
<!doctype html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>LisgloSIPS</title>
|
||||
<style>
|
||||
:root { color-scheme: light; font-family: Arial, sans-serif; background: #f5f6f8; color: #12121a; }
|
||||
body { margin: 0; min-height: 100vh; display: grid; place-items: center; }
|
||||
main { width: min(560px, calc(100% - 48px)); border-top: 4px solid #d9c3a0; padding: 32px 0; }
|
||||
h1 { margin: 0 0 12px; font-size: 32px; letter-spacing: 0; }
|
||||
p { margin: 0; color: #5d606b; line-height: 1.7; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<main>
|
||||
<h1>LisgloSIPS</h1>
|
||||
<p>聆界SIP管理平台服务入口已就绪。</p>
|
||||
</main>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
import { createServer } from 'node:http';
|
||||
|
||||
const host = process.env.HOST || '127.0.0.1';
|
||||
const port = Number.parseInt(process.env.PORT || '3000', 10);
|
||||
|
||||
const server = createServer((request, response) => {
|
||||
response.setHeader('Content-Type', 'application/json; charset=utf-8');
|
||||
response.setHeader('Cache-Control', 'no-store');
|
||||
response.setHeader('X-Content-Type-Options', 'nosniff');
|
||||
|
||||
if (request.method === 'GET' && (request.url === '/healthz' || request.url === '/api/health')) {
|
||||
response.writeHead(200);
|
||||
response.end(JSON.stringify({ status: 'ok', service: 'lisglosips-api-placeholder' }));
|
||||
return;
|
||||
}
|
||||
|
||||
response.writeHead(404);
|
||||
response.end(JSON.stringify({ status: 'not_found' }));
|
||||
});
|
||||
|
||||
server.listen(port, host);
|
||||
|
||||
function shutdown() {
|
||||
server.close((error) => process.exit(error ? 1 : 0));
|
||||
setTimeout(() => process.exit(1), 10_000).unref();
|
||||
}
|
||||
|
||||
process.on('SIGTERM', shutdown);
|
||||
process.on('SIGINT', shutdown);
|
||||
|
||||
+72
@@ -0,0 +1,72 @@
|
||||
#!/bin/sh
|
||||
set -eu
|
||||
|
||||
PKI_ROOT=/etc/lisglosips/pki
|
||||
CA_DIR="$PKI_ROOT/ca"
|
||||
CERT_DIR="$PKI_ROOT/certs"
|
||||
PRIVATE_DIR="$PKI_ROOT/private"
|
||||
HOST_NAME=${HOST_NAME:-yanzi}
|
||||
TAILSCALE_IP=${TAILSCALE_IP:-100.90.90.91}
|
||||
|
||||
umask 077
|
||||
install -d -m 0700 "$CA_DIR"
|
||||
install -d -m 0755 "$CERT_DIR"
|
||||
install -d -m 0700 "$PRIVATE_DIR"
|
||||
|
||||
if [ ! -s "$CA_DIR/lisglosips-dev-ca.key" ] || [ ! -s "$CA_DIR/lisglosips-dev-ca.crt" ]; then
|
||||
openssl req -x509 -newkey rsa:4096 -sha256 -days 3650 -nodes \
|
||||
-subj '/CN=LisgloSIPS Development CA/O=LisgloSIPS' \
|
||||
-keyout "$CA_DIR/lisglosips-dev-ca.key" \
|
||||
-out "$CA_DIR/lisglosips-dev-ca.crt"
|
||||
fi
|
||||
|
||||
cat >"$PKI_ROOT/server-cert.cnf" <<EOF
|
||||
[req]
|
||||
prompt = no
|
||||
distinguished_name = dn
|
||||
req_extensions = req_ext
|
||||
|
||||
[dn]
|
||||
CN = lisglosips.local
|
||||
O = LisgloSIPS
|
||||
|
||||
[req_ext]
|
||||
subjectAltName = @alt_names
|
||||
|
||||
[alt_names]
|
||||
DNS.1 = lisglosips.local
|
||||
DNS.2 = $HOST_NAME
|
||||
DNS.3 = grafana.lisglosips.local
|
||||
DNS.4 = homer.lisglosips.local
|
||||
IP.1 = $TAILSCALE_IP
|
||||
IP.2 = 127.0.0.1
|
||||
|
||||
[server_ext]
|
||||
basicConstraints = critical,CA:FALSE
|
||||
keyUsage = critical,digitalSignature,keyEncipherment
|
||||
extendedKeyUsage = serverAuth
|
||||
subjectAltName = @alt_names
|
||||
EOF
|
||||
|
||||
openssl req -new -newkey rsa:3072 -nodes -sha256 \
|
||||
-config "$PKI_ROOT/server-cert.cnf" \
|
||||
-keyout "$PRIVATE_DIR/server.key" \
|
||||
-out "$PKI_ROOT/server.csr"
|
||||
|
||||
openssl x509 -req -sha256 -days 397 \
|
||||
-in "$PKI_ROOT/server.csr" \
|
||||
-CA "$CA_DIR/lisglosips-dev-ca.crt" \
|
||||
-CAkey "$CA_DIR/lisglosips-dev-ca.key" \
|
||||
-CAcreateserial \
|
||||
-extfile "$PKI_ROOT/server-cert.cnf" \
|
||||
-extensions server_ext \
|
||||
-out "$CERT_DIR/server.crt"
|
||||
|
||||
chown root:root "$CA_DIR/lisglosips-dev-ca.key" "$PKI_ROOT/server.csr" "$PKI_ROOT/server-cert.cnf"
|
||||
chown root:www-data "$PRIVATE_DIR/server.key"
|
||||
chown root:root "$CA_DIR/lisglosips-dev-ca.crt" "$CERT_DIR/server.crt"
|
||||
chmod 0600 "$CA_DIR/lisglosips-dev-ca.key" "$PKI_ROOT/server.csr" "$PKI_ROOT/server-cert.cnf"
|
||||
chmod 0640 "$PRIVATE_DIR/server.key"
|
||||
chmod 0644 "$CA_DIR/lisglosips-dev-ca.crt" "$CERT_DIR/server.crt"
|
||||
|
||||
openssl verify -CAfile "$CA_DIR/lisglosips-dev-ca.crt" "$CERT_DIR/server.crt"
|
||||
@@ -0,0 +1,42 @@
|
||||
[Unit]
|
||||
Description=LisgloSIPS %i service
|
||||
After=network-online.target mysql.service redis-server.service
|
||||
Wants=network-online.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
User=lisglosips
|
||||
Group=lisglosips
|
||||
WorkingDirectory=/opt/lisglosips/current
|
||||
Environment=NODE_ENV=production
|
||||
EnvironmentFile=/etc/lisglosips/%i.env
|
||||
ExecStart=/usr/bin/node ${LISGLOSIPS_ENTRYPOINT}
|
||||
Restart=on-failure
|
||||
RestartSec=3s
|
||||
TimeoutStartSec=30s
|
||||
TimeoutStopSec=30s
|
||||
KillSignal=SIGTERM
|
||||
UMask=0027
|
||||
|
||||
NoNewPrivileges=true
|
||||
PrivateTmp=true
|
||||
PrivateDevices=true
|
||||
ProtectSystem=strict
|
||||
ProtectHome=true
|
||||
ProtectKernelTunables=true
|
||||
ProtectKernelModules=true
|
||||
ProtectControlGroups=true
|
||||
ProtectClock=true
|
||||
ProtectHostname=true
|
||||
RestrictSUIDSGID=true
|
||||
RestrictRealtime=true
|
||||
LockPersonality=true
|
||||
CapabilityBoundingSet=
|
||||
AmbientCapabilities=
|
||||
RestrictAddressFamilies=AF_UNIX AF_INET AF_INET6
|
||||
StateDirectory=lisglosips
|
||||
RuntimeDirectory=lisglosips
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
NODEJS_VERSION=22.22.2-1nodesource1
|
||||
NODEJS_RUNTIME=v22.22.2
|
||||
PNPM_VERSION=10.33.0
|
||||
COREPACK_VERSION=0.34.6
|
||||
NGINX_VERSION=1.24.0-2ubuntu7.12
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
GRAFANA_VERSION=13.0.2
|
||||
GRAFANA_PACKAGE=grafana_13.0.2_26816849631_linux_amd64.deb
|
||||
GRAFANA_SHA256=ce64458852e49b897cabe7dee32741c9cf1bb623092ee4c6f7250aa7c6b83453
|
||||
GRAFANA_MIRROR=https://mirrors.tuna.tsinghua.edu.cn/grafana/apt
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"registry-mirrors": [
|
||||
"https://docker.m.daocloud.io"
|
||||
],
|
||||
"live-restore": true,
|
||||
"log-driver": "local",
|
||||
"log-opts": {
|
||||
"max-size": "20m",
|
||||
"max-file": "3"
|
||||
},
|
||||
"no-new-privileges": true
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
apiVersion: 1
|
||||
|
||||
providers:
|
||||
- name: LisgloSIPS
|
||||
orgId: 1
|
||||
folder: LisgloSIPS
|
||||
type: file
|
||||
disableDeletion: true
|
||||
editable: false
|
||||
options:
|
||||
path: /etc/grafana/provisioning/dashboards/lisglosips
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
apiVersion: 1
|
||||
|
||||
deleteDatasources:
|
||||
- name: Prometheus
|
||||
orgId: 1
|
||||
- name: HOMER PostgreSQL
|
||||
orgId: 1
|
||||
|
||||
datasources:
|
||||
- name: Prometheus
|
||||
uid: lisglosips-prometheus
|
||||
type: prometheus
|
||||
access: proxy
|
||||
url: http://127.0.0.1:9090
|
||||
isDefault: true
|
||||
editable: false
|
||||
|
||||
- name: HOMER PostgreSQL
|
||||
uid: lisglosips-homer-postgres
|
||||
type: postgres
|
||||
access: proxy
|
||||
url: 127.0.0.1:5432
|
||||
user: homer_user
|
||||
database: homer_data
|
||||
editable: false
|
||||
jsonData:
|
||||
sslmode: disable
|
||||
postgresVersion: 1600
|
||||
timescaledb: false
|
||||
secureJsonData:
|
||||
password: $HOMER_DB_PASSWORD
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
GRAFANA_USER=grafana
|
||||
GRAFANA_GROUP=grafana
|
||||
GRAFANA_HOME=/usr/share/grafana
|
||||
LOG_DIR=/var/log/grafana
|
||||
DATA_DIR=/data/grafana
|
||||
MAX_OPEN_FILES=10000
|
||||
CONF_DIR=/etc/grafana
|
||||
CONF_FILE=/etc/grafana/grafana.ini
|
||||
RESTART_ON_UPGRADE=true
|
||||
PLUGINS_DIR=/data/grafana/plugins
|
||||
PROVISIONING_CFG_DIR=/etc/grafana/provisioning
|
||||
PID_FILE_DIR=/run/grafana
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
{
|
||||
"annotations": {"list": []},
|
||||
"editable": false,
|
||||
"panels": [
|
||||
{
|
||||
"type": "stat",
|
||||
"title": "Targets Up",
|
||||
"datasource": {"type": "prometheus", "uid": "lisglosips-prometheus"},
|
||||
"targets": [{"expr": "sum(up)", "refId": "A"}],
|
||||
"gridPos": {"h": 8, "w": 8, "x": 0, "y": 0}
|
||||
},
|
||||
{
|
||||
"type": "timeseries",
|
||||
"title": "A/B CPU Usage",
|
||||
"datasource": {"type": "prometheus", "uid": "lisglosips-prometheus"},
|
||||
"targets": [{"expr": "100 - avg by (server) (rate(node_cpu_seconds_total{mode=\"idle\"}[5m])) * 100", "legendFormat": "{{server}}", "refId": "A"}],
|
||||
"gridPos": {"h": 8, "w": 16, "x": 8, "y": 0}
|
||||
},
|
||||
{
|
||||
"type": "timeseries",
|
||||
"title": "A/B Memory Usage",
|
||||
"datasource": {"type": "prometheus", "uid": "lisglosips-prometheus"},
|
||||
"targets": [{"expr": "100 * (1 - node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes)", "legendFormat": "{{server}}", "refId": "A"}],
|
||||
"gridPos": {"h": 8, "w": 24, "x": 0, "y": 8}
|
||||
}
|
||||
],
|
||||
"schemaVersion": 41,
|
||||
"tags": ["lisglosips", "infrastructure"],
|
||||
"templating": {"list": []},
|
||||
"time": {"from": "now-1h", "to": "now"},
|
||||
"title": "LisgloSIPS Infrastructure Overview",
|
||||
"uid": "lisglosips-infra-overview",
|
||||
"version": 1
|
||||
}
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
HEPLIFYSERVER_HEPADDR=100.90.90.91:9060
|
||||
HEPLIFYSERVER_HEPTCPADDR=
|
||||
HEPLIFYSERVER_HEPTLSADDR=
|
||||
HEPLIFYSERVER_DBSHEMA=homer7
|
||||
HEPLIFYSERVER_DBDRIVER=postgres
|
||||
HEPLIFYSERVER_DBADDR=127.0.0.1:5432
|
||||
HEPLIFYSERVER_DBUSER=homer_user
|
||||
HEPLIFYSERVER_DBPASS=<HOMER_DB_PASSWORD>
|
||||
HEPLIFYSERVER_DBDATATABLE=homer_data
|
||||
HEPLIFYSERVER_DBCONFTABLE=homer_config
|
||||
HEPLIFYSERVER_DBDROPDAYS=7
|
||||
HEPLIFYSERVER_DBROTATE=true
|
||||
HEPLIFYSERVER_DBBUFFER=200000
|
||||
HEPLIFYSERVER_DBBULK=200
|
||||
HEPLIFYSERVER_DBWORKER=4
|
||||
HEPLIFYSERVER_DBTIMER=2
|
||||
HEPLIFYSERVER_LOGLVL=info
|
||||
HEPLIFYSERVER_LOGSTD=true
|
||||
HEPLIFYSERVER_PROMADDR=127.0.0.1:9096
|
||||
HEPLIFYSERVER_PROMTARGETIP=100.90.90.91
|
||||
HEPLIFYSERVER_PROMTARGETNAME=server-b
|
||||
HEPLIFYSERVER_DEDUP=false
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
<!doctype html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<title>LisgloSIPS HOMER</title>
|
||||
</head>
|
||||
<body>
|
||||
<main>
|
||||
<h1>LisgloSIPS HOMER</h1>
|
||||
<p>HOMER API 与 HEP 存储服务已就绪。</p>
|
||||
</main>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -0,0 +1,73 @@
|
||||
{
|
||||
"database_data": {
|
||||
"LocalNode": {
|
||||
"node": "LocalNode",
|
||||
"user": "homer_user",
|
||||
"pass": "<HOMER_DB_PASSWORD>",
|
||||
"name": "homer_data",
|
||||
"keepalive": true,
|
||||
"host": "127.0.0.1"
|
||||
}
|
||||
},
|
||||
"database_config": {
|
||||
"node": "LocalConfig",
|
||||
"user": "homer_user",
|
||||
"pass": "<HOMER_DB_PASSWORD>",
|
||||
"name": "homer_config",
|
||||
"keepalive": true,
|
||||
"host": "127.0.0.1"
|
||||
},
|
||||
"hep_relay": {
|
||||
"host": "100.90.90.91",
|
||||
"port": 9060
|
||||
},
|
||||
"prometheus_config": {
|
||||
"enable": true,
|
||||
"host": "http://127.0.0.1:9090",
|
||||
"api": "api/v1"
|
||||
},
|
||||
"influxdb_config": {
|
||||
"enable": false
|
||||
},
|
||||
"loki_config": {
|
||||
"enable": false
|
||||
},
|
||||
"grafana_config": {
|
||||
"enable": true,
|
||||
"host": "http://127.0.0.1:3001",
|
||||
"path": "grafana",
|
||||
"proxy_control": false
|
||||
},
|
||||
"http_settings": {
|
||||
"host": "127.0.0.1",
|
||||
"port": 9080,
|
||||
"root": "/usr/local/homer/dist",
|
||||
"gzip": false,
|
||||
"gzip_static": false,
|
||||
"path": "/",
|
||||
"debug": false
|
||||
},
|
||||
"swagger": {
|
||||
"enable": true,
|
||||
"api_json": "/etc/homer/swagger.json",
|
||||
"api_host": "homer.lisglosips.local"
|
||||
},
|
||||
"system_settings": {
|
||||
"logpath": "/var/log/homer",
|
||||
"logname": "homer-app.log",
|
||||
"loglevel": "info",
|
||||
"logstdout": true
|
||||
},
|
||||
"auth_settings": {
|
||||
"type": "internal",
|
||||
"jwt_secret": "<HOMER_JWT_SECRET>",
|
||||
"gravatar": false,
|
||||
"token_expire": 1200,
|
||||
"user_groups": ["admin", "user", "support"]
|
||||
},
|
||||
"api_settings": {
|
||||
"enable_token_access": false,
|
||||
"add_captid_to_resolve": false
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
server {
|
||||
listen 443 ssl;
|
||||
listen [::]:443 ssl;
|
||||
server_name grafana.lisglosips.local;
|
||||
|
||||
include /etc/nginx/snippets/lisglosips-tls.conf;
|
||||
add_header Strict-Transport-Security "max-age=300" always;
|
||||
add_header X-Content-Type-Options "nosniff" always;
|
||||
add_header X-Frame-Options "DENY" always;
|
||||
add_header Referrer-Policy "same-origin" always;
|
||||
|
||||
location / {
|
||||
include /etc/nginx/snippets/lisglosips-proxy.conf;
|
||||
proxy_pass http://127.0.0.1:3001;
|
||||
}
|
||||
}
|
||||
|
||||
server {
|
||||
listen 443 ssl;
|
||||
listen [::]:443 ssl;
|
||||
server_name homer.lisglosips.local;
|
||||
|
||||
include /etc/nginx/snippets/lisglosips-tls.conf;
|
||||
add_header Strict-Transport-Security "max-age=300" always;
|
||||
add_header X-Content-Type-Options "nosniff" always;
|
||||
add_header X-Frame-Options "DENY" always;
|
||||
add_header Referrer-Policy "same-origin" always;
|
||||
|
||||
location / {
|
||||
include /etc/nginx/snippets/lisglosips-proxy.conf;
|
||||
proxy_pass http://127.0.0.1:9080;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
data_directory = '/data/homer/postgresql'
|
||||
listen_addresses = '127.0.0.1'
|
||||
port = 5432
|
||||
password_encryption = 'scram-sha-256'
|
||||
timezone = 'UTC'
|
||||
log_timezone = 'UTC'
|
||||
|
||||
max_connections = 100
|
||||
shared_buffers = '512MB'
|
||||
effective_cache_size = '2GB'
|
||||
maintenance_work_mem = '128MB'
|
||||
work_mem = '8MB'
|
||||
min_wal_size = '256MB'
|
||||
max_wal_size = '2GB'
|
||||
checkpoint_completion_target = 0.9
|
||||
random_page_cost = 1.1
|
||||
|
||||
logging_collector = on
|
||||
log_directory = '/var/log/postgresql'
|
||||
log_filename = 'postgresql-homer-%Y-%m-%d.log'
|
||||
log_rotation_age = '1d'
|
||||
log_truncate_on_rotation = on
|
||||
log_min_duration_statement = 1000
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
ARGS=--web.listen-address=127.0.0.1:9100 --collector.systemd --collector.processes
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
ARGS=--config.file=/etc/prometheus/prometheus.yml --storage.tsdb.path=/data/prometheus --storage.tsdb.retention.time=15d --storage.tsdb.retention.size=10GB --web.listen-address=127.0.0.1:9090 --web.enable-lifecycle
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
global:
|
||||
scrape_interval: 15s
|
||||
evaluation_interval: 15s
|
||||
external_labels:
|
||||
environment: development
|
||||
platform: lisglosips
|
||||
|
||||
rule_files:
|
||||
- /etc/prometheus/rules/*.yml
|
||||
|
||||
scrape_configs:
|
||||
- job_name: prometheus
|
||||
static_configs:
|
||||
- targets: ["127.0.0.1:9090"]
|
||||
|
||||
- job_name: node
|
||||
static_configs:
|
||||
- targets: ["100.90.90.90:9100"]
|
||||
labels:
|
||||
server: server-a
|
||||
- targets: ["127.0.0.1:9100"]
|
||||
labels:
|
||||
server: server-b
|
||||
|
||||
- job_name: mysql
|
||||
static_configs:
|
||||
- targets: ["127.0.0.1:9104"]
|
||||
|
||||
- job_name: postgresql_homer
|
||||
static_configs:
|
||||
- targets: ["127.0.0.1:9187"]
|
||||
|
||||
- job_name: redis
|
||||
static_configs:
|
||||
- targets: ["127.0.0.1:9121"]
|
||||
|
||||
- job_name: heplify_server
|
||||
static_configs:
|
||||
- targets: ["127.0.0.1:9096"]
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
groups:
|
||||
- name: lisglosips-base
|
||||
rules:
|
||||
- alert: LisgloSIPSTargetDown
|
||||
expr: up == 0
|
||||
for: 2m
|
||||
labels:
|
||||
severity: warning
|
||||
annotations:
|
||||
summary: "Prometheus target {{ $labels.instance }} is down"
|
||||
|
||||
- alert: LisgloSIPSDiskLow
|
||||
expr: 100 * node_filesystem_avail_bytes{fstype!~"tmpfs|overlay"} / node_filesystem_size_bytes{fstype!~"tmpfs|overlay"} < 15
|
||||
for: 10m
|
||||
labels:
|
||||
severity: warning
|
||||
annotations:
|
||||
summary: "Disk free space below 15% on {{ $labels.instance }}"
|
||||
|
||||
Executable
+187
@@ -0,0 +1,187 @@
|
||||
#!/bin/bash
|
||||
set -euo pipefail
|
||||
|
||||
: "${HOMER_DB_PASSWORD:?missing HOMER_DB_PASSWORD}"
|
||||
: "${HOMER_UI_PASSWORD:?missing HOMER_UI_PASSWORD}"
|
||||
: "${HOMER_JWT_SECRET:?missing HOMER_JWT_SECRET}"
|
||||
: "${GRAFANA_ADMIN_PASSWORD:?missing GRAFANA_ADMIN_PASSWORD}"
|
||||
: "${MYSQL_EXPORTER_PASSWORD:?missing MYSQL_EXPORTER_PASSWORD}"
|
||||
: "${REDIS_EXPORTER_PASSWORD:?missing REDIS_EXPORTER_PASSWORD}"
|
||||
: "${POSTGRES_EXPORTER_PASSWORD:?missing POSTGRES_EXPORTER_PASSWORD}"
|
||||
|
||||
STAGE=${STAGE:-/tmp/lisglosips-s06-stage-202606202230}
|
||||
BUILD=/var/tmp/lisglosips-s06-go-20260620/bin
|
||||
HOMER_SOURCE=/var/tmp/lisglosips-s06-go-20260620/pkg/mod/github.com/sipcapture/homer-app@v0.0.0-20251021161517-9b1336352aa0
|
||||
|
||||
test -d "$STAGE/systemd"
|
||||
|
||||
export DEBIAN_FRONTEND=noninteractive
|
||||
systemctl mask prometheus-postgres-exporter.service 2>/dev/null || true
|
||||
apt-get install -y prometheus-postgres-exporter=0.15.0-1ubuntu0.3
|
||||
apt-mark hold prometheus-postgres-exporter >/dev/null
|
||||
|
||||
for account in homer heplify redis-exporter; do
|
||||
if ! getent passwd "$account" >/dev/null; then
|
||||
useradd --system --home-dir /nonexistent --no-create-home --shell /usr/sbin/nologin "$account"
|
||||
fi
|
||||
done
|
||||
|
||||
install -o root -g root -m 0755 "$BUILD/heplify-server" /usr/local/bin/heplify-server
|
||||
install -o root -g root -m 0755 "$BUILD/homer-app" /usr/local/bin/homer-app
|
||||
install -o root -g root -m 0755 "$BUILD/redis_exporter" /usr/local/bin/redis_exporter
|
||||
install -o root -g root -m 0644 "$STAGE/versions.env" /etc/lisglosips/monitoring-versions.env
|
||||
|
||||
systemctl stop postgresql.service
|
||||
chown root:postgres /data/homer
|
||||
chmod 0750 /data/homer
|
||||
install -d -o postgres -g postgres -m 0700 /data/homer/postgresql
|
||||
if [ -z "$(find /data/homer/postgresql -mindepth 1 -maxdepth 1 -print -quit)" ]; then
|
||||
rsync -aHAX /var/lib/postgresql/16/main/ /data/homer/postgresql/
|
||||
fi
|
||||
chown -R postgres:postgres /data/homer/postgresql
|
||||
chmod 0700 /data/homer/postgresql
|
||||
install -d -o root -g postgres -m 0750 /etc/postgresql/16/main/conf.d
|
||||
install -o root -g postgres -m 0640 "$STAGE/postgresql/lisglosips-homer.conf" /etc/postgresql/16/main/conf.d/lisglosips-homer.conf
|
||||
grep -Eq "^[[:space:]]*include_dir[[:space:]]*=[[:space:]]*'conf.d'" /etc/postgresql/16/main/postgresql.conf
|
||||
systemctl start postgresql.service
|
||||
pg_isready -h 127.0.0.1 -p 5432
|
||||
|
||||
runuser -u postgres -- psql -v ON_ERROR_STOP=1 -v homer_pw="$HOMER_DB_PASSWORD" -v exporter_pw="$POSTGRES_EXPORTER_PASSWORD" <<'SQL'
|
||||
SELECT format('CREATE ROLE homer_user LOGIN PASSWORD %L', :'homer_pw')
|
||||
WHERE NOT EXISTS (SELECT 1 FROM pg_roles WHERE rolname = 'homer_user') \gexec
|
||||
SELECT format('ALTER ROLE homer_user LOGIN PASSWORD %L', :'homer_pw') \gexec
|
||||
SELECT 'CREATE DATABASE homer_config OWNER homer_user'
|
||||
WHERE NOT EXISTS (SELECT 1 FROM pg_database WHERE datname = 'homer_config') \gexec
|
||||
SELECT 'CREATE DATABASE homer_data OWNER homer_user'
|
||||
WHERE NOT EXISTS (SELECT 1 FROM pg_database WHERE datname = 'homer_data') \gexec
|
||||
SELECT format('CREATE ROLE postgres_exporter LOGIN PASSWORD %L', :'exporter_pw')
|
||||
WHERE NOT EXISTS (SELECT 1 FROM pg_roles WHERE rolname = 'postgres_exporter') \gexec
|
||||
SELECT format('ALTER ROLE postgres_exporter LOGIN PASSWORD %L', :'exporter_pw') \gexec
|
||||
GRANT pg_monitor TO postgres_exporter;
|
||||
SQL
|
||||
|
||||
mysql --protocol=socket <<SQL
|
||||
CREATE USER IF NOT EXISTS 'mysqld_exporter'@'127.0.0.1' IDENTIFIED WITH caching_sha2_password BY '$MYSQL_EXPORTER_PASSWORD';
|
||||
ALTER USER 'mysqld_exporter'@'127.0.0.1' IDENTIFIED WITH caching_sha2_password BY '$MYSQL_EXPORTER_PASSWORD';
|
||||
GRANT PROCESS, REPLICATION CLIENT ON *.* TO 'mysqld_exporter'@'127.0.0.1';
|
||||
GRANT SELECT ON performance_schema.* TO 'mysqld_exporter'@'127.0.0.1';
|
||||
FLUSH PRIVILEGES;
|
||||
SQL
|
||||
|
||||
sed -i '/^user redis_exporter /d' /etc/redis/users.acl
|
||||
printf 'user redis_exporter on >%s ~* -@all +ping +info +client +config|get +latency +memory +select +slowlog +scan +type +strlen +llen +scard +zcard +xlen +hscan +sscan +zscan +get\n' "$REDIS_EXPORTER_PASSWORD" >>/etc/redis/users.acl
|
||||
chown root:redis /etc/redis/users.acl
|
||||
chmod 0640 /etc/redis/users.acl
|
||||
systemctl restart redis-server.service
|
||||
|
||||
install -d -o root -g root -m 0755 /etc/lisglosips/secrets
|
||||
sed "s/<HOMER_DB_PASSWORD>/$HOMER_DB_PASSWORD/g" "$STAGE/heplify/heplify.env.example" >/etc/lisglosips/secrets/heplify.env
|
||||
chown root:heplify /etc/lisglosips/secrets/heplify.env
|
||||
chmod 0640 /etc/lisglosips/secrets/heplify.env
|
||||
|
||||
cat >/etc/lisglosips/secrets/grafana.env <<EOF
|
||||
GF_SECURITY_ADMIN_USER=admin
|
||||
GF_SECURITY_ADMIN_PASSWORD=$GRAFANA_ADMIN_PASSWORD
|
||||
GF_USERS_ALLOW_SIGN_UP=false
|
||||
GF_AUTH_ANONYMOUS_ENABLED=false
|
||||
GF_SERVER_HTTP_ADDR=127.0.0.1
|
||||
GF_SERVER_HTTP_PORT=3001
|
||||
GF_SERVER_DOMAIN=grafana.lisglosips.local
|
||||
GF_SERVER_ROOT_URL=https://grafana.lisglosips.local/
|
||||
GF_SECURITY_COOKIE_SECURE=true
|
||||
GF_SECURITY_COOKIE_SAMESITE=strict
|
||||
GF_SECURITY_DISABLE_GRAVATAR=true
|
||||
GF_ANALYTICS_REPORTING_ENABLED=false
|
||||
GF_ANALYTICS_CHECK_FOR_UPDATES=false
|
||||
GF_PLUGINS_PREINSTALL_DISABLED=true
|
||||
GF_PLUGINS_CHECK_FOR_PLUGIN_UPDATES=false
|
||||
GF_PLUGINS_PLUGIN_ADMIN_ENABLED=false
|
||||
HOMER_DB_PASSWORD=$HOMER_DB_PASSWORD
|
||||
EOF
|
||||
chown root:grafana /etc/lisglosips/secrets/grafana.env
|
||||
chmod 0640 /etc/lisglosips/secrets/grafana.env
|
||||
|
||||
cat >/etc/prometheus/mysqld-exporter.cnf <<EOF
|
||||
[client]
|
||||
user=mysqld_exporter
|
||||
password=$MYSQL_EXPORTER_PASSWORD
|
||||
host=127.0.0.1
|
||||
port=3306
|
||||
EOF
|
||||
chown root:prometheus /etc/prometheus/mysqld-exporter.cnf
|
||||
chmod 0640 /etc/prometheus/mysqld-exporter.cnf
|
||||
|
||||
cat >/etc/lisglosips/secrets/postgres-exporter.env <<EOF
|
||||
DATA_SOURCE_NAME=postgresql://postgres_exporter:$POSTGRES_EXPORTER_PASSWORD@127.0.0.1:5432/postgres?sslmode=disable
|
||||
EOF
|
||||
chown root:prometheus /etc/lisglosips/secrets/postgres-exporter.env
|
||||
chmod 0640 /etc/lisglosips/secrets/postgres-exporter.env
|
||||
|
||||
cat >/etc/lisglosips/secrets/redis-exporter.env <<EOF
|
||||
REDIS_ADDR=redis://127.0.0.1:6379
|
||||
REDIS_USER=redis_exporter
|
||||
REDIS_PASSWORD=$REDIS_EXPORTER_PASSWORD
|
||||
REDIS_EXPORTER_WEB_LISTEN_ADDRESS=127.0.0.1:9121
|
||||
EOF
|
||||
chown root:redis-exporter /etc/lisglosips/secrets/redis-exporter.env
|
||||
chmod 0640 /etc/lisglosips/secrets/redis-exporter.env
|
||||
|
||||
install -d -o root -g homer -m 0750 /etc/homer
|
||||
sed -e "s/<HOMER_DB_PASSWORD>/$HOMER_DB_PASSWORD/g" -e "s/<HOMER_JWT_SECRET>/$HOMER_JWT_SECRET/g" "$STAGE/homer/webapp_config.json.example" >/etc/homer/webapp_config.json
|
||||
install -o root -g homer -m 0640 "$HOMER_SOURCE/swagger.json" /etc/homer/swagger.json
|
||||
chown homer:homer /etc/homer/webapp_config.json
|
||||
chmod 0600 /etc/homer/webapp_config.json
|
||||
install -d -o root -g homer -m 0750 /usr/local/homer/dist
|
||||
install -o root -g homer -m 0640 "$STAGE/homer/index.html" /usr/local/homer/dist/index.html
|
||||
install -d -o homer -g homer -m 0750 /var/log/homer
|
||||
|
||||
install -d -o prometheus -g prometheus -m 0750 /data/prometheus
|
||||
install -d -o root -g prometheus -m 0750 /etc/prometheus/rules
|
||||
install -o root -g prometheus -m 0640 "$STAGE/prometheus/prometheus.yml" /etc/prometheus/prometheus.yml
|
||||
install -o root -g prometheus -m 0640 "$STAGE/prometheus/rules/lisglosips.yml" /etc/prometheus/rules/lisglosips.yml
|
||||
install -o root -g prometheus -m 0640 "$STAGE/prometheus/prometheus.env" /etc/default/lisglosips-prometheus
|
||||
install -o root -g prometheus -m 0640 "$STAGE/prometheus/node-exporter-b.env" /etc/default/lisglosips-node-exporter
|
||||
promtool check config /etc/prometheus/prometheus.yml
|
||||
promtool check rules /etc/prometheus/rules/lisglosips.yml
|
||||
|
||||
install -d -o grafana -g grafana -m 0750 /data/grafana /data/grafana/plugins
|
||||
install -o root -g root -m 0644 "$STAGE/grafana/grafana-server.default" /etc/default/grafana-server
|
||||
install -d -o root -g grafana -m 0750 /etc/grafana/provisioning/datasources /etc/grafana/provisioning/dashboards /etc/grafana/provisioning/dashboards/lisglosips
|
||||
install -o root -g grafana -m 0640 "$STAGE/grafana/datasources.yml" /etc/grafana/provisioning/datasources/lisglosips.yml
|
||||
install -o root -g grafana -m 0640 "$STAGE/grafana/dashboards.yml" /etc/grafana/provisioning/dashboards/lisglosips.yml
|
||||
install -o root -g grafana -m 0640 "$STAGE/grafana/lisglosips-overview.json" /etc/grafana/provisioning/dashboards/lisglosips/lisglosips-overview.json
|
||||
|
||||
for unit in heplify-server homer-app lisglosips-prometheus lisglosips-node-exporter lisglosips-mysqld-exporter lisglosips-postgres-exporter lisglosips-redis-exporter; do
|
||||
install -o root -g root -m 0644 "$STAGE/systemd/$unit.service" "/etc/systemd/system/$unit.service"
|
||||
done
|
||||
install -d -o root -g root -m 0755 /etc/systemd/system/grafana-server.service.d
|
||||
install -o root -g root -m 0644 "$STAGE/systemd/grafana-override.conf" /etc/systemd/system/grafana-server.service.d/lisglosips.conf
|
||||
systemctl unmask grafana-server.service
|
||||
|
||||
install -o root -g root -m 0644 "$STAGE/nginx/monitoring.conf" /etc/nginx/sites-available/lisglosips-monitoring.conf
|
||||
ln -sfn /etc/nginx/sites-available/lisglosips-monitoring.conf /etc/nginx/sites-enabled/lisglosips-monitoring.conf
|
||||
install -o root -g root -m 0755 /tmp/lisglosips-issue-dev-cert.s06 /usr/local/sbin/lisglosips-issue-dev-cert
|
||||
rm -f /tmp/lisglosips-issue-dev-cert.s06
|
||||
/usr/local/sbin/lisglosips-issue-dev-cert >/dev/null 2>&1
|
||||
nginx -t
|
||||
|
||||
runuser -u homer -- /usr/local/bin/homer-app -webapp-config-path=/etc/homer -create-table-db-config >/var/log/homer/init-config.log 2>&1
|
||||
runuser -u homer -- /usr/local/bin/homer-app -webapp-config-path=/etc/homer -populate-table-db-config -force-password="$HOMER_UI_PASSWORD" >>/var/log/homer/init-config.log 2>&1
|
||||
chmod 0640 /var/log/homer/init-config.log
|
||||
|
||||
systemctl daemon-reload
|
||||
systemctl disable --now prometheus-node-exporter.service prometheus-node-exporter.socket 2>/dev/null || true
|
||||
systemctl mask prometheus-node-exporter.service 2>/dev/null || true
|
||||
systemctl disable --now openipmi.service 2>/dev/null || true
|
||||
systemctl mask openipmi.service 2>/dev/null || true
|
||||
systemctl enable --now heplify-server.service homer-app.service lisglosips-node-exporter.service lisglosips-mysqld-exporter.service lisglosips-postgres-exporter.service lisglosips-redis-exporter.service lisglosips-prometheus.service grafana-server.service
|
||||
systemctl reload nginx.service
|
||||
|
||||
sleep 5
|
||||
for unit in postgresql heplify-server homer-app lisglosips-node-exporter lisglosips-mysqld-exporter lisglosips-postgres-exporter lisglosips-redis-exporter lisglosips-prometheus grafana-server nginx; do
|
||||
systemctl is-active "$unit" >/dev/null
|
||||
done
|
||||
curl -fsS http://127.0.0.1:9090/-/ready
|
||||
curl -fsS http://127.0.0.1:3001/api/health
|
||||
curl -fsS http://127.0.0.1:9096/metrics >/dev/null
|
||||
printf 'S06_SERVER_B_CONFIG=PASS\n'
|
||||
@@ -0,0 +1,4 @@
|
||||
[Service]
|
||||
EnvironmentFile=/etc/lisglosips/secrets/grafana.env
|
||||
ReadWritePaths=/data/grafana /var/log/grafana
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
[Unit]
|
||||
Description=LisgloSIPS Heplify Server
|
||||
After=network-online.target postgresql.service
|
||||
Wants=network-online.target
|
||||
Requires=postgresql.service
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
User=heplify
|
||||
Group=heplify
|
||||
EnvironmentFile=/etc/lisglosips/secrets/heplify.env
|
||||
ExecStart=/usr/local/bin/heplify-server
|
||||
Restart=on-failure
|
||||
RestartSec=3s
|
||||
TimeoutStopSec=30s
|
||||
UMask=0027
|
||||
NoNewPrivileges=true
|
||||
PrivateTmp=true
|
||||
PrivateDevices=true
|
||||
ProtectSystem=strict
|
||||
ProtectHome=true
|
||||
ProtectKernelTunables=true
|
||||
ProtectKernelModules=true
|
||||
ProtectControlGroups=true
|
||||
RestrictSUIDSGID=true
|
||||
LockPersonality=true
|
||||
CapabilityBoundingSet=
|
||||
RestrictAddressFamilies=AF_UNIX AF_INET AF_INET6
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
[Unit]
|
||||
Description=LisgloSIPS HOMER App
|
||||
After=network-online.target postgresql.service
|
||||
Wants=network-online.target
|
||||
Requires=postgresql.service
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
User=homer
|
||||
Group=homer
|
||||
ExecStart=/usr/local/bin/homer-app -webapp-config-path=/etc/homer
|
||||
Restart=on-failure
|
||||
RestartSec=3s
|
||||
TimeoutStopSec=30s
|
||||
UMask=0027
|
||||
NoNewPrivileges=true
|
||||
PrivateTmp=true
|
||||
PrivateDevices=true
|
||||
ProtectSystem=strict
|
||||
ProtectHome=true
|
||||
ProtectKernelTunables=true
|
||||
ProtectKernelModules=true
|
||||
ProtectControlGroups=true
|
||||
RestrictSUIDSGID=true
|
||||
LockPersonality=true
|
||||
CapabilityBoundingSet=
|
||||
RestrictAddressFamilies=AF_UNIX AF_INET AF_INET6
|
||||
ReadWritePaths=/etc/homer /var/log/homer
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
@@ -0,0 +1,27 @@
|
||||
[Unit]
|
||||
Description=LisgloSIPS MySQL Exporter
|
||||
After=mysql.service
|
||||
Requires=mysql.service
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
User=prometheus
|
||||
Group=prometheus
|
||||
ExecStart=/usr/bin/prometheus-mysqld-exporter --config.my-cnf=/etc/prometheus/mysqld-exporter.cnf --web.listen-address=127.0.0.1:9104
|
||||
Restart=on-failure
|
||||
RestartSec=3s
|
||||
NoNewPrivileges=true
|
||||
PrivateTmp=true
|
||||
PrivateDevices=true
|
||||
ProtectSystem=strict
|
||||
ProtectHome=true
|
||||
ProtectKernelTunables=true
|
||||
ProtectKernelModules=true
|
||||
ProtectControlGroups=true
|
||||
RestrictSUIDSGID=true
|
||||
LockPersonality=true
|
||||
CapabilityBoundingSet=
|
||||
RestrictAddressFamilies=AF_UNIX AF_INET AF_INET6
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
@@ -0,0 +1,30 @@
|
||||
[Unit]
|
||||
Description=LisgloSIPS Node Exporter
|
||||
After=network-online.target lisglosips-exporter-firewall.service
|
||||
Wants=network-online.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
User=prometheus
|
||||
Group=prometheus
|
||||
EnvironmentFile=/etc/default/lisglosips-node-exporter
|
||||
ExecStart=/usr/bin/prometheus-node-exporter $ARGS
|
||||
Restart=on-failure
|
||||
RestartSec=3s
|
||||
NoNewPrivileges=true
|
||||
PrivateTmp=true
|
||||
ProtectSystem=strict
|
||||
ProtectHome=true
|
||||
ProtectKernelTunables=true
|
||||
ProtectKernelModules=true
|
||||
ProtectControlGroups=true
|
||||
RestrictSUIDSGID=true
|
||||
LockPersonality=true
|
||||
CapabilityBoundingSet=
|
||||
RestrictAddressFamilies=AF_UNIX AF_INET AF_INET6
|
||||
IPAddressDeny=any
|
||||
IPAddressAllow=127.0.0.0/8
|
||||
IPAddressAllow=100.90.90.91/32
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
@@ -0,0 +1,29 @@
|
||||
[Unit]
|
||||
Description=LisgloSIPS PostgreSQL Exporter
|
||||
After=postgresql.service
|
||||
Requires=postgresql.service
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
User=prometheus
|
||||
Group=prometheus
|
||||
EnvironmentFile=/etc/lisglosips/secrets/postgres-exporter.env
|
||||
ExecStart=/usr/bin/prometheus-postgres-exporter --web.listen-address=127.0.0.1:9187
|
||||
Restart=on-failure
|
||||
RestartSec=3s
|
||||
NoNewPrivileges=true
|
||||
PrivateTmp=true
|
||||
PrivateDevices=true
|
||||
ProtectSystem=strict
|
||||
ProtectHome=true
|
||||
ProtectKernelTunables=true
|
||||
ProtectKernelModules=true
|
||||
ProtectControlGroups=true
|
||||
RestrictSUIDSGID=true
|
||||
LockPersonality=true
|
||||
CapabilityBoundingSet=
|
||||
RestrictAddressFamilies=AF_UNIX AF_INET AF_INET6
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
[Unit]
|
||||
Description=LisgloSIPS Prometheus
|
||||
After=network-online.target
|
||||
Wants=network-online.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
User=prometheus
|
||||
Group=prometheus
|
||||
EnvironmentFile=/etc/default/lisglosips-prometheus
|
||||
ExecStart=/usr/bin/prometheus $ARGS
|
||||
ExecReload=/bin/kill -HUP $MAINPID
|
||||
Restart=on-failure
|
||||
RestartSec=3s
|
||||
TimeoutStopSec=30s
|
||||
UMask=0027
|
||||
NoNewPrivileges=true
|
||||
PrivateTmp=true
|
||||
PrivateDevices=true
|
||||
ProtectSystem=full
|
||||
ProtectHome=true
|
||||
ProtectKernelTunables=true
|
||||
ProtectKernelModules=true
|
||||
ProtectControlGroups=true
|
||||
RestrictSUIDSGID=true
|
||||
LockPersonality=true
|
||||
CapabilityBoundingSet=
|
||||
RestrictAddressFamilies=AF_UNIX AF_INET AF_INET6
|
||||
ReadWritePaths=/data/prometheus
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
[Unit]
|
||||
Description=LisgloSIPS Redis Exporter
|
||||
After=redis-server.service
|
||||
Requires=redis-server.service
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
User=redis-exporter
|
||||
Group=redis-exporter
|
||||
EnvironmentFile=/etc/lisglosips/secrets/redis-exporter.env
|
||||
ExecStart=/usr/local/bin/redis_exporter
|
||||
Restart=on-failure
|
||||
RestartSec=3s
|
||||
NoNewPrivileges=true
|
||||
PrivateTmp=true
|
||||
PrivateDevices=true
|
||||
ProtectSystem=strict
|
||||
ProtectHome=true
|
||||
ProtectKernelTunables=true
|
||||
ProtectKernelModules=true
|
||||
ProtectControlGroups=true
|
||||
RestrictSUIDSGID=true
|
||||
LockPersonality=true
|
||||
CapabilityBoundingSet=
|
||||
RestrictAddressFamilies=AF_UNIX AF_INET AF_INET6
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
@@ -0,0 +1,18 @@
|
||||
POSTGRESQL_VERSION=16.14
|
||||
PROMETHEUS_VERSION=2.45.3
|
||||
NODE_EXPORTER_VERSION=1.7.0
|
||||
MYSQLD_EXPORTER_VERSION=0.15.0
|
||||
POSTGRES_EXPORTER_VERSION=0.15.0
|
||||
REDIS_EXPORTER_VERSION=1.86.0
|
||||
GRAFANA_VERSION=13.0.2
|
||||
GO_BOOTSTRAP_VERSION=1.22.2
|
||||
GO_BUILD_TOOLCHAIN_VERSION=1.25.11
|
||||
HEPLIFY_SERVER_SOURCE=github.com/sipcapture/heplify-server@v1.60.2-0.20260512101233-c74dc3d216ac
|
||||
HEPLIFY_SERVER_SHA256=feab75e8a970190ee41952bae74ce453188f6d7ea973017cbec1f5b75cc2b9c5
|
||||
HOMER_APP_SOURCE=github.com/sipcapture/homer-app@v0.0.0-20251021161517-9b1336352aa0
|
||||
HOMER_APP_SHA256=240436d1e666db8ca2173679adda9649037c6d5502182f96b25562b7cd757e94
|
||||
REDIS_EXPORTER_SHA256=5eda3529bf231a3c841d8261895bc09d2f750a3684c8a224edae71f4f80484db
|
||||
DOCKER_VERSION=29.1.3
|
||||
DOCKER_COMPOSE_VERSION=2.40.3
|
||||
DOCKER_RUNTIME_STATUS=installed-disabled
|
||||
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
#!/bin/bash
|
||||
set -euo pipefail
|
||||
|
||||
health_url="${LISGLOSIPS_HEALTH_URL:-http://127.0.0.1:3000/api/v2/health/ready}"
|
||||
services=(
|
||||
mysql
|
||||
redis-server
|
||||
nginx
|
||||
lisglosips@api
|
||||
lisglosips@cdr-worker
|
||||
lisglosips@recording-worker
|
||||
heplify-server
|
||||
lisglosips-prometheus.service
|
||||
grafana-server
|
||||
)
|
||||
|
||||
echo "== release =="
|
||||
readlink -f /opt/lisglosips/current
|
||||
test -L /opt/lisglosips/current
|
||||
test -d "$(readlink -f /opt/lisglosips/current)"
|
||||
|
||||
echo "== services =="
|
||||
for service in "${services[@]}"; do
|
||||
state="$(systemctl is-active "$service" || true)"
|
||||
printf '%s\t%s\n' "$service" "$state"
|
||||
test "$state" = active
|
||||
done
|
||||
|
||||
echo "== api =="
|
||||
curl --max-time 5 --fail --silent "$health_url"
|
||||
echo
|
||||
|
||||
echo "== nginx =="
|
||||
nginx -t
|
||||
|
||||
echo "== backups =="
|
||||
find /data/backups/mysql -mindepth 1 -maxdepth 1 -type d | sort | tail -1
|
||||
find /data/backups/redis -mindepth 1 -maxdepth 1 -type d | sort | tail -1
|
||||
|
||||
echo "preflight ok"
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
#!/bin/bash
|
||||
set -euo pipefail
|
||||
|
||||
if [ "$#" -ne 1 ]; then
|
||||
echo "usage: $0 /opt/lisglosips/releases/<release-id>" >&2
|
||||
exit 64
|
||||
fi
|
||||
|
||||
target="$1"
|
||||
case "$target" in
|
||||
/opt/lisglosips/releases/*) ;;
|
||||
*)
|
||||
echo "target must be under /opt/lisglosips/releases" >&2
|
||||
exit 64
|
||||
;;
|
||||
esac
|
||||
|
||||
target="$(readlink -f "$target")"
|
||||
test -d "$target"
|
||||
test -f "$target/package.json"
|
||||
|
||||
previous="$(readlink -f /opt/lisglosips/current)"
|
||||
echo "previous=$previous"
|
||||
echo "target=$target"
|
||||
|
||||
ln -sfn "$target" /opt/lisglosips/current
|
||||
systemctl restart lisglosips@api lisglosips@cdr-worker lisglosips@recording-worker
|
||||
nginx -t
|
||||
systemctl reload nginx
|
||||
curl --max-time 10 --fail --silent http://127.0.0.1:3000/api/v2/health/ready
|
||||
echo
|
||||
echo "rollback ok"
|
||||
Reference in New Issue
Block a user