Security Azure

Fides on Ubuntu 24.04 on Azure User Guide

| Product: Fides on Ubuntu 24.04 LTS on Azure

Overview

Fides is an open source privacy engineering platform. It is the system of record for how personal data moves through your organisation: you describe your systems and datasets against a shared privacy taxonomy, and Fides turns that description into working machinery. It maps and inventories the personal data you hold, stores and enforces the consent and communication preferences your customers have given you, and automates the fulfilment of data subject access and erasure requests by executing them against the systems the data actually lives in. Everything is available through a documented REST API as well as the web interface, so privacy controls can be wired into your software delivery process rather than bolted on afterwards.

The cloudimg image installs Fides 2.86.2 into a dedicated Python 3.13 virtual environment, backs it with a local PostgreSQL 16 database, adds Redis 7 as the cache and task broker, runs the web application under uvicorn bound to loopback with a separate celery worker for privacy request execution, and fronts the whole thing with nginx. Fides holds several distinct secrets and the upstream project publishes a working sample value for nearly all of them, so this image generates every one of them uniquely on the first boot of each VM: the encryption key that protects stored connection credentials and access tokens, the identifier and secret of the root API client, the administrator username and password, the token signing secret, the database password, the cache password, and the TLS certificate. None of them is baked into the image, and each published default is actively refused by the running server. Backed by 24/7 cloudimg support.

What is included:

  • Fides 2.86.2 installed in a dedicated Python 3.13 virtual environment and run by uvicorn as the fides systemd service
  • The Fides Admin UI and REST API on :80, and on :443 with a per VM self signed certificate
  • A celery worker (fides-worker) that executes privacy requests off the web request path
  • PostgreSQL 16 bound to loopback, holding the data map, taxonomy, consent and privacy request tables
  • Redis 7 bound to loopback, serving as both the Fides cache and the celery broker
  • The fideslang privacy taxonomy seeded and ready to use, with 85 data categories, 56 data uses and 15 data subjects
  • A per VM application encryption key protecting stored connection secrets, generated on first boot and never shared between VMs
  • A per VM administrator account, root OAuth client id and secret, token signing secret, database password and cache password
  • Fides' own anonymous readiness endpoints for Azure Load Balancer probes, reporting the database and cache state rather than merely that a socket is open
  • fides.service, fides-worker.service, nginx.service, postgresql.service and redis-server.service as systemd units, enabled and active
  • 24/7 cloudimg support

Prerequisites

An active Azure subscription, an SSH key pair, and a VNet plus subnet in the target region. Standard_B2s (2 vCPU / 4 GiB RAM) is the recommended size and runs the full stack comfortably. NSG inbound: allow 22/tcp from your management network, 443/tcp for the Admin UI and API, and 80/tcp if you want plain HTTP as well. Because Fides holds a map of where your personal data lives, treat port exposure conservatively and restrict access to the networks that need to reach it.

Step 1 - Deploy from the Azure Marketplace

Sign in to the Azure Portal, choose Create a resource, search the Marketplace for Fides by cloudimg, and select Create. On Basics pick your subscription, resource group, region and size; under Administrator account choose SSH public key and paste your key; under Inbound port rules allow SSH (22) and HTTPS (443). Then Review + create -> Create.

Step 2 - Deploy from the Azure CLI

az vm create \
  --resource-group <your-rg> \
  --name fides \
  --image <marketplace-image-urn> \
  --size Standard_B2s \
  --admin-username azureuser \
  --ssh-key-values ~/.ssh/id_rsa.pub \
  --public-ip-sku Standard

az vm open-port --resource-group <your-rg> --name fides --port 443 --priority 1001
az vm open-port --resource-group <your-rg> --name fides --port 80 --priority 1002

Step 3 - Confirm the services are running

SSH to the VM as azureuser and confirm the whole stack is up. Fides splits readiness across three anonymous endpoints, and this matters: a bare GET /health reports only the web server and returns "cache": "skipped", so it stays green even when both datastores are unreachable. Ask for the cache explicitly, and check the database on its own route.

