Required Oracle Grants
Grant these privileges to the TuneVault service account before running health checks.
🔒 Minimum required grants
Run as SYS or SYSTEM. Replace tunevault_user with your service account name.
-- Core catalog access (covers DBA_* views, V$* views) GRANT SELECT_CATALOG_ROLE TO tunevault_user; GRANT CREATE SESSION TO tunevault_user; -- Alert log access (not covered by SELECT_CATALOG_ROLE) GRANT SELECT ON V_$DIAG_ALERT_EXT TO tunevault_user;
control_management_pack_access = DIAGNOSTIC+TUNING). TuneVault checks for this license and skips these queries gracefully on Standard Edition.
SQL Queries Executed Against Your Database
All queries are read-only SELECTs run during a health check. Click any row to see the literal SQL.
SELECT d.NAME, i.INSTANCE_NAME, i.HOST_NAME, i.VERSION, d.PLATFORM_NAME, TO_CHAR(i.STARTUP_TIME, 'YYYY-MM-DD HH24:MI:SS'), ROUND(SYSDATE - i.STARTUP_TIME), (SELECT VALUE FROM v$parameter WHERE name = 'cpu_count'), ROUND((SELECT TO_NUMBER(VALUE)/1024/1024/1024 FROM v$parameter WHERE name = 'sga_target'), 1), ROUND((SELECT TO_NUMBER(VALUE)/1024/1024/1024 FROM v$parameter WHERE name = 'pga_aggregate_target'), 1), (SELECT TO_NUMBER(VALUE) FROM v$parameter WHERE name = 'db_block_size') FROM v$database d, v$instance i
SELECT BANNER FROM V$VERSION WHERE ROWNUM = 1
SELECT
ts.TABLESPACE_NAME,
ROUND(um.USED_SPACE * ts.BLOCK_SIZE / 1024/1024/1024, 1) AS used_gb,
ROUND(um.TABLESPACE_SIZE * ts.BLOCK_SIZE / 1024/1024/1024, 1) AS total_gb,
ROUND(um.USED_PERCENT, 1) AS pct_used,
CASE WHEN df.autoext > 0 THEN 1 ELSE 0 END AS autoextend
FROM DBA_TABLESPACE_USAGE_METRICS um
JOIN DBA_TABLESPACES ts ON ts.TABLESPACE_NAME = um.TABLESPACE_NAME
LEFT JOIN (
SELECT TABLESPACE_NAME,
SUM(CASE WHEN AUTOEXTENSIBLE = 'YES' THEN 1 ELSE 0 END) AS autoext
FROM DBA_DATA_FILES GROUP BY TABLESPACE_NAME
) df ON df.TABLESPACE_NAME = um.TABLESPACE_NAME
ORDER BY um.USED_PERCENT DESCSELECT UNDOBLKS, TXNCOUNT, MAXQUERYLEN, MAXCONCURRENCY,
TUNED_UNDORETENTION, EXPIREDBLKS, UNEXPIREDBLKS, ACTIVEBLKS
FROM V$UNDOSTAT WHERE ROWNUM = 1 ORDER BY END_TIME DESC
-- Undo tablespace size
SELECT d.TABLESPACE_NAME, SUM(d.BYTES)/1073741824,
SUM(d.BYTES - NVL(f.FREE_BYTES,0))/1073741824,
ROUND(SUM(d.BYTES - NVL(f.FREE_BYTES,0))/SUM(d.BYTES)*100,1), t.RETENTION
FROM DBA_DATA_FILES d JOIN DBA_TABLESPACES t ON t.TABLESPACE_NAME=d.TABLESPACE_NAME
LEFT JOIN (SELECT FILE_ID, SUM(BYTES) AS FREE_BYTES FROM DBA_FREE_SPACE GROUP BY FILE_ID) f
ON f.FILE_ID=d.FILE_ID
WHERE t.CONTENTS='UNDO' GROUP BY d.TABLESPACE_NAME, t.RETENTIONSELECT TABLESPACE_NAME, ROUND(TABLESPACE_SIZE/1073741824,2),
ROUND(FREE_SPACE/1073741824,2),
ROUND((TABLESPACE_SIZE-FREE_SPACE)/NULLIF(TABLESPACE_SIZE,0)*100,1)
FROM DBA_TEMP_FREE_SPACE
-- Top temp-consuming sessions
SELECT s.SID, s.SERIAL#, s.USERNAME,
ROUND(s.BLOCKS*8192/1048576,1), s.TABLESPACE
FROM V$TEMPSEG_USAGE s ORDER BY s.BLOCKS DESC FETCH FIRST 10 ROWS ONLYSELECT OWNER, SEGMENT_NAME, SEGMENT_TYPE,
ROUND(SUM(BYTES)/1073741824, 2) AS size_gb
FROM DBA_SEGMENTS
WHERE OWNER NOT IN ('SYS','SYSTEM','DBSNMP','SYSMAN','OUTLN','MDSYS',
'ORDSYS','XDB','CTXSYS','WMSYS','EXFSYS')
GROUP BY OWNER, SEGMENT_NAME, SEGMENT_TYPE
ORDER BY size_gb DESC FETCH FIRST 10 ROWS ONLYSELECT COUNT(*) AS problem_count,
COUNT(CASE WHEN STATUS = 'OFFLINE' THEN 1 END) AS offline_count
FROM DBA_DATA_FILES WHERE STATUS NOT IN ('AVAILABLE','ONLINE')
SELECT COUNT(*) AS object_count,
ROUND(SUM(SPACE)*8192/1073741824, 2) AS size_gb
FROM DBA_RECYCLEBINSELECT ROUND(SUM(VALUE)/1024/1024/1024, 1) FROM V$SGA
-- Buffer cache hit ratio
SELECT ROUND((1 - (phys.VALUE / (db_gets.VALUE + con_gets.VALUE))) * 100, 1)
FROM (SELECT VALUE FROM V$SYSSTAT WHERE NAME = 'physical reads') phys,
(SELECT VALUE FROM V$SYSSTAT WHERE NAME = 'db block gets') db_gets,
(SELECT VALUE FROM V$SYSSTAT WHERE NAME = 'consistent gets') con_gets
-- Library cache hit ratio
SELECT ROUND(SUM(PINS - RELOADS) / NULLIF(SUM(PINS), 0) * 100, 1) FROM V$LIBRARYCACHE
-- Dictionary cache hit ratio
SELECT ROUND(SUM(GETS - GETMISSES) / NULLIF(SUM(GETS), 0) * 100, 1) FROM V$ROWCACHE
-- Shared pool free %
SELECT ROUND(free_bytes.val / total_bytes.val * 100, 1)
FROM (SELECT SUM(BYTES) AS val FROM V$SGASTAT WHERE POOL='shared pool' AND NAME='free memory') free_bytes,
(SELECT SUM(BYTES) AS val FROM V$SGASTAT WHERE POOL='shared pool') total_bytesSELECT ROUND((SELECT TO_NUMBER(VALUE)/1024/1024/1024 FROM v$parameter WHERE name='pga_aggregate_target'),1), ROUND((SELECT VALUE/1024/1024/1024 FROM V$PGASTAT WHERE NAME='total PGA allocated'),1), ROUND((SELECT VALUE/1024/1024/1024 FROM V$PGASTAT WHERE NAME='maximum PGA allocated'),1), (SELECT VALUE FROM V$PGASTAT WHERE NAME='over allocation count'), ROUND((SELECT VALUE FROM V$PGASTAT WHERE NAME='cache hit percentage'),1) FROM DUAL
SELECT STAT_NAME, VALUE FROM V$OSSTAT WHERE STAT_NAME IN ( 'NUM_CPUS','IDLE_TIME','BUSY_TIME','USER_TIME', 'SYS_TIME','IOWAIT_TIME', 'PHYSICAL_MEMORY_BYTES','FREE_MEMORY_BYTES' )
SELECT SQL_ID, SUBSTR(SQL_TEXT, 1, 500), EXECUTIONS,
ROUND(ELAPSED_TIME/1000000, 1), ROUND(CPU_TIME/1000000, 1),
BUFFER_GETS, DISK_READS, ROWS_PROCESSED,
CASE WHEN EXECUTIONS > 0 THEN ROUND(ELAPSED_TIME/EXECUTIONS/1000, 2) ELSE 0 END,
CASE WHEN EXECUTIONS > 0 THEN ROUND(BUFFER_GETS/EXECUTIONS) ELSE 0 END,
PLAN_HASH_VALUE
FROM V$SQL
WHERE EXECUTIONS > 0 AND ELAPSED_TIME > 0
AND PARSING_SCHEMA_NAME NOT IN ('SYS','SYSTEM','DBSNMP','SYSMAN','OUTLN',
'MDSYS','ORDSYS','EXFSYS','WMSYS','APPQOSSYS','DBSFWUSER')
AND SQL_TEXT NOT LIKE '%v$%' AND SQL_TEXT NOT LIKE '%V$%'
AND COMMAND_TYPE IN (2, 3, 6, 7, 189)
ORDER BY ELAPSED_TIME DESC FETCH FIRST 10 ROWS ONLYSELECT EVENT, WAIT_CLASS, TOTAL_WAITS,
ROUND(TIME_WAITED/100, 1) AS time_waited_s,
CASE WHEN TOTAL_WAITS > 0
THEN ROUND((TIME_WAITED/100/TOTAL_WAITS)*1000, 2) ELSE 0 END AS avg_wait_ms
FROM V$SYSTEM_EVENT
WHERE WAIT_CLASS NOT IN ('Idle') AND TOTAL_WAITS > 0
ORDER BY TIME_WAITED DESC FETCH FIRST 15 ROWS ONLYSELECT i.OWNER, i.INDEX_NAME, i.TABLE_NAME,
ROUND(s.LEAF_BLOCKS *
(SELECT TO_NUMBER(VALUE) FROM v$parameter WHERE name='db_block_size')
/ 1024/1024) AS size_mb,
i.BLEVEL, s.LEAF_BLOCKS, i.CLUSTERING_FACTOR,
NVL(s.PCT_DIRECT_ACCESS,100), i.STATUS,
CASE
WHEN i.STATUS != 'VALID' THEN 'unusable'
WHEN i.BLEVEL > 4 THEN 'critical'
WHEN NVL(s.PCT_DIRECT_ACCESS,100) < 50 THEN 'critical'
WHEN i.BLEVEL > 3 THEN 'fragmented'
WHEN NVL(s.PCT_DIRECT_ACCESS,100) < 70 THEN 'fragmented'
ELSE 'ok'
END AS health_status
FROM DBA_INDEXES i
LEFT JOIN DBA_IND_STATISTICS s
ON s.OWNER=i.OWNER AND s.INDEX_NAME=i.INDEX_NAME AND s.PARTITION_NAME IS NULL
WHERE i.OWNER NOT IN ('SYS','SYSTEM','DBSNMP','SYSMAN','OUTLN','MDSYS','ORDSYS',
'EXFSYS','WMSYS','XDB','CTXSYS','APPQOSSYS','DBSFWUSER',
'APEX_040000','APEX_040200','APEX_050000','FLOWS_FILES')
AND i.INDEX_TYPE = 'NORMAL' AND NVL(s.LEAF_BLOCKS, 0) > 100
ORDER BY CASE WHEN i.STATUS != 'VALID' THEN 1 WHEN i.BLEVEL > 4 THEN 2
WHEN i.BLEVEL > 3 THEN 3 ELSE 4 END,
s.LEAF_BLOCKS DESC NULLS LAST
FETCH FIRST 20 ROWS ONLY-- Resource limits
SELECT RESOURCE_NAME, CURRENT_UTILIZATION, MAX_UTILIZATION, INITIAL_ALLOCATION, LIMIT_VALUE
FROM V$RESOURCE_LIMIT
WHERE RESOURCE_NAME IN (
'sessions','processes','enqueue_locks','enqueue_resources',
'dml_locks','temporary_table_locks','transactions','max_rollback_segments'
)
-- Session counts
SELECT COUNT(*), COUNT(CASE WHEN STATUS='ACTIVE' AND TYPE='USER' THEN 1 END),
COUNT(CASE WHEN TYPE='USER' THEN 1 END) FROM V$SESSION
-- Blocked sessions
SELECT COUNT(*) FROM V$SESSION WHERE BLOCKING_SESSION IS NOT NULL AND STATUS='ACTIVE'
-- Long-running SQL
SELECT COUNT(*), ROUND(MAX((SYSDATE - SQL_EXEC_START)*1440), 1)
FROM V$SESSION WHERE STATUS='ACTIVE' AND TYPE='USER'
AND SQL_EXEC_START IS NOT NULL AND (SYSDATE - SQL_EXEC_START)*1440 > 5SELECT d.VALUE AS disk_sorts, m.VALUE AS mem_sorts
FROM (SELECT VALUE FROM V$SYSSTAT WHERE NAME='sorts (disk)') d,
(SELECT VALUE FROM V$SYSSTAT WHERE NAME='sorts (memory)') m
SELECT s.VALUE AS long_scans, i.VALUE AS index_lookups
FROM (SELECT VALUE FROM V$SYSSTAT WHERE NAME='table scans (long tables)') s,
(SELECT VALUE FROM V$SYSSTAT WHERE NAME='table fetch by rowid') iSELECT COUNT(*),
COUNT(CASE WHEN OBJECT_TYPE IN ('PACKAGE BODY','PACKAGE') THEN 1 END),
COUNT(CASE WHEN OBJECT_TYPE='PROCEDURE' THEN 1 END),
COUNT(CASE WHEN OBJECT_TYPE='VIEW' THEN 1 END),
COUNT(CASE WHEN OBJECT_TYPE='TRIGGER' THEN 1 END)
FROM DBA_OBJECTS WHERE STATUS='INVALID'
AND OWNER NOT IN ('SYS','SYSTEM','DBSNMP','SYSMAN','OUTLN','MDSYS','ORDSYS',
'XDB','CTXSYS','WMSYS','EXFSYS','APPQOSSYS','DBSFWUSER','OJVMSYS','DVSYS','LBACSYS')
SELECT COUNT(*), COUNT(CASE WHEN LAST_ANALYZED IS NULL THEN 1 END)
FROM DBA_TAB_STATISTICS WHERE STALE_STATS='YES'
AND OWNER NOT IN ('SYS','SYSTEM','DBSNMP','SYSMAN','OUTLN','MDSYS','ORDSYS','XDB','CTXSYS','WMSYS','EXFSYS')
-- SCN headroom
SELECT CURRENT_SCN,
ROUND((TO_NUMBER(SYSDATE-TO_DATE('01-01-1988','DD-MM-YYYY'))*24*3600*16384*1024-CURRENT_SCN)/(24*3600*16384),0)
FROM V$DATABASESELECT TO_CHAR(ORIGINATING_TIMESTAMP,'YYYY-MM-DD HH24:MI:SS'), MESSAGE_TEXT
FROM V$DIAG_ALERT_EXT
WHERE ORIGINATING_TIMESTAMP > SYSDATE-1
AND (MESSAGE_TEXT LIKE 'ORA-%' OR MESSAGE_TEXT LIKE '%checkpoint%'
OR MESSAGE_TEXT LIKE '%corruption%' OR MESSAGE_TEXT LIKE '%recovery%'
OR MESSAGE_TEXT LIKE '%error%' OR MESSAGE_TEXT LIKE '%warning%'
OR MESSAGE_TEXT LIKE '%TNS-%' OR MESSAGE_TEXT LIKE '%instance%'
OR MESSAGE_TEXT LIKE 'Thread%')
ORDER BY ORIGINATING_TIMESTAMP DESC FETCH FIRST 200 ROWS ONLY
-- Note: requires GRANT SELECT ON V_$DIAG_ALERT_EXT (not in SELECT_CATALOG_ROLE)SELECT INPUT_TYPE, STATUS,
TO_CHAR(START_TIME,'YYYY-MM-DD HH24:MI:SS'),
TO_CHAR(END_TIME,'YYYY-MM-DD HH24:MI:SS'),
ROUND((SYSDATE - END_TIME)*24, 1) AS hours_ago,
ROUND(OUTPUT_BYTES/1073741824, 2) AS size_gb, ELAPSED_SECONDS
FROM (
SELECT INPUT_TYPE, STATUS, START_TIME, END_TIME, OUTPUT_BYTES, ELAPSED_SECONDS,
ROW_NUMBER() OVER (PARTITION BY INPUT_TYPE ORDER BY END_TIME DESC) AS RN
FROM V$RMAN_BACKUP_JOB_DETAILS WHERE STATUS='COMPLETED'
) WHERE RN=1SELECT NAME, ROUND(SPACE_LIMIT/1073741824,2), ROUND(SPACE_USED/1073741824,2),
ROUND(SPACE_RECLAIMABLE/1073741824,2), NUMBER_OF_FILES
FROM V$RECOVERY_FILE_DEST
SELECT FILE_TYPE, ROUND(PERCENT_SPACE_USED,1),
ROUND(PERCENT_SPACE_RECLAIMABLE,1), NUMBER_OF_FILES
FROM V$FLASH_RECOVERY_AREA_USAGE ORDER BY PERCENT_SPACE_USED DESC
SELECT ROUND(SUM(BLOCKS * BLOCK_SIZE)/1073741824, 2)
FROM V$ARCHIVED_LOG WHERE COMPLETION_TIME > SYSDATE-1 AND STANDBY_DEST='NO'SELECT LOG_MODE, ROUND((SYSDATE - STARTUP_TIME)*24) FROM V$DATABASE, V$INSTANCE
SELECT TO_CHAR(COMPLETION_TIME,'YYYY-MM-DD HH24'), COUNT(*),
ROUND(SUM(BLOCKS*BLOCK_SIZE)/1048576, 1)
FROM V$ARCHIVED_LOG WHERE COMPLETION_TIME > SYSDATE-1 AND STANDBY_DEST='NO'
GROUP BY TO_CHAR(COMPLETION_TIME,'YYYY-MM-DD HH24') ORDER BY 1 DESC
SELECT ROUND(COUNT(*)/24.0, 1), COUNT(*) FROM V$LOG_HISTORY WHERE FIRST_TIME > SYSDATE-1SELECT COUNT(*), SUM(BLOCKS) FROM V$BACKUP_CORRUPTION SELECT COUNT(*), SUM(BLOCKS) FROM V$COPY_CORRUPTION
SELECT COUNT(*) FROM DBA_USERS_WITH_DEFPWD WHERE ACCOUNT_STATUS = 'OPEN'
-- Dangerous PUBLIC grants
SELECT COUNT(*) FROM DBA_SYS_PRIVS WHERE GRANTEE = 'PUBLIC'
AND PRIVILEGE IN ('CREATE PROCEDURE','CREATE ANY PROCEDURE',
'CREATE ANY TRIGGER','ALTER SYSTEM','ALTER DATABASE',
'DROP ANY TABLE','EXECUTE ANY PROCEDURE')
-- DBA-privileged user count
SELECT COUNT(DISTINCT GRANTEE) FROM DBA_SYS_PRIVS
WHERE PRIVILEGE='DBA' AND GRANTEE NOT IN ('SYS','SYSTEM','DBA','SYSMAN')SELECT LIMIT FROM DBA_PROFILES WHERE PROFILE='DEFAULT' AND RESOURCE_NAME='PASSWORD_VERIFY_FUNCTION'
SELECT VALUE FROM V$PARAMETER WHERE NAME='audit_trail'
-- Schema-only (no-auth) open accounts
SELECT COUNT(*) FROM DBA_USERS
WHERE ACCOUNT_STATUS='OPEN' AND AUTHENTICATION_TYPE='NONE'
AND USERNAME NOT IN ('SYS','SYSTEM')APPS.DUAL at connection time to detect EBS. These queries only run if the probe succeeds. On non-EBS Oracle databases, this entire section is skipped. The EBS detection result is passed to the AI Summary only.
SELECT 1 FROM APPS.DUAL WHERE ROWNUM = 1 -- If this query fails (ORA-00942), EBS is not present. All APPS.* queries are skipped.
SELECT b.user_concurrent_queue_name, b.node_name,
a.running_processes, a.max_processes
FROM apps.fnd_concurrent_queues a, apps.fnd_concurrent_queues_vl b
WHERE a.concurrent_queue_id = b.concurrent_queue_idSELECT phase_code, COUNT(*) FROM APPS.FND_CONCURRENT_REQUESTS
WHERE phase_code IN ('P','R') GROUP BY phase_code
SELECT component_type, component_name, component_status, startup_mode
FROM apps.fnd_svc_components WHERE component_type LIKE 'WF%' ORDER BY 1, 2SELECT COUNT(*) FROM APPS.WF_NOTIFICATIONS WHERE STATUS='OPEN' AND MAIL_STATUS='MAIL' AND BEGIN_DATE < SYSDATE - 1/24 SELECT COUNT(*) FROM APPS.WF_ERROR
V$VERSION for 'ENTERPRISE' and DBA_FEATURE_USAGE_STATISTICS for Diagnostics Pack before running any AWR/ADDM queries. Skipped silently on Standard Edition.
SELECT MIN(SNAP_ID), MAX(SNAP_ID), MIN(BEGIN_INTERVAL_TIME), MAX(END_INTERVAL_TIME)
FROM DBA_HIST_SNAPSHOT WHERE END_INTERVAL_TIME > SYSDATE - :lookback_hours/24
SELECT TASK_ID, TASK_NAME FROM DBA_HIST_ADDM_TASKS
WHERE BEGIN_SNAP_ID = :begin_snap AND END_SNAP_ID = :end_snap
AND STATUS = 'COMPLETED' FETCH FIRST 1 ROWS ONLY
SELECT f.FINDING_ID, f.TYPE, f.MESSAGE, f.BENEFIT,
d.ATTR1 AS recommendation, d.TYPE AS rec_type
FROM DBA_ADVISOR_FINDINGS f
LEFT JOIN DBA_ADVISOR_RECOMMENDATIONS d ON d.TASK_ID=f.TASK_ID AND d.FINDING_ID=f.FINDING_ID
WHERE f.TASK_ID = :task_id ORDER BY f.BENEFIT DESC NULLS LAST FETCH FIRST 20 ROWS ONLYShell Commands
The complete whitelist of OS commands the proxy may execute on your server. All are read-only status checks. No commands modify files, change configuration, or write to disk.
V$OSSTAT via Oracle SQL. Shell commands only run on EBS 12.2.x environments to check application-tier service status — and only the exact commands below are permitted.
| Command | Purpose | Condition | Writes? |
|---|---|---|---|
— none — |
All core OS metrics (CPU, memory, I/O wait) are read via V$OSSTAT through the Oracle connection. No shell commands required for any of the 200+ core health checks. |
always | NO |
All commands use $ADMIN_SCRIPTS_HOME — no hardcoded paths. Only status subcommands are whitelisted. admanagedsrvctl.sh is the unified controller for all managed servers in EBS 12.2.x.
| Command | Purpose | Condition | Writes? |
|---|---|---|---|
adcmctl.sh status |
Concurrent Manager status. Reports running/max processes per queue. | EBS detected | NO |
adalnctl.sh status |
APPS TNS Listener status. Checks if the application-tier listener is running. | EBS detected | NO |
admanagedsrvctl.sh status oacore_server1 |
OACore managed server status. Hosts Oracle Applications Framework pages. | EBS detected | NO |
admanagedsrvctl.sh status forms_server1 |
Forms managed server status. Required for EBS forms-based modules. | EBS detected | NO |
admanagedsrvctl.sh status oafm_server1 |
OA Framework managed server status. Hosts OAF-based Oracle Applications pages. | EBS detected | NO |
admanagedsrvctl.sh status wfmlrsvc |
Workflow Mailer managed service status. Reports if outbound workflow notifications are processing. | EBS detected | NO |
admanagedsrvctl.sh status opp |
Output Post Processor status. Handles PDF/EXCEL output for concurrent requests. | EBS detected | NO |
adadminsrvctl.sh status |
Admin Server status. The WLS administration server managing all managed servers. | EBS detected | NO |
adnodemgrctl.sh status |
Node Manager status. Controls WebLogic managed server lifecycle on this node. | EBS detected | NO |
adopmnctl.sh status |
OPMN status. Oracle Process Manager and Notification Server for OHS/Apache. | EBS detected | NO |
mwactl.sh status |
Middleware Agent status. Monitors and reports on WLS middleware component health. | EBS detected | NO |
adapcctl.sh status |
Apache/OHS status. Reports if the Oracle HTTP Server is running and accepting requests. | EBS detected | NO |
$ADMIN_SCRIPTS_HOME at runtime; the path itself is never accepted from the caller. Execution is logged with timestamp, requester IP, exit code, and a SHA-256 hash of the output.
What TuneVault Never Does
Explicit guarantees. These are enforced in the proxy binary — not policy statements.
127.0.0.1:3100 only. No external inbound ports. No VPN. No SSH tunnel.Customer Lockdown Script
Run this before installing the proxy. It creates a restricted OS user, sets filesystem permissions, and configures sudoers with a command whitelist.
- Creates restricted OS user
tunevaultwith no login shell - Sets
tunevaultas owner of the proxy install directory - Configures sudoers with explicit command whitelist (NOPASSWD for allowed commands only)
- Prints a verification summary — inspect before running proxy
Full script — read every line before running:
#!/bin/bash # tunevault-lockdown.sh # ================================================== # Run this on your Oracle server BEFORE installing # the TuneVault proxy. It creates a restricted OS # user and configures minimal sudo privileges. # # Usage: # chmod +x tunevault-lockdown.sh # sudo ./tunevault-lockdown.sh [--install-dir /opt/tunevault] # # Requirements: bash, sudo, useradd/adduser # Tested on: Oracle Linux 7/8/9, RHEL 7/8/9, Ubuntu 20/22 # ================================================== set -euo pipefail INSTALL_DIR=${1:-/opt/tunevault} PROXY_USER=tunevault PROXY_GROUP=tunevault LOG_FILE=/var/log/tunevault-lockdown.log # Must run as root if [ "$(id -u)" != "0" ]; then echo "ERROR: Must run as root (sudo ./tunevault-lockdown.sh)" exit 1 fi echo "========================================" echo " TuneVault Lockdown Script" echo " $(date)" echo " Install dir: $INSTALL_DIR" echo "========================================" echo "" # --- Create restricted group and user --- if ! getent group "$PROXY_GROUP" &>/dev/null; then echo "[1/4] Creating group: $PROXY_GROUP" groupadd --system "$PROXY_GROUP" else echo "[1/4] Group $PROXY_GROUP already exists — skipping" fi if ! id "$PROXY_USER" &>/dev/null; then echo "[2/4] Creating user: $PROXY_USER (no login shell, system account)" useradd \ --system \ --gid "$PROXY_GROUP" \ --shell /sbin/nologin \ --no-create-home \ --comment "TuneVault proxy service account" \ "$PROXY_USER" else echo "[2/4] User $PROXY_USER already exists — skipping" fi # --- Create and lock down install directory --- echo "[3/4] Setting up install directory: $INSTALL_DIR" mkdir -p "$INSTALL_DIR" chown "$PROXY_USER:$PROXY_GROUP" "$INSTALL_DIR" chmod 750 "$INSTALL_DIR" # Log directory (writable by proxy user only) mkdir -p "$INSTALL_DIR/logs" chown "$PROXY_USER:$PROXY_GROUP" "$INSTALL_DIR/logs" chmod 700 "$INSTALL_DIR/logs" # --- Configure sudoers whitelist --- echo "[4/4] Writing sudoers whitelist..." SUDOERS_FILE=/etc/sudoers.d/tunevault # Remove existing file if present [ -f "$SUDOERS_FILE" ] && rm -f "$SUDOERS_FILE" cat > "$SUDOERS_FILE" << 'SUDOERS_EOF' # TuneVault proxy — minimal sudo whitelist (EBS 12.2.x application tier) # Generated by tunevault-lockdown.sh # DO NOT edit manually — re-run lockdown.sh to update # # All paths use $ADMIN_SCRIPTS_HOME set in the oracle user environment. # Only "status" subcommands are listed — start/stop/restart are NOT granted. Defaults:tunevault env_keep += "ADMIN_SCRIPTS_HOME" # Non-managed services Cmnd_Alias TUNEVAULT_NON_MANAGED = \ $ADMIN_SCRIPTS_HOME/adcmctl.sh status, \ $ADMIN_SCRIPTS_HOME/adalnctl.sh status, \ $ADMIN_SCRIPTS_HOME/adadminsrvctl.sh status, \ $ADMIN_SCRIPTS_HOME/adnodemgrctl.sh status, \ $ADMIN_SCRIPTS_HOME/adopmnctl.sh status, \ $ADMIN_SCRIPTS_HOME/mwactl.sh status, \ $ADMIN_SCRIPTS_HOME/adapcctl.sh status # Managed servers (unified admanagedsrvctl.sh controller) Cmnd_Alias TUNEVAULT_MANAGED = \ $ADMIN_SCRIPTS_HOME/admanagedsrvctl.sh status oacore_server1, \ $ADMIN_SCRIPTS_HOME/admanagedsrvctl.sh status forms_server1, \ $ADMIN_SCRIPTS_HOME/admanagedsrvctl.sh status oafm_server1, \ $ADMIN_SCRIPTS_HOME/admanagedsrvctl.sh status wfmlrsvc, \ $ADMIN_SCRIPTS_HOME/admanagedsrvctl.sh status opp tunevault ALL=(oracle) NOPASSWD: TUNEVAULT_NON_MANAGED, TUNEVAULT_MANAGED # No other commands are permitted SUDOERS_EOF # Validate sudoers file syntax if command -v visudo &>/dev/null; then if visudo -c -f "$SUDOERS_FILE" &>/dev/null; then echo " sudoers syntax OK: $SUDOERS_FILE" else echo "ERROR: sudoers validation failed — removing $SUDOERS_FILE" rm -f "$SUDOERS_FILE" exit 1 fi fi chmod 440 "$SUDOERS_FILE" # --- Print verification summary --- echo "" echo "========================================" echo " Lockdown complete. Verify before use:" echo "========================================" echo "" echo "OS user:" id tunevault 2>/dev/null || echo " ERROR: user not found" echo "" echo "Install directory:" ls -la "$INSTALL_DIR" 2>/dev/null | head -5 echo "" echo "Sudoers whitelist:" cat "$SUDOERS_FILE" 2>/dev/null echo "" echo "Shell (should be /sbin/nologin or /bin/false):" getent passwd tunevault | cut -d: -f7 echo "" echo "========================================" echo " Next: install the proxy as user 'tunevault'" echo " See: https://tunevault.app/docs/oracle-setup" echo "========================================"
Audit & Logging
Every action TuneVault takes on your infrastructure is recorded. Your security team can audit the complete history at any time.
/opt/tunevault/logs/. Each entry records: timestamp, command or query type, execution duration, success/failure, and a result hash (not the data itself).lockdown-verify.sh to capture a live 30-second syscall trace of the proxy using strace or auditd. See every file open, network connection, and process execution in real time.tunevault:tunevault and not writable by other accounts. Hash chaining detects any post-hoc modification.Network Egress from the Proxy
Exactly two outbound HTTPS connections. No inbound ports. No third parties.
X-API-Key header — generated locally on your server at install time. TuneVault never holds Oracle credentials.tunevault.app. No inbound rules, no VPN, no NAT punching.