Database Service Firewall : Access Control to a PDB in RAC

johannes-plenio-276383-unsplash

(Photo by Johannes Plenio, on Unsplash)

Conferences are great. Not only for the technical content, also for the people. Recently during DOAG, I had very interesting conversations (yes, several conversations 🙂 ) with Martin Berger about how to control who is connecting to which database in a complex environment. Among other topics, we mentioned that it was possible, starting with Oracle 12.2, to set Access Control Lists to allow connections to a database service (in Non-CDB or PDB) from specific IP addresses.
This new feature Database Service Firewall was introduced with Oracle 12.2. It should not to be confused with Database Firewall, which is a dedicated system used to monitor traffic from and to databases, and is part of Oracle Audit Vault and Database Firewall product.

As I never used Database Service Firewall, I decided to give it a try in a Multitenant environment with RAC.
My lab is a 2-node RAC cluster with Grid Infrastructure 18, a 18.3 RAC Container database called metal, and one pluggable database called opeth.

Service creation

I start by creating a new service called favourite_band on this PDB with 1 preferred instance :

srvctl add service -db metal -service favourite_band -pdb opeth -preferred metal1
srvctl start service -db metal -service favourite_band

Listeners modification

Following My Oracle Support Doc 2360283.1 on Database Firewall Service, I now want to filter all connections to PDB opeth, except those coming from the IP address 192.168.78.51.

First, I modify local listeners to enable firewall functionality :

srvctl modify listener -listener listener -endpoints TCP:1521:FIREWALL=ON

And then, I add LOCAL_REGISTRATION_ADDRESS_LISTENER=ON in listener.ora. This is a mandatory parameter to allow service ACLs to be processed by listeners.

Access Control List creation

The Access Control List we are about to create is handled by the local listeners, as described in the documentation : “Local listeners and server processes validate all inbound client connections against the ACL.”

OK, now let’s try to add an Access Control Entry to prevent any host except IP 192.168.78.51 to connect to PDB opeth, using any service of course :

SQL> connect / as sysdba
Connected.

SQL> show con_name
CON_NAME
------------------------------
CDB$ROOT

SQL> execute dbsfwuser.DBMS_SFW_ACL_ADMIN.ip_add_pdb_ace('opeth','192.168.78.51') ;
BEGIN dbsfwuser.DBMS_SFW_ACL_ADMIN.ip_add_pdb_ace('opeth','192.168.78.51') ; END;

*
ERROR at line 1:
ORA-20014: Must run as DBSFWUSER user
ORA-06512: at "DBSFWUSER.DBMS_SFW_ACL_ADMIN", line 322
ORA-06512: at "DBSFWUSER.DBMS_SFW_ACL_ADMIN", line 920
ORA-06512: at line 1

Oops, even SYS is not allowed to run this package : “This package is owned by the DBSFWUSER schema. The procedures in this package can be run only by the DBSFWUSER user.”

So I have to unlock this user in order to connect (or use proxy user authentication). It does not work with ALTER SESSION SET CURRENT_SCHEMA either :

SQL> alter user dbsfwuser identified by aggressive_password account unlock ;
User altered.

SQL> connect dbsfwuser/aggressive_password
Connected.

Now that’s much better :

SQL> execute dbsfwuser.DBMS_SFW_ACL_ADMIN.ip_add_pdb_ace('opeth','192.168.78.51') ;
PL/SQL procedure successfully completed.

SQL> execute dbsfwuser.DBMS_SFW_ACL_ADMIN.commit_acl ;
PL/SQL procedure successfully completed.

If I try to connect to service favourite_band, which refers to PDB opeth, from host 192.168.78.52, I get the following error :

# sqlplus mikael@favourite_band

SQL*Plus: Release 18.0.0.0.0 - Production on Wed Dec 12 13:40:42 2018
Version 18.3.0.0.0

Copyright (c) 1982, 2018, Oracle. All rights reserved.

Enter password:

ERROR:
ORA-12506: TNS:listener rejected connection based on service ACL filtering

But from server 192.168.78.51, everything is fine :

# sqlplus mikael@favourite_band

SQL*Plus: Release 18.0.0.0.0 - Production on Wed Dec 12 13:42:27 2018
Version 18.3.0.0.0

