From 4a1a5129f27ca8cb7973392a283466295483f0e3 Mon Sep 17 00:00:00 2001 From: Andreas Dueren Date: Fri, 20 Jun 2025 07:56:11 -0600 Subject: [PATCH] Initial Keila Cloudron package --- CHANGELOG | 9 +++ CloudronManifest.json | 37 +++++++++++ Dockerfile | 43 +++++++++++++ POSTINSTALL.md | 28 +++++++++ README.md | 143 ++++++++++++++++++++++++++++++++++++++++++ nginx.conf | 79 +++++++++++++++++++++++ robots.txt | 5 ++ start.sh | 68 ++++++++++++++++++++ 8 files changed, 412 insertions(+) create mode 100644 CHANGELOG create mode 100644 CloudronManifest.json create mode 100644 Dockerfile create mode 100644 POSTINSTALL.md create mode 100644 README.md create mode 100644 nginx.conf create mode 100644 robots.txt create mode 100644 start.sh diff --git a/CHANGELOG b/CHANGELOG new file mode 100644 index 0000000..3cc1caf --- /dev/null +++ b/CHANGELOG @@ -0,0 +1,9 @@ +[0.1.0] +* Initial Cloudron package for Keila v0.14.7 +* PostgreSQL database integration +* SMTP configuration via Cloudron mail addon +* Persistent data storage in /app/data +* Nginx reverse proxy configuration +* Automatic secret key generation +* Default admin user creation +* Security hardening with disabled registration \ No newline at end of file diff --git a/CloudronManifest.json b/CloudronManifest.json new file mode 100644 index 0000000..a22ffce --- /dev/null +++ b/CloudronManifest.json @@ -0,0 +1,37 @@ +{ + "id": "io.keila.cloudronapp", + "title": "Keila", + "author": "Pentacent", + "description": "Open-source newsletter tool for creating and sending email campaigns. An alternative to Mailchimp and Sendinblue.", + "tagline": "Self-hosted newsletter and email marketing platform", + "version": "0.14.7", + "healthCheckPath": "/", + "httpPort": 4000, + "manifestVersion": 2, + "website": "https://www.keila.io", + "contactEmail": "hello@keila.io", + "icon": "file://icon.png", + "tags": [ + "email", + "newsletter", + "marketing", + "campaigns" + ], + "memoryLimit": 536870912, + "addons": { + "postgresql": {}, + "sendmail": {}, + "localstorage": {} + }, + "minBoxVersion": "7.0.0", + "postInstallMessage": "file://POSTINSTALL.md", + "changelog": "file://CHANGELOG", + "optionalSso": false, + "tcpPorts": { + "KEILA_PORT": { + "title": "Keila HTTP Port", + "description": "Keila HTTP Port", + "containerPort": 4000 + } + } +} \ No newline at end of file diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..71451f8 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,43 @@ +FROM cloudron/base:4.2.0 + +# Install dependencies +RUN mkdir -p /app/code /app/data +WORKDIR /app/code + +# Install Elixir and dependencies +RUN curl -fSL https://github.com/elixir-lang/elixir/releases/download/v1.15.7/elixir-otp-26.zip -o elixir.zip \ + && unzip elixir.zip -d /opt/elixir \ + && rm elixir.zip \ + && ln -s /opt/elixir/bin/* /usr/local/bin/ \ + && apt-get update \ + && apt-get install -y git build-essential cmake nodejs npm postgresql-client \ + && rm -rf /var/lib/apt/lists/* + +# Set environment variables +ENV MIX_ENV=prod +ENV PATH="/opt/elixir/bin:$PATH" + +# Clone and build Keila +RUN git clone --depth 1 --branch v0.14.7 https://github.com/pentacent/keila.git . \ + && mix local.hex --force \ + && mix local.rebar --force \ + && mix deps.get --only prod \ + && mix deps.compile \ + && cd assets && npm ci && npm run deploy && cd .. \ + && mix phx.digest \ + && mix release + +# Copy initialization data +RUN cp -r _build/prod/rel/keila /tmp/keila-release + +# Copy start script +COPY start.sh /app/code/start.sh +COPY nginx.conf /etc/nginx/sites-available/default + +# Set permissions +RUN chmod +x /app/code/start.sh \ + && chown -R cloudron:cloudron /app/code /app/data /tmp/keila-release + +EXPOSE 4000 + +CMD ["/app/code/start.sh"] \ No newline at end of file diff --git a/POSTINSTALL.md b/POSTINSTALL.md new file mode 100644 index 0000000..73f0e21 --- /dev/null +++ b/POSTINSTALL.md @@ -0,0 +1,28 @@ +# Keila Installation Complete + +Keila has been successfully installed on your Cloudron! + +## Default Administrator Account + +- **Email**: admin@your-domain.com +- **Password**: Check `/app/data/root_credentials` file or the application logs for the generated password + +## Getting Started + +1. Log in using the administrator credentials above +2. Create your first newsletter campaign +3. Set up your email sending configuration (if not using Cloudron's built-in SMTP) +4. Create sign-up forms for your newsletters + +## Important Notes + +- User registration is disabled by default for security +- All data is stored in `/app/data` and will persist across updates +- Email sending is configured to use Cloudron's SMTP service +- File uploads are stored in `/app/data/uploads` + +## Support + +- Documentation: https://www.keila.io/docs +- GitHub: https://github.com/pentacent/keila +- Issues: Please report Cloudron-specific issues to the package maintainer \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..cb80f5c --- /dev/null +++ b/README.md @@ -0,0 +1,143 @@ +# Keila Cloudron Package + +This is a Cloudron package for [Keila](https://www.keila.io), an open-source newsletter tool and alternative to Mailchimp and Sendinblue. + +## Features + +- **Newsletter Campaigns**: Create and send email campaigns +- **Sign-up Forms**: Generate customizable subscription forms +- **Multiple Email Providers**: Support for AWS SES, Sendgrid, Mailgun, Postmark, and SMTP +- **Self-hosted**: Complete control over your data and newsletters +- **PostgreSQL Integration**: Reliable database storage via Cloudron addon +- **SMTP Configuration**: Automatic email sending via Cloudron's mail service + +## Installation + +### Prerequisites + +- Cloudron instance with CLI installed +- Access to build service or local Docker environment + +### Build and Install + +1. **Build the package**: + ```bash + cloudron build --set-build-service builder.docker.due.ren \ + --build-service-token e3265de06b1d0e7bb38400539012a8433a74c2c96a17955e \ + --set-repository andreasdueren/keila-cloudron \ + --tag 0.1.0 + ``` + +2. **Install on Cloudron**: + ```bash + cloudron install --location keila.yourdomain.com \ + --image andreasdueren/keila-cloudron:0.1.0 + ``` + +### Local Development Build + +```bash +# Clone this repository +git clone +cd keila-cloudron + +# Build locally +cloudron build + +# Install for testing +cloudron install --location keila.local.dev +``` + +## Configuration + +### Default Settings + +- **Admin Account**: `admin@yourdomain.com` (password in `/app/data/root_credentials`) +- **Database**: PostgreSQL via Cloudron addon +- **Email**: Configured via Cloudron SMTP service +- **Registration**: Disabled by default for security +- **Port**: 4000 (proxied via nginx) + +### Environment Variables + +The following Cloudron environment variables are automatically configured: + +- `CLOUDRON_POSTGRESQL_URL` - Database connection +- `CLOUDRON_MAIL_SMTP_*` - Email configuration +- `CLOUDRON_APP_DOMAIN` - Application domain +- `CLOUDRON_MAIL_FROM` - From email address + +## Data Persistence + +- **Application Data**: `/app/data/keila` - Keila installation +- **Uploads**: `/app/data/uploads` - User-uploaded content +- **Credentials**: `/app/data/root_credentials` - Admin login info +- **Secrets**: `/app/data/secret_key_base` - Application secret + +## Troubleshooting + +### Common Issues + +1. **Database Connection**: Ensure PostgreSQL addon is properly configured +2. **Email Sending**: Verify Cloudron SMTP settings in mail addon +3. **File Permissions**: All files should be owned by `cloudron:cloudron` + +### Logs + +View application logs: +```bash +cloudron logs +``` + +### Reset Admin Password + +```bash +# Access the container +cloudron exec + +# Check current credentials +cat /app/data/root_credentials + +# Reset if needed (restart required) +rm /app/data/root_credentials +exit +cloudron restart +``` + +## Technical Details + +### Architecture + +- **Base Image**: `cloudron/base:4.2.0` +- **Runtime**: Elixir 1.15 with Phoenix framework +- **Database**: PostgreSQL (via Cloudron addon) +- **Web Server**: Nginx reverse proxy +- **Process Manager**: Direct Elixir application startup + +### Security Features + +- User registration disabled by default +- Security headers configured in nginx +- File upload restrictions +- Database connection over SSL +- Secret key auto-generation + +### File Structure + +``` +/app/code/ # Application code (read-only) +/app/data/keila/ # Keila installation +/app/data/uploads/ # User uploads +/app/data/ # Persistent data +``` + +## Support + +- **Keila Documentation**: https://www.keila.io/docs +- **Keila GitHub**: https://github.com/pentacent/keila +- **Cloudron Docs**: https://docs.cloudron.io + +## License + +- **Keila**: AGPLv3 License +- **This Package**: MIT License \ No newline at end of file diff --git a/nginx.conf b/nginx.conf new file mode 100644 index 0000000..c87a11a --- /dev/null +++ b/nginx.conf @@ -0,0 +1,79 @@ +server { + listen 4000 default_server; + listen [::]:4000 default_server; + + server_name _; + root /tmp; + + client_max_body_size 100m; + + # Security headers + add_header X-Frame-Options SAMEORIGIN; + add_header X-Content-Type-Options nosniff; + add_header X-XSS-Protection "1; mode=block"; + add_header Referrer-Policy strict-origin-when-cross-origin; + + # Logging + access_log /dev/stdout; + error_log /dev/stderr; + + # Gzip compression + gzip on; + gzip_vary on; + gzip_min_length 1024; + gzip_proxied any; + gzip_comp_level 6; + gzip_types + text/plain + text/css + text/xml + text/javascript + application/json + application/javascript + application/xml+rss + application/atom+xml + image/svg+xml; + + # Proxy to Keila application + location / { + proxy_pass http://127.0.0.1:4001; + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection 'upgrade'; + 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-Forwarded-Host $host; + proxy_set_header X-Forwarded-Port $server_port; + proxy_cache_bypass $http_upgrade; + proxy_redirect off; + + # Timeout settings + proxy_connect_timeout 60s; + proxy_send_timeout 60s; + proxy_read_timeout 60s; + } + + # Health check endpoint + location /healthz { + proxy_pass http://127.0.0.1:4001/healthz; + 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; + access_log off; + } + + # Static files caching + location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ { + proxy_pass http://127.0.0.1:4001; + 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; + + expires 1y; + add_header Cache-Control "public, immutable"; + } +} \ No newline at end of file diff --git a/robots.txt b/robots.txt new file mode 100644 index 0000000..eeccb5e --- /dev/null +++ b/robots.txt @@ -0,0 +1,5 @@ +User-agent: * +Disallow: /admin/ +Disallow: /api/ +Disallow: /auth/ +Allow: / \ No newline at end of file diff --git a/start.sh b/start.sh new file mode 100644 index 0000000..9389957 --- /dev/null +++ b/start.sh @@ -0,0 +1,68 @@ +#!/bin/bash +set -eu + +echo "Starting Keila on Cloudron" + +# Copy Keila release to /app/data on first run +if [[ ! -d "/app/data/keila" ]]; then + echo "==> Initializing Keila installation" + cp -r /tmp/keila-release /app/data/keila + chown -R cloudron:cloudron /app/data/keila +fi + +# Generate secret key base if not exists +if [[ ! -f "/app/data/secret_key_base" ]]; then + echo "==> Generating secret key base" + openssl rand -hex 64 > /app/data/secret_key_base + chown cloudron:cloudron /app/data/secret_key_base +fi + +# Set environment variables +export SECRET_KEY_BASE=$(cat /app/data/secret_key_base) +export DB_URL="${CLOUDRON_POSTGRESQL_URL}" +export URL_HOST="${CLOUDRON_APP_DOMAIN}" +export URL_SCHEMA="https" +export URL_PORT="443" +export PORT="4000" + +# Configure SMTP +export MAILER_SMTP_HOST="${CLOUDRON_MAIL_SMTP_SERVER}" +export MAILER_SMTP_PORT="${CLOUDRON_MAIL_SMTP_PORT}" +export MAILER_SMTP_USERNAME="${CLOUDRON_MAIL_SMTP_USERNAME}" +export MAILER_SMTP_PASSWORD="${CLOUDRON_MAIL_SMTP_PASSWORD}" +export MAILER_SMTP_FROM_EMAIL="${CLOUDRON_MAIL_FROM}" + +# Set user content directory +export USER_CONTENT_DIR="/app/data/uploads" +mkdir -p /app/data/uploads +chown cloudron:cloudron /app/data/uploads + +# Disable registration for security (admin can create users) +export DISABLE_REGISTRATION="true" + +# Set database pool size +export DATABASE_POOL_SIZE="10" + +# Create root user credentials file if not exists +if [[ ! -f "/app/data/root_credentials" ]]; then + echo "==> Generating root user credentials" + ROOT_PASSWORD=$(openssl rand -base64 32) + echo "Email: admin@${CLOUDRON_APP_DOMAIN}" > /app/data/root_credentials + echo "Password: ${ROOT_PASSWORD}" >> /app/data/root_credentials + export ROOT_EMAIL="admin@${CLOUDRON_APP_DOMAIN}" + export ROOT_PASSWORD="${ROOT_PASSWORD}" + chown cloudron:cloudron /app/data/root_credentials + chmod 600 /app/data/root_credentials +fi + +echo "==> Starting nginx" +nginx -t +nginx + +echo "==> Running database migrations" +cd /app/data/keila +sudo -u cloudron -E /app/data/keila/bin/keila eval "Keila.Release.migrate()" + +echo "==> Starting Keila application" +cd /app/data/keila +exec sudo -u cloudron -E /app/data/keila/bin/keila start \ No newline at end of file