Schedule icon
Commands icon
Process icon
Encrypt icon
Decrypt icon
Switch icon
Log icon
If icon
SlackIncomingWebhook icon

OpenPGP Partner Payload Signature and Integrity Gate

Verify OpenPGP digital signatures and MDC payload integrity on partner data files, gate unauthenticated transfers, and alert Slack on tamper.

Categories
BusinessInfrastructure

Automate cryptographic admission control for partner and vendor data files. This blueprint verifies OpenPGP digital signatures and Modification Detection Code (MDC) integrity packets before sensitive data reaches downstream ingestion systems. Unsigned files, mismatched signer identities, or tampered bytes are intercepted and routed to quarantine alerts in Slack.

How it works

  1. scheduled_verification_cycle (Schedule, 0 */6 * * *, disabled by default) runs periodic compliance checks across staged partner payloads.
  2. stage_audit_payload (io.kestra.plugin.scripts.shell.Commands) stages an audit manifest batch file in Kestra internal storage.
  3. encrypt_and_sign_payload (io.kestra.plugin.crypto.openpgp.Encrypt) encrypts the payload with the recipient public key and applies an ASCII-armored OpenPGP one-pass digital signature using the sender secret key.
  4. decrypt_and_verify_signature (io.kestra.plugin.crypto.openpgp.Decrypt, with allowFailure: true) decrypts the payload with the recipient private key, enforces SHA-256 one-pass signature verification against trusted public keys, and asserts that the signing identity matches expected_signer_identity. RFC 9580 and RFC 4880 MDC integrity verification occurs automatically during stream decryption.
  5. evaluate_signature_status (io.kestra.plugin.core.flow.Switch) evaluates outputs.decrypt_and_verify_signature.uri is defined. On success (VALID), log_compliance_verified confirms the cryptographic check, and check_dry_run_dispatch (If) either logs promotion via log_payload_promoted (when dry_run=false) or logs dry-run verification via log_dry_run_verified (when dry_run=true).
  6. On failure (QUARANTINE), evaluate_quarantine_alert (If) dispatches an incident card to Slack via alert_signature_tamper if quarantine_on_tamper=true, or logs quiet suppression via log_quarantine_suppressed if quarantine_on_tamper=false.
  7. log_audit_cycle_summary (io.kestra.plugin.core.log.Log) records the final audit summary with execution telemetry.
  8. alert_on_flow_error (SlackIncomingWebhook) in the flow-level errors block notifies on-call teams if unexpected runtime exceptions occur.

What you get

  • Automated cryptographic admission control preventing unauthenticated partner data from corrupting data warehouses.
  • Zero-trust signature enforcement validating that files were signed by authorized keys rather than third-party actors.
  • Automatic Modification Detection Code (MDC) verification that rejects truncated or tampered ciphertexts.
  • Safe dry-run evaluation toggle and configurable quarantine Slack alerts detailing execution ID, expected signer, and security remediation guidance.

Who it's for

  • Data Platform Engineers building secure vendor and partner ingestion pipelines.
  • Security and Compliance Engineers enforcing SOC 2, HIPAA, or PCI-DSS cryptographic verification controls.
  • Financial and Healthcare data custodians handling confidential exchange payloads.

Why orchestrate this with Kestra

  • Declarative OpenPGP cryptography without managing complex local GnuPG keyrings on worker nodes.
  • Built-in failure tolerance and conditional branching allowing graceful quarantine routing instead of opaque pipeline crashes.
  • Native Kestra internal storage streams encrypted files directly through Bouncy Castle memory buffers without writing cleartext to local disk.
  • Centralized audit trail with execution history and tamper alerting out of the box.

Prerequisites

  • Kestra 0.18.0 or later with plugin-crypto and plugin-slack available.
  • An OpenPGP key pair for the sender (signing) and recipient (encryption/decryption).
  • Slack Incoming Webhook URL for security notification dispatches.

Secrets

  • SLACK_WEBHOOK: Incoming Webhook URL for Slack security notifications.
  • PGP_RECIPIENT_PUBLIC_KEY: ASCII-armored OpenPGP public key of the receiving system (gpg --armor --export recipient@internal.example.com).
  • PGP_RECIPIENT_PRIVATE_KEY: ASCII-armored OpenPGP private key of the receiving system (gpg --armor --export-secret-keys recipient@internal.example.com).
  • PGP_RECIPIENT_PRIVATE_KEY_PASSPHRASE: Passphrase protecting the recipient private key (leave empty string if unencrypted).
  • PGP_SENDER_PUBLIC_KEY: ASCII-armored OpenPGP public key of the sender partner (gpg --armor --export partner-secops@vendor.example.com).
  • PGP_SENDER_PRIVATE_KEY: ASCII-armored OpenPGP private key of the sender partner for signing.
  • PGP_SENDER_PRIVATE_KEY_PASSPHRASE: Passphrase protecting the sender signing private key.