systemctl is-active fides fides-worker nginx postgresql redis-server
curl -s "http://127.0.0.1/health?include_cache=true"; echo
curl -s http://127.0.0.1/health/database; echo

Expected output:

active
active
active
active
active
{"webserver":"healthy","version":"2.86.2","cache":"healthy"}
{"database":"healthy","pools":{"api_sync_primary":{"health":"healthy","prewarming":null},"api_sync_readonly":{"health":"skipped","prewarming":null},"api_async_primary":{"health":"healthy","prewarming":null},"api_async_readonly":{"health":"skipped","prewarming":null}},"async_readonly_pool_prewarmed":null,"database_revision":"b034cd68950d"}

The fides, fides-worker, nginx, postgresql and redis-server services all reporting active, the readiness endpoint reporting the web server and cache healthy, the database endpoint reporting healthy with its connection pools, the worker endpoint reporting workers enabled, and the Fides CLI reporting version 2.86.2

The first boot takes a few extra seconds compared with later boots, because that is when the per VM secrets are generated and the application database is created. If the endpoints are not yet answering, wait a moment and try again.

Use /health/workers to confirm the celery worker registered with the broker. This is worth checking because Fides ships with task_always_eager enabled by default, under which privacy requests run inside the web process and a worker would sit idle. This image disables it, so the worker genuinely does the work.

curl -s http://127.0.0.1/health/workers; echo

