Deployment guide

Oracle Database 26ai EE on Oracle Linux 9

Overview

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

The image runs Oracle Database 26ai Enterprise Edition 23.26.1.0.0, the base release from Oracle's installation media, on a fully patched Oracle Linux 9. 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 license is included.

Oracle Database 26ai (the 23.26 release of the 23ai long-term support family) 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. 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 database and no password in the image. The AMI carries the Oracle home only. On first boot, a one-shot service, oracle-db-26ai-firstboot.service, creates the database on your instance with dbca (about 15 minutes on a 2 vCPU, 8 GiB instance such as m5.large). No two instances share a database, a DBID or any password history. With no listener running, the service sets unique random passwords on SYS, SYSTEM and PDBADMIN, re-keys every other password account with a random value and leaves it locked. It then starts the listener on 127.0.0.1 only and proves that each new password works and 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). Every Oracle unit therefore 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.

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 license 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, then choose Subscribe.
  • Choose Launch your software, then choose Launch from EC2 Console.
  • In Version, choose Oracle Database 26ai EE on Oracle Linux 9.
  • 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) and set Metadata response hop limit to 1.
  • Choose Launch instance.

First boot creates the database, then sets and proves the passwords before the database can accept connections. Allow about 15 minutes after the status checks pass. Step 3 shows how to follow progress.

2

Launch from the AWS CLI

Replace the angle-bracket 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-26ai-ee}]'

The AMI provides the root volume (50 GiB) and the /opt/oracle data volume (40 GiB). To get more space, grow either one at launch with --block-device-mappings. When the instance is running, read its public address:

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

Connect over SSH

The SSH login user for Oracle Database 26ai EE on Oracle Linux 9 is ec2-user.

ssh -i <your-key.pem> ec2-user@<public-ip>

SSH comes up while first boot is still creating the database. This takes about 15 minutes on a 2 vCPU, 8 GiB instance such as m5.large, measured on a fresh instance. Follow progress live. dbca prints its progress as percentages:

sudo journalctl -u oracle-db-26ai-firstboot.service -b -f

The ready marker exists only after the database is created and every password is set and proven. Check for it:

sudo test -f /var/lib/sac/data/oracle-db-26ai-firstboot.done && echo "first boot complete"
first boot complete

If nothing prints, first boot is still running. First boot also records its milestones in /var/log/sac/oracle-db-firstboot.events (root only): db-create-start, db-created, rotated, listener-started, verified and ready.

sudo cat /var/log/sac/oracle-db-firstboot.events | awk '{print $1"s", $3, $4}'

On a fresh t3.large (2 vCPU, 8 GiB, the same size as m5.large), the timeline in seconds since boot was:

31.11s start 42.53s db-create-start 856.81s db-created 814s 857.70s rotated 859.18s listener-started 127.0.0.1:1521 870.02s verified 898.80s ready

Creating the database takes most of the time (814 seconds here). The first read of each block of the AMI's volumes comes from Amazon S3 while EBS loads the blocks lazily, so first boot is slower than later restarts.

4

Retrieve the generated credentials

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

The file looks like this. Your instance has its own unique passwords:

# Oracle Database 26ai 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, is locked and expired with a random password that nobody knows. 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 that file 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-26ai-firstboot.service -b sudo reboot

If the notice says database creation failed, a reboot cannot repair a half-created database. Terminate the instance and launch a new one, because each launch creates its own database. If first boot fails again, contact SecureAnyCloud support and include the journal output.

5

Run the security self-test

The image includes 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 is the 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-26ai-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: Oracle home is the 23.26.1.0.0 base release (oraversion -compositeVersion; no Release Update) PASS: Oracle home edition EE PASS: database VERSION_FULL 23.26.1.0.0 PASS: database created by this instance's first boot (814s with dbca) PASS: systemd has a cgroup2 tree (/sys/fs/cgroup): IPAddressDeny is enforceable PASS: sac-oracle-imds-egress.service enabled and active, nftables table sac_oracle_imds loaded PASS: oracle-db.service: IPAddressDeny=168.63.129.16/32 169.254.169.254/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/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 3 -> 5), 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 (4085 entries) PASS: first boot order: rotated -> listener (loopback) -> verified -> ready PASS: listener pid 14895 started at 899.05s, after rotation at 857.70s 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-26ai-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 1024 127.0.0.1:1521 0.0.0.0:*
7

Edition and base release

The login environment of the oracle operating system user 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 AI Database 26ai Enterprise Edition Release 23.26.1.0.0 - Production INSTANCE_NAME VERSION_FULL STATUS ---------------- ----------------- ------------ ORCL 23.26.1.0.0 OPEN

VERSION_FULL is 23.26.1.0.0: the 26ai 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). Your instance's first boot created the database, which is not part of the AMI. Its creation time is therefore your first boot, and its DBID is unique to the instance. Oracle Database 26ai 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, 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.

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, then choose Test and 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 of these actions, 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

Or configure TCPS (TLS) with an Oracle wallet. See "Configuring Transport Layer Security Encryption" in the Oracle AI Database 26ai Security Guide, https://docs.oracle.com/en/database/oracle/oracle-database/26/dbseg/configuring-transport-layer-security-encryption.html.

2. Bind the listener to all interfaces and restart it. As the oracle user, run:

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 from 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). To make sure 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, which Oracle Linux 9 boots by default.
  • 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 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 5 bytes 300 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, but 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, run sudo systemctl disable --now sac-oracle-imds-egress.service and sudo systemctl daemon-reload, then restart oracle-db.service.

11

The Oracle data volume

The Oracle home, datafiles, redo logs and archive area live 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 40G 10G 28G 27% /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 (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 these values 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 are under /opt/oracle/diag:

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

AWS CLI

The image does not include 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

As the oracle user, take an online backup of the container and its pluggable databases with Recovery Manager. 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. For a consistent copy, shut the database down cleanly first (or use backup mode).

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 includes no Oracle license. Run this image only under your own Oracle Database Enterprise Edition license. 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; SecureAnyCloud is not affiliated with or endorsed by Oracle.

Patching. The image contains the 23.26.1.0.0 base release with no Release Update, OJVM or other Oracle patch. Oracle makes Release Updates available to customers with Oracle support. Apply them with your own Oracle support entitlement, following Oracle's patching documentation (https://docs.oracle.com/en/database/oracle/oracle-database/26/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 26ai documentation at https://docs.oracle.com/en/database/oracle/oracle-database/26/.

Deploy on AWS

Launch Oracle Database 26ai EE on Oracle Linux 9 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