Hardened Mode — Startup Security Enforcement in Xferity MFT
Hardened Mode
Section titled “Hardened Mode”Hardened mode is a configuration-enforcement layer that causes Xferity to fail at startup if any security requirement is not met. It is designed for production deployments where configuration drift, plaintext secrets, or insecure transport options are unacceptable.
Hardened mode is opt-in. Development and staging environments can run without it.
Enabling hardened mode
Section titled “Enabling hardened mode”security: hardened_mode: trueWith runtime flag (no config change required):
xferity run-service --expect-existing-stateNote: security.hardened_mode in config and --expect-existing-state on the CLI are separate but complementary controls, described below.
What hardened mode enforces
Section titled “What hardened mode enforces”When security.hardened_mode: true, Xferity performs the following checks at startup. Any violation causes startup to fail with an error message. There is no silent degradation.
1. No plaintext secrets in config.yaml
Section titled “1. No plaintext secrets in config.yaml”Xferity scans the config YAML for these key names:
passwordpassphraseprivate_keytokensecretapi_keyapikeysendgrid_apidefault_webhook_urldsnauth_tokenmetrics_token
If any of these keys contains a literal value (not a secret reference), startup fails.
Allowed values: Secret references using env:, file:, vault:, aws-sm:, azure-kv:, local-vault:, or literal: prefixes.
Example — rejected in hardened mode:
sftp: password: MySecretPassword123 # REJECTEDExample — accepted in hardened mode:
sftp: password: env:SFTP_PASSWORD # OK — resolved at runtime2. No plaintext secrets in flow YAML files
Section titled “2. No plaintext secrets in flow YAML files”All flow YAML files in the flows_path directory are scanned at startup. The same key list applies. If any flow contains a plaintext secret, startup fails with the file name and key path.
Flow secret keys that trigger rejection:
password,passphrase,token,secret,api_key,apikey,private_key- Any key ending in
_password,_passphrase,_token,_secret,_api_key,_private_key
3. PostgreSQL must use TLS
Section titled “3. PostgreSQL must use TLS”When using the PostgreSQL state backend, sslmode: disable is rejected. The minimum accepted value is sslmode: require.
# REJECTED in hardened mode:state: postgres: sslmode: disable
# Required minimum:state: postgres: sslmode: requireThe default sslmode is require when not configured explicitly.
4. Auth credentials must be secret references
Section titled “4. Auth credentials must be secret references”auth.local.password_refmust useenv:,file:, orvault:prefixauth.oidc.client_secretmust useenv:,file:, orvault:prefix
A literal password or client secret in the auth config causes startup to fail.
5. Licensing API token must be a secret reference
Section titled “5. Licensing API token must be a secret reference”When licensing.online_sync.enabled: true:
licensing.online_sync.api_tokenmust useenv:,file:, orvault:prefix- Online sync endpoint must use HTTPS
6. Transport security posture escalation
Section titled “6. Transport security posture escalation”In hardened mode, the security posture engine escalates these findings from warning to error:
| Finding | Normal severity | Hardened severity |
|---|---|---|
sftp_insecure_host_key | warning | error |
sftp_insecure_host_key_global | warning | error |
ftps_tls_skip_verify | warning | error |
as2_tls_skip_verify | warning | error |
This means any SFTP partner with allow_insecure_host_key: true, any FTPS partner with tls.insecure_skip_verify: true, or any AS2 partner with https_tls.insecure_skip_verify: true causes the posture report’s healthy field to be false.
The --expect-existing-state flag
Section titled “The --expect-existing-state flag”This CLI flag is a separate hardened-deployment control:
xferity run-service --expect-existing-stateWhen this flag is set, Xferity fails at startup if the licensing state file is absent. The licensing state file is written during the first successful startup and contains runtime activation state.
Why this matters: An attacker who gains operating system access and deletes the state file would otherwise cause Xferity to start with a clean slate, potentially bypassing license enforcement checks. The --expect-existing-state flag prevents this: if the state file is missing on a node that has previously run, startup is refused.
Use this flag on all hardened production nodes after their initial deployment. On first deployment (before any state file exists), run without this flag to allow startup.
You can also set it in config:
licensing: expect_existing_state: trueMinimum hardened mode config
Section titled “Minimum hardened mode config”security: hardened_mode: true
state: backend: postgres postgres: host: db.internal user: xferity dbname: xferity password: env:XFERITY_DB_PASSWORD sslmode: require
auth: mode: local local: username: admin password_ref: env:XFERITY_ADMIN_PASSWORD
audit: enabled: true path: /var/log/xferity/audit.jsonl tamper_evidence_enabled: true
secrets: local_vault: master_key_ref: env:MFT_MASTER_KEYWhat hardened mode does not protect against
Section titled “What hardened mode does not protect against”Hardened mode is one layer of defense. It does not replace:
- Network controls — restrict access to SFTP port 22, the Xferity UI port, and the database port via firewall rules
- File system permissions — protect config files, key files, and the audit log directory with appropriate OS permissions
- Certificate lifecycle management — use the posture engine and monitoring to catch expiring certificates before they cause failures
- Access governance — control who has administrative access to the Xferity UI and the underlying host
- Backup and disaster recovery — regularly back up the PostgreSQL database and audit log
Verifying your hardened configuration
Section titled “Verifying your hardened configuration”Run validation before deploying:
xferity validate --config /etc/xferity/config.yamlThis runs all the same startup checks, including hardened mode enforcement, without starting any services.
Use dry-run to test flows without transferring files:
xferity run my-flow --dry-run