Copyright (c) 1982, 2018, Oracle. All rights reserved.

Enter password:
Last Successful login time: Fri Dec 07 2018 23:36:31 +01:00

Connected to:
Oracle Database 18c Enterprise Edition Release 18.0.0.0.0 - Production
Version 18.3.0.0.0

Useful table and view

There are 2 interesting objects : table dbsfwuser.ip_acl, which contains ACLs that are committed, and view v$ip_acl, which contains ACLs sent to the local listener.

SQL> select * from v$ip_acl ;

SERVICE_NAME                   HOST                 CON_ID
------------------------------ -------------------- ----------
FAVOURITE_BAND.RACATTACK       192.168.78.51        3
OPETH.RACATTACK                192.168.78.51        3

The first row refers to our newly created service, and the second one refers to the default service name that comes with every PDB creation.

SQL> select * from dbsfwuser.ip_acl ;

SERVICE_NAME                                                     HOST
---------------------------------------------------------------- ----------------------
"7C18DF5B044067E2E053334EA8C09FD1.RACATTACK"                     192.168.78.51
"FAVOURITE_BAND.RACATTACK"                                       192.168.78.51
"OPETH.RACATTACK"                                                192.168.78.51

What is this first strange service name ? Thanks to Franck Pachot and this excellent blog post, I found out that it is an internal service named after the GUID of the PDB. It is not supposed to be used.

If you remember, at the beginning of this post, I created service favourite_band on 1 preferred instance of my RAC database. So if I check v$ip_acl on the other instance, I only have the default PDB service :

SQL> select * from v$ip_acl ;
SERVICE_NAME                   HOST                 CON_ID
------------------------------ -------------------- ----------
OPETH.RACATTACK                192.168.78.51        3

But thanks to gv$ip_acl, I can check what’s active on all instances :

SQL> select * from gv$ip_acl ;

INST_ID    SERVICE_NAME                   HOST                 CON_ID
---------- ------------------------------ -------------------- ----------
1          FAVOURITE_BAND.RACATTACK       192.168.78.51        3
1          OPETH.RACATTACK                192.168.78.51        3
2          OPETH.RACATTACK                192.168.78.51        3

Now, let’s see what v$ip_acl and dbsfwuser.ip_acl contain in different cases :

What happens when I stop my service ?

  • Stop service favourite_band :
# srvctl stop service -db metal -service favourite_band
# srvctl status service -service favourite_band -db metal
Service favourite_band is not running.
  • Check v$ip_acl :
SQL> select * from v$ip_acl ;

SERVICE_NAME                   HOST                 CON_ID
------------------------------ -------------------- ----------
FAVOURITE_BAND.RACATTACK       192.168.78.51        3
OPETH.RACATTACK                192.168.78.51        3

Service favourite_band still appears.

  • Check dbsfwuser.ip_acl :
SQL> select * from dbsfwuser.ip_acl ;
SERVICE_NAME                                                     HOST
---------------------------------------------------------------- ----------------------
"7C18DF5B044067E2E053334EA8C09FD1.RACATTACK"                     192.168.78.51
"FAVOURITE_BAND.RACATTACK"                                       192.168.78.51
"OPETH.RACATTACK"                                                192.168.78.51

Likewise, service favourite_band still appears.

  • Issue a commit :
SQL> execute DBMS_SFW_ACL_ADMIN.commit_acl;
PL/SQL procedure successfully completed.
  • Check v$ip_acl again :
SQL> select * from v$ip_acl ;

SERVICE_NAME                   HOST                 CON_ID
------------------------------ -------------------- ----------
FAVOURITE_BAND.RACATTACK       192.168.78.51        3
OPETH.RACATTACK                192.168.78.51        3

Likewise.

  • Check dbsfwuser.ip_acl again :
SQL> select * from dbsfwuser.ip_acl ;

SERVICE_NAME                                                      HOST
----------------------------------------------------------------- --------------------
"7C18DF5B044067E2E053334EA8C09FD1.RACATTACK"                      192.168.78.51
"FAVOURITE_BAND.RACATTACK"                                        192.168.78.51
"OPETH.RACATTACK"                                                 192.168.78.51

Likewise. Thus, those 2 objects do not reflect the status of the service.

