Od
Databases AWS

Oracle Database 21c EE on AWS User Guide

| Product: Oracle Database 21c EE on Oracle Linux 8 on AWS

Overview

This guide covers launching and using the Oracle Database 21c EE on Oracle Linux 8 AMI from cloudimg on Amazon EC2. It is one version of the cloudimg Hardened AMIs for Oracle Database 19c, 21c & 26ai Enterprise (BYOL) listing on AWS Marketplace; you pick it as the software version when you subscribe and launch.

The image runs Oracle Database 21c Enterprise Edition 21.3.0.0.0, the base release from Oracle's installation media, on a fully patched Oracle Linux 8. No Oracle Release Update or other Oracle patch is applied: you apply Release Updates with your own Oracle support entitlement (see Licensing and patching). The listing is Bring Your Own License: no Oracle Database licence is included.

Oracle Database 21c has no non container architecture, so the starter database is a multitenant container database (CDB) named ORCL with one pluggable database (PDB) named ORCLPDB. Applications connect to the ORCLPDB service; the ORCL container root is for administration.

What cloudimg adds to this version:

  • No usable password ships in the image. On the first boot a one shot service, oracle-db-21c-firstboot.service, runs before the database is reachable. With no listener running it sets unique random passwords on SYS, SYSTEM and PDBADMIN, re-keys every other password account with a random value and leaves it locked, starts the listener on 127.0.0.1 only, proves each new password works and each historic and default password is refused, and only then writes /root/oracle-db-credentials.txt (root only) and the ready marker that lets oracle-db.service start the database. If any step fails, the database stays stopped.
  • Nothing listens on the network but SSH. The listener is bound to 127.0.0.1:1521 and the listing's security group opens TCP 22 only. You connect through an SSH tunnel, or open port 1521 deliberately with network encryption (step 9).
  • No database process can reach instance metadata. SQL can make network calls (UTL_HTTP, UTL_TCP, Java), so every Oracle unit carries a systemd IP deny for 169.254.169.254, and an nftables rule blocks the same address for the oracle user outside the unit. SQL injection in your application cannot turn into instance role credential theft (step 10).
  • IMDSv2 only in every example, with the hop limit at 1.
  • A dedicated EBS volume at /opt/oracle for the Oracle home, datafiles, redo and archive.

This is a headless image: you administer Oracle Database over SSH with SQL*Plus, and connect graphical tools such as Oracle SQL Developer through an SSH tunnel.

Prerequisites

  • An AWS account where you can launch EC2 instances and subscribe to AWS Marketplace products
  • An EC2 key pair in the target Region
  • A VPC and subnet, with a security group that allows inbound TCP 22 from your management network only
  • Your own Oracle Database Enterprise Edition licence for the instance (Bring Your Own License)
  • The AWS CLI version 2 on your workstation if you plan to launch from the command line

Recommended instance type: m5.large (2 vCPU, 8 GiB). The starter database is sized for it. For production choose m5.xlarge or larger and raise the database memory (step 12).

Step 1: Launch the instance from AWS Marketplace

  1. Open the listing, choose View purchase options, then Subscribe.
  2. Choose Launch your software, then Launch from EC2 Console.
  3. In Version, choose Oracle Database 21c EE on Oracle Linux 8.
  4. Choose m5.large or larger, your key pair, and a security group that opens TCP 22 from your own network only. Port 1521 does not need to be open.
  5. Under Advanced details, keep Metadata version at V2 only (token required) and set the Metadata response hop limit to 1.
  6. Choose Launch instance.

First boot sets and proves the passwords before the database may start; allow a few minutes after the status checks pass.

Step 2: Launch from the AWS CLI

