O4
Storage AWS

OpenAS2 4.14.0 on AWS User Guide

| Product: OpenAS2 4.14.0 on Ubuntu 24.04 on AWS

Overview

This guide covers launching and using the OpenAS2 4.14.0 on Ubuntu 24.04 AMI from cloudimg on Amazon EC2. It is one version of the cloudimg SFTP and file server listing on AWS Marketplace; you pick it as the software version when you subscribe and launch.

OpenAS2 is an open source Java implementation of the AS2 protocol (RFC 4130), the standard way trading partners exchange EDI and other business documents over the internet. Every message is signed with the sender's key and encrypted for the receiver, and the receiver returns a signed receipt (an MDN) that proves delivery. OpenAS2 watches an outbox directory per partner and sends what you drop there, and writes what partners send you to an inbox directory.

The image is built from the OpenAS2 4.14.0 source release, which includes the fixes for a pre-authentication SQL injection through the Message-ID header and a Message-ID path traversal that affect releases before 4.9.0. Upstream's defaults are built for a trusted test network; the cloudimg image changes them:

  • TLS from the first boot. OpenAS2's receivers listen on loopback only. nginx serves them on port 10443 with a certificate generated on your instance's first boot: https://<your-host>:10443/as2 for AS2 messages and https://<your-host>:10443/mdn for asynchronous receipts. A request for any host name the instance does not serve is dropped.
  • Unsigned messages are refused. A message that is not signed by a partner whose certificate you have imported is rejected with signed-message-required, and a message for an AS2 ID you have not configured is rejected too. Nothing is stored for either.
  • Nothing shared between instances. Upstream ships a demo keystore with live private keys (password testas2), demo AS2 IDs (MyCompany_OID, PartnerA_OID) and a demo database. None of them is on the image. First boot generates this instance's AS2 IDs, its AS2 keystore (RSA 3072), its passwords and its message tracking database.
  • Closed until first boot, and fail closed. OpenAS2 cannot start without the files first boot creates, and nginx ships switched off. First boot starts OpenAS2 on loopback, opens the TLS listener, then proves a signed and encrypted AS2 message and its receipt through nginx; if anything fails, the listener is closed again.
  • Hardened defaults. OpenAS2 runs as its own unprivileged account in a systemd sandbox that can write only to its message store and logs; OpenAS2 and nginx are both blocked from the instance metadata service; the AMI requires IMDSv2; no management console, REST API or database network port is enabled.
  • Fully patched Ubuntu 24.04 LTS at build time, with unattended security upgrades switched on.

What is on the image: OpenAS2 4.14.0 in /opt/openas2 with its configuration in /etc/openas2, the OpenJDK 17 runtime, nginx, Ubuntu 24.04 LTS, cloud-init and the AWS Systems Manager agent.

Default user: ubuntu · Architecture: x86_64 · Recommended instance type: m5.large

Prerequisites

  • An AWS account subscribed to the listing, an EC2 key pair and a VPC subnet with a route to the internet (or a private route your partners use).
  • A security group that allows TCP 22 (SSH) from your own networks and TCP 10443 (AS2 over TLS) from your trading partners' addresses.

Step 1: Launch the instance

In the AWS Marketplace listing choose Continue to Subscribe, then Continue to Configuration, pick OpenAS2 4.14.0 on Ubuntu 24.04 as the software version and your Region, then Launch through EC2. Choose m5.large, your key pair and a security group as above. Messages, receipts and the tracking database live on the root volume; size it for the documents you keep.

With the AWS CLI (replace the values in angle brackets):

aws ec2 run-instances --region <region> --image-id <ami-id-from-the-listing> \
  --instance-type m5.large --key-name <your-key-pair> \
  --security-group-ids <your-security-group> --subnet-id <your-subnet> \
  --metadata-options HttpTokens=required,HttpEndpoint=enabled \
  --block-device-mappings 'DeviceName=/dev/sda1,Ebs={VolumeSize=30,VolumeType=gp3}' \
  --tag-specifications 'ResourceType=instance,Tags=[{Key=Name,Value=openas2}]'

Step 2: Connect to your instance

