How to Secure a Self-Hosted Specify 7 Environment

:gear: This guide provides a practical security baseline for institutions managing an internet-accessible production deployment. It covers host security, TLS/HTTPS, Django security settings, and ongoing operations.

Recommended Background Reading


Step 1: Harden the Host

Make sure to configure a firewall before exposing Specify to the public internet.

  1. Apply all outstanding operating system updates and reboot if required.
  2. Disable password authentication for SSH in favor of SSH keys.
  3. Configure a local firewall to strictly limit inbound traffic.
  4. Enable automatic security patches for the host OS where possible.
  5. Ensure system time is synchronized using NTP (chrony or systemd-timesyncd).

Example: Host Firewall Setup (Ubuntu with UFW)

sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw allow 22/tcp
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw enable
sudo ufw status


Step 2: Choose and Prepare Your Compose Scenario

Pick the deployment strategy that matches your institutional scale. See this guide for access to these deployment schemes:

  • all-in-one: Best for simpler, single-instance initial deployments.
  • specifycloud: Production-style multi-instance template approach.

Action Items

  1. Copy or edit your scenario files (docker-compose.yml, specify.env, and your nginx.conf templates).
  2. Ensure you use persistent, named volumes for both your database data and runtime web assets.
  3. Remove any unused service definitions from your composition files.

[!warning] Database Security
Never publish your MariaDB container network port (3306) to the host’s public network interface. Keep your database isolated on the internal container network.


Step 3: Configure DNS and TLS (HTTPS)

3.1 DNS Entry

Create an A/AAAA or CNAME record with your DNS provider pointing your public hostname to your server’s public IP address:
specify.yourinstitution.eduYour Public IP

3.2 TLS Certificates

  • Public Deployments: Use automated Let’s Encrypt certificates via certbot.
  • Internal/Institutional Deployments: Use certificates generated by your organization’s internal PKI.

3.3 Reverse Proxy Configuration

Your reverse proxy (nginx) handles SSL/TLS termination, routes static assets, and proxies application traffic. It should be configured to force HTTPS and inject security headers.

You can review the default nginx.conf files in the docker-compositions repository once you have been granted access.


Step 4: Lock Down App-Level Host and Origin Settings

Modify the environment variables inside your specify.env to lock down Django’s security middleware:

ALLOWED_HOSTS=specify.yourinstitution.edu
CSRF_TRUSTED_ORIGINS=https://specify.yourinstitution.edu

[!danger] Avoid Wildcards
Do not leave permissive defaults like *, http://*, or https://* in a production environment. Doing so exposes your application to HTTP Host Header injections and Cross-Site Request Forgery (CSRF) vulnerabilities.

Additional Verification Checks

  • Confirm that DEBUG=False is set in production.
  • Generate a unique, long, random string for your application SECRET_KEY.

Step 5: Protect Data Services & Internal Networking

  1. Network Isolation: Keep MariaDB, Celery, and Redis/Memcached on dedicated internal networks within your container runtime.
  2. Encrypted Backups: Encrypt all database dumps and asset backups both during transit and while at rest.

Step 6: Start and Validate the Deployment

Launching the Stack

Docker Compose:

cd docker/all-in-one
docker compose up -d
docker compose ps
docker compose logs -f

Podman Compose:

cd podman/all-in-one
podman-compose up -d
podman-compose ps
podman-compose logs -f

Validation Checklist

  • Navigating to http://specify.yourinstitution.edu correctly redirects to https://specify.yourinstitution.edu.
  • The web browser displays a valid, trusted TLS certificate padlock icon with no warnings.
  • Core workflows (logging in, running queries, viewing records and attachments) operate cleanly over HTTPS.
  • An external port scan confirms port 3306 is completely inaccessible from outside the host.

Ongoing Security Operations

Set an operational maintenance routine from day one:

  • Patching Schedules: Apply host operating system security updates monthly. Track tagged Specify 7 repository releases on GitHub or in Announcements & News for software and security updates.
  • Backup Management: Automate daily database and asset snapshots.
  • Access Control: Enforce the principle of least privilege for server access and use Multi-Factor Authentication (MFA) wherever possible.

Common Mistakes to Avoid

  • :x: Leaving ALLOWED_HOSTS=* active in production.
  • :x: Mapping container database ports to public host interfaces.
  • :x: Relying on unencrypted HTTP connections for authentication.
  • :x: Neglecting to set up automated certificate renewals.

Questions?

If your deployment runs into issues or encounters environmental errors, please grab your container logs and create a thread in our Get Help category.

Be sure to include:

  1. Your deployment framework (Docker or Podman).
  2. Your sanitized configuration files (docker-compose.yml, proxy files).
  3. The exact error trace from your application container logs.