Expected output (the worker name contains your VM's own hostname):

{"workers_enabled":true,"workers":["celery@<your-vm-hostname>"],"queue_counts":{"fidesops.messaging":0,"fides.privacy_preferences":0,"fides.privacy_request_exports":0,"fides.privacy_request_ingestion":0,"fides.dsr":0,"fidesplus.consent_webhooks":0,"fidesplus.discovery_monitors_detection":0,"fidesplus.discovery_monitors_classification":0,"fidesplus.discovery_monitors_promotion":0,"fidesplus.bulk_consent_import":0,"fides":0}}

The queue names prefixed fidesplus belong to Ethyca's commercial edition. They are declared by the shared task routing and stay empty on this open source image; only the fides queues carry work here.

Step 4 - Retrieve the per VM credentials

Every VM generates its own credentials on first boot and writes them to a root only file. Nothing is shared between deployments and there is no default password to change.

sudo stat -c '%a %U:%G %n' /root/fides-credentials.txt
sudo grep -E '^FIDES_ROOT_USERNAME|^FIDES_OAUTH_CLIENT_ID' /root/fides-credentials.txt

Expected output (your client id will differ, it is generated per VM):

600 root:root /root/fides-credentials.txt
FIDES_ROOT_USERNAME=cloudimgadmin
FIDES_OAUTH_CLIENT_ID=f4dccfb4a240558e11bf4509

Read the whole file, including the administrator password and the OAuth client secret, with:

sudo cat /root/fides-credentials.txt

The per VM credentials file showing the generated administrator username, a masked password, the OAuth root client id and a masked client secret, with 0600 root only permissions on both the credentials file and the Fides configuration, alongside the credential verification helper reporting login, token, OAuth and published default rejection all passing

The file records the URL Fides resolved for itself. Azure's instance metadata service returns an empty value for a Standard SKU public IP, so a VM can only discover its own private address. If you are connecting from outside the VNet, browse to the VM's public IP address or DNS name, which you will find on the VM's Overview blade in the Azure Portal.

You can re-run the credential check at any time. It proves the per VM credentials authenticate end to end and that each of the published upstream sample credentials is refused.

sudo bash /usr/local/sbin/fides-verify-login.sh

Expected output:

FIDES_LOGIN_VERIFIED user=cloudimgadmin health=ok token=ok oauth=ok defaults=rejected

Step 5 - Sign in to the Admin UI

Open https://<your-vm-public-ip>/ in a browser. The image ships a self signed certificate generated for this VM, so your browser will warn on first visit until you install a certificate of your own (see Step 10). Sign in with the username and password from Step 4.

The Fides Admin UI sign in page, showing the Fides wordmark above a sign in card with username and password fields

After signing in you land on the home view, which reports how many systems Fides is currently managing and offers the three things you will do most: add systems to your data map, review the systems already there, and process privacy requests.

The Fides Admin UI home view after signing in, reporting one system currently under management, with cards for adding systems, viewing systems and reviewing privacy requests, and the left navigation showing Overview, Data inventory, Privacy requests, Core configuration and Settings

Step 6 - Browse the privacy taxonomy

Everything in Fides is described against fideslang, a shared privacy taxonomy of data categories (what kind of personal data this is), data uses (why you process it) and data subjects (whose data it is). The image seeds the full default taxonomy on first boot, so it is ready to use immediately. In the Admin UI open Core configuration -> Taxonomy.

The Fides Admin UI taxonomy view, showing the data categories tree rendered as a graph with User Data branching into Contact Data, Biometric Data, Demographic Data, Device Data, Financial Data, Government ID, Health and Medical Data, Location Data, Payment Data, Privacy Preferences, Sensor Data and Social Data, each expanding into specific categories such as User Contact Email and National Identification Number

The same taxonomy is available over the API. Authenticate first. The login endpoint expects the password base64 encoded, which is fiddly to get right by hand, so the image ships fides-token: it reads this VM's own credentials and prints a fresh access token, keeping the password off your shell history. Every API step below starts by minting a token that way, so each one is independently runnable.

TOKEN=$(sudo fides-token)
echo "access token length: ${#TOKEN}"
curl -sS -H "Authorization: Bearer $TOKEN" http://127.0.0.1/api/v1/data_category \
  | python3 -c 'import json,sys; print(len(json.load(sys.stdin)), "data categories")'

Expected output (the token is a JWE, so its length varies):

access token length: 4412
85 data categories

The token is a bearer credential in its own right. Treat it like a password and do not paste it into shared logs.

The authenticated REST API round trip, showing the access token length, the protected user endpoint returning HTTP 401 without a token and HTTP 200 with one, and the upstream published sample login being refused

Step 7 - Add a system to your data map

A system in Fides is anything that processes personal data: an application you wrote, a database, or a third party service. Each system carries one or more privacy declarations saying what data it handles, why, and about whom. That is the data map.

Register a system:

TOKEN=$(sudo fides-token)
curl -sS -o /tmp/fides-system.json -w 'HTTP %{http_code}\n' \
  -X POST http://127.0.0.1/api/v1/system \
  -H "Authorization: Bearer $TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{
    "fides_key": "customer_crm",
    "name": "Customer CRM",
    "system_type": "Service",
    "description": "Customer relationship management platform holding contact details and purchase history.",
    "privacy_declarations": [
      {
        "name": "Contact customers about their orders",
        "data_categories": ["user.contact"],
        "data_use": "essential.service",
        "data_subjects": ["customer"]
      }
    ]
  }'

Expected output:

HTTP 201

Now read the data map back:

TOKEN=$(sudo fides-token)
curl -sS -H "Authorization: Bearer $TOKEN" http://127.0.0.1/api/v1/system | python3 -c '
import json, sys
for s in json.load(sys.stdin):
    print("system:", s["fides_key"], "-", s["name"])
    for d in (s.get("privacy_declarations") or []):
        print("  declares:", d["name"])
        print("    data use:       ", d["data_use"])
        print("    data categories:", ", ".join(d["data_categories"]))
        print("    data subjects:  ", ", ".join(d["data_subjects"]))
'

Expected output:

system: customer_crm - Customer CRM
  declares: Contact customers about their orders
    data use:        essential.service
    data categories: user.contact
    data subjects:   customer

The privacy taxonomy and data map over the API, showing 85 data categories, 56 data uses and 15 data subjects seeded, and the customer_crm system with its privacy declaration listing the data use, data categories and data subjects

The system appears immediately in Data inventory -> System inventory in the Admin UI, with its data uses and description.

The Fides Admin UI system inventory, showing the Customer CRM system in the table with its Essential data use tag, description and Edit and Delete actions

