feat: wire Postiz spaces to pull secrets from Infisical at runtime
- Template uses entrypoint-wrapper.sh to inject secrets at container start - Only INFISICAL_* credentials + POSTGRES_PASSWORD in .env (3 values) - All other secrets (JWT, EMAIL_PASS, OAuth, social API keys) from Infisical - Generator produces minimal .env templates per space - Added infisical_slug to spaces.yml for each Postiz project - Added missing EMAIL_PASS + POSTGRES_PASSWORD to Infisical projects Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
dc95494ab8
commit
8ef5c678c2
|
|
@ -1,9 +1,10 @@
|
|||
---
|
||||
id: TASK-6
|
||||
title: Remove plaintext .env files from server
|
||||
status: To Do
|
||||
status: In Progress
|
||||
assignee: []
|
||||
created_date: '2026-02-25 05:02'
|
||||
updated_date: '2026-02-25 05:04'
|
||||
labels:
|
||||
- security
|
||||
- infisical
|
||||
|
|
|
|||
|
|
@ -4,17 +4,31 @@
|
|||
# Generated by generate.sh from spaces.yml — DO NOT EDIT DIRECTLY.
|
||||
# To modify, edit spaces.yml and re-run: ./generate.sh
|
||||
# Primary: {{PRIMARY_DOMAIN}} | Fallback: {{FALLBACK_DOMAIN}}
|
||||
#
|
||||
# Secrets (JWT_SECRET, EMAIL_PASS, social API keys, OAuth secrets) are
|
||||
# injected at runtime by the Infisical entrypoint wrapper.
|
||||
# Only INFISICAL_* credentials and POSTGRES_PASSWORD go in .env.
|
||||
|
||||
services:
|
||||
postiz-{{SPACE_SLUG}}:
|
||||
image: {{POSTIZ_IMAGE}}
|
||||
container_name: postiz-{{SPACE_SLUG}}
|
||||
restart: unless-stopped
|
||||
# Infisical wrapper injects secrets then runs original entrypoint
|
||||
entrypoint: ["/infisical-entrypoint.sh"]
|
||||
command: ["docker-entrypoint.sh", "sh", "-c", "nginx && pnpm run pm2"]
|
||||
environment:
|
||||
# --- Infisical (credentials from .env) ---
|
||||
INFISICAL_CLIENT_ID: '${INFISICAL_CLIENT_ID}'
|
||||
INFISICAL_CLIENT_SECRET: '${INFISICAL_CLIENT_SECRET}'
|
||||
INFISICAL_PROJECT_SLUG: '{{INFISICAL_SLUG}}'
|
||||
INFISICAL_ENV: 'prod'
|
||||
INFISICAL_URL: 'http://infisical:8080'
|
||||
|
||||
# --- App Config (non-secret, stays in compose) ---
|
||||
MAIN_URL: 'https://{{PRIMARY_DOMAIN}}'
|
||||
FRONTEND_URL: 'https://{{PRIMARY_DOMAIN}}'
|
||||
NEXT_PUBLIC_BACKEND_URL: 'https://{{PRIMARY_DOMAIN}}/api'
|
||||
JWT_SECRET: '${JWT_SECRET}'
|
||||
DATABASE_URL: 'postgresql://postiz:${POSTGRES_PASSWORD}@postiz-{{SPACE_SLUG}}-postgres:5432/postiz'
|
||||
REDIS_URL: 'redis://postiz-{{SPACE_SLUG}}-redis:6379'
|
||||
BACKEND_INTERNAL_URL: 'http://localhost:3000'
|
||||
|
|
@ -22,7 +36,7 @@ services:
|
|||
IS_GENERAL: '{{IS_GENERAL}}'
|
||||
DISABLE_REGISTRATION: '{{DISABLE_REG}}'
|
||||
|
||||
# Pocket ID OAuth
|
||||
# Pocket ID OAuth (config only — client_id/secret from Infisical)
|
||||
{{OAUTH_BLOCK}}
|
||||
|
||||
# Storage
|
||||
|
|
@ -30,29 +44,7 @@ services:
|
|||
UPLOAD_DIRECTORY: '{{UPLOAD_DIR}}'
|
||||
NEXT_PUBLIC_UPLOAD_DIRECTORY: '{{UPLOAD_DIR}}'
|
||||
|
||||
# Social Media API Settings (from .env or Infisical)
|
||||
X_API_KEY: '${X_API_KEY:-}'
|
||||
X_API_SECRET: '${X_API_SECRET:-}'
|
||||
LINKEDIN_CLIENT_ID: '${LINKEDIN_CLIENT_ID:-}'
|
||||
LINKEDIN_CLIENT_SECRET: '${LINKEDIN_CLIENT_SECRET:-}'
|
||||
REDDIT_CLIENT_ID: '${REDDIT_CLIENT_ID:-}'
|
||||
REDDIT_CLIENT_SECRET: '${REDDIT_CLIENT_SECRET:-}'
|
||||
THREADS_APP_ID: '${THREADS_APP_ID:-}'
|
||||
THREADS_APP_SECRET: '${THREADS_APP_SECRET:-}'
|
||||
FACEBOOK_APP_ID: '${FACEBOOK_APP_ID:-}'
|
||||
FACEBOOK_APP_SECRET: '${FACEBOOK_APP_SECRET:-}'
|
||||
YOUTUBE_CLIENT_ID: '${YOUTUBE_CLIENT_ID:-}'
|
||||
YOUTUBE_CLIENT_SECRET: '${YOUTUBE_CLIENT_SECRET:-}'
|
||||
TIKTOK_CLIENT_ID: '${TIKTOK_CLIENT_ID:-}'
|
||||
TIKTOK_CLIENT_SECRET: '${TIKTOK_CLIENT_SECRET:-}'
|
||||
DISCORD_CLIENT_ID: '${DISCORD_CLIENT_ID:-}'
|
||||
DISCORD_CLIENT_SECRET: '${DISCORD_CLIENT_SECRET:-}'
|
||||
DISCORD_BOT_TOKEN_ID: '${DISCORD_BOT_TOKEN_ID:-}'
|
||||
MASTODON_URL: '${MASTODON_URL:-https://mastodon.social}'
|
||||
MASTODON_CLIENT_ID: '${MASTODON_CLIENT_ID:-}'
|
||||
MASTODON_CLIENT_SECRET: '${MASTODON_CLIENT_SECRET:-}'
|
||||
|
||||
# Email
|
||||
# Email (config only — EMAIL_PASS from Infisical)
|
||||
EMAIL_PROVIDER: '{{EMAIL_PROVIDER}}'
|
||||
EMAIL_FROM_NAME: '{{EMAIL_FROM_NAME}}'
|
||||
EMAIL_FROM_ADDRESS: '{{EMAIL_FROM}}'
|
||||
|
|
@ -60,17 +52,19 @@ services:
|
|||
EMAIL_PORT: '{{EMAIL_PORT}}'
|
||||
EMAIL_SECURE: '{{EMAIL_SECURE}}'
|
||||
EMAIL_USER: '{{EMAIL_USER}}'
|
||||
EMAIL_PASS: '${EMAIL_PASS}'
|
||||
NODE_TLS_REJECT_UNAUTHORIZED: '0'
|
||||
|
||||
# AI
|
||||
OPENAI_API_KEY: '${OPENAI_API_KEY:-}'
|
||||
|
||||
# Misc
|
||||
NX_ADD_PLUGINS: false
|
||||
API_LIMIT: {{API_LIMIT}}
|
||||
|
||||
# --- Secrets injected by Infisical at runtime ---
|
||||
# JWT_SECRET, EMAIL_PASS, OPENAI_API_KEY,
|
||||
# POSTIZ_OAUTH_CLIENT_ID, POSTIZ_OAUTH_CLIENT_SECRET,
|
||||
# X_API_KEY, X_API_SECRET, LINKEDIN_CLIENT_ID, etc.
|
||||
|
||||
volumes:
|
||||
- /opt/infisical/entrypoint-wrapper.sh:/infisical-entrypoint.sh:ro
|
||||
- postiz-{{SPACE_SLUG}}-config:/config/
|
||||
- postiz-{{SPACE_SLUG}}-uploads:/uploads/
|
||||
labels:
|
||||
|
|
|
|||
31
generate.sh
31
generate.sh
|
|
@ -79,11 +79,12 @@ get_space_override() {
|
|||
generate_space() {
|
||||
local space="$1"
|
||||
|
||||
local primary_domain fallback_domain email_from slug
|
||||
local primary_domain fallback_domain email_from slug infisical_slug
|
||||
primary_domain=$(get_space "$space" "primary_domain")
|
||||
fallback_domain=$(get_space "$space" "fallback_domain")
|
||||
email_from=$(get_space "$space" "email_from")
|
||||
slug=$(get_space "$space" "slug")
|
||||
infisical_slug=$(get_space "$space" "infisical_slug")
|
||||
|
||||
if [[ -z "$primary_domain" || "$primary_domain" == "null" ]]; then
|
||||
echo -e "${RED} Error: spaces.${space}.primary_domain is required${NC}"
|
||||
|
|
@ -95,6 +96,11 @@ generate_space() {
|
|||
slug="$space"
|
||||
fi
|
||||
|
||||
# Infisical slug defaults to postiz-<space>
|
||||
if [[ -z "$infisical_slug" || "$infisical_slug" == "null" ]]; then
|
||||
infisical_slug="postiz-${space}"
|
||||
fi
|
||||
|
||||
# Read defaults (with per-space overrides)
|
||||
local postiz_image postiz_port postgres_image redis_image temporal_image temporal_pg_image
|
||||
local email_provider email_from_name email_host email_port email_secure email_user
|
||||
|
|
@ -141,7 +147,7 @@ generate_space() {
|
|||
# Build dynamic blocks
|
||||
# -----------------------------------------------------------------------
|
||||
|
||||
# OAuth block
|
||||
# OAuth block (config only — client_id/secret injected by Infisical)
|
||||
local oauth_block
|
||||
if [[ "$use_oauth" == "true" ]]; then
|
||||
oauth_block=" POSTIZ_GENERIC_OAUTH: 'true'
|
||||
|
|
@ -151,9 +157,8 @@ generate_space() {
|
|||
POSTIZ_OAUTH_AUTH_URL: '${oauth_auth_url}'
|
||||
POSTIZ_OAUTH_TOKEN_URL: '${oauth_token_url}'
|
||||
POSTIZ_OAUTH_USERINFO_URL: '${oauth_userinfo_url}'
|
||||
POSTIZ_OAUTH_CLIENT_ID: '\${POSTIZ_OAUTH_CLIENT_ID}'
|
||||
POSTIZ_OAUTH_CLIENT_SECRET: '\${POSTIZ_OAUTH_CLIENT_SECRET}'
|
||||
POSTIZ_OAUTH_SCOPE: 'openid profile email'"
|
||||
POSTIZ_OAUTH_SCOPE: 'openid profile email'
|
||||
# POSTIZ_OAUTH_CLIENT_ID + CLIENT_SECRET from Infisical"
|
||||
else
|
||||
oauth_block=" # OAuth disabled for this space"
|
||||
fi
|
||||
|
|
@ -209,6 +214,7 @@ generate_space() {
|
|||
-e "s|{{DISABLE_REG}}|${disable_reg}|g" \
|
||||
-e "s|{{IS_GENERAL}}|${is_general}|g" \
|
||||
-e "s|{{API_LIMIT}}|${api_limit}|g" \
|
||||
-e "s|{{INFISICAL_SLUG}}|${infisical_slug}|g" \
|
||||
"$TEMPLATE" > "$outfile"
|
||||
|
||||
# Replace multi-line blocks (sed can't do these well, use temp files)
|
||||
|
|
@ -235,6 +241,21 @@ generate_space() {
|
|||
|
||||
echo -e "${GREEN} ✓ ${outfile}${NC}"
|
||||
|
||||
# Generate minimal .env template (only Infisical creds + postgres password)
|
||||
local envfile="${OUTDIR}/env.space-${space}"
|
||||
cat > "$envfile" <<EOF
|
||||
# Minimal secrets for ${space} — all other secrets from Infisical
|
||||
# Copy to server alongside docker-compose.space-${space}.yml
|
||||
|
||||
# Infisical Machine Identity (shared across spaces)
|
||||
INFISICAL_CLIENT_ID=
|
||||
INFISICAL_CLIENT_SECRET=
|
||||
|
||||
# Postgres password (needed by postgres container directly)
|
||||
POSTGRES_PASSWORD=
|
||||
EOF
|
||||
echo -e "${GREEN} ✓ ${envfile}${NC}"
|
||||
|
||||
# Collect domains for tunnel/dns generation
|
||||
ALL_PRIMARY_DOMAINS+=("$primary_domain")
|
||||
ALL_FALLBACK_DOMAINS+=("$fallback_domain")
|
||||
|
|
|
|||
|
|
@ -67,6 +67,7 @@ spaces:
|
|||
fallback_domain: socials.valleyofthecommons.com
|
||||
email_from: noreply@rmail.online
|
||||
infisical_project_id: a76d3d2c-205e-4356-a8ad-2a2c19724d8c
|
||||
infisical_slug: postiz-crypto-commons
|
||||
postiz:
|
||||
disable_registration: true
|
||||
email_from_name: Crypto Commons
|
||||
|
|
@ -79,6 +80,7 @@ spaces:
|
|||
fallback_domain: socials.p2pfoundation.net
|
||||
email_from: noreply@rmail.online
|
||||
infisical_project_id: ea4b3b47-12d5-40d1-9a80-30bc48cdba7a
|
||||
infisical_slug: postiz-p2pfoundation
|
||||
postiz:
|
||||
email_from_name: P2P Foundation Socials
|
||||
services:
|
||||
|
|
@ -90,6 +92,7 @@ spaces:
|
|||
fallback_domain: socials.bondingcurve.tech
|
||||
email_from: noreply@rmail.online
|
||||
infisical_project_id: 255cb27d-e8d3-459d-af2c-faff55b46d9f
|
||||
infisical_slug: postiz-bondingcurve
|
||||
postiz:
|
||||
email_from_name: Bonding Curve Research
|
||||
services:
|
||||
|
|
|
|||
Loading…
Reference in New Issue