For open-source (OSS) Kestra setups, provide secrets as base64-encoded environment variables:

export SECRET_SLACK_WEBHOOK=$(echo -n "https://hooks.slack.com/services/T00/B00/XXXX" | base64)
export SECRET_PGP_RECIPIENT_PUBLIC_KEY=$(echo -n "-----BEGIN PGP PUBLIC KEY BLOCK-----..." | base64)
export SECRET_PGP_RECIPIENT_PRIVATE_KEY=$(echo -n "-----BEGIN PGP PRIVATE KEY BLOCK-----..." | base64)
export SECRET_PGP_RECIPIENT_PRIVATE_KEY_PASSPHRASE=$(echo -n "secret-passphrase" | base64)
export SECRET_PGP_SENDER_PUBLIC_KEY=$(echo -n "-----BEGIN PGP PUBLIC KEY BLOCK-----..." | base64)
export SECRET_PGP_SENDER_PRIVATE_KEY=$(echo -n "-----BEGIN PGP PRIVATE KEY BLOCK-----..." | base64)
export SECRET_PGP_SENDER_PRIVATE_KEY_PASSPHRASE=$(echo -n "secret-passphrase" | base64)

Quick start

  1. Generate a test GPG key pair locally using standard tools:
    docker run --rm -it alpine sh -c "apk add --no-cache gnupg && gpg --batch --passphrase '' --quick-generate-key 'partner-secops@vendor.example.com' default default never && gpg --armor --export 'partner-secops@vendor.example.com'"
    
  2. Configure the required secrets in Kestra KV store or environment variables.
  3. If testing with a custom containerized mock partner SFTP or key server, avoid host port conflicts (do not use 8080):
    docker run -d --name pgp-mock-store -p 8085:8085 alpine sh -c "while true; do sleep 3600; done"
    docker network connect YOUR_KESTRA_NETWORK pgp-mock-store
    
    (Run docker network ls to locate your active Kestra Docker bridge network).
  4. Execute the flow manually in the Kestra UI to verify that the cryptographic signature and MDC integrity check succeed.
  5. Set disabled: false on scheduled_verification_cycle to automate continuous monitoring.

How to extend

  • Add downstream S3 or PostgreSQL storage ingestion tasks in the VALID case to consume the verified cleartext URI ({{ outputs.decrypt_and_verify_signature.uri }}).
  • Connect a secondary quarantine bucket upload task in the QUARANTINE case to preserve tampered payloads for forensic analysis.
  • Integrate PagerDuty or Opsgenie webhooks to escalate repeated signature validation failures during automated batches.

Inputs

  • expected_signer_identity (STRING, default "partner-secops@vendor.example.com"): Expected signer User ID or email identity matching the key ring.
  • quarantine_on_tamper (SELECT: "true", "false", default "true"): Log incident telemetry and route tampered payloads to quarantine alerting.
  • dry_run (SELECT: "true", "false", default "true"): Audit signature verification and decryption without downstream cleartext promotion.

Outputs

  • stage_audit_payload.outputFiles['audit_manifest.json']: Staged audit manifest file storage URI.
  • encrypt_and_sign_payload.uri: Internal storage URI of the encrypted and signed OpenPGP message.
  • decrypt_and_verify_signature.uri: Internal storage URI of the verified decrypted cleartext payload.

Links

Pitfalls

  • Keyring Armor Formatting: Keys must include full -----BEGIN PGP PUBLIC KEY BLOCK----- and -----END PGP PUBLIC KEY BLOCK----- headers and trailers.
  • MDC Integrity Packets: The io.kestra.plugin.crypto.openpgp.Decrypt task strictly rejects legacy unauthenticated SED packets (RFC 9580 Section 5.7). Messages must be created with integrity protection (SEIP or AEAD).
  • Passphrase Quoting: When using secret passphrases with special characters, always store them via {{ secret('NAME') }} to prevent Pebble template parsing errors.
  • User ID Binding: requiredSignerUsers validates against the primary User ID or parsed email address within angle brackets. Ensure the identity string in expected_signer_identity matches the key metadata exactly.
See How

New to Kestra?

Use blueprints to kickstart your first workflows.