Replace the placeholders with your AMI ID (from the listing's launch page for this version), key pair, subnet and security group:

aws ec2 run-instances \
  --image-id <ami-id> \
  --instance-type m5.large \
  --key-name <key-name> \
  --subnet-id <subnet-id> \
  --security-group-ids <security-group-id> \
  --metadata-options HttpTokens=required,HttpEndpoint=enabled,HttpPutResponseHopLimit=1 \
  --tag-specifications 'ResourceType=instance,Tags=[{Key=Name,Value=oracle-db-21c-ee}]'

The image's root volume (50 GiB) and its /opt/oracle data volume (30 GiB) come from the AMI; grow either at launch with --block-device-mappings if you need more space. Read the public address once the instance is running:

aws ec2 describe-instances --instance-ids <instance-id> \
  --query "Reservations[].Instances[].PublicIpAddress" --output text

Step 3: Connect over SSH

Version SSH login user
Oracle Database 21c EE on Oracle Linux 8 ec2-user
ssh -i <your-key.pem> ec2-user@<public-ip>

SSH can come up before first boot has finished. The ready marker exists only after every password has been set and proven:

sudo test -f /var/lib/cloudimg/oracle-db-21c-firstboot.done && echo "first boot complete"
first boot complete

If nothing prints, first boot is still running; follow it with sudo journalctl -u oracle-db-21c-firstboot.service -b -f.

Step 4: Retrieve the generated credentials

sudo cat /root/oracle-db-credentials.txt

The file looks like this, with unique passwords on your instance:

# Oracle Database 21c Enterprise Edition - per-instance credentials (cloudimg)
# Generated <date> on <instance-id>. Root-only (0600). Unique to this instance.

SYS_PASSWORD=<your generated SYS password>
SYSTEM_PASSWORD=<your generated SYSTEM password>
PDBADMIN_PASSWORD=<your generated PDBADMIN password>

ORACLE_SID=ORCL
PDB_SERVICE=ORCLPDB
LISTENER=127.0.0.1:1521 (localhost only - use an SSH tunnel, or see the guide to open it deliberately with encryption)

Every other database account is locked and expired with a random password.
Connect: sudo su - oracle ; . ~/setEnv.sh ; sqlplus /nolog ; then CONNECT system@//localhost:1521/ORCLPDB

Use SYSTEM for administrative and application connections, SYS for SYSDBA operations, and PDBADMIN to administer ORCLPDB. The file is readable by root only:

sudo stat -c '%a %U:%G %n' /root/oracle-db-credentials.txt
600 root:root /root/oracle-db-credentials.txt

Every other password account in the container and the pluggable database, including DBSNMP and the sample schemas, has a random password nobody knows and is locked and expired. To use one, set your own password and unlock it as SYSDBA in the container that owns it:

ALTER USER <name> IDENTIFIED BY "<new-strong-password>" ACCOUNT UNLOCK;

If first boot fails

First boot fails closed: it stops the database and the listener, writes no credentials file, and leaves /root/oracle-db-FIRSTBOOT-FAILED.txt. Read it and the journal, then reboot once to retry (first boot runs on every boot until it succeeds):

sudo cat /root/oracle-db-FIRSTBOOT-FAILED.txt
sudo journalctl -u oracle-db-21c-firstboot.service -b
sudo reboot

If it fails again, contact cloudimg support with the journal output.

Step 5: Run the security self-test

The image ships a self-test that re-proves the instance's security state:

sudo /usr/local/sbin/cloudimg-oracle-selftest

Each check prints a PASS line and the run ends with SELFTEST_OK (output from a fresh instance):

PASS: ready marker present
PASS: no first-boot error notice
PASS: /root/oracle-db-credentials.txt is 0600 root:root
PASS: SYS@ORCL refuses a historic/default password
PASS: SYS@ORCL refuses a historic/default password
PASS: SYS@ORCL accepts its per-instance password
PASS: SYSTEM@ORCL refuses a historic/default password
PASS: SYSTEM@ORCL refuses a historic/default password
PASS: SYSTEM@ORCL accepts its per-instance password
PASS: PDBADMIN@ORCLPDB refuses a historic/default password
PASS: PDBADMIN@ORCLPDB refuses a historic/default password
PASS: PDBADMIN@ORCLPDB accepts its per-instance password
PASS: every other password account is locked
PASS: no literal IDENTIFIED BY password in v$sql or the unified audit trail
PASS: listener.ora binds 127.0.0.1 only
PASS: no database socket on a non-loopback address
PASS: spfile dispatchers are loopback-only
PASS: oracle-db-21c-firstboot.service enabled
PASS: first boot runs the closed-until-proven OCS logic
PASS: first boot runs before oracle-db.service
PASS: oracle-db.service requires first boot
PASS: oracle-db.service gated on the ready marker
PASS: memory fits m5.large: sga_target 2304M, pga_aggregate_target 768M
PASS: systemd has a cgroup2 tree (/sys/fs/cgroup/unified): IPAddressDeny is enforceable
PASS: systemd.legacy_systemd_cgroup_controller=0 on this boot, every installed kernel and /etc/default/grub
PASS: cloudimg-oracle-imds-egress.service enabled and active, nftables table cloudimg_oracle_imds loaded
PASS: oracle-db.service: IPAddressDeny=169.254.169.254/32 168.63.129.16/32 fd00:0ec2:0000:0000:0000:0000:0000:0254/128 ; requires cloudimg-oracle-imds-egress.service
PASS: control: root outside the Oracle units reaches 169.254.169.254 (IMDSv2 token HTTP 200)
PASS: inside oracle-db.service's cgroup (/sys/fs/cgroup/unified/system.slice/oracle-db.service): 169.254.169.254 refused, loopback control connects
PASS: oracle shell outside the units: 169.254.169.254 refused by the nftables rule (counter 2 -> 4), loopback control connects
PASS: no credential value from /root/oracle-db-credentials.txt in /var/log
PASS: no credential value from /root/oracle-db-credentials.txt in the journal (1804 entries)
PASS: first boot order: rotated -> listener (loopback) -> verified -> ready
PASS: listener pid 2954 started at 213.86s, after rotation at 197.47s
PASS: no build secrets or response files left in the image
PASS: no fwupd client private key
SELFTEST_OK

The self-test checks the instance as delivered. After you change a password, open port 1521, lift the metadata block or resize the database memory as this guide describes, the matching checks report FAIL by design.

Step 6: The database service and the listener

oracle-db.service starts the database and the listener at boot, and only after the first boot ready marker exists:

systemctl is-enabled oracle-db.service oracle-db-21c-firstboot.service cloudimg-oracle-imds-egress.service
systemctl is-active oracle-db.service
enabled
enabled
enabled
active

The listener binds 127.0.0.1:1521 and nothing else:

sudo ss -Hltn 'sport = :1521'
LISTEN 0      128    127.0.0.1:1521 0.0.0.0:*

Step 7: Edition and base release

The oracle operating system user's login environment sets ORACLE_HOME, ORACLE_SID and the library path. Query the edition banner and the full version:

sudo su - oracle -c 'sqlplus -S -L / as sysdba' <<'SQL'
SET PAGESIZE 50 LINESIZE 120 FEEDBACK OFF
COLUMN banner FORMAT a80
SELECT banner FROM v$version WHERE banner LIKE 'Oracle%';
SELECT instance_name, version_full, status FROM v$instance;
EXIT;
SQL
BANNER
--------------------------------------------------------------------------------
Oracle Database 21c Enterprise Edition Release 21.0.0.0.0 - Production

INSTANCE_NAME     VERSION_FULL       STATUS
---------------- ----------------- ------------
ORCL         21.3.0.0.0       OPEN

VERSION_FULL is 21.3.0.0.0: the 21c base release with no Release Update applied.

Step 8: The container and the pluggable database

sudo su - oracle -c 'sqlplus -S -L / as sysdba' <<'SQL'
SET PAGESIZE 50 LINESIZE 120 FEEDBACK OFF
COLUMN name FORMAT a12
COLUMN open_mode FORMAT a12
SELECT name, open_mode FROM v$pdbs WHERE name = 'ORCLPDB';
EXIT;
SQL
NAME         OPEN_MODE
------------ ------------
ORCLPDB      READ WRITE

ORCLPDB opens automatically with the container (saved state). Oracle Database 21c allows up to three user pluggable databases per container without the Oracle Multitenant option; more need that separately licensed option.

To work in the pluggable database as SYSTEM, switch to the oracle user and connect to the ORCLPDB service, replacing <SYSTEM_PASSWORD> with the value from the credentials file:

sudo su - oracle
. ~/setEnv.sh
sqlplus system/<SYSTEM_PASSWORD>@//localhost:1521/ORCLPDB

At the SQL> prompt you can create schemas, tables and data:

CREATE TABLE products (
  id    NUMBER GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
  name  VARCHAR2(100),
  price NUMBER(10,2)
);
INSERT INTO products (name, price) VALUES ('Mechanical Keyboard', 89.00);
COMMIT;
SELECT name, price FROM products;

For the container root use sqlplus / as sysdba as the oracle user, or sqlplus sys/<SYS_PASSWORD>@//localhost:1521/ORCL as sysdba; to administer the PDB as its own administrator use sqlplus pdbadmin/<PDBADMIN_PASSWORD>@//localhost:1521/ORCLPDB.

Step 9: Connect from SQL Developer or JDBC

Through an SSH tunnel (recommended)

On your workstation, forward local port 1521 to the listener and leave the tunnel running:

ssh -i <your-key.pem> -N -L 1521:127.0.0.1:1521 ec2-user@<public-ip>

Then connect to localhost:1521:

Host:          localhost (through the SSH tunnel)
Port:          1521
Service name:  ORCLPDB
Username:      SYSTEM
Password:      <your generated SYSTEM password>
JDBC URL:      jdbc:oracle:thin:@//localhost:1521/ORCLPDB

In SQL Developer, create a connection of type Basic with those values and choose Test, then Connect. For the container root use the ORCL service and connect as SYS with the SYSDBA role.

Opening port 1521 deliberately

If applications must connect directly, do all three steps, and only for your own networks:

1. Require Oracle native network encryption and integrity. As the oracle user:

sudo su - oracle
. ~/setEnv.sh
cat >> $ORACLE_HOME/network/admin/sqlnet.ora <<'EOF'
SQLNET.ENCRYPTION_SERVER = required
SQLNET.ENCRYPTION_TYPES_SERVER = (AES256)
SQLNET.CRYPTO_CHECKSUM_SERVER = required
SQLNET.CRYPTO_CHECKSUM_TYPES_SERVER = (SHA256)
EOF

Or configure TCPS (TLS) with an Oracle wallet: see "Configuring Transport Layer Security Authentication" in the Oracle Database 21c Security Guide, https://docs.oracle.com/en/database/oracle/oracle-database/21/dbseg/configuring-transport-layer-security-authentication.html.

2. Bind the listener to all interfaces and restart it as the oracle user:

sed -i 's/(HOST = 127.0.0.1)/(HOST = 0.0.0.0)/' $ORACLE_HOME/network/admin/listener.ora
lsnrctl stop
lsnrctl start

3. Allow TCP 1521 from your own CIDR only, never 0.0.0.0/0:

aws ec2 authorize-security-group-ingress --group-id <security-group-id> --protocol tcp --port 1521 --cidr <your-cidr>

The JDBC thin driver and SQL Developer negotiate the required encryption automatically.

Step 10: Instance metadata is blocked for the database

Oracle SQL can reach any address once a DBA grants a network ACL (UTL_HTTP with custom headers can even request an IMDSv2 token). So that a SQL injection in your application can never read the instance role's credentials, the image blocks the instance metadata service for the database in two independent ways:

  • oracle-db.service carries IPAddressDeny=169.254.169.254 168.63.129.16 fd00:ec2::254 (a systemd drop-in), which covers every process the service starts. systemd enforces this on a cgroup2 tree, so the image boots Oracle Linux 8 with systemd.legacy_systemd_cgroup_controller=0 (the hybrid hierarchy; all cgroup v1 controllers are unchanged).
  • cloudimg-oracle-imds-egress.service loads an nftables rule that rejects the same addresses for sockets owned by the oracle user, so an instance or listener started from a shell outside the service is blocked too.

See the drop-in and the rule:

systemctl cat oracle-db.service | sed -n '/cloudimg-imds-egress.conf/,$p'
sudo nft list table inet cloudimg_oracle_imds
# /etc/systemd/system/oracle-db.service.d/cloudimg-imds-egress.conf
# cloudimg: no process of this Oracle unit reaches instance metadata (oracle-db-secure.sh)
[Unit]
Requires=cloudimg-oracle-imds-egress.service
After=cloudimg-oracle-imds-egress.service

[Service]
IPAddressDeny=169.254.169.254 168.63.129.16 fd00:ec2::254
table inet cloudimg_oracle_imds {
    chain output {
        type filter hook output priority filter; policy accept;
        meta skuid 54321 ip daddr { 168.63.129.16, 169.254.169.254 } counter packets 4 bytes 240 reject with icmp port-unreachable
        meta skuid 54321 ip6 daddr fd00:ec2::254 counter packets 0 bytes 0 reject with icmpv6 port-unreachable
    }
}

Prove it: root on the instance still gets an IMDSv2 token, the oracle user does not:

TOKEN_CODE=$(curl -s -o /dev/null -w '%{http_code}' -X PUT -H 'X-aws-ec2-metadata-token-ttl-seconds: 60' http://169.254.169.254/latest/api/token)
echo "root: HTTP $TOKEN_CODE"
sudo -u oracle curl -s -m 5 -o /dev/null -w 'oracle: HTTP %{http_code}\n' -X PUT -H 'X-aws-ec2-metadata-token-ttl-seconds: 60' http://169.254.169.254/latest/api/token || true
root: HTTP 200
oracle: HTTP 000

HTTP 000 means the connection was refused before any HTTP exchange. The self-test (step 5) also probes from inside the running oracle-db.service.

If your database genuinely needs AWS credentials, for example DBMS_CLOUD against Amazon S3, prefer a credential object with keys scoped to that bucket over the instance role. To lift the block deliberately, remove /etc/systemd/system/oracle-db.service.d/cloudimg-imds-egress.conf, run sudo systemctl disable --now cloudimg-oracle-imds-egress.service and sudo systemctl daemon-reload, then restart oracle-db.service.

Step 11: The Oracle data volume

The Oracle home, datafiles, redo logs and archive area are on a dedicated EBS volume mounted at /opt/oracle:

findmnt -no SOURCE,FSTYPE,TARGET /opt/oracle
df -h /opt/oracle
/dev/nvme1n1 ext4   /opt/oracle
Filesystem      Size  Used Avail Use% Mounted on
/dev/nvme1n1     30G   12G   17G  41% /opt/oracle

Snapshot and grow this volume independently of the root volume. After growing it in EC2, extend the filesystem with sudo resize2fs <device>.

Step 12: Managing the database

systemctl status oracle-db.service --no-pager
sudo systemctl stop oracle-db.service      # dbshut: clean shutdown
sudo systemctl start oracle-db.service     # dbstart
sudo systemctl restart oracle-db.service

Memory and instance size

The starter database uses automatic shared memory management with 3072 MB in total (an sga_target of about 2.3 GiB and a pga_aggregate_target of about 768 MiB) to fit m5.large. On a larger instance raise it as SYSDBA in the container root, leaving at least 2 GiB for the operating system, then restart the service:

ALTER SYSTEM SET sga_max_size=6G SCOPE=SPFILE;
ALTER SYSTEM SET sga_target=6G SCOPE=SPFILE;
ALTER SYSTEM SET pga_aggregate_target=2G SCOPE=BOTH;

Logs

The alert log and the listener log live under /opt/oracle/diag:

sudo su - oracle -c 'find /opt/oracle/diag -name alert_ORCL.log'

AWS CLI

The image does not ship the AWS CLI. Install the current version 2 from AWS if you need it: https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html.

Step 13: Backups

Take an online backup of the container and its pluggable databases with Recovery Manager as the oracle user. Online backups need the database in ARCHIVELOG mode (the starter database runs in NOARCHIVELOG; switching needs a restart into MOUNT and ALTER DATABASE ARCHIVELOG):

sudo su - oracle
. ~/setEnv.sh
rman target / <<'RMAN'
BACKUP DATABASE PLUS ARCHIVELOG;
RMAN

For schema level exports use Data Pump against the pluggable database:

expdp system/<SYSTEM_PASSWORD>@//localhost:1521/ORCLPDB schemas=APPUSER directory=DATA_PUMP_DIR dumpfile=appuser.dmp

You can also snapshot the /opt/oracle EBS volume; shut the database down cleanly first (or use backup mode) for a consistent copy.

Step 14: Licensing and patching

Bring Your Own License. The AMI price covers cloudimg's image hardening, first boot setup and support; it includes no Oracle licence. Run this image only under your own Oracle Database Enterprise Edition licence, and license any Enterprise Edition option or management pack you use (for example Partitioning, Advanced Security, Multitenant beyond three PDBs, Diagnostics and Tuning Packs) under your Oracle agreement. Oracle Database is a product of Oracle Corporation; cloudimg is not affiliated with or endorsed by Oracle.

Patching. The image ships the 21.3.0.0.0 base release with no Release Update, OJVM or other Oracle patch. Oracle Release Updates are available to customers with Oracle support: apply them with your own Oracle support entitlement, using Oracle's patching documentation (https://docs.oracle.com/en/database/oracle/oracle-database/21/upgrd/autoupgrade-patching.html) and the Critical Patch Update advisories (https://www.oracle.com/security-alerts/). Oracle Linux packages were fully patched when the image was built; keep them current with sudo dnf upgrade.

Support

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

For Oracle Database administration questions see the Oracle Database 21c documentation at https://docs.oracle.com/en/database/oracle/oracle-database/21/.