Version SSH user
OpenAS2 4.14.0 on Ubuntu 24.04 ubuntu
ssh -i <your-key.pem> ubuntu@<public-ip>

Step 3: Check the services

First boot creates the keys, passwords and database, starts OpenAS2, opens nginx and proves an AS2 exchange before it finishes (about half a minute):

systemctl is-active openas2 nginx openas2-firstboot
sudo test -f /var/lib/cloudimg/openas2-firstboot.done && echo FIRSTBOOT_DONE
active
active
active
FIRSTBOOT_DONE

Only SSH and nginx listen on public addresses; the two OpenAS2 receivers listen on loopback:

sudo ss -lntp | awk 'NR>1 && $4 !~ /127\.0\.0\.(53|54)/ {print $4}' | sort
0.0.0.0:10443
0.0.0.0:22
127.0.0.1:10080
127.0.0.1:10081
[::]:10443
[::]:22

Step 4: Your AS2 details

The instance's details are in a file only root can read. Show the URLs, AS2 IDs and certificate paths (the passwords in the same file are not printed here):

sudo grep -E '^(OPENAS2_|AS2_LOCAL_ID|AS2_LOOPBACK_TEST_ID|AS2_PUBLIC_CERT|TLS_CERT|AS2_KEYSTORE_FILE)' /root/openas2-credentials.txt
sudo grep -oE '^[A-Z0-9_]+PASSWORD=' /root/openas2-credentials.txt
OPENAS2_AS2_URL=https://ec2-<public-ip-dashed>.compute-1.amazonaws.com:10443/as2
OPENAS2_MDN_URL=https://ec2-<public-ip-dashed>.compute-1.amazonaws.com:10443/mdn
AS2_LOCAL_ID=cloudimg-<12 hex characters>
AS2_LOOPBACK_TEST_ID=cloudimg-<12 hex characters>-loopback
AS2_PUBLIC_CERT=/etc/openas2/local-as2-public.cer
TLS_CERT=/etc/ssl/openas2/openas2.crt
AS2_KEYSTORE_FILE=/etc/openas2/as2_certs.p12
AS2_KEYSTORE_PASSWORD=
SSL_TRUST_KEYSTORE_PASSWORD=
H2_DB_PASSWORD=
  • OPENAS2_AS2_URL is where your trading partners send AS2 messages, and OPENAS2_MDN_URL where they send asynchronous receipts. The host is the instance's public DNS name from EC2 instance metadata (the IP address when the subnet has no public DNS names); to use your own DNS name see Step 9.
  • AS2_LOCAL_ID is your AS2 ID. It was generated on this instance; you can change it to the ID you agree with your partners (Step 7).
  • AS2_PUBLIC_CERT is your AS2 certificate. Partners use it to verify your signatures and to encrypt messages for you.
  • AS2_LOOPBACK_TEST_ID is a second station on the same server, used only by the built in self test (Step 6).

Step 5: What to give your trading partners

A partner needs your AS2 ID, your AS2 URL, your AS2 certificate and, because the TLS certificate is self signed, either that certificate or its fingerprint:

sudo openssl x509 -noout -subject -enddate -fingerprint -sha256 -in /etc/openas2/local-as2-public.cer
sudo openssl x509 -noout -subject -fingerprint -sha256 -in /etc/ssl/openas2/openas2.crt
subject=O = cloudimg OpenAS2, OU = AS2, CN = cloudimg-<12 hex characters>
notAfter=<three years after first boot> GMT
sha256 Fingerprint=<AS2 certificate fingerprint>
subject=CN = ec2-<public-ip-dashed>.compute-1.amazonaws.com, O = cloudimg OpenAS2
sha256 Fingerprint=<TLS certificate fingerprint>

Send them the two .cer/.crt files (sudo cat prints them in PEM form). They contain public keys only.

Step 6: Run the built in AS2 self test

The image ships an end to end check you can run at any time; first boot runs it too. It sends a real signed and encrypted AS2 message from your station to the self test station through nginx and TLS, checks the payload arrives byte for byte with a signed receipt, then sends an unsigned message and a message for an unknown AS2 ID through nginx (with a forged X-Forwarded-For header) and checks both are refused, nothing is stored, and OpenAS2 logs the real client address:

sudo openas2-verify.sh
listeners: 10080/10081 on 127.0.0.1 only, nginx TLS 10443 ; unknown Host/SNI dropped ; GET /as2 403, / 404 ; certificate verifies for ec2-<public-ip-dashed>.compute-1.amazonaws.com
round trip: signed+encrypted cloudimg-selftest-<timestamp> local(cloudimg-<id>) -> nginx TLS -> loopback(cloudimg-<id>-loopback) arrived byte-identical; signed MDN 'processed'
unsigned message refused (signed-message-required), unknown AS2 ID refused (no partnership, HTTP 500), nothing stored ; OpenAS2 logged the real peer <private-ip>, not the forged X-Forwarded-For
OPENAS2_ROUNDTRIP_OK

It removes the files it created. Its messages stay in the tracking database (Step 10) as a record.

Step 7: Add a trading partner

You need the partner's AS2 ID, their AS2 URL and their AS2 certificate. Import the certificate into this instance's keystore under an alias of your choice (lower case letters, digits, dot, dash, underscore). The tool reads the keystore password itself, so it never appears on a command line, and restarts OpenAS2:

sudo openas2-import-partner-cert acme ./acme-as2.cer

Then describe the partner and one partnership per direction in /etc/openas2/partnerships.xml (sudo nano /etc/openas2/partnerships.xml). Your own station is the partner named local; keep its as2_id, or change it to the ID you agreed with your partners:

<partner name="acme" as2_id="ACME_AS2_ID" x509_alias="acme" email="edi@acme.example"/>

<partnership name="local-to-acme">
    <sender name="local"/>
    <receiver name="acme"/>
    <pollerConfig enabled="true"/>
    <attribute name="protocol" value="as2"/>
    <attribute name="as2_url" value="https://as2.acme.example/as2"/>
    <attribute name="as2_mdn_to" value="edi@your-company.example"/>
    <attribute name="as2_mdn_options" value="signed-receipt-protocol=optional, pkcs7-signature; signed-receipt-micalg=optional, $attribute.sign$"/>
    <attribute name="encrypt" value="AES256"/>
    <attribute name="sign" value="SHA-256"/>
    <attribute name="resend_max_retries" value="3"/>
</partnership>

<partnership name="acme-to-local">
    <sender name="acme"/>
    <receiver name="local"/>
    <attribute name="reject_unsigned_messages" value="true"/>
</partnership>

Apply it:

sudo systemctl restart openas2

If the partner's AS2 endpoint uses HTTPS with a self signed certificate, add that certificate to OpenAS2's TLS trust store, /etc/openas2/ssl_trust_certs.p12 (password SSL_TRUST_KEYSTORE_PASSWORD in the credentials file); a certificate from a public certificate authority needs nothing.

Step 8: Send and receive files

To send a file to a partner, put it in that partner's outbox directory, named after the partner's AS2 ID. OpenAS2 checks the outbox every 5 seconds, then signs, encrypts and sends the file and removes it from the outbox:

sudo install -o openas2 -g openas2 -m 0640 ./invoice-1001.edi /var/lib/openas2/data/outbox/ACME_AS2_ID/

Files that cannot be sent are moved to /var/lib/openas2/data/outbox/error/. Received files and receipts are written under /var/lib/openas2/data/<sender ID>-<receiver ID>/:

Directory Contents
inbox/ Payloads received from partners (decrypted and signature checked)
msgheaders/<date>/ The AS2 headers of each received message
mdn/<date>/ Receipts (MDNs) sent and received
/var/lib/openas2/data/inbox/error/ Messages that were refused, with the reason

Received files are stored under generated names (date, time and a random suffix), never under a name or Message-ID the sender chose. The original file name is in the matching msgheaders file and in the log.

OpenAS2 logs to /var/log/openas2/openas2.log (one compressed file per day, 30 days kept):

sudo tail -n 5 /var/log/openas2/openas2.log

Step 9: Your own DNS name

