{"id":37700,"date":"2025-03-15T11:52:34","date_gmt":"2025-03-15T10:52:34","guid":{"rendered":"https:\/\/www.dbi-services.com\/blog\/?p=37700"},"modified":"2025-03-15T12:07:58","modified_gmt":"2025-03-15T11:07:58","slug":"what-is-the-most-secure-way-to-call-an-external-job-from-an-oracle-pluggable-database","status":"publish","type":"post","link":"https:\/\/www.dbi-services.com\/blog\/what-is-the-most-secure-way-to-call-an-external-job-from-an-oracle-pluggable-database\/","title":{"rendered":"What is the most secure way to call an external job from an Oracle (pluggable) database?"},"content":{"rendered":"\n<p>I recently asked myself this question after having been confronted with a quite challenging situation, which will be been dissected in this post. Indeed, most of the DBA are aware about several mechanism offered by the Oracle database engine to call external programs.<br>However, we rarely have the opportunity to get deep understanding of all of them due to the broad skills required.<\/p>\n\n\n\n<p>A couple of days ago, we have been asked a by an IT architect to have a look at some SHELL scripts &amp; C programs executed on a database server by database processes; It all began with &#8220;strange&#8221; UNIX file permissions such as:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: bash; title: ; notranslate\" title=\"\">\n&#x5B;oracle@svl-oat ~]$ ls -l \/opt\/oracle_scripts\/run_prog.sh\n-rwxr-sr-x 1 oracle oinstall 68 Mar 12 19:28 \/opt\/oracle_scripts\/run_prog.sh\n<\/pre><\/div>\n\n\n<p>Interesting isn&#8217;t it ? SETUID &amp; SETGID have been set on the scripts to probably gain elevated privileges ? But, for which purpose? <\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"h-which-methods-do-we-have-to-start-external-jobs-from-oracle-pluggable-database\">Which methods do we have to start external jobs from Oracle (pluggable) database?<\/h2>\n\n\n\n<p>Before moving into all the beloved technical details and reverse engineering. Let&#8217;s quickly summarize all options Oracle RBDMS offers.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"h-dbms-scheduler-jobs\">DBMS_SCHEDULER jobs<\/h3>\n\n\n\n<p>According to the DBMS_SCHEDULER sub-program CREATE_JOB. Oracle supports several types for calling external programs:<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table><thead><tr><th>Type<\/th><th>Description<\/th><\/tr><\/thead><tbody><tr><td>STORED_PROCEDURE<\/td><td>Calling external C\/C++ subprogram or Java Stored procedure<\/td><\/tr><tr><td>EXECUTABLE<\/td><td>Calling anything that can be executed from the command line of the operating system<\/td><\/tr><tr><td>EXTERNAL_SCRIPT<\/td><td>Calling an external script that uses the command shell of the computer running the job<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p>Please refer to the <a href=\"https:\/\/docs.oracle.com\/en\/database\/oracle\/oracle-database\/23\/arpls\/DBMS_SCHEDULER.html#GUID-7E744D62-13F6-40E9-91F0-1569E6C38BBC\">DBMS_SCHEDULER<\/a> package for details.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"h-java-stored-procedure\">Java Stored procedure<\/h2>\n\n\n\n<p>Indeed, Oracle RBDMS can spawn a dedicated server process on the database server using Java stored procedure and call a SHELL script.<\/p>\n\n\n\n<p><br>The following Java program source code has been merged from two different code base:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>https:\/\/oracle-base.com\/articles\/8i\/shell-commands-from-plsql<\/li>\n\n\n\n<li>https:\/\/innerlife.io\/oracle-db-cmd-java<\/li>\n<\/ul>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: java; title: ; notranslate\" title=\"\">\nCREATE OR REPLACE AND COMPILE JAVA SOURCE NAMED &quot;JavaExecHostCmd&quot; AS\nimport java.io.*;\npublic\nclass JavaExecHostCmd {\n public\n  static String executeCommand(String command) {\n    String buff = null;\n    String output = &quot;&quot;;\n    try {\n      String&#x5B;] finalCommand;\n      finalCommand = new String&#x5B;3];\n      finalCommand&#x5B;0] = &quot;\/bin\/bash&quot;;\n      \/\/ finalCommand&#x5B;0] = &quot;&quot;;\n      finalCommand&#x5B;1] = &quot;-c&quot;;\n      finalCommand&#x5B;2] = command;\n\n      \/\/ Execute the command...\n      final Process pr = Runtime.getRuntime().exec(finalCommand);\n\n      \/\/ Capture output from STDOUT...\n      BufferedReader br_in = null;\n      try {\n        br_in = new BufferedReader(new InputStreamReader(pr.getInputStream()));\n        output = output + &quot;STDOUT: &quot; + &quot;\\n&quot;;\n        while ((buff = br_in.readLine()) != null) {\n          \/\/ System.out.println(&quot;stdout: &quot; + buff);\n          output = output + buff + &quot;\\n&quot;;\n          try {\n            Thread.sleep(100);\n          } catch (Exception e) {\n          }\n        }\n        br_in.close();\n      } catch (IOException ioe) {\n        \/\/ System.out.println(&quot;Error printing process output.&quot;);\n        output = output + &quot;Error printing process output.&quot; + &quot;\\n&quot;;\n        \/\/ ioe.printStackTrace();\n      } finally {\n        try {\n          br_in.close();\n        } catch (Exception ex) {\n        }\n      }\n\n      \/\/ Capture output from STDERR...\n      BufferedReader br_err = null;\n      try {\n        br_err = new BufferedReader(new InputStreamReader(pr.getErrorStream()));\n        output = output + &quot;\\n&quot; + &quot;STDERR: &quot; + &quot;\\n&quot;;\n        while ((buff = br_err.readLine()) != null) {\n          \/\/ System.out.println(&quot;stderr: &quot; + buff);\n          output = output + buff + &quot;\\n&quot;;\n          try {\n            Thread.sleep(100);\n          } catch (Exception e) {\n          }\n        }\n        br_err.close();\n        return output;\n      } catch (IOException ioe) {\n        \/\/ System.out.println(&quot;Error printing execution errors.&quot;);\n        output = output + &quot;Error printing execution errors.&quot; + &quot;\\n&quot;;\n        \/\/ ioe.printStackTrace();\n      } finally {\n        try {\n          br_err.close();\n        } catch (Exception ex) {\n        }\n      }\n    } catch (Exception ex) {\n      output = output + ex.getLocalizedMessage() + &quot;\\n&quot;;\n    }\n    return output;\n  }\n};\n\/\n<\/pre><\/div>\n\n\n<p>Basically, the program executes a &#8220;\/bin\/bash -c &lt;Command&gt;&#8221; and returns its result.<\/p>\n\n\n\n<p>To interface the Java program a simple PL\/SQL function is required:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: sql; title: ; notranslate\" title=\"\">\nCREATE OR REPLACE FUNCTION run_shell_cmd_with_java (p_command  IN  VARCHAR2) \n   RETURN  VARCHAR2 \n   AS LANGUAGE JAVA \n   NAME &#039;JavaExecHostCmd.executeCommand (java.lang.String) return String&#039;;\n\/\n<\/pre><\/div>\n\n\n<p>Finally, we need to grant access to the Oracle OJVM for desired database schema:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: sql; title: ; notranslate\" title=\"\">\nDECLARE\n   l_schema VARCHAR2(30) := &#039;JEW&#039;; -- Adjust as required.\nBEGIN\n   DBMS_JAVA.grant_permission(l_schema, &#039;java.io.FilePermission&#039;, &#039;&lt;&lt;ALL FILES&gt;&gt;&#039;, &#039;read ,write, execute, delete&#039;);\n   DBMS_JAVA.grant_permission(l_schema, &#039;SYS:java.lang.RuntimePermission&#039;, &#039;writeFileDescriptor&#039;, &#039;&#039;);\n   DBMS_JAVA.grant_permission(l_schema, &#039;SYS:java.lang.RuntimePermission&#039;, &#039;readFileDescriptor&#039;, &#039;&#039;);\nEND;\n\/\n<\/pre><\/div>\n\n\n<p><strong>Safe Harbor statement:<\/strong> Please ensure you do not blindly apply the same java.io.FilePermission which basically allows to run anything on the database server \ud83d\ude42<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"h-external-c-c-programs-aka-extproc\">External C\/C++ programs aka &#8220;extproc&#8221;<\/h2>\n\n\n\n<p>That&#8217;s probably one of the least known option but as old as the Oracle JVM both introduced in 1999 with Oracle 8i.<\/p>\n\n\n\n<p>The following source code was mostly generated with the support of a LLM but slightly adapted to mimic the behavior as the Java program:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: cpp; title: ; notranslate\" title=\"\">\ncat &lt;&lt;EOF_&gt; \/opt\/oracle_scripts\/c_extproc.c\n\/\/ Source: ChatGPT\n#include &lt;stdio.h&gt;\n#include &lt;stdlib.h&gt;\n#include &lt;string.h&gt;  \/\/ Include the string.h header to use strcpy and strlen\n\n#define MAX_BUFFER 1024\n\n\/\/ Function to execute a Linux command and return the output\nchar* execute_command(const char *command) {\n    FILE *fp;\n    char *output = (char*)malloc(MAX_BUFFER * sizeof(char));  \/\/ Allocate memory for the output\n    if (output == NULL) {\n        perror(&quot;malloc failed&quot;);\n        return NULL;\n    }\n\n    fp = popen(command, &quot;r&quot;);  \/\/ Open a pipe to the command\n    if (fp == NULL) {\n        perror(&quot;popen failed&quot;);\n        free(output);\n        return NULL;\n    }\n\n    \/\/ Buffer to accumulate the command output\n    char buffer&#x5B;MAX_BUFFER];\n    size_t output_size = 0;\n\n    \/\/ Read the output line by line and accumulate it in &#039;output&#039;\n    while (fgets(buffer, sizeof(buffer), fp) != NULL) {\n        size_t len = strlen(buffer);\n\n        \/\/ Reallocate memory if necessary\n        if (output_size + len &gt;= MAX_BUFFER) {\n            output = realloc(output, output_size + len + 1);  \/\/ Add space for the new content\n            if (output == NULL) {\n                perror(&quot;realloc failed&quot;);\n                fclose(fp);\n                return NULL;\n            }\n        }\n\n        \/\/ Append the line to the output\n        strcpy(output + output_size, buffer);\n        output_size += len;\n    }\n\n    fclose(fp);\n    return output;\n}\nEOF_\n<\/pre><\/div>\n\n\n<p>Compile the source using GNU compiler and finally resolve the library dependencies (combine links):<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: ; notranslate\" title=\"\">\ngcc -fPIC -c \/opt\/oracle_scripts\/c_extproc.c -o \/opt\/oracle_scripts\/c_extproc.o\nld -shared -o \/opt\/oracle_scripts\/c_extproc.so \/opt\/oracle_scripts\/c_extproc.o\n<\/pre><\/div>\n\n\n<p>In our case, the program does not contain any &#8220;call to the function &#8211; aka. main progtam&#8221;, as such it&#8217;s just a library.<\/p>\n\n\n\n<p>In the same way as we interfaced the Java program, we need to bind the external library with a PL\/SQL function:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: sql; title: ; notranslate\" title=\"\">\nCREATE OR REPLACE LIBRARY shell_c_lib is &#039;\/opt\/oracle_scripts\/c_extproc.so&#039;;\n\/\nCREATE OR REPLACE FUNCTION run_shell_cmd_with_c (command IN VARCHAR2)  \n RETURN  VARCHAR2  \n AS  EXTERNAL\n NAME &quot;execute_command&quot;  \n LIBRARY  shell_c_lib  \n LANGUAGE C  \n PARAMETERS (command string, RETURN string);  \n\/\n<\/pre><\/div>\n\n\n<p>Hold on a second, for Java we had to allow the Oracle JVM to access the file sytem to execute an Operating System command. Is anything required for external C\/C++ programs ?<\/p>\n\n\n\n<p>Yes, usually ISVs recommend to configure EXTPROC_DLLS parameter within the Oracle Listener. However, in our Oracle Cloud LAB we used an unsafe shortcut which consists to whitelist any library present on the database server filesystem by editing following Oracle Home configuration file:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: bash; title: ; notranslate\" title=\"\">\n&#x5B;oracle@svl-ora-t ~]$ grep &#039;^SET EXTPROC_DLLS=ANY&#039; ${ORACLE_HOME}\/hs\/admin\/extproc.ora\nSET EXTPROC_DLLS=ANY\n<\/pre><\/div>\n\n\n<p><strong>Safe Harbor statement:<\/strong> Please ensure you do not blindly follow this post for your own safety \ud83d\ude09<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"h-host-command-from-pl-sql\">HOST command from PL\/SQL<\/h2>\n\n\n\n<p>Uh? Does it exists? LLM training on the job! Of course, not \ud83d\ude09<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"h-dbms-scheduler\">DBMS_SCHEDULER <\/h2>\n\n\n\n<p>The Oracle scheduler support several job types when working with external programs:<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"h-executable-and-external-script\">EXECUTABLE and EXTERNAL_SCRIPT<\/h3>\n\n\n\n<p>Create credentials for jobs from type EXTERNAL_SCRIPT (aka. existing local Operating System user credentials):<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: sql; title: ; notranslate\" title=\"\">\nBEGIN\n  DBMS_CREDENTIAL.CREATE_CREDENTIAL(\n    credential_name =&gt; &#039;test_creds&#039;,\n    username        =&gt; &#039;test&#039;,\n    password        =&gt; &#039;test&#039;);\nEND;\n\/\n<\/pre><\/div>\n\n\n<p>Once, created if needed the credentials can be applied at pluggable database level only though init. parameter PDB_OS_CREDENTIAL.<\/p>\n\n\n\n<p>Create the scheduler jobs:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: sql; title: ; notranslate\" title=\"\">\nBEGIN\n  DBMS_SCHEDULER.CREATE_JOB(\n     job_name=&gt;&#039;EXEC_SHELL_CMD_AS_EXECUTABLE&#039;,\n     job_type=&gt;&#039;EXECUTABLE&#039;,\n     job_action=&gt;&#039;\/opt\/oracle_scripts\/run_prog.sh&#039;,\n     enabled=&gt;TRUE,\n     auto_drop=&gt;FALSE,\n     repeat_interval =&gt; &#039;freq=secondly;bysecond=5;&#039;\n     );\n\n\t-- EXTERNAL_SCRIPT requires credentials ! \n\t-- Please Refer to DBMS_SCHEDULER package documentation for details\n  DBMS_SCHEDULER.CREATE_JOB(\n     job_name=&gt;&#039;EXEC_SHELL_CMD_AS_EXTERNAL_SCRIPT&#039;,\n     job_type=&gt;&#039;EXTERNAL_SCRIPT&#039;,\n     job_action=&gt;&#039;\/opt\/oracle_scripts\/run_prog.sh&#039;,\n     credential_name=&gt;&#039;test_creds&#039;,\n     enabled=&gt;TRUE,\n     auto_drop=&gt;FALSE,\n     repeat_interval =&gt; &#039;freq=secondly;bysecond=15;&#039;\n     );   \nEND;\n\/\n<\/pre><\/div>\n\n\n<p><strong>Nota Bene: <\/strong>DBMS_SCHEDULER job_type EXECUTABLE do not capture STDOUT as long as no credentials are applied (created with DBMS_CREDENTIALS)! at least this was the case on my Oracle 19c Release Update 23 database<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"h-stored-procedure-and-plsql-block-java-and-external-c-program\">STORED_PROCEDURE and PLSQL_BLOCK (Java and External C program)<\/h3>\n\n\n\n<p>Functions are not supported by the Oracle Scheduler. Thus, we created a simple instrumentation table:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: sql; title: ; notranslate\" title=\"\">\nDROP TABLE debug;\nCREATE TABLE debug \n   (EXECUTED_DATE TIMESTAMP default SYSTIMESTAMP, \n    use_case VARCHAR2(20), \n    output VARCHAR2(1024)\n   );\n<\/pre><\/div>\n\n\n<p>And finally, created on top of the functions for the e Java and C program stored procedures:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: sql; title: ; notranslate\" title=\"\">\nCREATE OR REPLACE  PROCEDURE runproc_shell_cmd_with_c  AS\n   l_output VARCHAR2(100); \nBEGIN  \n   l_output := run_shell_cmd_with_c(&#039;\/usr\/bin\/whoami&#039;); \n   INSERT INTO debug(use_case, output) VALUES(&#039;c_program&#039;, l_output);\n END;\n \/\n\nCREATE OR REPLACE  PROCEDURE runproc_shell_cmd_with_java  AS\n   l_output VARCHAR2(100); \nBEGIN  \n   l_output := run_shell_cmd_with_java(&#039;\/usr\/bin\/whoami&#039;); \n   INSERT INTO debug(use_case, output) VALUES(&#039;java_program&#039;, l_output);\n END;\n \/\n<\/pre><\/div>\n\n\n<p>Create the scheduler jobs:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: sql; title: ; notranslate\" title=\"\">\n-- Once executed as STORED_PROCEDURE\nBEGIN\n  DBMS_SCHEDULER.CREATE_JOB(\n     job_name=&gt;&#039;EXEC_SHELL_CMD_USING_C_STOREDPROC&#039;,\n     job_type=&gt;&#039;STORED_PROCEDURE&#039;,\n     job_action=&gt;&#039;runproc_shell_cmd_with_c&#039;,\n     enabled=&gt;TRUE,\n     auto_drop=&gt;FALSE,\n     repeat_interval =&gt; &#039;freq=secondly;bysecond=5;&#039;\n     );\n    \n  DBMS_SCHEDULER.CREATE_JOB(\n     job_name=&gt;&#039;EXEC_SHELL_CMD_USING_JAVA_STOREDPROC&#039;,\n     job_type=&gt;&#039;STORED_PROCEDURE&#039;,\n     job_action=&gt;&#039;runproc_shell_cmd_with_java&#039;,\n     enabled=&gt;TRUE,\n     auto_drop=&gt;FALSE,\n     repeat_interval =&gt; &#039;freq=secondly;bysecond=5;&#039;\n     );\nEND;\n\/\n\n-- Once executed as anonymous PL\/SQL block\nBEGIN\n  DBMS_SCHEDULER.CREATE_JOB(\n     job_name=&gt;&#039;EXEC_SHELL_CMD_USING_C_PLSQLBLOCK&#039;,\n     job_type=&gt;&#039;PLSQL_BLOCK&#039;,\n     job_action=&gt;&#039;runproc_shell_cmd_with_c;&#039;,\n     enabled=&gt;TRUE,\n     auto_drop=&gt;FALSE,\n     repeat_interval =&gt; &#039;freq=secondly;bysecond=5;&#039;\n     );\n    \n  DBMS_SCHEDULER.CREATE_JOB(\n     job_name=&gt;&#039;EXEC_SHELL_CMD_USING_JAVA_PLSQLBLOCK&#039;,\n     job_type=&gt;&#039;PLSQL_BLOCK&#039;,\n     job_action=&gt;&#039;runproc_shell_cmd_with_java;&#039;,\n     enabled=&gt;TRUE,\n     auto_drop=&gt;FALSE,\n     repeat_interval =&gt; &#039;freq=secondly;bysecond=15;&#039;\n     );\nEND;\n\/\n<\/pre><\/div>\n\n\n<h2 class=\"wp-block-heading\" id=\"h-test-cases\">Test cases<\/h2>\n\n\n\n<p>So any guess about old Unix command that prints the effective user id ? whoami?<\/p>\n\n\n\n<figure class=\"wp-block-table is-style-stripes has-medium-font-size\"><table class=\"has-fixed-layout\"><thead><tr><th>Execution method<\/th><th>Type of database connection<\/th><th>whoami ?<\/th><\/tr><\/thead><tbody><tr><td>SELECT <br>   run_shell_cmd_with_java(\u2019\/usr\/bin\/whoami\u2019) AS stdout <br>FROM DUAL;<\/td><td>Connected to PDB over SQL*NET<\/td><td>grid<\/td><\/tr><tr><td><\/td><td>Connected to PDB over BEQUEATH protocol<\/td><td>oracle<\/td><\/tr><tr><td>SELECT <br>   run_shell_cmd_with_c(\u2019\/usr\/bin\/whoami\u2019) AS stdout <br>FROM DUAL;<\/td><td>Connected to PDB over SQL*NET<\/td><td>grid<\/td><\/tr><tr><td><\/td><td>Connected to PDB over BEQUEATH protocol<\/td><td>oracle<\/td><\/tr><tr><td>exec DBMS_SCHEDULER.RUN_JOB(\u2018EXEC_SHELL_CMD_AS_EXECUTABLE\u2019);<\/td><td>Database job executed manually<\/td><td>nobody<\/td><\/tr><tr><td><\/td><td>Database job scheduled<\/td><td>nobody<\/td><\/tr><tr><td>exec DBMS_SCHEDULER.RUN_JOB(\u2018EXEC_SHELL_CMD_AS_EXTERNAL_SCRIPT\u2019);<\/td><td>Database job executed manually<\/td><td>test<\/td><\/tr><tr><td><\/td><td><br>Database job scheduled<\/td><td>test<\/td><\/tr><tr><td>exec DBMS_SCHEDULER.RUN_JOB(\u2018EXEC_SHELL_CMD_USING_C_STOREDPROC\u2019);<\/td><td>Database job executed manually<\/td><td>grid<\/td><\/tr><tr><td><\/td><td>Database job scheduled<\/td><td>oracle<\/td><\/tr><tr><td>exec DBMS_SCHEDULER.RUN_JOB(\u2018EXEC_SHELL_CMD_USING_JAVA_STOREDPROC\u2019);<\/td><td>Database job executed manually<\/td><td>grid<\/td><\/tr><tr><td><\/td><td>Database job scheduled<\/td><td>oracle<\/td><\/tr><tr><td>exec DBMS_SCHEDULER.RUN_JOB(\u2018EXEC_SHELL_CMD_USING_C_PLSQLBLOCK\u2019);<\/td><td>Database job executed manually<\/td><td>grid<\/td><\/tr><tr><td><\/td><td><br>Database job scheduled<\/td><td>oracle<\/td><\/tr><tr><td>exec DBMS_SCHEDULER.RUN_JOB(\u2018EXEC_SHELL_CMD_USING_JAVA_PLSQLBLOCK\u2019);<\/td><td>Database job executed manually<\/td><td>grid<\/td><\/tr><tr><td><\/td><td>Database job scheduled<\/td><td>oracle<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p>Comments:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>With Oracle Grid Infrastructure the entire Single Client Access Name (SCAN) listeners belong to Operating System User grid<\/li>\n\n\n\n<li>BEQUEATH Protocol is local Operating System authentication method only based on the traditional OSDBA, OSOPER. etc .. O.S group mapping<\/li>\n\n\n\n<li>Oracle Scheduler job coordination CJQn processes and workers Jnn processes are spawned when database starts.<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"h-additional-background-information\">Additional background information<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"h-oracle-cloud-database-home-specificity\">Oracle Cloud database home specificity<\/h3>\n\n\n\n<p>On our LAB we had to tweak the Oracle Home as documented per Oracle support note, for some reason on OCI the external job feature is not working by default and according to the note it\u2019s a feature \ud83d\ude42<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><tbody><tr><td>DBMS_SCHEDULER Job (External Job) Failed With ORA-27300, ORA-27301, ORA-27302: failure occurred at: sjsec 6d [OCI DBCS] (Doc ID 2647889.1)<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"h-dbms-scheduler-job-type-executable\">DBMS_SCHEDULER job type EXECUTABLE<\/h2>\n\n\n\n<p>By default any job is executed under the Unix pseudo user \u201cnobody\u201d which is a low-privileged Unix namespace and as such considered as secure. Oracle allows to tweak the behavior by adapting ${ORACLE_HOME}\/rdbms\/admin\/externaljob.ora:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: ; notranslate\" title=\"\">\n&#x5B;oracle@svl-ora-t ~]$ grep &#039;^run_&#039; ${ORACLE_HOME}\/rdbms\/admin\/externaljob.ora\nrun_user = nobody\nrun_group = nobody\n<\/pre><\/div>\n\n\n<h3 class=\"wp-block-heading\" id=\"h-dbms-scheduler-amp-linux-pam-pluggable-authentication-modules-kernel-module\">DBMS_SCHEDULER &amp; Linux PAM (Pluggable Authentication Modules) kernel module<\/h3>\n\n\n\n<p>Oracle Scheduler switch the user in background like you would run an \u201csu\u201d command. If this is not possible because of security reasons. the Job wont\u2019 be executed at all<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: bash; title: ; notranslate\" title=\"\">\n&#x5B;oracle@svl-ora-t ~]$ su - test\nPassword:\nsu: Permission denied\n<\/pre><\/div>\n\n\n<p>Some hardened system on which we worked did not allow to \u201cescape\u201d from oracle O.S user and as such all Oracle Scheduler jobs failed due to the PAM config preventing switching to another user unless member of wheel O.S group:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: bash; title: ; notranslate\" title=\"\">\n# Uncomment the following line to require a user to be in the &quot;wheel&quot; group.\nauth           required        pam_wheel.so use_uid\n<\/pre><\/div>\n\n\n<p>Oracle Support note which is still valid nowadays despite Oracle Linux 6 reference:<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><tbody><tr><td>Dbms_scheduler Job Executes Shell Script Failed With Error: ORA-27369: job of type EXECUTABLE failed with exit code: 7 !@#\u2013!@#7#@!\u2013#@! (Doc ID 2312329.1)<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"h-isolate-oracle-jvm-runtime-execution-to-least-privileged-o-s-user\">Isolate Oracle JVM runtime execution to least privileged O.S user<\/h3>\n\n\n\n<p>Oracle delivers Oracle JVM isolation feature using a DBMS_JAVA sub-program to bind execution of program to a specific Operating System user. This setting requires to be conneted as SYS:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: sql; title: ; notranslate\" title=\"\">\nSQL&gt; show user \n\n&quot;SYS&quot;\n\nSQL&gt; alter session set container=APP_001I;\n\nSession altered.\n\nSQL&gt; exec dbms_java.set_runtime_exec_credentials(dbuser=&gt;&#039;JEW&#039;, osuser=&gt;&#039;test&#039;, ospass =&gt;&#039;test&#039;);\n\nPL\/SQL procedure successfully completed.\n<\/pre><\/div>\n\n\n<p>From now on, all sub-subsequent execution will be started under the Operating System user &#8220;test&#8221;. Unfortunately, all executions fail with following error message<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: sql; title: ; notranslate\" title=\"\">\nCannot run program &quot;\/bin\/bash&quot;: cannot execute \/u01\/app\/oracle\/product\/19.0.0.0\/dbhome_1\/bin\/jssu\n<\/pre><\/div>\n\n\n<p>Unfortunately, we were not able to fix this issue which has been well described in following post:<br><a href=\"https:\/\/mvelikikh.blogspot.com\/2020\/04\/secure-use-of-runtimeexec-functionality.html\">https:\/\/mvelikikh.blogspot.com\/2020\/04\/secure-use-of-runtimeexec-functionality.html<\/a><\/p>\n\n\n\n<p>Unbinding the OJVM runtime environment requires as well SYS:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: sql; title: ; notranslate\" title=\"\">\nSQL&gt; alter session set container=APP_001I;\n\nSession altered.\n\nSQL&gt; exec dbms_java.set_runtime_exec_credentials(dbuser=&gt;&#039;JEW&#039;, osuser=&gt;null, ospass =&gt; null);\n\nPL\/SQL procedure successfully completed.\n<\/pre><\/div>\n\n\n<h2 class=\"wp-block-heading\" id=\"h-summary\">Summary<\/h2>\n\n\n\n<p>Well, there is no blueprint to deduct from this post which method suits the best for extermal programs, but at least it provides an overview and reproducible test cases to understand under which circumstances some files may or may not need special file system flags.<\/p>\n\n\n\n<p>Indeed, the dissected use cases for several scripts \/ C programs were amended with SETUID, GID flag to mitigate some past issues nobody remember leaving the system an undesired state, hackers could potentially take advantage of.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>I recently asked myself this question after having been confronted with a quite challenging situation, which will be been dissected in this post. Indeed, most of the DBA are aware about several mechanism offered by the Oracle database engine to call external programs.However, we rarely have the opportunity to get deep understanding of all of [&hellip;]<\/p>\n","protected":false},"author":25,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[2966,59],"tags":[281,214,64,1375,3585],"type_dbi":[2728],"class_list":["post-37700","post","type-post","status-publish","format-standard","hentry","category-oci","category-oracle","tag-dbms_scheduler","tag-java","tag-multitenant","tag-oci","tag-reverse_engineering","type-oracle"],"acf":[],"yoast_head":"<!-- This site is optimized with the Yoast SEO Premium plugin v27.2 (Yoast SEO v27.2) - https:\/\/yoast.com\/product\/yoast-seo-premium-wordpress\/ -->\n<title>What is the most secure way to call an external job from an Oracle (pluggable) database?<\/title>\n<meta name=\"description\" content=\"Get insights how to safely execute external procedures (C\/C++ programs. shell scripts. Java Programs) on database servers in appropriate Linux kernel namespace\" \/>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/www.dbi-services.com\/blog\/what-is-the-most-secure-way-to-call-an-external-job-from-an-oracle-pluggable-database\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"What is the most secure way to call an external job from an Oracle (pluggable) database?\" \/>\n<meta property=\"og:description\" content=\"Get insights how to safely execute external procedures (C\/C++ programs. shell scripts. Java Programs) on database servers in appropriate Linux kernel namespace\" \/>\n<meta property=\"og:url\" content=\"https:\/\/www.dbi-services.com\/blog\/what-is-the-most-secure-way-to-call-an-external-job-from-an-oracle-pluggable-database\/\" \/>\n<meta property=\"og:site_name\" content=\"dbi Blog\" \/>\n<meta property=\"article:published_time\" content=\"2025-03-15T10:52:34+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2025-03-15T11:07:58+00:00\" \/>\n<meta name=\"author\" content=\"J\u00e9r\u00f4me Witt\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"J\u00e9r\u00f4me Witt\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"6 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\/\/www.dbi-services.com\/blog\/what-is-the-most-secure-way-to-call-an-external-job-from-an-oracle-pluggable-database\/#article\",\"isPartOf\":{\"@id\":\"https:\/\/www.dbi-services.com\/blog\/what-is-the-most-secure-way-to-call-an-external-job-from-an-oracle-pluggable-database\/\"},\"author\":{\"name\":\"J\u00e9r\u00f4me Witt\",\"@id\":\"https:\/\/www.dbi-services.com\/blog\/#\/schema\/person\/a2d3ecddaf732850101a39b9d62c31b7\"},\"headline\":\"What is the most secure way to call an external job from an Oracle (pluggable) database?\",\"datePublished\":\"2025-03-15T10:52:34+00:00\",\"dateModified\":\"2025-03-15T11:07:58+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\/\/www.dbi-services.com\/blog\/what-is-the-most-secure-way-to-call-an-external-job-from-an-oracle-pluggable-database\/\"},\"wordCount\":1374,\"commentCount\":0,\"keywords\":[\"dbms_scheduler\",\"Java\",\"multitenant\",\"OCI\",\"reverse_engineering\"],\"articleSection\":[\"OCI\",\"Oracle\"],\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\/\/www.dbi-services.com\/blog\/what-is-the-most-secure-way-to-call-an-external-job-from-an-oracle-pluggable-database\/#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\/\/www.dbi-services.com\/blog\/what-is-the-most-secure-way-to-call-an-external-job-from-an-oracle-pluggable-database\/\",\"url\":\"https:\/\/www.dbi-services.com\/blog\/what-is-the-most-secure-way-to-call-an-external-job-from-an-oracle-pluggable-database\/\",\"name\":\"What is the most secure way to call an external job from an Oracle (pluggable) database?\",\"isPartOf\":{\"@id\":\"https:\/\/www.dbi-services.com\/blog\/#website\"},\"datePublished\":\"2025-03-15T10:52:34+00:00\",\"dateModified\":\"2025-03-15T11:07:58+00:00\",\"author\":{\"@id\":\"https:\/\/www.dbi-services.com\/blog\/#\/schema\/person\/a2d3ecddaf732850101a39b9d62c31b7\"},\"description\":\"Get insights how to safely execute external procedures (C\/C++ programs. shell scripts. Java Programs) on database servers in appropriate Linux kernel namespace\",\"breadcrumb\":{\"@id\":\"https:\/\/www.dbi-services.com\/blog\/what-is-the-most-secure-way-to-call-an-external-job-from-an-oracle-pluggable-database\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/www.dbi-services.com\/blog\/what-is-the-most-secure-way-to-call-an-external-job-from-an-oracle-pluggable-database\/\"]}]},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/www.dbi-services.com\/blog\/what-is-the-most-secure-way-to-call-an-external-job-from-an-oracle-pluggable-database\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Accueil\",\"item\":\"https:\/\/www.dbi-services.com\/blog\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"What is the most secure way to call an external job from an Oracle (pluggable) database?\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/www.dbi-services.com\/blog\/#website\",\"url\":\"https:\/\/www.dbi-services.com\/blog\/\",\"name\":\"dbi Blog\",\"description\":\"\",\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/www.dbi-services.com\/blog\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":\"Person\",\"@id\":\"https:\/\/www.dbi-services.com\/blog\/#\/schema\/person\/a2d3ecddaf732850101a39b9d62c31b7\",\"name\":\"J\u00e9r\u00f4me Witt\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/secure.gravatar.com\/avatar\/17095c081578ca53f52ec4030ba2bde72cc18badb325cd2ba1ee2831106507ad?s=96&d=mm&r=g\",\"url\":\"https:\/\/secure.gravatar.com\/avatar\/17095c081578ca53f52ec4030ba2bde72cc18badb325cd2ba1ee2831106507ad?s=96&d=mm&r=g\",\"contentUrl\":\"https:\/\/secure.gravatar.com\/avatar\/17095c081578ca53f52ec4030ba2bde72cc18badb325cd2ba1ee2831106507ad?s=96&d=mm&r=g\",\"caption\":\"J\u00e9r\u00f4me Witt\"},\"description\":\"J\u00e9rome Witt started his Consultant career a few years ago. He is specialized in database and infrastructure management, engineering, and optimization. He is very skilled in Oracle high availability, backup &amp; recovery, and tuning technologies. His expertise also includes the open source field (Linux\/Unix), advanced Perl, Shell, Windows PowerShell programming, and Automation tools (UC4). J\u00e9r\u00f4me Witt is Oracle Certified Professional 11g (OCP 11g), Oracle Certified Expert Tuning (OCE), and ITIL V3 Foundation certified. Prior to joining dbi services, J\u00e9r\u00f4me Witt was Consultant at Trivadis in Basel. He also worked as a Junior Automation specialist at Selmoni AG in Basel. J\u00e9r\u00f4me Witt holds a BTS degree in Information Systems and Industrial Networks from France. His branch-related experience covers Pharma, Health Care, Banking &amp; Financial Services, Energy, Automotive etc.\",\"url\":\"https:\/\/www.dbi-services.com\/blog\/author\/jerome-witt\/\"}]}<\/script>\n<!-- \/ Yoast SEO Premium plugin. -->","yoast_head_json":{"title":"What is the most secure way to call an external job from an Oracle (pluggable) database?","description":"Get insights how to safely execute external procedures (C\/C++ programs. shell scripts. Java Programs) on database servers in appropriate Linux kernel namespace","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/www.dbi-services.com\/blog\/what-is-the-most-secure-way-to-call-an-external-job-from-an-oracle-pluggable-database\/","og_locale":"en_US","og_type":"article","og_title":"What is the most secure way to call an external job from an Oracle (pluggable) database?","og_description":"Get insights how to safely execute external procedures (C\/C++ programs. shell scripts. Java Programs) on database servers in appropriate Linux kernel namespace","og_url":"https:\/\/www.dbi-services.com\/blog\/what-is-the-most-secure-way-to-call-an-external-job-from-an-oracle-pluggable-database\/","og_site_name":"dbi Blog","article_published_time":"2025-03-15T10:52:34+00:00","article_modified_time":"2025-03-15T11:07:58+00:00","author":"J\u00e9r\u00f4me Witt","twitter_card":"summary_large_image","twitter_misc":{"Written by":"J\u00e9r\u00f4me Witt","Est. reading time":"6 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/www.dbi-services.com\/blog\/what-is-the-most-secure-way-to-call-an-external-job-from-an-oracle-pluggable-database\/#article","isPartOf":{"@id":"https:\/\/www.dbi-services.com\/blog\/what-is-the-most-secure-way-to-call-an-external-job-from-an-oracle-pluggable-database\/"},"author":{"name":"J\u00e9r\u00f4me Witt","@id":"https:\/\/www.dbi-services.com\/blog\/#\/schema\/person\/a2d3ecddaf732850101a39b9d62c31b7"},"headline":"What is the most secure way to call an external job from an Oracle (pluggable) database?","datePublished":"2025-03-15T10:52:34+00:00","dateModified":"2025-03-15T11:07:58+00:00","mainEntityOfPage":{"@id":"https:\/\/www.dbi-services.com\/blog\/what-is-the-most-secure-way-to-call-an-external-job-from-an-oracle-pluggable-database\/"},"wordCount":1374,"commentCount":0,"keywords":["dbms_scheduler","Java","multitenant","OCI","reverse_engineering"],"articleSection":["OCI","Oracle"],"inLanguage":"en-US","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/www.dbi-services.com\/blog\/what-is-the-most-secure-way-to-call-an-external-job-from-an-oracle-pluggable-database\/#respond"]}]},{"@type":"WebPage","@id":"https:\/\/www.dbi-services.com\/blog\/what-is-the-most-secure-way-to-call-an-external-job-from-an-oracle-pluggable-database\/","url":"https:\/\/www.dbi-services.com\/blog\/what-is-the-most-secure-way-to-call-an-external-job-from-an-oracle-pluggable-database\/","name":"What is the most secure way to call an external job from an Oracle (pluggable) database?","isPartOf":{"@id":"https:\/\/www.dbi-services.com\/blog\/#website"},"datePublished":"2025-03-15T10:52:34+00:00","dateModified":"2025-03-15T11:07:58+00:00","author":{"@id":"https:\/\/www.dbi-services.com\/blog\/#\/schema\/person\/a2d3ecddaf732850101a39b9d62c31b7"},"description":"Get insights how to safely execute external procedures (C\/C++ programs. shell scripts. Java Programs) on database servers in appropriate Linux kernel namespace","breadcrumb":{"@id":"https:\/\/www.dbi-services.com\/blog\/what-is-the-most-secure-way-to-call-an-external-job-from-an-oracle-pluggable-database\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/www.dbi-services.com\/blog\/what-is-the-most-secure-way-to-call-an-external-job-from-an-oracle-pluggable-database\/"]}]},{"@type":"BreadcrumbList","@id":"https:\/\/www.dbi-services.com\/blog\/what-is-the-most-secure-way-to-call-an-external-job-from-an-oracle-pluggable-database\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Accueil","item":"https:\/\/www.dbi-services.com\/blog\/"},{"@type":"ListItem","position":2,"name":"What is the most secure way to call an external job from an Oracle (pluggable) database?"}]},{"@type":"WebSite","@id":"https:\/\/www.dbi-services.com\/blog\/#website","url":"https:\/\/www.dbi-services.com\/blog\/","name":"dbi Blog","description":"","potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/www.dbi-services.com\/blog\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":"Person","@id":"https:\/\/www.dbi-services.com\/blog\/#\/schema\/person\/a2d3ecddaf732850101a39b9d62c31b7","name":"J\u00e9r\u00f4me Witt","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/secure.gravatar.com\/avatar\/17095c081578ca53f52ec4030ba2bde72cc18badb325cd2ba1ee2831106507ad?s=96&d=mm&r=g","url":"https:\/\/secure.gravatar.com\/avatar\/17095c081578ca53f52ec4030ba2bde72cc18badb325cd2ba1ee2831106507ad?s=96&d=mm&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/17095c081578ca53f52ec4030ba2bde72cc18badb325cd2ba1ee2831106507ad?s=96&d=mm&r=g","caption":"J\u00e9r\u00f4me Witt"},"description":"J\u00e9rome Witt started his Consultant career a few years ago. He is specialized in database and infrastructure management, engineering, and optimization. He is very skilled in Oracle high availability, backup &amp; recovery, and tuning technologies. His expertise also includes the open source field (Linux\/Unix), advanced Perl, Shell, Windows PowerShell programming, and Automation tools (UC4). J\u00e9r\u00f4me Witt is Oracle Certified Professional 11g (OCP 11g), Oracle Certified Expert Tuning (OCE), and ITIL V3 Foundation certified. Prior to joining dbi services, J\u00e9r\u00f4me Witt was Consultant at Trivadis in Basel. He also worked as a Junior Automation specialist at Selmoni AG in Basel. J\u00e9r\u00f4me Witt holds a BTS degree in Information Systems and Industrial Networks from France. His branch-related experience covers Pharma, Health Care, Banking &amp; Financial Services, Energy, Automotive etc.","url":"https:\/\/www.dbi-services.com\/blog\/author\/jerome-witt\/"}]}},"_links":{"self":[{"href":"https:\/\/www.dbi-services.com\/blog\/wp-json\/wp\/v2\/posts\/37700","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.dbi-services.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.dbi-services.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.dbi-services.com\/blog\/wp-json\/wp\/v2\/users\/25"}],"replies":[{"embeddable":true,"href":"https:\/\/www.dbi-services.com\/blog\/wp-json\/wp\/v2\/comments?post=37700"}],"version-history":[{"count":21,"href":"https:\/\/www.dbi-services.com\/blog\/wp-json\/wp\/v2\/posts\/37700\/revisions"}],"predecessor-version":[{"id":37725,"href":"https:\/\/www.dbi-services.com\/blog\/wp-json\/wp\/v2\/posts\/37700\/revisions\/37725"}],"wp:attachment":[{"href":"https:\/\/www.dbi-services.com\/blog\/wp-json\/wp\/v2\/media?parent=37700"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.dbi-services.com\/blog\/wp-json\/wp\/v2\/categories?post=37700"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.dbi-services.com\/blog\/wp-json\/wp\/v2\/tags?post=37700"},{"taxonomy":"type","embeddable":true,"href":"https:\/\/www.dbi-services.com\/blog\/wp-json\/wp\/v2\/type_dbi?post=37700"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}