Security Considerations for Using `df.http()` in pg_durable and the `http-allow-azure-domains` Cargo Feature
Using df.http() in pg_durable requires explicit role-based EXECUTE privileges, and the http-allow-azure-domains Cargo feature mitigates SSRF by blocking private IPs, bare IP addresses, and redirects while allowing only approved Azure service domains.
The pg_durable extension enables durable workflows inside PostgreSQL, including the ability to make outbound HTTP calls via df.http(). Because this opens the database to the network, the Microsoft pg_durable repository implements a defense-in-depth security model combining compile-time feature flags, runtime privilege checks, and SSRF hardening. Understanding the security considerations for using df.http() and the http-allow-azure-domains feature is critical before granting production access.
Why df.http() Is Opt-In by Default
On a fresh installation, the EXECUTE privilege on df.http() is revoked from PUBLIC in src/lib.rs (lines 540–560). This means no role can invoke the function until a database administrator explicitly grants access. The permission model is further specified in docs/spec-http-function-permissions.md.
A DBA can grant permission using standard SQL:
GRANT EXECUTE ON FUNCTION df.http(text, text, text, jsonb, integer) TO analytics_user;
Alternatively, the helper function df.grant_usage(role, include_http => true) streamlines the process by granting both standard workflow privileges and HTTP access (see src/lib.rs, lines 570–590). This opt-in design prevents accidental exposure of outbound network capabilities to untrusted database users.
Compile-Time Feature Flags for HTTP Access
The available Cargo features determine which URLs df.http() may reach at build time. The behavior is controlled in src/ssrf.rs (lines 6–14), and human-readable guidance is documented in docs/http-security.md.
- No feature flag — All outbound HTTP is blocked at both DSL-time and execution-time.
http-allow-azure-domains— The SSRF IP blocklist is active, bare IPs are blocked, redirects are blocked, and only Azure data-plane suffixes are allowed.http-allow-test-domains— Extends the Azure feature by also allowingapi.github.comandhttpbingo.orgfor end-to-end testing. This implieshttp-allow-azure-domains.http-allow-all— Disables all SSRF protections for development use only.
Production deployments should build with http-allow-azure-domains and never enable http-allow-all.
SSRF Protections in src/ssrf.rs
The src/ssrf.rs file implements the core allow-list and SSRF defense logic that underpins the http-allow-azure-domains feature.
IP Blocklist for Private and Loopback Addresses
The check_blocked_ip function (lines 76–94) rejects resolved addresses that fall into restricted ranges:
- IPv4 private ranges:
10.0.0.0/8,172.16.0.0/12,192.168.0.0/16 - IPv4 link-local:
169.254.0.0/16 - IPv4 loopback:
127.0.0.0/8and reserved0.0.0.0/8 - IPv6 loopback, unspecified, link-local, and unique-local (
fc00::/7)
When http-allow-all is not enabled, this blocklist is enforced via #[cfg(not(feature = "http-allow-all"))].
Endpoint Allow-List Validation
When compiled with http-allow-azure-domains or http-allow-test-domains, the validate_url_allowlist function (lines 61–69) performs strict hostname checks:
- Extract the hostname using
extract_host, which stops at/,?, or#to prevent query or fragment bypasses. - Reject any bare IP address (IPv4 or IPv6).
- Match Azure suffixes against the
AZURE_DOMAIN_SUFFIXESlist (lines 46–67). Every suffix begins with a leading dot, ensuring at least one sub-domain label is present. - If
http-allow-test-domainsis enabled, also allow exact matches fromTEST_EXACT_DOMAINS.
If validation fails, the request aborts with an error such as:
Blocked: 'evil.com' is not in the allowed endpoint list. Only requests to approved Azure service domains are permitted.
DNS-Rebinding Protection
Even allowed hostnames could resolve to internal addresses through DNS rebinding. The SsrfSafeResolver wraps the standard DNS resolver and filters results through check_blocked_ip. If all resolved addresses are blocked, the request fails with a message prefixed by "Blocked:" and containing the word "restricted" (lines 99–108).
The helper is_ssrf_block_error recognizes these messages, allowing consistent audit logging when workflows are denied.
Runtime Privilege Enforcement
Before executing any network call, the executor in src/activities/execute_http.rs verifies that the submitting role holds EXECUTE privilege on df.http() via has_function_privilege.
If the check fails, the workflow aborts immediately with:
Blocked: role 'alice' does not have EXECUTE privilege on df.http().
This design ensures that both compile-time domain restrictions and runtime role-based permissions must be satisfied before any outbound traffic is generated.
Practical Deployment and Code Examples
Granting HTTP Privileges to a Role
Use explicit GRANT statements or the helper function to enable HTTP access for a specific role:
-- Direct grant
GRANT EXECUTE ON FUNCTION df.http(text, text, text, jsonb, integer) TO analytics_user;
-- Helper function that also grants other workflow privileges
SELECT df.grant_usage('analytics_user', include_http => true);
These patterns are defined in src/lib.rs (lines 540–590).
Building with http-allow-azure-domains
Compile the extension with the Azure allow-list feature enabled:
cargo build --release --features http-allow-azure-domains
The official Dockerfile (lines 9–10) already includes this flag, making it the default for production container images.
Calling df.http() from a Workflow
Once privileges and features are configured, start a workflow that calls an allowed Azure endpoint:
SELECT df.start(
df.http('https://myaccount.blob.core.windows.net/container/blob', 'GET')
);
If the calling role lacks EXECUTE on df.http(), the workflow fails with a clear "Blocked" error before any network packet is sent.
Testing with the E2E Feature Flag
For CI pipelines or integration suites, enable the test domain feature:
cargo test --features http-allow-test-domains
This mode allows api.github.com and httpbingo.org in addition to Azure domains. The test suite in src/ssrf.rs covers edge cases including bare IPs, query/fragment bypasses, and Unicode homographs.
Summary
df.http()is opt-in by default becauseEXECUTEis revoked fromPUBLICinsrc/lib.rsduring installation.- The
http-allow-azure-domainsCargo feature restricts outbound traffic to approved Azure data-plane domains and implies SSRF protections. - SSRF defenses include a private IP blocklist (
check_blocked_ip), hostname allow-list validation (validate_url_allowlist), and DNS-rebinding guards (SsrfSafeResolver). - Runtime enforcement via
has_function_privilegeinsrc/activities/execute_http.rsensures only explicitly authorized roles can trigger HTTP requests. - Production builds should always use
http-allow-azure-domains;http-allow-allis strictly for development.
Frequently Asked Questions
What happens if I compile pg_durable without any HTTP feature flags?
All outbound HTTP is blocked. df.http() will fail at both DSL-time and execution-time because the SSRF allow-list code in src/ssrf.rs requires an explicit feature to permit any domain.
Can a malicious workflow bypass the Azure domain allow-list using redirects?
No. The http-allow-azure-domains feature explicitly blocks redirects. Even if an initial request targets an allowed Azure domain, following a redirect to a non-allowed endpoint is prevented by the SSRF logic in src/ssrf.rs.
How do I audit which roles can execute df.http()?
Query the PostgreSQL system catalogs to inspect function privileges, or review the audit checklist in SECURITY.md. Because privileges are revoked from PUBLIC by default, any role with access must have been explicitly granted EXECUTE, making the audit surface small and deterministic.
Is http-allow-all safe for local development?
It is intended only for development. This feature disables the IP blocklist, bare-IP rejection, and domain allow-list, removing all SSRF protections. It should never be enabled in production builds or container images.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →