Aktuell ist das Thema Datenbank Security sehr wichtig und dringend geworden. In den letzten 20 Jahren hat es dafür kaum Interesse gegeben. Viele Anbieter nutzen die CIS Benchmarks(tm) – beispielsweise für die Oracle Datenbank 19c – als Quelle für Ihre Checks und Empfehlungen.
Im CIS Benchmark steht unter anderem:
„This CIS Benchmark was created using a consensus review process comprised of a global community of subject matter experts. The process combines real world experience with data-based information to create technology specific guidance to assist users to secure their environments. Consensus participants provide perspective from a diverse set of backgrounds including consulting, software development, audit and compliance, security research, operations, government, and legal.„
Quelle: CIS – Center for Internet Security: Oracle Datenbank 19c Benchmark Version 1.1.0, Seite 7
Was nicht zu finden ist, ist der Hinweis, dass die Umsetzung der Empfehlungen ohne weiteres Hinterfragen dazu führen kann, dass die Applikation nicht mehr funktioniert. Schauen wir uns einmal die Empfehlung zum Instance Parameter global_names
an. Die folgenden Zeilen sind vom CIS Benchmark übernommen:
Description:
The global_names
setting requires that the name of a database link matches that of the remote database it will connect to. This setting should have a value of TRUE.
Rationale:
Not requiring database connections to match the domain that is being called remotely could allow unauthorized domain sources to potentially connect via brute-force tactics.
Quelle: CIS – Center for Internet Security: Oracle Datenbank 19c Benchmark Version 1.1.0, Kapitel 2.2.3 / Seite 27
Diese simple Änderung eines Instanz-Parameters kann Applikationen, die Datenbank-Links nutzen, funktionsunfähig machen. Abgesehen davon, ist die Begründung mehr als nur schwach. Warum soll sich ein Angreifer die Mühe machen, eine Datenbank und DB Links anzulegen, nur um brute-force Attacken auf Benutzer und Passwörter durchzuführen? Das geht doch viel einfacher und mit weniger Aufwand mit jedem beliebigen Oracle Client!
Welche Auswirkung hat diese Änderung?
Damit die Auswirkungen besser verständlich sind, muss man die Bestandteile des GLOBAL_NAMES
kennen und verstehen.
Die Verwirrung wird noch größer, da es einerseits den Instance Parameter global_names
und andererseits den GLOBAL_NAME
der Datenbank gibt. Der Instance Parameter global_names
kann nur die Werte TRUE
oder FALSE
enthalten.
Der GLOBAL_NAME
setzt sich aus dem Namen der Datenbank (db_name
) und der Datenbank Domain (db_domain
) zusammen. Der DBCA schlägt die DNS-Domain als db_domain
vor, dass ist aber nicht verpflichtend und nur eine der Möglichkeiten. In der Praxis wird die db_domain
auch gerne genutzt um die verschiedenen Umgebungen (PROD, DEV, TEST, QA,…) zu unterscheiden, sofern man dies nicht mittels db_name
umsetzt.
Was der CIS Benchmark und alle uns bekannten Security Scanner verschweigen: wird GLOBAL_NAMES
auf TRUE
gesetzt, müssen folgende Punkte beachtet werden:
- Mittels „
ALTER DATABASE RENAME GLOBAL_NAME TO '<db_name>.<db_domain>';
“ muss der Globale Name der Datenbank korrigiert werden. Erfolgt dieser Schritt nicht, ist kein Connect via Datenbank-Link auf diese Datenbank möglich. - Es müssen alle Datenbanken, die über DB Links miteinander verknüpft sind, gleichzeitig umgestellt werden. Ein Connect zwischen Datenbanken mit unterschiedlichen Einstellungen ist nicht möglich.
- Bei allen Datenbank-Links wird automatisch die
DB_DOMAIN
angehängt. Da diese mit Sicherheit nicht entsprechend bedacht wurde, würde voraussichtlich kein einziger DB Link mehr funktionieren. - Bei der Nutzung eines DB Links wird überprüft, ob der DB Link Name mit den
GLOBAL_NAME
der Zieldatenbank übereinstimmt. Dies wird in der Praxis meist nicht der Fall sein! - Wegen dieser Anforderungen darf es pro Ziel-Datenbank nur einen DB Link geben.
- Somit müssen voraussichtlich alle DB Links gelöscht und mit geänderten Namen angelegt werden. Da die Namen der DB Links auch in vielen Datenbankobjekten (Views, Synonyme, PL/SQL und Java Code) vorkommen können, müssen diese dort meist ebenfalls geändert werden.
Somit hat die Änderung von GLOBAL_NAMES
eine massive Auswirkung auf den Betrieb von Applikationen, die Datenbank-Links benötigen. Das kann von einer einfachen Betriebsstörung bis zum Stillstand der Anwendung reichen.
Das Fatale an genau dieser Empfehlung ist, dass sie keinerlei Rücksicht darauf nimmt, ob Datenbank-Links genutzt werden oder nicht. Es wird lediglich auf den aktuellen Wert von global_names
abgefragt. Nutzt man keine Datenbank-Links, führt die Änderung des Parameters selbstverständlich zu keinen weiteren Problemen, da ja nur bestehende DB-Links betroffen sind. Die Änderung bewirkt also keinerlei Verbesserung der aktuellen Datenbank Security und ist somit sinnlos.
In einer realen Datenbank Umgebung muss man jedoch noch viele weitere Punkte beachten, bevor man den Parameter ändern darf:
- Nutzt man Datenbank Links?
- Welche anderen Datenbanken werden mit diesen Links angesprochen? Haben diese ihrerseits weiterführende Links (es müssen ja alle Datenbanken gleichzeitig umgestellt werden)?
- In welchen Datenbank Objekten werden die DB-Links referenziert (Views, Synonyme, PL/SQL und Java Code)?
- In welchen SQL Statements werden die DB-Links genutzt?
Beschreibung des Beispiel-Environments
In diesem Beispiel nutzen wir zwei Datenbanken. Eine non-CDB Datenbank mit dem Namen DB19 sowie eine Multitenant Datenbank db19ee mit einer PDB names PDB1.
- In der Datenbank DB19 gibt es einen Benutzer DEMO sowie einige DB Links.
- In der Pluggable Database PDB1 heisst der Benutzer DEMOPDB1 und hat ebenfalls einige DB Links.
In den Schemata DEMO sowie DEMOPDB1 simulieren wir mittels einfacher Views, dass die Datenbank-Links auch in Datenbankobjekten genutzt werden. Neben Views gibt es auch noch andere Datenbankobjekte, die relevant sein können:
- SYNONYME
- (stored) Code, egal ob PL/SQL, JAVA oder in Zukunft auch Java Script, etc.
- MVIEWS
- VIEWS
Setup für das Beispiel
In beiden Datenbanken ist der Instance Parameter global_names
auf FALSE
gesetzt.
TNSNAMES.ORA
Für unser Beispiel nutzen wir folgenden Einträge im TNSNAMES.ORA:
SHOP =
(DESCRIPTION=
(ADDRESS=(PROTOCOL=tcp)(HOST=localhost)(PORT=1521))
(CONNECT_DATA=(SERVICE_NAME=db19.world))
)
FINANZ =
(DESCRIPTION=
(ADDRESS=(PROTOCOL=tcp)(HOST=localhost)(PORT=1521))
(CONNECT_DATA=(SERVICE_NAME=db19.world))
)
DEMOPDB1 =
(DESCRIPTION=
(ADDRESS=(PROTOCOL=tcp)(HOST=localhost)(PORT=1521))
(CONNECT_DATA=(SERVICE_NAME=pdb1))
)
Erzeugen der Benutzer, DB Links und Views
Mit den folgenden SQL Scripts werden die Benutzer, DB-Links und Views angelegt.
sqlplus system/oracle@localhost:1521/pdb1
create user demopdb1 identified by demopdb1;
grant dba to demopdb1;
drop public database link FINANZ;
drop public database link SHOP;
drop public database link LAGER;
create public database link FINANZ
connect to demo identified by demo using 'FINANZ';
create public database link SHOP
connect to demo identified by demo using 'SHOP';
create public database link LAGER
connect to demopdb1 identified by demopdb1 using 'DEMOPDB1';
connect demopdb1/demopdb1@localhost:1521/pdb1
create database link SHOPINFO
connect to demo identified by demo using 'SHOP';
connect system/oracle@localhost:1521/db19.world
create user demo identified by demo;
grant dba to demo;
connect demo/demo@localhost:1521/db19.world
create database link PDBLAGER
connect to demopdb1 identified by demopdb1 using 'DEMOPDB1';
create public database link LAGER
connect to demopdb1 identified by demopdb1 using 'DEMOPDB1';
create or replace view PDBLAGER_VIEWS as
select * from user_views@PDBLAGER;
create or replace view LAGER_DB as
select * from V$DATABASE@LAGER;
connect demopdb1/demopdb1@localhost:1521/pdb1
create or replace view FINANZ_USERS as
select * from all_users@FINANZ;
create or replace view SHOP_TABLES as
select * from all_tables@SHOP;
create or replace view SHOPINFO_VIEWS as
select * from all_views@SHOPINFO;
create or replace view LAGER_VIEWS as
select * from user_views@LAGER;
Environment prüfen
Mit den folgenden Statements – jeweils als der Applikationsbenutzer DEMO bzw. DEMOPDB1 abgesetzt – kann man die aktuelle Konfiguration auslesen:
Script „check_env.sql“
set pages 50 lines 160 tab off
col GLOBAL_NAME for a12
col NAME for a20
col VALUE for a20
col owner for a16
col db_link for a16
col username for a16
col host for a16
col valid for a5
SELECT * FROM global_name;
SELECT con_id, name, value FROM v$SYSTEM_PARAMETER
WHERE name in ('db_name','db_domain','global_names') ;
SELECT owner, db_link, username, host, valid FROM dba_db_links;
Output für die Datenbank DB19 (non-CDB):
@check_env
GLOBAL_NAME
------------
DB19.WORLD
CON_ID NAME VALUE
---------- -------------------- -----
0 db_domain world
0 global_names FALSE
0 db_name db19
OWNER DB_LINK USERNAME HOST VALID
------- ---------------- --------- --------- -----
DEMO PDBLAGER.WORLD DEMOPDB1 DEMOPDB1 YES
PUBLIC LAGER.WORLD DEMOPDB1 DEMOPDB1 YES
DB19 wurde offensichtlich mit db_domain=world
angelegt. Das war seitens Oracle viele Jahre lang (beginnend mit Oracle 7 oder 8) der Default für diesen Parameter. Da es sich um eine non-CDB Datenbank handelt, ist die CON_ID nicht relevant.
Testen wir jetzt, ob die Datenbank-Links aktuell funktionieren:
connect demo/demo@localhost:1521/db19.world
select view_name from user_views;
VIEW_NAME
---------------
PDBLAGER_VIEWS
LAGER_DB
select count(*) from PDBLAGER_VIEWS;
COUNT(*)
----------
4
select name from LAGER_DB;
NAME
--------------------
DB19EE
Das klappt schon mal.
Output für DB19EE / PDB1:
@check_env
GLOBAL_NAME
------------
PDB1
CON_ID NAME VALUE
---------- -------------------- ------
0 db_domain
0 global_names FALSE
0 db_name db19ee
OWNER DB_LINK USERNAME HOST VALID
--------- --------- ---------------- --------- -----
PUBLIC FINANZ DEMO FINANZ YES
PUBLIC SHOP DEMO SHOP YES
PUBLIC LAGER DEMOPDB1 DEMOPDB1 YES
DEMOPDB1 SHOPINFO DEMO SHOP YES
Da die Instance Parameter alle mit CON_ID=0
aufgeführt werden, bedeutet das, dass die Parameter in der CDB ROOT gesetzt sind (und nicht für einzelne PDBs).
Auch hier wollen wir die Funktion der Views und DB Links überprüfen:
connect demopdb1/demopdb1@localhost:1521/pdb1
select view_name from user_views;
VIEW_NAME
---------------
FINANZ_USERS
SHOP_TABLES
SHOPINFO_VIEWS
LAGER_VIEWS
select count(*) from FINANZ_USERS;
COUNT(*)
----------
4
select count(*) from SHOP_TABLES;
COUNT(*)
----------
1800
select count(*) from SHOPINFO_VIEWS;
COUNT(*)
----------
7073
select count(*) from LAGER_VIEWS;
COUNT(*)
----------
4
Alle Views liefern korrekte Ergebnisse. Die Abfragen auf die Views werden wir gleich wieder benötigen.
Ändern von GLOBAL_NAMES für die Datenbank DB19 (non-CDB)
Was passiert, wenn der DBA der Datenbank DB19 der Empfehlung eines Security Scanner Toolas folgt und global_names
auf TRUE setzt ? Da der Parameter nicht dynamisch ist, muss die Instance neu gestartet werden:
sqlplus / as sysdba
alter system set global_names = TRUE scope=spfile;
shutdown immediate;
startup;
Keine Fehlermeldung, alles schaut gut aus. Führen wir nochmals die Prüfung des Environments durch:
@check_env
GLOBAL_NAME
------------
DB19.WORLD
CON_ID NAME VALUE
------ ------------- --------------------
0 db_domain world
0 global_names TRUE *** wurde korrekt geändert
0 db_name db19
OWNER DB_LINK USERNAME HOST VALID
------- ---------------- --------- --------- -----
DEMO PDBLAGER.WORLD DEMOPDB1 DEMOPDB1 YES
PUBLIC LAGER.WORLD DEMOPDB1 DEMOPDB1 YES
Alles sieht gut aus! Die Änderung hat funktioniert.
Applikation starten und testen
Die Applikation startet (als Benutzer Demo) und greift auf die Views mit Datenbank-Links zu:
connect demo/demo@localhost:1521/db19.world
select count(*) from PDBLAGER_VIEWS;
ERROR at line 1:
ORA-02085: database link PDBLAGER.WORLD connects to PDB1
select name from LAGER_DB;
ERROR at line 1:
ORA-02085: database link LAGER.WORLD connects to PDB1
ORA-02085: database link LAGER.WORLD connects to PDB1
Jeder Zugriff scheitert mit ORA-02085. Was sagt Oracle zu dem Fehler?
02085, 00000, "database link %s connects to %s"
// *Cause: a database link connected to a database with a different name.
// The connection is rejected.
// *Action: create a database link with the same name as the database it
// connects to, or set global_names=false.
//
OK, nachdem wir global_names
absichtlich so gesetzt haben, bleibt uns also nichts anderes übrig als die Datenbank-Links zu korrigieren, oder?
Noch ein Versuch:
CREATE DATABASE LINK PDB1
connect to demopdb1 identified by demopdb1 using 'DEMOPDB1';
Database link created.
select name from v$database@PDB1;
ERROR at line 1:
ORA-02085: database link PDB1.WORLD connects to PDB1
Den Datenbank-Link jetzt einfach PDB1 zu nennen, hilft anscheinend nicht! In der Fehlermeldung steht jetzt aber PDB1.WORLD! Schauen wir uns nochmals das Environment an:
@check_env
GLOBAL_NAME
------------
DB19.WORLD
CON_ID NAME VALUE
---------- -------------------- -------
0 db_domain world
0 global_names TRUE
0 db_name db19
OWNER DB_LINK USERNAME HOST VALID
------- ---------------- --------- --------- -----
DEMO PDBLAGER.WORLD DEMOPDB1 DEMOPDB1 YES
PUBLIC LAGER.WORLD DEMOPDB1 DEMOPDB1 YES
DEMO PDB1.WORLD DEMOPDB1 DEMOPDB1 YES
Wie oben erwähnt, ist das der alte Defaultwert der db_domain
! Alle Datenbanken, die mit Datenbank-Links verbunden sind, müssen entweder die gleiche db_domain
haben, oder man muss die db_domain
beim Anlegen des DB Links mit angeben.
Einfluss von DB_DOMAIN
Angenommen die db_domain
der Ziel-Datenbank ist SCHULUNG, dann lässt sich der Link folgendermaßen anlegen.
drop database link PDB1;
CREATE DATABASE LINK PDB1.SCHULUNG
connect to demopdb1 identified by demopdb1 using 'DEMOPDB1';
Database link created.
SELECT owner, db_link, username, host, valid FROM dba_db_links;
OWNER DB_LINK USERNAME HOST VALID
------- ---------------- --------- --------- -----
DEMO PDBLAGER.WORLD DEMOPDB1 DEMOPDB1 YES
PUBLIC LAGER.WORLD DEMOPDB1 DEMOPDB1 YES
DEMO PDB1.SCHULUNG DEMOPDB1 DEMOPDB1 YES
Wenn auf der Zieldatenbank der db_domain
Parameter nicht gesetzt ist, funktioniert das nicht. Dann steht man vor der Entscheidung, ob man bei allen Datenbanken eine db_domain
einführen oder diese auf allen Datenbanken löschen sollte. Dabei gibt es natürlich noch weitere Faktoren zu berücksichtigen:
- Die
db_domain
ist Teil desSERVICE_NAMES
bei Connect Strings (siehe TNSNAMES.ORA beim Setup). - Die
db_domain
spielt auch bei Data Guard Konfigurationen eine Rolle. - Da die Datenbank-Links nach dem Umsetzen des
global_names
Parameters mit der Kombinationdb_name.db_domain
der Ziel Datenbank übereinstimmen müssen, wird es problematisch, wenn man den gleichendb_name
in verschiedenen Umgebungen (Produktion, Test, Dev, Schulung, QA,…) genutzt hat. Diedb_name
s und noch schlimmer: auch die Namen der PDBs müssen jetzt im Unternehmen eindeutig sein.
Hand aufs Herz: Wer hat mit der Einführung von Multitenant Datenbanken darauf geachtet, dass die PDBs über alle CDBs hinweg eindeutige Namen haben?
Nach dem Abwägen aller dieser Punkte erscheint es am sinnvollsten, für die verschiedenen Umgebungen (PROD, DEV, TEST,…) entsprechende db_domains
zu nutzen. Für unser Beispiel nehmen wir an, dass es sich bei beiden Datenbanken um Produktionsdatenbanken handelt und die db_domain
somit auf PROD geändert werden soll. Da auch die db_domain
nicht dynamisch geändert werden kann, gehen wir wie folgt vor:
alter system set db_domain=prod scope=spfile;
shutdown immediate;
startup;
Schauen wir uns wieder das Environment an:
@check_env
GLOBAL_NAME
------------
DB19.WORLD
CON_ID NAME VALUE
---------- -------------------- ------
0 db_domain PROD
0 global_names TRUE
0 db_name db19
OWNER DB_LINK USERNAME HOST VALID
------- ---------------- --------- --------- -----
DEMO PDBLAGER.WORLD DEMOPDB1 DEMOPDB1 YES
PUBLIC LAGER.WORLD DEMOPDB1 DEMOPDB1 YES
DEMO PDB1.SCHULUNG DEMOPDB1 DEMOPDB1 YES
Die db_domain
ist jetzt PROD. Perfekt! Der GLOBAL_NAME
der Datenbank steht aber noch auf DB19.WORLD! Das muss noch korrigiert werden:
alter database rename global_name to DB19.PROD;
GLOBAL_NAME
------------
DB19.PROD
Damit war die Umstellung doch ganz einfach und schmerzlos, oder?
ORA-12154: TNS:could not resolve the connect identifier specified
Wir starten die Applikation, die sich an der Datenbank anmelden will
sqlplus demo/demo@SHOP
SQL*Plus: Release 19.0.0.0.0 - Production on Wed Aug 30 21:32:25 2023
Version 19.19.0.0.0
Copyright (c) 1982, 2023, Oracle. All rights reserved.
ERROR:
ORA-12154: TNS:could not resolve the connect identifier specified
Ooops. Das Ändern der db_domain
sorgt dafür, dass die Applikation sich nicht mehr anmelden kann! Der Grund liegt darin, dass eben die db_domain
Teil des SERVICE_NAMENs ist und dieser jetzt DB19.PROD lautet. Der Listener zeigt das ganz deutlich:
lsnrctl services
LSNRCTL for Linux: Version 19.0.0.0.0 - Production on 30-AUG-2023 21:33:40
Copyright (c) 1991, 2023, Oracle. All rights reserved.
Connecting to (ADDRESS=(PROTOCOL=tcp)(HOST=)(PORT=1521))
Services Summary...
Service "db19.PROD" has 1 instance(s).
Instance "db19", status READY, has 1 handler(s) for this service...
Handler(s):
"DEDICATED" established:0 refused:0 state:ready
LOCAL SERVER
Service "db19XDB.PROD" has 1 instance(s).
Instance "db19", status READY, has 1 handler(s) for this service...
Handler(s):
"D000" established:0 refused:0 current:0 max:1022 state:ready
DISPATCHER <machine: ol8, pid: 80944>
(ADDRESS=(PROTOCOL=tcp)(HOST=ol8.localdomain)(PORT=9873))
Somit müssen wir die Einträge aller TNSNAMES.ORA Dateien und aller anderen Stellen, in denen Connect Informationen vorkommen, korrigieren.
In der TNSNAMES.ora machen wir aus:
SHOP =
(DESCRIPTION=
(ADDRESS=(PROTOCOL=tcp)(HOST=localhost)(PORT=1521))
(CONNECT_DATA=(SERVICE_NAME=db19.world)))
FINANZ =
(DESCRIPTION=
(ADDRESS=(PROTOCOL=tcp)(HOST=localhost)(PORT=1521))
(CONNECT_DATA=(SERVICE_NAME=db19.world)))
DEMOPDB1 =
(DESCRIPTION=
(ADDRESS=(PROTOCOL=tcp)(HOST=localhost)(PORT=1521))
(CONNECT_DATA=(SERVICE_NAME=pdb1)))
jetzt:
SHOP =
(DESCRIPTION=
(ADDRESS=(PROTOCOL=tcp)(HOST=localhost)(PORT=1521))
(CONNECT_DATA=(SERVICE_NAME=db19.prod)))
FINANZ =
(DESCRIPTION=
(ADDRESS=(PROTOCOL=tcp)(HOST=localhost)(PORT=1521))
(CONNECT_DATA=(SERVICE_NAME=db19.prod)))
DEMOPDB1 =
(DESCRIPTION=
(ADDRESS=(PROTOCOL=tcp)(HOST=localhost)(PORT=1521))
(CONNECT_DATA=(SERVICE_NAME=pdb1)))
Der nächste Versuch, sich mit der Applikation anzumelden ist jetzt erfolgreich:
sqlplus demo/demo@SHOP
Funktionieren die Datenbank-Links jetzt vielleicht schon?
ORA-02019: connection description for remote database not found
Wir versuchen wieder auf die View(s) auf der PDB1 zuzugreifen.
select count(*) from PDBLAGER_VIEWS;
*
ERROR at line 1:
ORA-02019: connection description for remote database not found
Jetzt bekommen wir einen neuen Fehler! Wie schaut der View aus, den wir da nutzen und welcher Datenbank-Link ist hier gemeint?
create or replace view PDBLAGER_VIEWS as select * from user_views@PDBLAGER;
OWNER DB_LINK USERNAME HOST VALID
------- ---------------- --------- --------- -----
DEMO PDBLAGER.WORLD DEMOPDB1 DEMOPDB1 YES
PUBLIC LAGER.WORLD DEMOPDB1 DEMOPDB1 YES
DEMO PDB1.SCHULUNG DEMOPDB1 DEMOPDB1 YES
Was kann hier wieder das Problem sein? Erraten, die geänderte db_domain
bereitet uns weiteres Ungemach. Der DB_LINK heisst noch „PDBLAGER.world“, da im SELECT aber keine Domain angegeben wurde, wird automatisch auf „PDBLAGER.prod“ ergänzt – nur: den DB Link gibt es nicht! Also legen wir diesen doch einfach an und probieren es aus:
create database link PDBLAGER.prod
connect to demopdb1 identified by demopdb1 using 'DEMOPDB1';
select count(*) from PDBLAGER_VIEWS;
*
ERROR at line 1:
ORA-02085: database link PDBLAGER.PROD connects to PDB1
Anscheinend wird der Database Link jetzt zwar gefunden, wir scheitern aber wieder daran, dass der Datenbank-Link PDBLAGER auf PDB1 zeigt. Wir müssen natürlich einen DB Link PDB1.PROD erzeugen, weil wir ja alle Datenbanken auf die db_domain=PROD
umstellen wollen:
create database link PDB1.prod
connect to demopdb1 identified by demopdb1 using 'DEMOPDB1';
select name from v$database@pdb1;
*
ERROR at line 1:
ORA-02085: database link PDB1.PROD connects to PDB1
Solange wir die CDB Datenbank noch nicht umgestellt haben, werden wir leider nicht mehr via Database Link darauf zugreifen können. Also stellen wir auch die CDB Datenbank um.
Umstellen der CDB Datenbank mit der Pluggable Database PDB1
Was müssen wir alles beachten:
global_names
auf TRUEdb_domain
auf PROD- Datenbank GLOBAL_NAME korrigieren
- Anpassungen in der/den TNSNAMES.ORA(s) bzw. Applikations Connectstrings
sqlplus / as sysdba
select * from global_name;
GLOBAL_NAME
------------
DB19EE
alter system set global_names=TRUE scope=spfile;
alter system set db_domain=PROD scope=spfile;
shutdown immediate;
startup;
alter database rename global_name to DB19EE.PROD;
select * from global_name;
GLOBAL_NAME
------------
DB19EE.PROD
Für die CDB passt der Globale Name jetzt.
Zur Sicherheit das Environment für die PDB1 verifizieren:
connect demopdb1/demopdb1@localhost/pdb1.prod;
GLOBAL_NAME
------------
PDB1
CON_ID NAME VALUE
---------- -------------------- ------
0 db_domain PROD
0 global_names TRUE
0 db_name db19ee
OWNER DB_LINK USERNAME HOST VALID
--------- --------- --------- --------- -----
PUBLIC FINANZ DEMO FINANZ YES
PUBLIC SHOP DEMO SHOP YES
PUBLIC LAGER DEMOPDB1 DEMOPDB1 YES
DEMOPDB1 SHOPINFO DEMO SHOP YES
GLOBAL_NAME in PDB1 anpassen
Auch in der PDB müssen wir den GLOBAL_NAME
noch korrigieren, das ist leider etwas aufwändiger:
connect / as sysdba
select * from global_name;
GLOBAL_NAME
------------
DB19EE.PROD
alter pluggable database PDB1 close;
alter pluggable database PDB1 open restricted;
alter pluggable database PDB1 close;
alter pluggable database PDB1 open;
alter session set container = PDB1;
select * from global_name;
GLOBAL_NAME
------------
PDB1.PROD
Durch das Öffnen der PDB im restricted mode, wird die Änderung des GLOBAL_NAMES
auf die gleiche Domain wie der CDB durchgeführt. Sollte dies wider Erwarten erwarten nicht automatisch funktionieren, muss der entsprechende
„ALTER DATABASE RENAME GLOBAL_NAME ...;
“
in der mit restricted mode geöffneten PDB durchgeführt werden.
Zuletzt das TNSNAMES.ORA File wie folgt korrigieren.
SHOP =
(DESCRIPTION=
(ADDRESS=(PROTOCOL=tcp)(HOST=localhost)(PORT=1521))
(CONNECT_DATA=(SERVICE_NAME=db19.prod)))
FINANZ =
(DESCRIPTION=
(ADDRESS=(PROTOCOL=tcp)(HOST=localhost)(PORT=1521))
(CONNECT_DATA=(SERVICE_NAME=db19.prod)))
DEMOPDB1 =
(DESCRIPTION=
(ADDRESS=(PROTOCOL=tcp)(HOST=localhost)(PORT=1521))
(CONNECT_DATA=(SERVICE_NAME=pdb1.prod)))
Erfolgreicher Zugriff von der non-CDB Datenbank via DB Link auf PDB1
Kann man jetzt von der DB19 endlich auf die PDB1 mittels Datenbank-Link zugreifen?
connect demo/demo@localhost/db19.prod
select name from v$database@pdb1;
NAME
---------
DB19EE
Heureka, wir haben es geschafft. Der Zugriff von DB19 auf PDB1 via DB Link funktioniert! Klappt auch der Zugriff über die Views?
select view_name from user_views;
VIEW_NAME
---------------
PDBLAGER_VIEWS
LAGER_DB
select count(*) from PDBLAGER_VIEWS;
*
ERROR at line 1:
ORA-02085: database link PDBLAGER.PROD connects to PDB1.PROD
Natürlich nicht, weil in der View ja immer noch der „falsche“ Datenbank-Link enthalten ist.
Anpassungen von DB Links und Applikations Code auf der nonCDB Datenbank
Wir müssen somit mit den Datenbank-Links aufräumen und dann den Applikationscode ändern.
SELECT owner, db_link, username, host, valid FROM dba_db_links;
OWNER DB_LINK USERNAME HOST VALID
------- ---------------- --------- --------- -----
PUBLIC LAGER.WORLD DEMOPDB1 DEMOPDB1 YES
DEMO PDBLAGER.WORLD DEMOPDB1 DEMOPDB1 YES
DEMO PDB1.PROD DEMOPDB1 DEMOPDB1 YES
DEMO PDB1.SCHULUNG DEMOPDB1 DEMOPDB1 YES
DEMO PDBLAGER.PROD DEMOPDB1 DEMOPDB1 YES
drop public database link LAGER.WORLD;
drop database link PDBLAGER.WORLD;
drop database link PDB1.SCHULUNG;
drop database link PDBLAGER.PROD;
-- jetzt noch die View Codes reparieren:
create or replace view PDBLAGER_VIEWS as select * from user_views@PDB1;
create or replace view LAGER_DB as select * from V$DATABASE@PDB1;
select count(*) from PDBLAGER_VIEWS;
COUNT(*)
----------
4
select name from LAGER_DB;
NAME
--------------------
DB19EE
Anpassungen von DB Links und Applikations Code in der PDB1
Jetzt muss man das noch in der PDB1 Datenbank nachziehen.
connect demopdb1/demopdb1@localhost/pdb1.prod
SELECT owner, db_link, username, host, valid FROM dba_db_links;
OWNER DB_LINK USERNAME HOST VALID
--------- --------- --------- --------- -----
PUBLIC FINANZ DEMO FINANZ YES
PUBLIC SHOP DEMO SHOP YES
PUBLIC LAGER DEMOPDB1 DEMOPDB1 YES
DEMOPDB1 SHOPINFO DEMO SHOP YES
-- alle vorhandenen DB Links einmal verwerfen
drop public database link FINANZ;
*
ERROR at line 1:
ORA-02024: database link not found
ORA-02024: database link not found
Jetzt haben wir wieder ein neues Problem. Sobald db_domain
einen Wert hat, kann man Datenbank-Links OHNE Domain nicht mehr droppen! Das geht nur, wenn die db_domain
wieder zurückgesetzt und alles neu gestartet wird. Dann könnte man die DB Links droppen.
Merke:
Die DB Links ohne db_domain muss man droppen bevor man die db_domain konfiguriert!
Also legen wir die jetzt benötigen DB Links zusätzlich an:
-- dieser ersetzt die DB Links FINANZ und SHOP:
create public database link db19
connect to demo identified by demo using 'SHOP';
select name from v$database@db19;
NAME
--------------------
DB19
-- jetzt brauchen wir noch den Ersatz für den DB Link Lager:
create public database link pdb1
connect to demopdb1 identified by demopdb1 using 'DEMOPDB1';
ORA-02082: a loopback database link must have a connection qualifier
Ooops, das nächste Problem!
ORA-02082: a loopback database link must have a connection qualifier
Sobald man bei global_name=true
einen Datenbank-Link auf die eigene Datenbank benötigt, geht das plötzlich nicht mehr so einfach. Was sagt die Fehlermeldung genau aus:
02082, 00000, "a loopback database link must have a connection qualifier"
// *Cause: An attempt was made to create a database link with the same name
// as the current database.
// *Action: a loopback database link needs a trailing qualifier, for example
// MYDB.EXAMPLE.COM@INST1 - the '@INST1' is the qualifier
//
Also fügen wir einen Qualifier hinzu
create public database link pdb1@loop1
connect to demopdb1 identified by demopdb1 using 'DEMOPDB1';
Database link created.
select name from v$database@pdb1@loop1;
NAME
--------------------
DB19EE
Funktioniert offensichtlich, aber es ist verwirrend, dass man hier zweimal „@“ hat! Ein weiterer Datenbank-Link fehlt uns noch, der Ersatz für SHOPINFO:
create database link db19
connect to demo identified by demo using 'SHOP';
select name from v$database@db19;
NAME
--------------------
DB19
Scheint zu passen. Prüfen wir jetzt die angelegten Datenbank-Links, bevor wir den View Code anpassen:
SELECT owner, db_link, username, host, valid
FROM dba_db_links;
OWNER DB_LINK USERNAME HOST VALID
--------- ---------------- --------- --------- -----
PUBLIC FINANZ DEMO FINANZ YES
PUBLIC SHOP DEMO SHOP YES
PUBLIC DB19.PROD DEMO SHOP YES
PUBLIC LAGER DEMOPDB1 DEMOPDB1 YES
DEMOPDB1 SHOPINFO DEMO SHOP YES
DEMOPDB1 DB19.PROD DEMO SHOP YES
PUBLIC PDB1.PROD@LOOP1 DEMOPDB1 DEMOPDB1 YES
Etwas unübersichtlich, weil die „alten“ DB Links noch vorhanden sind. Die können wir aber ausblenden.
SELECT owner, db_link, username, host, valid
FROM dba_db_links where DB_LINK like '%.%';
OWNER DB_LINK USERNAME HOST VALID
--------- ---------------- --------- --------- -----
PUBLIC DB19.PROD DEMO SHOP YES
DEMOPDB1 DB19.PROD DEMO SHOP YES
PUBLIC PDB1.PROD@LOOP1 DEMOPDB1 DEMOPDB1 YES
Passt alles, oder haben wir ein neues Problem?
Datenbank-Links wieder eindeutig machen!
Wir haben zwei Datenbank-Links mit dem Namen DB19.PROD*. In diesem Beispiel haben wir das Glück, dass das Ziel der gleiche Benutzer ist. Was wäre, wenn die Links auf verschiedene Datenbankuser in DB19 zeigen sollen? Beispielsweise auf die Schemata SHOP_OWNER
und FINANZ_OWNER
? Außerdem ist auf den ersten Blick nicht klar, welcher der beiden DB Links genutzt wird. Bei Datenbank-Links verwendet die Datenbank immer den privaten Link, sofern es Namensgleichheiten gibt. Ist man z.B. mit dem Benutzer DEMOPDB1 angemeldet, würde der private Link genutzt werden. Für alle anderen Datenbank Benutzer wird der PUBLIC Datenbank-Link verwendet. Was wäre, wenn die Links jetzt auf verschiedene Schemata verbinden würden? Das Ergebnis wäre davon abhängig, mit welchem Datenbank Benutzer man angemeldet ist! Das bedeutet, dass man dies bei der Applikationsentwicklung entsprechend berücksichtigen muss! Ein möglicher Workaround ist die Nutzung von Qualifiern – hier ein Beispiel:
create public database link DB19@SHOP
connect to shop_owner identified by shop_owner_pwd using 'SHOP';
create public database link DB19@FINANZ
connect to finanz_owner identified by finanz_owner_pwd using 'FINANZ';
-- diese ersetzen den public link DB19, deshalb löschen wir diesen gleich:
drop public database link DB19;
Um dies zu testen, müssen wir die beiden Benutzer noch anlegen:
connect demo/demo@SHOP;
create user shop_owner identified by shop_owner_pwd;
grant dba to shop_owner;
create user finanz_owner identified by finanz_owner_pwd;
grant dba to finanz_owner;
DB Links testen:
connect demopdb1/demopdb1@DEMOPDB1;
select username from v$session@DB19@shop
where sid=(select distinct sid from v$mystat@DB19@shop);
USERNAME
----------------
SHOP_OWNER
select username from v$session@DB19@finanz
where sid=(select distinct sid from v$mystat@DB19@finanz);
USERNAME
----------------
FINANZ_OWNER
OWNER DB_LINK USERNAME HOST VALID
--------- ---------------- ------------- --------- -----
DEMOPDB1 DB19.PROD DEMO SHOP YES
PUBLIC PDB1.PROD@LOOP1 DEMOPDB1 DEMOPDB1 YES
PUBLIC DB19.PROD@SHOP SHOP_OWNER SHOP YES
PUBLIC DB19.PROD@FINANZ FINANZ_OWNER FINANZ YES
Applikations Code in PDB1 anpassen
Jetzt müssen wir nur noch die View Codes korrigieren:
create or replace view FINANZ_USERS as select * from all_users@DB19@FINANZ;
create or replace view SHOP_TABLES as select * from all_tables@DB19@SHOP;
create or replace view SHOPINFO_VIEWS as select * from all_views@DB19@SHOP;
create or replace view LAGER_VIEWS as select * from user_views@PDB1@LOOP1;
select count(*) from FINANZ_USERS;
COUNT(*)
----------
27
select count(*) from SHOP_TABLES;
COUNT(*)
----------
1800
select count(*) from SHOPINFO_VIEWS;
COUNT(*)
----------
7073
select count(*) from LAGER_VIEWS;
COUNT(*)
----------
4
Nachdem die Ergebnisse mit den ursprünglichen übereinstimmen, scheinen wir die Umstellung geschafft zu haben. Sind damit alle etwaigen Probleme schon abgedeckt?
Leider nein, folgende Themen wurden nicht behandelt:
- Die Connect Strings sind aktuell verwirrend, weil am Datenbank-Link (HOST) nicht erkennbar ist, dass es sich um die gleiche Datenbank handelt. Es ist empfehlenswert, im Zuge der Anpassungen auch die Connect-Strings gleichlautend mit dem
GLOBAL_NAME
zu benennen. - Manche Softwarelösungen nutzen den
GLOBAL_NAME
, um Linzenkeys an eine Datenbank zu knüpfen. Durch die Änderung desGLOBAL_NAME
sind diese Lizenzkeys nicht mehr gültig. - Applikationshersteller haben oft hardcoded Datenbank-Link Namen in ihren verschlüsselten Codes (wrapped PL/SQL oder Java,…). Eine Änderung bedeutet, dass der Softwareentwickler dies in seiner Applikation ändern muss. Dazu sind nicht alle Softwarehersteller bereit.
Mit Sicherheit finden sich noch weitere Fallen, die hier nicht berücksichtigt wurden.
Zusammenfassung / Management Summary
Wenn man, wie viele Security Scanner empfehlen, einfach den Instance-Parameter global_names
auf TRUE setzt und Datenbank-Links verwendet, läuft man Gefahr, dass die über den Link angeschlossenen Datenbanken ebenfalls betroffen sind und Applikationen nicht mehr funktionieren. Eine solche Änderung bedeutet massive Aufwände für DBAs und Entwickler und muss als eigenes Projekt geplant und umgesetzt werden, um den Betrieb der Applikationen sicherstellen zu können.