What happens I, then, remove my service ?

  • Remove service favourite_band :
srvctl remove service -db metal -service favourite_band -f
  • Check v$ip_acl :
SQL> select * from v$ip_acl ;

SERVICE_NAME                   HOST                 CON_ID
------------------------------ -------------------- ----------
FAVOURITE_BAND.RACATTACK       192.168.78.51        3
OPETH.RACATTACK                192.168.78.51        3

Service favourite_band still appears.

  • Check dbsfwuser.ip_acl :
SQL> select * from dbsfwuser.ip_acl ;

SERVICE_NAME                                                     HOST
---------------------------------------------------------------- ----------------------
"7C18DF5B044067E2E053334EA8C09FD1.RACATTACK"                     192.168.78.51
"FAVOURITE_BAND.RACATTACK"                                       192.168.78.51
"OPETH.RACATTACK"                                                192.168.78.51

Likewise.

  • Issue a commit :
SQL> execute DBMS_SFW_ACL_ADMIN.commit_acl;
PL/SQL procedure successfully completed.
  • Check v$ip_acl :
SQL> select * from v$ip_acl ;

SERVICE_NAME                   HOST                 CON_ID
------------------------------ -------------------- ----------
OPETH.RACATTACK                192.168.78.51        3

Now that’s interesting : service favourite_band is gone.

  • Check dbsfwuser.ip_acl :
SQL> select * from dbsfwuser.ip_acl ;

SERVICE_NAME                                                     HOST
---------------------------------------------------------------- ----------------------
"7C18DF5B044067E2E053334EA8C09FD1.RACATTACK"                     192.168.78.51
"FAVOURITE_BAND.RACATTACK"                                       192.168.78.51
"OPETH.RACATTACK"                                                192.168.78.51

But still, it appears in dbsfwuser.ip_acl even if the commit has been issued. This is not a big problem as the PDB is still protected, but it can be misleading.
Now, how to go back to a clean situation ?
The only solution I have for the moment is to recreate and restart the service, remove the Access Control List, commit the change, check everything disappeared, and then remove the service again … But I am sure there is a better solution.

Remove Access Control List

Finally, to allow all hosts to access again PDB opeth, I remove the corresponding ACL and commit using the following procedures :

SQL> execute DBMS_SFW_ACL_ADMIN.ip_remove_pdb_acl('opeth') ;
PL/SQL procedure successfully completed.

SQL> execute DBMS_SFW_ACL_ADMIN.commit_acl ;
PL/SQL procedure successfully completed.

… and also modify local listeners with (FIREWALL=OFF) :

srvctl modify listener -listener listener -endpoints TCP:1521:FIREWALL=OFF

Be careful not to forget this last step … I did, and then I could not connect from any host anymore, even though dbsfwuser.ip_acl and v$ip_acl returned no rows … But of course, the documentation says “If no ACLs are configured for a service, all connections are rejected for that service.”

Note that you can also remove one specific entry from the ACL with procedure DBMS_SFW_ACL_ADMIN.ip_remove_pdb_ace.

Bonus point

Maybe you noticed that v$ip_acl never contains the GUID service … What if I try to connect to PDB opeth, from an authorized host, with the GUID service ?

# sqlplus mikael@"(DESCRIPTION=(CONNECT_DATA=(SERVICE_NAME=7c18df5b044067e2e053334ea8c09fd1.racattack))(ADDRESS=(PROTOCOL=TCP)(HOST=racatt-scan.racattack)(PORT=1521)))"

SQL*Plus: Release 18.0.0.0.0 - Production on Sat Dec 15 16:47:39 2018
Version 18.3.0.0.0

Copyright (c) 1982, 2018, Oracle. All rights reserved.

Enter password:

ERROR:
ORA-12506: TNS:listener rejected connection based on service ACL filtering

So it is not possible to connect to the PDB through it with an authorized host, even though this host is in dbsfwuser.ip_acl

Use with Connection Manager (CMAN)

This functionnality could be used with CMAN by authorizing only CMAN’s hosts in ACLs of PDBs, and then, manage clients’ hosts in CMAN configuration instead of managing them in a distributed way accross all PDBs. Thanks to Ludovico Caldara for this idea 🙂

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s