Using Connection Manager to protect a database
Posted by Martin Bach on July 11, 2011
I have known about Oracle’s connection manager for quite a while but never managed to use it in anger. In short there was no need to do so. Now however I have been asked to help in finding a solution to an interesting problem.
In summary, my customer is running DR and UAT in the same data centre. Now for technical reasons they rely on RMAN to refresh UAT, no array mirror splits on the SAN (which would be way faster!) possible. The requirement is to prevent an RMAN session with target=UAT and auxiliary=DR from overwriting the DR databases, all of which are RAC databases on 11.2.The architecture included 2 separate networks for the DR hosts and the UAT hosts. DR was on 192.168.99.0/24 whereas UAT was on 192.168.100.0/24. A gateway host with two NICs connects the two. Initially there was a firewall on the gateway host to prevent traffic from UAT to DR, but because of the way Oracle connections work this proved impossible (the firewall was a simple set of IPTABLES rules). After initial discussions I decided to look at connection manager more closely as that is hailed as a solution to Oracle connectivity and firewalls.
Thinking more closely about the problem I realised that the firewall + static routes approach might not be the best one. So I decided to perform a test which didn’t involve IPTABLES or custom routes, and rely on CMAN only to protect the database. A UAT refresh isn’t something that’s going to happen very frequently, which allows me to shut down CMAN, and thus prevent any communication between the two networks. This is easier than maintaining a firewall-remember the easiest way to do something is not to do it at all.
Overview of Connection Manager
For those of you who aren’t familiar with CMAN, here’s a short summary (based on the official Oracle documentation).
Configuration of Oracle Connection Manager (CMAN) allows the clients to connect through a firewall [I haven’t verified this yet, ed]. CMAN is an executable that allows clients to connect despite a firewall being in place between the client and server. CMAN is similar to the Listener in that it reads a configuration file [called CMAN.ora, ed], which contains an address that Oracle Connection Manager listens for incoming connections. CMAN starts similar to the Listener and will enter a LISTEN state.
This solution [to the firewall issue with TCP redirects, ed] will make the REDIRECT happen inside the firewall and the client will not see it; CMAN comports as a proxy service between the client and the real database listener.
Interestingly, Connection Manager is fully integrated into the FAN/FCF framework and equally suitable for UCP connection pools.
Technically speaking Oracle database instances require the initialisation parameters local_listener and remote_listener to be set. In RAC databases, this is usually the case out of the box, however, in addition to the SCAN, the remote_listener must include the CMAN listener as well-an example is provided in this document.
Installing Connection Manager
Connection Manager is now part of the Oracle client, and you can install it by choosing the “custom” option. From the list of selectable options, pick “Oracle Net Listener” and “Oracle Connection Manager”.
From there on it’s exactly the same as any other client installation.
Testing
A quick test with 2 separate networks reveals that the concept actually works. The following hosts are used:
- cman 192.168.99.224
- cmandb 192.168.100.225
- client 192.168.99.31
The networks in use are:
- Public network: 192.168.99.0/24
- Private network: 192.168.100.0/24
As you can see CMANDB is on a different network than the other hosts.
Connection manager has been installed on host “cman”, with IP 192.168.99.224. The listener process has been configured to listen on port 1821. The corresponding cman.ora file has been configured as follows in $CLIENT_HOME/network/admin:
cman1 = (configuration= (address=(protocol=tcp)(host=192.168.99.224)(port=1821)) (rule_list= (rule=(src=*)(dst=127.0.0.1)(srv=cmon)(act=accept)) (rule=(src=192.168.99.0/24)(dst=192.168.100.225)(srv=*)(act=accept)) ) )
The file has been left in this minimalistic state deliberately. The only connection possible is to the database host. The cmon service must be allowed or otherwise the startup of the connection manager processes will fail.
The gateway host has 2 network interfaces, one for each network:
[root@cman ~]# ifconfig eth0 Link encap:Ethernet HWaddr 00:16:3E:F2:34:56 inet addr:192.168.99.224 Bcast:192.168.99.255 Mask:255.255.255.0 UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1 RX packets:1209 errors:0 dropped:0 overruns:0 frame:0 TX packets:854 errors:0 dropped:0 overruns:0 carrier:0 collisions:0 txqueuelen:1000 RX bytes:105895 (103.4 KiB) TX bytes:147462 (144.0 KiB) Interrupt:17 eth1 Link encap:Ethernet HWaddr 00:16:3E:52:4A:56 inet addr:192.168.100.224 Bcast:192.168.100.255 Mask:255.255.255.0 UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1 RX packets:334 errors:0 dropped:0 overruns:0 frame:0 TX packets:151 errors:0 dropped:0 overruns:0 carrier:0 collisions:0 txqueuelen:1000 RX bytes:36425 (35.5 KiB) TX bytes:22141 (21.6 KiB) Interrupt:16
Its routing table is defined as follows:
[root@cman ~]# route -n Kernel IP routing table Destination Gateway Genmask Flags Metric Ref Use Iface 192.168.100.0 0.0.0.0 255.255.255.0 U 0 0 0 eth1 192.168.101.0 0.0.0.0 255.255.255.0 U 0 0 0 eth2 192.168.99.0 0.0.0.0 255.255.255.0 U 0 0 0 eth0 169.254.0.0 0.0.0.0 255.255.0.0 U 0 0 0 eth2 [root@cman ~]
Configuration on host “CMANDB”
Note that host 192.168.100.225 is on the private network! The database CMANDB has its local and remote listener configured using the below entries in tnsnames.ora:
[oracle@cmandb admin]$ cat tnsnames.ora
[oracle@cmandb admin]$ cat tnsnames.ora LOCAL_CMANDB = (DESCRIPTION = (ADDRESS_LIST = (ADDRESS = (PROTOCOL = TCP)(PORT = 1521)(HOST = 192.168.100.225)) ) ) REMOTE_CMANDB = (DESCRIPTION = (ADDRESS_LIST = (ADDRESS = (PROTOCOL = TCP)(PORT = 1821)(HOST = 192.168.99.224)) (ADDRESS = (PROTOCOL = TCP)(PORT = 1521)(HOST = 192.168.100.225)) ) ) CMAN_LSNR = (DESCRIPTION = (ADDRESS_LIST = (ADDRESS = (PROTOCOL = TCP)(PORT = 1521)(HOST = 192.168.99.224)) ) )
- local listener is set to local_cmandb
- remote listener is set to remote_cmandb
- CMAN_LSNR is used for a test to verify that a connection to the CMAN listener is possible from this host
The host had only one network interface, connecting to the CMAN host:
[root@cmandb ~]# ifconfig eth0 Link encap:Ethernet HWaddr 00:16:3E:12:14:51 inet addr:192.168.100.225 Bcast:192.168.100.255 Mask:255.255.255.0 UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1 RX packets:627422 errors:0 dropped:0 overruns:0 frame:0 TX packets:456584 errors:0 dropped:0 overruns:0 carrier:0 collisions:0 txqueuelen:1000 RX bytes:2241758430 (2.0 GiB) TX bytes:32751153 (31.2 MiB)
The only change to the system was the addition of a default gateway. Unfortunately the CMAN process cannot listen on more than one IP address:
# route add default gw 192.168.100.224 eth0
The following routing table was in use during the testing:
[root@cmandb ~]# route -n Kernel IP routing table Destination Gateway Genmask Flags Metric Ref Use Iface 192.168.100.0 0.0.0.0 255.255.255.0 U 0 0 0 eth0 169.254.0.0 0.0.0.0 255.255.0.0 U 0 0 0 eth0 0.0.0.0 192.168.100.224 0.0.0.0 UG 0 0 0 eth0
After the database listener parameters have been changed to LOCAL_CMANDB and REMOTE_CMANDB, the alert log on the CMAN host recorded a number of service registrations. This is important as it allows Connection Manager to hand the connection request off to the database:
[oracle@cman trace]$ grep cmandb * 08-JUL-2011 10:19:55 * service_register * cmandb * 0 08-JUL-2011 10:25:28 * service_update * cmandb * 0 08-JUL-2011 10:35:28 * service_update * cmandb * 0 [...] 08-JUL-2011 11:15:10 * service_update * cmandb * 0 08-JUL-2011 11:15:28 * service_update * cmandb * 0 [oracle@cman trace]$
Additionally, the CMAN processes now know about the database service CMANDB:
CMCTL:cman1> show services Services Summary... Proxy service "cmgw" has 1 instance(s). Instance "cman", status READY, has 2 handler(s) for this service... Handler(s): "cmgw001" established:0 refused:0 current:0 max:256 state:ready <machine: 127.0.0.1, pid: 2298> (ADDRESS=(PROTOCOL=tcp)(HOST=127.0.0.1)(PORT=23589)) "cmgw000" established:0 refused:0 current:0 max:256 state:ready <machine: 127.0.0.1, pid: 2294> (ADDRESS=(PROTOCOL=tcp)(HOST=127.0.0.1)(PORT=31911)) Service "cmandb" has 1 instance(s). Instance "cmandb", status READY, has 1 handler(s) for this service... Handler(s): "DEDICATED" established:0 refused:0 state:ready REMOTE SERVER (DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(PORT=1521)(HOST=192.168.100.225))) Service "cmandbXDB" has 1 instance(s). Instance "cmandb", status READY, has 1 handler(s) for this service... Handler(s): "D000" established:0 refused:0 current:0 max:1022 state:ready DISPATCHER <machine: cmandb.localdomain, pid: 7167> (ADDRESS=(PROTOCOL=tcp)(HOST=cmandb.localdomain)(PORT=50347)) Service "cmon" has 1 instance(s). Instance "cman", status READY, has 1 handler(s) for this service... Handler(s): "cmon" established:1 refused:0 current:1 max:4 state:ready <machine: 127.0.0.1, pid: 2282> (ADDRESS=(PROTOCOL=tcp)(HOST=127.0.0.1)(PORT=59541)) The command completed successfully. CMCTL:cman1>
Connectivity Test
With the setup completed it was time to perform a test from a third host on the (public) network. Its IP address is 192.168.99.31. The below TNSnames entries were created:
[oracle@client admin]$ cat tnsnames.ora CMAN = (DESCRIPTION = (ADDRESS_LIST = (ADDRESS = (PORT=1821)(HOST=192.168.99.224)(PROTOCOL=TCP)) ) (CONNECT_DATA = (SERVICE_NAME = cmandb) (SERVER = DEDICATED) ) ) DIRECT = (DESCRIPTION = (ADDRESS_LIST = (ADDRESS = (PORT=1521)(HOST=192.168.100.225)(PROTOCOL=TCP)) ) (CONNECT_DATA = (SERVICE_NAME = cmandb) (SERVER = DEDICATED) ) )
The CMAN entry uses the Connection Manager gateway host to connect to database CMANDB, whereas the DIRECT entry tries to bypass the latter. A tnsping should show whether or not this is possible.
[oracle@client admin]$ tnsping direct TNS Ping Utility for Linux: Version 11.2.0.1.0 - Production on 08-JUL-2011 11:19:47</pre> Copyright (c) 1997, 2009, Oracle. All rights reserved. Used parameter files: Used TNSNAMES adapter to resolve the alias Attempting to contact (DESCRIPTION = (ADDRESS_LIST = (ADDRESS = (PORT=1521)(HOST=192.168.100.225)(PROTOCOL=TCP))) (CONNECT_DATA = (SERVICE_NAME = cmandb) (SERVER = DEDICATED))) TNS-12543: TNS:destination host unreachable [oracle@client admin]$ tnsping cman TNS Ping Utility for Linux: Version 11.2.0.1.0 - Production on 08-JUL-2011 10:53:27 Copyright (c) 1997, 2009, Oracle. All rights reserved. Used parameter files: Used TNSNAMES adapter to resolve the alias Attempting to contact (DESCRIPTION = (ADDRESS_LIST = (ADDRESS = (PORT=1821)(HOST=192.168.99.224)(PROTOCOL=TCP))) (CONNECT_DATA = (SERVICE_NAME = cmandb) (SERVER = DEDICATED))) OK (0 msec) [oracle@client admin]$ <pre>
Now that proves that a direct connection is impossible, and also that the connection manager’s listener is working. A tnsping doesn’t imply that a connection is possible though, this requires an end to end test with SQL*Plus:
[oracle@client admin]$ sqlplus system/xxx@cman SQL*Plus: Release 11.2.0.1.0 Production on Fri Jul 8 10:55:39 2011 Copyright (c) 1982, 2009, Oracle. All rights reserved. Connected to: Oracle Database 11g Enterprise Edition Release 11.2.0.2.0 - 64bit Production With the Partitioning, OLAP, Data Mining and Real Application Testing options SQL> select instance_name,host_name from v$instance; INSTANCE_NAME ---------------- HOST_NAME ---------------------------------------------------------------- cmandb cmandb.localdomain
The successful connection is also recorded in the CMAN log file:
Fri Jul 08 10:55:39 2011
08-JUL-2011 10:55:39 * (CONNECT_DATA=(SERVICE_NAME=cmandb)(SERVER=DEDICATED)(CID=(PROGRAM=sqlplus@client)(HOST=client)(USER=oracle))) * (ADDRESS=(PROTOCOL=tcp)(HOST=192.168.99.31)(PORT=16794)) * establish * cmandb * 0
(LOG_RECORD=(TIMESTAMP=08-JUL-2011 10:55:39)(EVENT=Ready)(CONN NO=0))
Simon Haslam said
Martin – when you say “but because of the way Oracle connections work this proved impossible” what do you mean? You mean because a listener accepts for any service/SID connection request from any source network? Isn’t there some way of restricting that in config (or am I getting confused)? Alternatively if you have a db host running, say, both DR and UAT instances, you could have two different listeners (e.g. live and test) and then simply restrict access by the firewall.
Just a thought,
Simon
Gokhan Atil said
We were connecting to the remote databases through CMAN (we were using it as proxy) in my previous company. To increase the security, there were also VPN tunnels between our company network and the remote networks.
As I remember, unpatched 10g version on windows, had some memory leak problems, and we were restarting its service periodically under heavy loads, but 11g version was working stable.
CMAN also supports SSL connections (although we haven’t used/tested that feature).
Regards
Gokhan
Log Buffer #229, A Carnival of the Vanities for DBAs | The Pythian Blog said
[...] Martin Bach solves an interesting problem, i.e. Using Connection Manager to protect a database. [...]