feat: wire rmaps sync server to pull secrets from Infisical at startup

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Jeff Emmett 2026-02-23 20:06:42 -08:00
parent ee692ae2dc
commit d6ac72e6cf
3 changed files with 95 additions and 2 deletions

View File

@ -15,6 +15,10 @@ RUN npm ci --only=production
# Copy server code and fix ownership
COPY --chown=nodejs:nodejs server.js verify-token.js ./
# Copy and set up Infisical entrypoint
COPY --chown=nodejs:nodejs entrypoint.sh ./
RUN chmod +x entrypoint.sh
# Create data directory for persistence and set ownership
RUN mkdir -p /app/data && chown -R nodejs:nodejs /app
@ -22,4 +26,5 @@ USER nodejs
EXPOSE 3001
ENTRYPOINT ["/app/entrypoint.sh"]
CMD ["node", "server.js"]

View File

@ -5,9 +5,15 @@ services:
restart: unless-stopped
environment:
- PORT=3001
# VAPID keys for push notifications
# Infisical secret injection
- INFISICAL_CLIENT_ID=${INFISICAL_CLIENT_ID}
- INFISICAL_CLIENT_SECRET=${INFISICAL_CLIENT_SECRET}
- INFISICAL_PROJECT_SLUG=rmaps
- INFISICAL_ENV=prod
- INFISICAL_URL=http://infisical:8080
# VAPID keys for push notifications (injected by Infisical at runtime)
- VAPID_PUBLIC_KEY=BNWACJudUOeHEZKEFB-0Wz086nHYsWzj12LqQ7lsUNT38ThtNUoZTJYEH9lttQitCROE2G3Ob71ZUww47yvCDbk
- VAPID_PRIVATE_KEY=${VAPID_PRIVATE_KEY:-x3yCse1Q4rbZ1XLgnJ1KpSuRlw2ccHDW0fMcKtQ1qcw}
- VAPID_PRIVATE_KEY=${VAPID_PRIVATE_KEY}
- VAPID_SUBJECT=mailto:push@rmaps.online
# Automatic location request interval (ms) - 0 to disable
- LOCATION_REQUEST_INTERVAL=60000

82
sync-server/entrypoint.sh Executable file
View File

@ -0,0 +1,82 @@
#!/bin/sh
# Infisical secret injection entrypoint
# Fetches secrets from Infisical API and injects them as env vars before starting the app.
# Required env vars: INFISICAL_CLIENT_ID, INFISICAL_CLIENT_SECRET
# Optional: INFISICAL_PROJECT_SLUG (default: rmaps), INFISICAL_ENV (default: prod),
# INFISICAL_URL (default: http://infisical:8080)
set -e
INFISICAL_URL="${INFISICAL_URL:-http://infisical:8080}"
INFISICAL_ENV="${INFISICAL_ENV:-prod}"
INFISICAL_PROJECT_SLUG="${INFISICAL_PROJECT_SLUG:-rmaps}"
if [ -z "$INFISICAL_CLIENT_ID" ] || [ -z "$INFISICAL_CLIENT_SECRET" ]; then
echo "[infisical] No credentials set, starting without secret injection"
exec "$@"
fi
echo "[infisical] Fetching secrets from ${INFISICAL_PROJECT_SLUG}/${INFISICAL_ENV}..."
# Use Node.js (already in the image) for reliable JSON parsing and HTTP calls
EXPORTS=$(node -e "
const http = require('http');
const https = require('https');
const url = new URL(process.env.INFISICAL_URL);
const client = url.protocol === 'https:' ? https : http;
const post = (path, body) => new Promise((resolve, reject) => {
const data = JSON.stringify(body);
const req = client.request({ hostname: url.hostname, port: url.port, path, method: 'POST',
headers: { 'Content-Type': 'application/json', 'Content-Length': data.length }
}, res => { let d = ''; res.on('data', c => d += c); res.on('end', () => resolve(JSON.parse(d))); });
req.on('error', reject);
req.end(data);
});
const get = (path, token) => new Promise((resolve, reject) => {
const req = client.request({ hostname: url.hostname, port: url.port, path, method: 'GET',
headers: { 'Authorization': 'Bearer ' + token }
}, res => { let d = ''; res.on('data', c => d += c); res.on('end', () => resolve(JSON.parse(d))); });
req.on('error', reject);
req.end();
});
(async () => {
try {
const auth = await post('/api/v1/auth/universal-auth/login', {
clientId: process.env.INFISICAL_CLIENT_ID,
clientSecret: process.env.INFISICAL_CLIENT_SECRET
});
if (!auth.accessToken) { console.error('[infisical] Auth failed'); process.exit(1); }
const slug = process.env.INFISICAL_PROJECT_SLUG;
const env = process.env.INFISICAL_ENV;
const secrets = await get('/api/v3/secrets/raw?workspaceSlug=' + slug + '&environment=' + env + '&secretPath=/&recursive=true', auth.accessToken);
if (!secrets.secrets) { console.error('[infisical] No secrets returned'); process.exit(1); }
// Output as shell-safe export statements
for (const s of secrets.secrets) {
// Single-quote the value to prevent shell expansion, escape existing single quotes
const escaped = s.secretValue.replace(/'/g, \"'\\\\''\" );
console.log('export ' + s.secretKey + \"='\" + escaped + \"'\");
}
} catch (e) { console.error('[infisical] Error:', e.message); process.exit(1); }
})();
" 2>&1) || {
echo "[infisical] WARNING: Failed to fetch secrets, starting with existing env vars"
exec "$@"
}
# Check if we got export statements or error messages
if echo "$EXPORTS" | grep -q "^export "; then
COUNT=$(echo "$EXPORTS" | grep -c "^export ")
eval "$EXPORTS"
echo "[infisical] Injected ${COUNT} secrets"
else
echo "[infisical] WARNING: $EXPORTS"
echo "[infisical] Starting with existing env vars"
fi
exec "$@"