Deployment guide

Oracle Database 21c EE on Oracle Linux 8

Overview

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, so no Oracle Database licence is included.

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

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

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

What you get:

  • No usable password ships in the image. On 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. It re-keys every other password account with a random value and leaves those accounts locked. It then starts the listener on 127.0.0.1 only, and proves that each new password works and that each historic and default password is refused. Only then does it write /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 you 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. 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.

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).

1

Launch the instance from AWS Marketplace

  • Open the listing, choose View purchase options, and then choose Subscribe.
  • Choose Launch your software, and then choose Launch from EC2 Console.
  • In Version, choose Oracle Database 21c EE on Oracle Linux 8.
  • 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.
  • Under Advanced details, keep Metadata version at V2 only (token required). Set the Metadata response hop limit to 1.
  • Choose Launch instance.

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

2

Launch from the AWS CLI

Replace the example values 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. If you need more space, grow either volume at launch with --block-device-mappings.

Read the public address once the instance is running:

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

Connect over SSH

For Oracle Database 21c EE on Oracle Linux 8, the SSH login user is 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/sac/data/oracle-db-21c-firstboot.done && echo "first boot complete"
first boot complete

If nothing prints, first boot is still running. Follow its progress with sudo journalctl -u oracle-db-21c-firstboot.service -b -f.

4

Retrieve the generated credentials

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

The file looks like this. The passwords on your instance are unique to it:

# Oracle Database 21c Enterprise Edition - per-instance credentials (sac)
# 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 (the login profile sets ORACLE_HOME) ; sqlplus /nolog ; then CONNECT system@//localhost:1521/ORCLPDB

Use SYSTEM for administrative and application connections, SYS for SYSDBA operations, and PDBADMIN to administer ORCLPDB. Only root can read the file:

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 that nobody knows and is locked and expired. To use one of these accounts, 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 that file and the journal, and 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 SecureAnyCloud support with the journal output.

5

Run the security self-test

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

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

Each check prints a PASS line, and the run ends with SELFTEST_OK. This output is 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: sac-oracle-imds-egress.service enabled and active, nftables table sac_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 sac-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. If 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.

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 sac-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:*
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, which is the 21c base release with no Release Update applied.

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 than three need that separately licensed option.

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

sudo su - oracle 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, run sqlplus / as sysdba as the oracle user, or run sqlplus sys/<SYS_PASSWORD>@//localhost:1521/ORCL as sysdba.

To administer the PDB as its own administrator, run sqlplus pdbadmin/<PDBADMIN_PASSWORD>@//localhost:1521/ORCLPDB.

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, complete all three steps below, and only for your own networks.

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

sudo su - oracle 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

Alternatively, 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.

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 in 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.
  • sac-oracle-imds-egress.service loads an nftables rule that rejects the same addresses for sockets owned by the oracle user. An instance or listener started from a shell outside the service is therefore blocked too.

View the drop-in and the rule:

systemctl cat oracle-db.service | sed -n '/sac-imds-egress.conf/,$p'
sudo nft list table inet sac_oracle_imds
# /etc/systemd/system/oracle-db.service.d/sac-imds-egress.conf
# sac: no process of this Oracle unit reaches instance metadata (oracle-db-secure.sh) [Unit] Requires=sac-oracle-imds-egress.service After=sac-oracle-imds-egress.service [Service] IPAddressDeny=169.254.169.254 168.63.129.16 fd00:ec2::254 table inet sac_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 the block works. Root on the instance still gets an IMDSv2 token, and 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/sac-imds-egress.conf. Then run sudo systemctl disable --now sac-oracle-imds-egress.service and sudo systemctl daemon-reload, and restart oracle-db.service.

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

You can snapshot and grow this volume independently of the root volume. After you grow it in EC2, extend the filesystem with sudo resize2fs <device>.

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, to fit m5.large. That is an sga_target of about 2.3 GiB and a pga_aggregate_target of about 768 MiB.

On a larger instance, raise the memory 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. If you need it, install the current version 2 from AWS: https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html.

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, and switching needs a restart into MOUNT and ALTER DATABASE ARCHIVELOG:

sudo su - oracle 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.

14

Licensing and patching

This listing is Bring Your Own License. The AMI price covers SecureAnyCloud's image hardening, first-boot setup and support, and it includes no Oracle licence. Run this image only under your own Oracle Database Enterprise Edition licence.

License any Enterprise Edition option or management pack you use under your Oracle agreement. Examples are Partitioning, Advanced Security, Multitenant beyond three PDBs, and the Diagnostics and Tuning Packs. Oracle Database is a product of Oracle Corporation. SecureAnyCloud 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: sales@secureanycloud.com
  • Website: https://secureanycloud.com
  • 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/.

Deploy on AWS

Launch Oracle Database 21c EE on Oracle Linux 8 on AWS with 24/7 support from SecureAnyCloud.

View on Marketplace

Need Help?

Our support team is available 24/7.

sales@secureanycloud.com

Deploy on AWS