9.1 KiB
Conexiuni la bazele de productie: tunel SSH (Bitvise) + ODBC/TNS
Cum se ajunge, de pe statia de lucru, la un Oracle care nu e expus direct in internet. Valabil pentru toate proiectele ROA. Scopul documentului: sa nu se mai piarda o sesiune intreaga redescoperind lantul.
Fara credentiale in git. Acest fisier NU contine parole, si nici un fisier din repo nu trebuie sa contina. Parolele stau in profilul Bitvise (criptate cu DPAPI, legate de contul Windows) si in capul celui care lucreaza. Daca ai nevoie de o parola pe care nu o ai, cere-o - nu o cauta prin fisiere si nu o ghici: Oracle poate avea
FAILED_LOGIN_ATTEMPTSin profil, iar cateva incercari gresite blocheaza contul de schema in PRODUCTIE.
Lantul, pe scurt
statie -> [tunel SSH Bitvise] -> 127.0.0.1:1521 -> Oracle de pe serverul de productie
^
aliasul TNS din instantclient\tnsnames.ora arata AICI
^
DSN-ul ODBC cu acelasi nume e ce primeste goConn.Connect()
Trei nume trebuie sa se potriveasca, si e usor de ratat: profilul Bitvise (ce port local deschide), aliasul TNS (ce host:port foloseste) si DSN-ul ODBC (ce alias TNS foloseste).
1. Tunelul SSH
Profilele Bitvise (.tlp) stau in D:\GoogleDrive\ (ex. vending.tlp). Un profil contine
gazda, portul, utilizatorul, parola criptata, cheile de gazda deja acceptate si regulile de
port forwarding - de obicei 127.0.0.1:1521 -> localhost:1521.
Ridicare din linie de comanda, fara interfata grafica:
$exe = 'C:\Program Files (x86)\Bitvise SSH Client\stnlc.exe'
Start-Process $exe -ArgumentList '-profile=D:\GoogleDrive\vending.tlp' -WindowStyle Hidden `
-RedirectStandardOutput "$env:TEMP\stnlc_out.txt" -RedirectStandardError "$env:TEMP\stnlc_err.txt"
stnlc foloseste singur parola stocata in profil - nu o trece pe linia de comanda, ar ajunge
in istoricul shell-ului. Ruleaza sub acelasi cont Windows sub care a fost salvat profilul,altfel
DPAPI nu poate decripta parola.
Verificare ca tunelul chiar e sus (nu te baza pe faptul ca procesul traieste):
(Test-NetConnection 127.0.0.1 -Port 1521 -WarningAction SilentlyContinue).TcpTestSucceeded
In stnlc_out.txt linia care conteaza e Added client-to-server forwarding rule on 127.0.0.1:1521 to localhost:1521. Inchidere: opreste procesul stnlc.
Capcana: un singur port local 1521 pentru mai multe destinatii. Daca ai deja un tunel ridicat catre alt client, al doilea nu mai poate lega portul si vei interoga alta baza fara sa-ti dai seama. Verifica intotdeauna cu ce te-ai conectat (vezi pasul 4).
Capcana inrudita: interfata grafica BvSsh.exe poate tine portul 1521 legat pe 0.0.0.0 cu o
sesiune moarta - sqlplus raspunde atunci ORA-12541: TNS:no listener, desi "tunelul pare sus".
stnlc isi poate lega totusi propria regula pe 127.0.0.1:1521 peste ea si merge. Nu te lua dupa
netstat: proba reala e interogarea din pasul 4.
2. Aliasul TNS
D:\ROA\instantclient_19_18\tnsnames.ora (si instantclient_11_2_0_2 pentru driverul ODBC pe
32 de biti). Aliasurile care merg prin tunel arata spre 127.0.0.1:
VENDING =
(DESCRIPTION =
(ADDRESS_LIST = (ADDRESS = (PROTOCOL = tcp)(HOST = 127.0.0.1)(PORT = 1521)))
(CONNECT_DATA = (SERVICE_NAME = XEPDB1))
)
Pentru sqlplus trebuie TNS_ADMIN setat pe folderul care contine tnsnames.ora:
$env:TNS_ADMIN = 'D:\ROA\instantclient_19_18'
3. DSN-ul ODBC
Aplicatia nu foloseste TNS direct: oConn.Connect(tcHost, tcUser, tcPassword)
(oproceduri_comune.prg) construieste dsn=<tcHost>;Uid=...;Pwd=... - deci tcHost e un nume
de DSN ODBC, nu un host si nu un alias TNS. DSN-urile sunt pe 32 de biti (VFP e pe 32 de biti),
deci in HKLM\SOFTWARE\WOW6432Node\ODBC\ODBC.INI, cu driverul din instantclient_11_2_0_2.
Listare rapida:
Get-ChildItem 'HKLM:\SOFTWARE\WOW6432Node\ODBC\ODBC.INI' |
Select-Object -ExpandProperty PSChildName
De obicei DSN-ul are acelasi nume cu aliasul TNS (VENDING, CENTRAL, ROA_CENTRAL...).
Administrare vizuala: C:\Windows\SysWOW64\odbcad32.exe (nu cel din System32, acela e pe
64 de biti si nu vede DSN-urile VFP).
4. Interogare si verificare
$env:TNS_ADMIN = 'D:\ROA\instantclient_19_18'
& 'D:\ROA\instantclient_19_18\sqlplus.exe' -S -L '<schema>/<parola>@VENDING' '@script.sql'
-L = o singura incercare de login (nu reincearca la parola gresita). Foloseste-l intotdeauna
pe productie.
Prima interogare, mereu, ca sa stii unde ai nimerit:
select user || ' @ ' || sys_context('USERENV','DB_NAME') from dual;
Pentru extragere in fisier, spool scrie local, nu pe server:
set pagesize 0 feedback off heading off linesize 100 trimspool on termout off
spool D:\cale\locala\iesire.txt
select ...;
spool off
Capcana PowerShell: intr-un here-string cu ghilimele duble (@"..."@) PowerShell expandeaza
$. Un regex Oracle care se termina cu $ (ancora de sfarsit) ajunge stricat in fisierul .sql
si interogarea intoarce 0 randuri fara nicio eroare. Foloseste here-string cu apostrof
(@'...'@) si inlocuieste caile printr-un token.
Capcana termout off: ascunde si mesajele de eroare. Daca fisierul spool iese gol, ruleaza
din nou fara termout off inainte sa banuiesti datele.
Acces SSH direct cu cheie publica (metoda rapida, recomandata)
Pentru clientii la care cheia publica e instalata pe server, nu mai e nevoie de profil Bitvise /
parola: se deschide tunelul direct cu ssh din ~\.ssh (cheile id_ed25519 / id_rsa).
# ridica tunelul: portul local 1521 -> Oracle (localhost:1521) de pe server
Start-Process ssh -ArgumentList @(
'-N','-o','ExitOnForwardFailure=yes','-o','StrictHostKeyChecking=accept-new',
'-o','BatchMode=yes',
'-L','1521:127.0.0.1:1521',
'-p','<PORT_SSH>','<user>@<host>') -WindowStyle Hidden
# verificare tunel
(Test-NetConnection 127.0.0.1 -Port 1521 -WarningAction SilentlyContinue).TcpTestSucceeded
BatchMode=yes = nu asteapta parola (foloseste cheia); daca pica, inseamna ca cheia publica nu e
instalata pe serverul respectiv. Portul SSH nu e neaparat 22: multi clienti ROA folosesc 22122.
Inchidere: opri procesul ssh (sau Get-Process ssh | Stop-Process).
Nu te baza pe shell-ul remote ca sa "vezi" ce e pe server: contul
romfastde pe clienti are shell restrans (netstat/ss/hostname/headnu exista). Proba reala e interogarea sqlplus dupa ridicarea tunelului, nu comenzile de pe server.
Schemele cunoscute (fara parole)
| Tunel / profil | Alias TNS + DSN | Schema Oracle |
|---|---|---|
vending.tlp |
VENDING |
VENDING (parola standard ROA) |
| conpress | ROA_CONPRESS |
xenoti |
cheie publica, SSH 82.76.217.177:22122, user romfast |
ROA_ROMCONSTRUCT |
ROMCONSTRUCT (parola standard ROA), SID ROA, Oracle 10g |
Profilele Bitvise sunt in D:\roa\BITVISE\ pe statia curenta (vending.tlp, conpress.tlp,
clever.tlp, automotive.tlp, avis.tlp, eduard.tlp, ems.tlp, romfast.tlp, sigma.tlp,
vadeco.tlp) - nu in D:\GoogleDrive\. Clientul Oracle prezent e instantclient_11_2_0_2
(TNS_ADMIN se seteaza pe el). VENDING este Oracle 18c XE, serviciu XEPDB1.
Masuratoare read-only rulata pe VENDING la 03.09.2026: ROACONT\docs\cercetare\piata_ai_2026_09\
(BACKTEST_VENDING.md plus scripturile din sql\bt_*.sql).
Restul aliasurilor din tnsnames.ora (ROA_CENTRAL, CENTRAL105, ROA_TEST, ...) sunt retea
interna si nu au nevoie de tunel.
Reguli pe productie
- Doar
SELECT. FaraINSERT/UPDATE/DELETE/DDL, faraALTER SESSIONcare schimba comportament persistent. Daca ai nevoie de date de lucru, extrage-le si lucreaza local. - Fara ghicit parole - vezi avertismentul de la inceput.
- Interogari marginite:
fetch first N rows only, si evita scanarile complete pe tabele mari in orele de lucru. - Inchide tunelul cand ai terminat; nu-l lasa deschis peste noapte.
- Datele extrase: codurile fiscale sunt publice si pot sta in repo (vezi
utile\Teste\cache_anaf\codes_*_reale.txt). Denumirile de parteneri, adresele, soldurile nu - nu le comite.
De unde vin loturile de coduri fiscale pentru teste
COMUN\utile\Teste\cache_anaf\codes_1000_reale.txt a fost extras din productia vending, tabela
nom_parteneri (23932 randuri, 6382 coduri romanesti distincte):
select cif from (
select distinct regexp_replace(upper(trim(cod_fiscal)), '[^0-9]', '') as cif
from nom_parteneri
where cod_fiscal is not null
and regexp_like(upper(trim(cod_fiscal)), '^(RO)?[[:space:]]*[0-9]{2,10}$')
) where length(cif) between 2 and 10
order by ora_hash(cif)
fetch first 1000 rows only;
order by ora_hash(cif) da o dispersie deterministica (aceeasi de fiecare data), spre
deosebire de dbms_random; ordonarea crescuta dupa cod ar fi inclinat lotul spre firme vechi,
deci spre Bucuresti. Suprapunere cu lotul mai vechi de 200: 8 coduri.
Folosire in proba end-to-end ANAF:
$env:E2E_CODURI = 'D:\ROA\ROADEF\COMUN\utile\Teste\cache_anaf\codes_1000_reale.txt'
$env:E2E_LIMITA = '1000'