Step 8 - Machine to machine access with the root OAuth client

Interactive login is for people. For scripts and CI, use the root OAuth client, whose id and secret are also generated per VM and recorded in the credentials file. It exchanges client credentials for a bearer token.

CLIENT_ID=$(sudo sed -n 's/^FIDES_OAUTH_CLIENT_ID=//p' /root/fides-credentials.txt)
CLIENT_SECRET=$(sudo sed -n 's/^FIDES_OAUTH_CLIENT_SECRET=//p' /root/fides-credentials.txt)
curl -sS -X POST http://127.0.0.1/api/v1/oauth/token \
  -d "client_id=$CLIENT_ID&client_secret=$CLIENT_SECRET&grant_type=client_credentials" \
  | python3 -c 'import json,sys; d=json.load(sys.stdin); print("token type:", d["token_type"], "| expires in:", d["expires_in"], "seconds")'

Expected output:

token type: Bearer | expires in: 691200 seconds

Step 9 - Review privacy requests

Privacy requests are Fides' term for data subject access and erasure requests. Open Privacy requests -> Request manager in the Admin UI to see the queue. A fresh VM has none until you connect a system and receive one. The image sets require_manual_request_approval, so requests wait for an operator to approve them before the worker executes anything against your connected systems, which is the safe default for a new deployment.

The queue is also available over the API:

TOKEN=$(sudo fides-token)
curl -sS -H "Authorization: Bearer $TOKEN" http://127.0.0.1/api/v1/privacy-request \
  | python3 -c 'import json,sys; d=json.load(sys.stdin); print("privacy requests:", d.get("total", 0))'

Expected output on a fresh VM:

privacy requests: 0

Step 10 - Install your own TLS certificate

The image generates a self signed certificate for the VM on first boot so that HTTPS works immediately. For production, replace it with a certificate for your own domain. Point a DNS record at the VM, then either drop your certificate and key in place of the generated pair and reload nginx, or use certbot.

sudo cp /path/to/fullchain.pem /etc/ssl/fides/fides.crt
sudo cp /path/to/privkey.pem   /etc/ssl/fides/fides.key
sudo chmod 0600 /etc/ssl/fides/fides.key
sudo nginx -t && sudo systemctl reload nginx

If you change the address Fides is reached on, add it to the allowed browser origins in /etc/fides/fides.toml under cors_origins, then restart the application:

sudo systemctl restart fides fides-worker

Server Components

Component Version Notes
Fides 2.86.2 Installed from the official ethyca-fides distribution into /opt/fides/venv
Python 3.13.15 Required by Fides 2.79.0 and later; Ubuntu 24.04's own interpreter is 3.12
PostgreSQL 16.14 Application database, bound to loopback
Redis 7.0.15 Cache and celery broker, bound to loopback, password protected
nginx 1.24.0 Reverse proxy on :80 and :443
Operating system Ubuntu 24.04 LTS Fully patched at build time, unattended security updates enabled

Filesystem Layout

Mount Size Purpose
/ 29 GB Root filesystem
/boot 881 MB Operating system kernel files
/boot/efi 105 MB UEFI boot partition (Gen2 Hyper V)
/mnt 16 GB Azure temporary resource disk

Key directories:

Path Purpose
/opt/fides/venv The Python 3.13 virtual environment holding Fides and its dependencies
/opt/fides/data The SaaS connector templates Fides loads at startup
/etc/fides/fides.toml The Fides configuration, holding every per VM secret (0600 fides:fides)
/etc/ssl/fides/ The per VM TLS certificate and private key
/root/fides-credentials.txt The per VM credentials, readable only by root (0600 root:root)
/var/lib/postgresql/16/main The PostgreSQL data directory

Managing the services

systemctl status fides --no-pager | head -5

Restart, stop and start the application and its worker together, because they share the same configuration:

sudo systemctl restart fides fides-worker
sudo systemctl stop    fides fides-worker
sudo systemctl start   fides fides-worker

Follow the logs:

sudo journalctl -u fides -f
sudo journalctl -u fides-worker -f

