{"id":40811629,"url":"https://github.com/croessner/pfxhttp","last_synced_at":"2026-01-21T21:18:18.168Z","repository":{"id":268693381,"uuid":"904847269","full_name":"croessner/pfxhttp","owner":"croessner","description":"Postfix-to-HTTP wrapper service","archived":false,"fork":false,"pushed_at":"2025-05-30T11:26:05.000Z","size":11325,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-05-30T15:30:51.832Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/croessner.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2024-12-17T17:00:39.000Z","updated_at":"2025-05-30T11:26:09.000Z","dependencies_parsed_at":"2025-01-13T11:26:59.932Z","dependency_job_id":"5bd182b6-b6d5-47d7-abde-b381868b891d","html_url":"https://github.com/croessner/pfxhttp","commit_stats":null,"previous_names":["croessner/pfxhttp"],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/croessner/pfxhttp","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/croessner%2Fpfxhttp","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/croessner%2Fpfxhttp/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/croessner%2Fpfxhttp/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/croessner%2Fpfxhttp/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/croessner","download_url":"https://codeload.github.com/croessner/pfxhttp/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/croessner%2Fpfxhttp/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28643328,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-21T18:04:35.752Z","status":"ssl_error","status_checked_at":"2026-01-21T18:03:55.054Z","response_time":86,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2026-01-21T21:18:17.398Z","updated_at":"2026-01-21T21:18:18.162Z","avatar_url":"https://github.com/croessner.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Pfxhttp – HTTP Proxy for Postfix\n\nPfxhttp is a lightweight HTTP proxy designed to integrate Postfix with external HTTP APIs for **socket maps** and **policy services**. This enables dynamic and flexible email workflows by connecting Postfix to modern APIs.\n\n# Table of contents\n\n\u003c!-- TOC --\u003e\n* [Pfxhttp – HTTP Proxy for Postfix](#pfxhttp--http-proxy-for-postfix)\n* [Table of contents](#table-of-contents)\n  * [Overview](#overview)\n  * [Getting Started](#getting-started)\n    * [Installation](#installation)\n      * [Prerequisites](#prerequisites)\n      * [Customizing the Build](#customizing-the-build)\n        * [Build Tags](#build-tags)\n      * [Verifying Your Configuration](#verifying-your-configuration)\n    * [Running as a System Service](#running-as-a-system-service)\n    * [Command-line Options](#command-line-options)\n  * [Configuration](#configuration)\n    * [Server Settings](#server-settings)\n    * [Response Cache](#response-cache)\n    * [JWT Authentication](#jwt-authentication)\n    * [HTTP Request/Response Compression](#http-requestresponse-compression)\n    * [Integrating with Postfix](#integrating-with-postfix)\n      * [Socket Maps](#socket-maps)\n      * [Policy Services](#policy-services)\n  * [Logging and Troubleshooting](#logging-and-troubleshooting)\n  * [Contributing](#contributing)\n  * [References](#references)\n\u003c!-- TOC --\u003e\n\n\n## Overview\n\nPfxhttp allows you to:\n\n- **Perform dynamic lookups** via socket maps, such as resolving virtual mailboxes or domains.\n- **Implement custom mail policy checks** through HTTP-based policy services.\n\nThe application is configured using a YAML file, specifying HTTP endpoints, the format of requests, and field mappings. It supports key Postfix features like query lookups and policy service hooks.\n\n## Getting Started\n\n### Installation\n\nPfxhttp is written in **Go**. It can be compiled with the following commands:\n\n```bash\nmake\nmake install\n```\n\n#### Prerequisites\n\n- Go 1.24 or later\n- For JWT support only:\n  - SQLite development libraries (libsqlite3-dev on Debian/Ubuntu, sqlite-devel on RHEL/CentOS)\n  - GCC or another C compiler for CGO\n\n#### Customizing the Build\n\nWhen building with JWT support, you may need to customize the SQLite library and include paths:\n\n```bash\n# For custom SQLite installation paths (only needed for JWT support)\nmake TAGS=jwt SQLITE_LIB_PATH=/path/to/sqlite/lib SQLITE_INCLUDE_PATH=/path/to/sqlite/include\n\n# On macOS with Homebrew (only needed for JWT support)\nmake TAGS=jwt SQLITE_LIB_PATH=/usr/local/opt/sqlite/lib SQLITE_INCLUDE_PATH=/usr/local/opt/sqlite/include\n```\n\nYou can also set these as environment variables when building with JWT support:\n\n```bash\nexport CGO_LDFLAGS=\"-L/path/to/sqlite/lib -lsqlite3\"\nexport CGO_CFLAGS=\"-I/path/to/sqlite/include\"\nmake TAGS=jwt\n```\n\n##### Build Tags\n\nPfxhttp supports optional features through build tags:\n\n- **jwt**: Enables JWT authentication support (requires SQLite)\n\nTo build with JWT support:\n```bash\nmake TAGS=jwt\n```\n\nTo build without JWT support (no SQLite dependency):\n```bash\nmake\n```\n\n#### Verifying Your Configuration\n\nIf you're building with JWT support, you can check your SQLite configuration with:\n\n```bash\nmake sqlite-config\n```\n\nAnd run the tests to ensure everything is working correctly:\n\n```bash\n# Run all tests\nmake test\n\n# For JWT builds only: Run the customsql package tests (SQLite-specific)\n# This is recommended for testing the SQLite functionality when using JWT\nmake test-customsql\n```\n\n\u003e **Note:** When testing JWT functionality with SQLite, always use the `test-customsql` target rather than trying to test individual files, as this ensures all dependencies are properly included.\n\nBy default, Pfxhttp and its associated man pages are installed in `/usr/local`.\n\nThe configuration is located in one of the following directories, based on priority:\n\n1. `/usr/local/etc/pfxhttp/`\n2. `/etc/pfxhttp/`\n3. `$HOME/.pfxhttp/`\n4. Current directory (`.`)\n\nThe expected configuration file name is `pfxhttp.yml`.\n\n### Running as a System Service\n\nPfxhttp is typically run as a **systemd** service. Below is an example unit file:\n\n```ini\n[Unit]\nDescription=PfxHTTP Postfix-to-HTTP server\nAfter=network.target\n\n[Service]\nType=simple\nRestart=always\nUser=pfxhttp\nGroup=pfxhttp\nEnvironmentFile=-/etc/default/pfxhttp\nExecStart=/usr/local/sbin/pfxhttp\nStandardOutput=journal\nStandardError=journal\nSyslogIdentifier=pfxhttp\nMemoryMax=50M\nCPUQuota=10%\n\nCapabilityBoundingSet=CAP_NET_BIND_SERVICE CAP_CHOWN\nPrivateTmp=true\nProtectSystem=full\nProtectHome=true\nNoNewPrivileges=true\nReadOnlyPaths=/etc\nProtectKernelModules=true\nMemoryDenyWriteExecute=true\nProtectControlGroups=true\nProtectKernelLogs=true\nProtectClock=true\nRestrictSUIDSGID=true\nProtectProc=invisible\nLimitNOFILE=1024\n#RestrictAddressFamilies=AF_INET AF_INET6\n\n[Install]\nWantedBy=multi-user.target\n```\n\nYou must create a user pfxhttp and a group pfxhttp before using this unit file!\n\nTo install and start the service:\n\n```bash\nsudo systemctl daemon-reload\nsudo systemctl enable pfxhttp\nsudo systemctl start pfxhttp\n```\n\n### Command-line Options\n\nPfxhttp provides the following command-line flags:\n\n- **--config**: Specifies the path to the configuration file. Overrides the default configuration file location.\n  ```bash\n  ./pfxhttp --config=/path/to/config.yml\n  ```\n\n- **--format**: Sets the logging format. Available options are `yaml`, `toml` or `json`.\n  ```bash\n  ./pfxhttp --format=json\n  ```\n\n\u003e Use these flags as needed to customize the behavior of the application during runtime.\n\n---\n\n## Configuration\n\nPfxhttp is configured through a YAML file named `pfxhttp.yml` (or a custom file specified with the `--config` and `--format` flags). The following are the main sections:\n\n### Server Settings\n\nThe `server` section contains global options, including:\n\n- **Listeners**: Define socket map and policy service listeners for Postfix integration.\n- **Logging**: Enable JSON-formatted logs and set verbosity (`debug`, `info`, or `error`).\n- **HTTP Client Options**: Configure connection limits, timeouts, and optional TLS settings.\n- **JWT Authentication**: Configure JWT authentication for HTTP requests with automatic token management.\n- **Response Cache**: Optional in-memory cache to serve responses when the backend is unavailable.\n\nBelow is a detailed example configuration for `pfxhttp.yml`:\n\n```yaml\nserver:\n  listen:\n    - kind: \"socket_map\"\n      name: \"demo_map\"\n      type: \"tcp\"\n      address: \"[::]\"\n      port: 23450\n\n    - kind: \"policy_service\"\n      name: \"example_policy\"\n      type: \"tcp\"\n      address: \"[::]\"\n      port: 23451\n\n  logging:\n    json: true\n    level: info\n\n  tls:\n    enabled: true\n    http_client_skip_verify: true\n\n  # Path to SQLite database for JWT token storage\n  jwt_db_path: \"/var/lib/pfxhttp/jwt.db\"\n\n  # Optional response cache to serve data during backend outages\n  response_cache:\n    enabled: true\n    ttl: 5m  # cache lifetime per entry\n\nsocket_maps:\n  demo_map:\n    target: \"https://127.0.0.1:9443/api/v1/custom/map\"\n    custom_headers:\n      - \"Authorization: Bearer \u003ctoken\u003e\"\n    payload: \u003e\n      {\n        \"key\": \"{{ .Key }}\"\n      }\n    status_code: 200\n    value_field: \"data.result\"\n    error_field: \"error\"\n    no_error_value: \"not-found\"\n\n  jwt_demo_map:\n    target: \"https://127.0.0.1:9443/api/v1/custom/map\"\n    jwt_auth:\n      enabled: true\n      token_endpoint: \"https://example.com/api/token\"\n      credentials:\n        username: \"foobar\"\n        password: \"secret\"\n    payload: \u003e\n      {\n        \"key\": \"{{ .Key }}\"\n      }\n    status_code: 200\n    value_field: \"data.result\"\n    error_field: \"error\"\n    no_error_value: \"not-found\"\n\npolicy_services:\n  example_policy:\n    target: \"https://127.0.0.1:9443/api/v1/custom/policy\"\n    custom_headers:\n      - \"Authorization: Bearer \u003ctoken\u003e\"\n    payload: \"{{ .Key }}\"\n    status_code: 200\n    value_field: \"policy.result\"\n    error_field: \"policy.error\"\n    no_error_value: \"OK\"\n\n  jwt_example_policy:\n    target: \"https://127.0.0.1:9443/api/v1/custom/policy\"\n    jwt_auth:\n      enabled: true\n      token_endpoint: \"https://example.com/api/token\"\n      credentials:\n        username: \"foobar\"\n        password: \"secret\"\n    payload: \"{{ .Key }}\"\n    status_code: 200\n    value_field: \"policy.result\"\n    error_field: \"policy.error\"\n    no_error_value: \"OK\"\n```\n\n**Important**: Postfix has a hardcoded socket map reply size limit of **100,000 bytes** (Postfix 3.9.1 or older).\n\n### Response Cache\n\nPfxhttp includes an optional in-memory response cache. It always forwards responses from your backend, but if the backend becomes unavailable, it can serve a previously cached response for a configurable time (TTL).\n\nBehavior:\n- On backend failure: if a valid cache entry exists for the same map/policy name and key, it is returned.\n- Cache population:\n  - Socket maps: cache only definitive successes (status \"OK\").\n  - Policy services: cache only definitive actions (anything other than empty).\n- Expiration: entries expire after the TTL.\n\nConfiguration (server section):\n```yaml\nserver:\n  response_cache:\n    enabled: true\n    ttl: 5m\n```\n\nNotes:\n- TTL must be between 1s and 168h (7 days).\n- Cache is in-memory and per-process; it is cleared on restart.\n- Keys are derived from the tuple (name, key). For policy services, the key is the JSON of the policy payload.\n\n### JWT Authentication\n\nPfxhttp supports JWT authentication for HTTP requests to the target endpoints. This allows you to securely authenticate with APIs that require JWT tokens. \n\n\u003e **Note:** JWT support is optional and requires building Pfxhttp with the `jwt` build tag (`make TAGS=jwt`). This feature depends on SQLite for token storage.\n\nThe JWT authentication feature includes:\n\n- Automatic token fetching from a token endpoint\n- Token storage in a SQLite database\n- Automatic token refresh when tokens expire\n\nTo configure JWT authentication:\n\n1. Make sure you've built Pfxhttp with JWT support:\n   ```bash\n   make TAGS=jwt\n   ```\n\n2. Set the `jwt_db_path` in the server section to specify where tokens will be stored:\n   ```yaml\n   server:\n     jwt_db_path: \"/var/lib/pfxhttp/jwt.db\"\n   ```\n\n3. Configure JWT authentication for each socket map or policy service that requires it:\n   ```yaml\n   socket_maps:\n     example:\n       target: \"https://api.example.com/endpoint\"\n       jwt_auth:\n         enabled: true\n         token_endpoint: \"https://api.example.com/token\"\n         credentials:\n           some_username_identifier: \"your_username\"\n           some_password_identifier: \"your_password\"\n         content_type: \"application/json\"  # Optional: \"application/x-www-form-urlencoded\" (default) or \"application/json\"\n   ```\n\nThe JWT token will be automatically fetched from the token endpoint and included in the Authorization header as a Bearer token for all requests to the target endpoint.\n\nIf you build Pfxhttp without the `jwt` build tag, the JWT configuration in the YAML file will be ignored, and no JWT authentication will be performed.\n\n---\n\n### HTTP Request/Response Compression\n\nPfxhttp allows you to control HTTP compression per target (socket map or policy service). This is useful when your backend supports gzip and you want to reduce bandwidth or comply with specific API requirements. Nauthilus backends support gzip exclusively.\n\n- http_request_compression: When true, Pfxhttp gzips the request body and sets Content-Encoding: gzip. Disabled by default.\n- http_response_compression: When true, Pfxhttp advertises Accept-Encoding: gzip and will transparently decompress gzip responses if the server replies with Content-Encoding: gzip. Disabled by default.\n\nNotes:\n- Compression settings are defined per target, not globally.\n- The HTTP client's automatic gzip handling is disabled to ensure per-target control.\n- Only gzip is supported currently.\n\nExample:\n\nsocket_maps:\n  demo:\n    target: https://127.0.0.1:9443/api/v1/custom/postfix/socket_map\n    http_request_compression: true\n    http_response_compression: true\n    payload: \u003e\n      {\n        \"key\": \"{{ .Key }}\"\n      }\n    status_code: 200\n    value_field: \"demo_value\"\n\npolicy_services:\n  policy:\n    target: https://127.0.0.1:9443/api/v1/custom/postfix/policy_service\n    http_request_compression: false\n    http_response_compression: true\n    payload: \"{{ .Key }}\"\n    status_code: 200\n    value_field: \"result\"\n\n### Integrating with Postfix\n\n#### Socket Maps\n\nTo configure Postfix to use a socket map, simply add it to your `main.cf`:\n\n```plaintext\n# main.cf\nvirtual_mailbox_domains = socketmap:tcp:127.0.0.1:23450:demo_map\n```\n\nHere, Postfix connects to the TCP socket map listener defined in `pfxhttp.yml` for `demo_map`.\n\n#### Policy Services\n\nTo use a policy service, include it in your recipient restrictions list in `main.cf`:\n\n```plaintext\n# main.cf\nsmtpd_recipient_restrictions =\n    permit_mynetworks,\n    reject_unauth_destination,\n    check_policy_service inet:127.0.0.1:23451\n```\n\nThis setup enables Postfix to query the policy service defined in `pfxhttp.yml` for `example_policy`.\n\n---\n\n## Logging and Troubleshooting\n\nLogs are output to the console by default and should be captured by the service manager (e.g., **systemd**). Log verbosity is configurable in the `pfxhttp.yml` file.\n\nIf Pfxhttp fails to start, verify the following:\n\n- Ensure the configuration file (`/etc/pfxhttp/pfxhttp.yml`) is valid and complete.\n- Ensure the service is running with the appropriate permissions for the configured resources.\n\n---\n\n## Contributing\n\nContributions are welcome! Feel free to submit pull requests or issues to improve the project. The project is distributed under the **MIT License**.\n\n---\n\n## References\n\n- [Postfix Documentation](http://www.postfix.org/)\n- [Nauthilus](https://github.com/croessner/nauthilus)\n- Manpages:\n - `pfxhttp(8)`: Overview and service management\n - `pfxhttp.yml(5)`: Detailed configuration guide\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcroessner%2Fpfxhttp","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcroessner%2Fpfxhttp","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcroessner%2Fpfxhttp/lists"}