With the rise of Cyber Crime, more and more companies are looking at an architecture with a second backup copy that is protected with an airgap. Below is the common architecture that I am seeing.
In this post I will walk through an example of how to implement a simple Java program that performs the tasks necessary to manage the airgap for a ZDLRA that is implemented in a cyber vault (DC1 Vault in the picture). Feel free to use this as a starting point to automate the process.
Commands
There are 3 commands that I need to be able execute remotely
- PAUSE -This will pause the replication server that I configured
- RESUME - This will resume the replication server that I configured
- QUERY - This will query the queue on the upstream to determine how much is left in the queue.
Config file (airgap.config).
- HOST - This is name of the scan listener on upstream ZDLRA.
- PORT - This is the Sqlnet port being used to connect to the upstream ZDLRA
- SERVICE_NAME - Service name of the database on the upstream ZDLRA
- USERNAME - The username to connect to the upstream database
- PASSWORD - Password for the user. Feel free to encrypt this in java.
- REPLICATION_SERVER - Replication server to manage
airgap.host=oracle-19c-test-tde
airgap.port=1521
airgap.service_name=ocipdb
airgap.username=bgrenn
airgap.password=oracle
airgap.replication_server=replairgap
Java code (airgap.java).
Java snippet start
import java.sql.*;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.FileInputStream;
import java.util.Date;
import java.util.Properties;
// Create a arigap class
public class airgap {
private Properties prop = new Properties();
Java snippet get properties
// Create a get_airgap_properties method
public void get_airgap_properties()
{
String fileName = "airgap.config";
try (FileInputStream fis = new FileInputStream(fileName)) {
prop.load(fis);
} catch (FileNotFoundException ex) {
System.out.println("cannot find config file airgap.config");
} catch (IOException ex) {
System.out.println("unknown issue finding config file airgap.config");
}
}
Java snippet pause replication server
// Create a pause_replication method
public void pause_replication()
{
try {
//Loading driver
Class.forName("oracle.jdbc.driver.OracleDriver");
//creating connection
Connection con = DriverManager.getConnection
("jdbc:oracle:thin:@//"+
prop.getProperty("airgap.host")+":"+
prop.getProperty("airgap.port")+"/"+
prop.getProperty("airgap.service_name"),
prop.getProperty("airgap.username"),
prop.getProperty("airgap.password"));
CallableStatement cs=con.prepareCall("{call dbms_ra.pause_replication_server(?)}");
//Set IN Parameters
String in1 = prop.getProperty("airgap.replication_server");
cs.setString(1,in1);
ResultSet rs = cs.executeQuery(); //executing statement
con.close(); //closing connection
System.out.println("replication server '"+ prop.getProperty("airgap.replication_server")+"' paused");
}
catch(Exception e) {
e.printStackTrace();
}
}
Java snippet resume replication server
// Create a pause_replication method
public void resume_replication()
{
try {
//Loading driver
Class.forName("oracle.jdbc.driver.OracleDriver");
//creating connection
Connection con = DriverManager.getConnection
("jdbc:oracle:thin:@//"+
prop.getProperty("airgap.host")+":"+
prop.getProperty("airgap.port")+"/"+
prop.getProperty("airgap.service_name"),
prop.getProperty("airgap.username"),
prop.getProperty("airgap.password"));
CallableStatement cs=con.prepareCall("{call dbms_ra.resume_replication_server(?)}");
//Set IN Parameters
String in1 = prop.getProperty("airgap.replication_server");
cs.setString(1,in1);
ResultSet rs = cs.executeQuery(); //executing statement
con.close(); //closing connection
System.out.println("replication server '"+ prop.getProperty("airgap.replication_server")+"' resumed");
}
catch(Exception e) {
e.printStackTrace();
}
}
Java snippet query replication server
- REPLICATION SERVER - name of the replication server
- TASKS QUEUED - Number of tasks in the queue to be replicated
- TOTAL GB QUEUED - Amount of data in the queue
- MINUTES IN QUEUE - The number of minutes the oldest replication piece has been in the queue.
// Create a queue_select method
public void queue_select()
{
try {
//Loading driver
Class.forName("oracle.jdbc.driver.OracleDriver");
//creating connection
Connection con = DriverManager.getConnection
("jdbc:oracle:thin:@//"+
prop.getProperty("airgap.host")+":"+
prop.getProperty("airgap.port")+"/"+
prop.getProperty("airgap.service_name"),
prop.getProperty("airgap.username"),
prop.getProperty("airgap.password"));
Statement s=con.createStatement(); //creating statement
ResultSet rs=s.executeQuery("select replication_server_name,"+
" count(*) tasks_queued,"+
" trunc(sum(total)/1024/1024/1024,0) AS TOTAL_GB_QUEUED,"+
" round("+
" (cast(current_timestamp as date) - cast(min(start_time) as date))"+
" * 24 * 60"+
" ) as queue_minutes "+
"from RA_SBT_TASK "+
" join ra_replication_config on (lib_name = SBT_library_name) "+
" where archived = 'N'"+
"group by replication_server_name"); //executing statement
System.out.println("Replication Server,Tasks Queued,Total GB Queued,Minutes in Queue");
while(rs.next()){
System.out.println(rs.getString(1)+","+
rs.getInt(2)+","+
rs.getInt(3)+","+
rs.getString(4));
}
con.close(); //closing connection
}
catch(Exception e) {
e.printStackTrace();
}
}
Java snippet main section
public static void main(String[] args)
{
// import java.sql.*;
airgap airgap = new airgap(); // Create a airgap object
airgap.get_airgap_properties(); // Call the queue_select() method
switch(args[0]) {
case "resume":
airgap.resume_replication(); // Call the resume_replication() method
break;
case "pause":
airgap.pause_replication(); // Call the pause_replication() method
break;
case "query":
airgap.queue_select(); // Call the queue_select() method
break;
default:
System.out.println("parameter must be one of 'resume','pause' or 'query'");
}
}
}
Executing the Java code (airgap.class).
javac airgap.java
$ java -Djava.security.egd=file:/dev/../dev/urandom -cp ojdbc8.jar:. airgap pause
replication server 'replairgap' paused
$ java -Djava.security.egd=file:/dev/../dev/urandom -cp ojdbc8.jar:. airgap resume
replication server 'replairgap' resumed
$ java -Djava.security.egd=file:/dev/../dev/urandom -cp ojdbc8.jar:. airgap query
Replication Server,Tasks Queued,Total GB Queued,Minutes in Queue
ra_replication_config,4,95,58