Point a DNS record at the instance, then set the name your partners use. This mints a new self signed TLS certificate for the name, updates nginx, the self test's trust store, the receipt URL and the URLs in /root/openas2-credentials.txt, restarts OpenAS2, and keeps the name across reboots:

sudo openas2-set-hostname as2.your-company.example

Send your partners the new TLS certificate or its fingerprint (Step 5). To use a certificate from your own certificate authority instead, copy it over the generated pair and reload nginx (running openas2-set-hostname again replaces it with a self signed one):

sudo install -m 0644 as2.your-company.example.crt /etc/ssl/openas2/openas2.crt
sudo install -m 0600 as2.your-company.example.key /etc/ssl/openas2/openas2.key
sudo nginx -t && sudo systemctl reload nginx

Partners that cannot use port 10443 can reach the instance through a Network Load Balancer listening on 443 and forwarding to 10443.

Step 10: The message tracking database

OpenAS2 records the state of every message in an embedded H2 database in /var/lib/openas2/DB, protected by a per instance password. The embedded database can only be opened while OpenAS2 is stopped; openas2-db reads SQL on standard input and the password from the configuration itself:

sudo systemctl stop openas2
echo "SELECT MSG_ID, DIRECTION, STATE, STATUS, CREATE_DT FROM msg_metadata ORDER BY ID DESC LIMIT 5;" | sudo openas2-db
sudo systemctl start openas2

Step 11: Security checks

The configuration and keys are readable only by root and the OpenAS2 account, and the passwords exist only in /etc/openas2/openas2.properties and the credentials file:

sudo stat -c '%a %U:%G %n' /etc/openas2/openas2.properties /etc/openas2/as2_certs.p12 /etc/openas2/ssl_trust_certs.p12 /etc/openas2/partnerships.xml /etc/ssl/openas2/openas2.key /root/openas2-credentials.txt
640 root:openas2 /etc/openas2/openas2.properties
640 root:openas2 /etc/openas2/as2_certs.p12
640 root:openas2 /etc/openas2/ssl_trust_certs.p12
640 root:openas2 /etc/openas2/partnerships.xml
600 root:root /etc/ssl/openas2/openas2.key
600 root:root /root/openas2-credentials.txt

Unsigned messages are refused for every partnership, and the receivers are bound to loopback:

sudo grep -oE '(reject_unsigned_messages|module\.AS2(MDN)?ReceiverModule\.http\.address)="[^"]*"' /etc/openas2/config.xml
module.AS2ReceiverModule.http.address="127.0.0.1"
module.AS2MDNReceiverModule.http.address="127.0.0.1"
reject_unsigned_messages="true"

OpenAS2 runs unprivileged in a sandbox that can write only to its message store and logs, and neither OpenAS2 nor nginx can reach the instance metadata service:

systemctl show openas2 -p User -p ProtectSystem -p ReadWritePaths -p NoNewPrivileges -p IPAddressDeny
systemctl show nginx -p IPAddressDeny
IPAddressDeny=fd00:ec2::254/128 168.63.129.16/32 169.254.169.254/32
User=openas2
ReadWritePaths=/var/lib/openas2 /var/log/openas2
ProtectSystem=strict
NoNewPrivileges=yes
IPAddressDeny=fd00:ec2::254/128 168.63.129.16/32 169.254.169.254/32

nginx passes OpenAS2 a fixed host name and the real client address from the connection; OpenAS2 logs the X-Forwarded-For value as the sender's address, so nginx never forwards the one a client sends:

grep -E 'proxy_set_header (Host|X-Forwarded-For|X-Real-IP)' /etc/nginx/openas2-proxy.conf
proxy_set_header Host $openas2_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $remote_addr;

No per instance password reached a log, the journal, shell history, backups or any other file:

sudo openas2-secret-leak-check.sh
LEAKCHECK_OK no per-instance password in the journal, /var/log (OpenAS2 and nginx logs included), shell history, MOTD, /var/backups, /etc (only openas2.properties), the OpenAS2 install and state trees, /usr/local or /tmp

Step 12: Server components