Run the Fides CLI as the service account with the appliance configuration already applied:

sudo fides-cli --version

Expected output:

fides, version 2.86.2

Scripts and Log Files

Path Purpose
/usr/local/sbin/fides-firstboot.sh Generates every per VM secret and the application database on first boot
/usr/local/sbin/fides-write-config.py Renders /etc/fides/fides.toml; refuses to write any published upstream default
/usr/local/sbin/fides-verify-login.sh Proves the per VM credentials work and the published defaults are refused
/usr/local/sbin/fides-cli Runs the Fides CLI as the fides service account
/usr/local/sbin/fides-token Prints a fresh API access token for this VM's root account
/var/lib/cloudimg/fides-firstboot.done First boot sentinel; its presence stops firstboot re-running
/var/lib/cloudimg/fides-bootstrap.ready Bootstrap marker the application units are gated on
journalctl -u fides Web application log
journalctl -u fides-worker Privacy request worker log
/var/log/nginx/access.log nginx access log

On Startup

On the very first boot, fides-firstboot.service runs before the application. It resolves the VM's address, generates the application encryption key, the OAuth root client id and secret, the administrator password, the token signing secret, the database password and the cache password, rotates the PostgreSQL role, creates the application database from the schema template shipped in the image, generates the TLS keypair, writes /etc/fides/fides.toml and /root/fides-credentials.txt, and only then creates /var/lib/cloudimg/fides-bootstrap.ready.

That last step matters. fides.service and fides-worker.service both carry ConditionPathExists on that marker, so on a first boot they physically cannot start until the secrets exist. The image ships without the marker, which is what guarantees the application never comes up against a half initialised database or an upstream default credential. On every subsequent boot the sentinel is present, firstboot exits immediately, and the services start normally.

Troubleshooting

The Admin UI returns 502. nginx is up but the application is not yet listening. Check systemctl status fides and sudo journalctl -u fides -n 50. On a first boot the application waits for firstboot to finish; give it a minute.

systemctl status fides shows the unit as inactive with a condition failure. The bootstrap marker is missing, which means firstboot has not completed. Check sudo journalctl -u fides-firstboot -n 50. Once firstboot succeeds the marker appears and the units start.

/health says healthy but the UI does not work. A bare GET /health reports only the web server. Ask for the cache with curl -s "http://127.0.0.1/health?include_cache=true" and check the database with curl -s http://127.0.0.1/health/database.

Login is refused. Confirm you are using the username and password from /root/fides-credentials.txt on this VM. Credentials are unique per VM and none of the upstream sample credentials will work by design. sudo bash /usr/local/sbin/fides-verify-login.sh will tell you which part of the chain is failing.

Privacy requests stay queued. Confirm the worker is registered with curl -s http://127.0.0.1/health/workers. If workers_enabled is false, the celery configuration in /etc/fides/fides.toml has been changed. Requests also wait for approval by design, so approve them in Privacy requests -> Request manager.

The browser warns about the certificate. The image ships a self signed certificate generated for the VM. Install your own certificate as described in Step 10.

Security Recommendations

  • Restrict the NSG so that 22/tcp, 80/tcp and 443/tcp are reachable only from the networks that need them. Fides holds a map of where your personal data lives, so treat it as sensitive infrastructure.
  • Replace the self signed certificate with one issued for your own domain and prefer 443/tcp over 80/tcp.
  • Back up /root/fides-credentials.txt somewhere safe, then consider removing it from the VM. The application reads its secrets from /etc/fides/fides.toml, not from that file.
  • Keep PostgreSQL and Redis on loopback. Neither needs to be reachable from the network for this appliance to work.
  • Create named user accounts in the Admin UI for your team rather than sharing the root administrator, and reserve the root OAuth client for automation.
  • Leave unattended security updates enabled so the operating system keeps receiving patches.

Support

cloudimg provides 24/7 support for this image. Contact support@cloudimg.co.uk with your Azure subscription id and the VM name. For questions about Fides itself, see the Ethyca documentation and the Fides GitHub repository.