Component Version Path
OpenAS2 4.14.0 (source tag v4.14.0, commit e97a6a2b) /opt/openas2
OpenJDK runtime 17.0.20.1 /usr/lib/jvm/java-17-openjdk-amd64
H2 database 2.5.252 /opt/openas2/lib/h2-2.5.252.jar
nginx 1.24.0 /etc/nginx/sites-available/cloudimg-openas2
Ubuntu 24.04 LTS
Path Purpose
/etc/openas2/config.xml OpenAS2 configuration (no passwords in it)
/etc/openas2/openas2.properties Per instance passwords and the receipt URL
/etc/openas2/partnerships.xml Partners and partnerships
/etc/openas2/as2_certs.p12 AS2 keystore: your keys and your partners' certificates
/etc/openas2/local-as2-public.cer Your AS2 certificate
/etc/ssl/openas2/ TLS certificate and key
/var/lib/openas2/data Outboxes, inboxes and receipts
/var/lib/openas2/DB Message tracking database
/var/log/openas2/ OpenAS2 logs
/root/openas2-credentials.txt URLs, AS2 IDs and passwords (mode 0600)
/usr/local/sbin/openas2-verify.sh End to end self test (Step 6)
/usr/local/sbin/openas2-import-partner-cert Import a partner certificate (Step 7)
/usr/local/sbin/openas2-set-hostname Host name and TLS certificate (Step 9)
/usr/local/sbin/openas2-db Query the tracking database (Step 10)
/usr/local/sbin/openas2-secret-leak-check.sh Proves no password reached a log
cat /opt/openas2/SOURCE_COMMIT
sudo journalctl -u openas2-firstboot --no-pager -o cat | grep -E 'Serving as|First boot complete'

Troubleshooting

  • A partner's message is refused with signed-message-required. The partner sent it unsigned. Ask them to sign with SHA-256; unsigned messages are never accepted.
  • A partner's message is refused with HTTP 500 and "Partnership not found" in the log. The AS2-From / AS2-To IDs do not match a partnership in /etc/openas2/partnerships.xml. Check the IDs on both sides and restart OpenAS2.
  • integrity-check-failed or decryption-failed. The partner's certificate in your keystore, or your certificate on their side, is not the one being used. Re-import theirs (Step 7) and send them yours again (Step 5).
  • The partner's TLS connection is closed straight away. nginx drops requests for host names it does not serve. The partner must use the host in OPENAS2_AS2_URL, one of the instance's IP addresses, or the name you set with openas2-set-hostname.
  • Sending to a partner fails with a certificate error. Their HTTPS certificate is self signed: add it to the TLS trust store (Step 7).
  • Nothing answers after launch. Check systemctl status openas2-firstboot and journalctl -u openas2-firstboot. Until first boot completes the server is deliberately closed.

Security recommendations

  • Allow port 10443 in the security group from your trading partners' addresses only, and port 22 from your own networks only.
  • Keep /root/openas2-credentials.txt and /etc/openas2 secret; your AS2 private key is in the keystore.
  • Remove the self test station (loopback) and its partnership from partnerships.xml once you no longer need the self test, if you prefer; it can only receive messages signed with your own key.
  • Renew your AS2 certificate before it expires (three years after first boot) and send the new one to your partners. OpenAS2 supports a fallback certificate during the switch-over (x509_alias_fallback, see the comments in upstream's documentation).
  • Keep the operating system patched; unattended security upgrades are on, and sudo apt-get update && sudo apt-get upgrade applies everything else.
  • Back up /etc/openas2 (it holds your keys) to an encrypted location, and /var/lib/openas2 for the message history.

Support and licensing

OpenAS2 is open source under the GNU General Public License v3; there is no per user or per CPU licence fee. The image is built without changes to the OpenAS2 source from the upstream release tag v4.14.0, and the licence text is at /opt/openas2/LICENSE.txt. The AMI price covers cloudimg's hardening, first boot setup and support. OpenAS2 is a trademark of its respective owners; cloudimg is not affiliated with or endorsed by the OpenAS2 project.

  • Email: support@cloudimg.co.uk
  • Website: https://www.cloudimg.co.uk
  • Support: 24/7, with a 24 hour response SLA