214 lines
9.1 KiB
Markdown
214 lines
9.1 KiB
Markdown
# 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_ATTEMPTS` in 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:
|
|
|
|
```powershell
|
|
$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):
|
|
|
|
```powershell
|
|
(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`:
|
|
|
|
```powershell
|
|
$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:
|
|
|
|
```powershell
|
|
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
|
|
|
|
```powershell
|
|
$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:
|
|
|
|
```sql
|
|
select user || ' @ ' || sys_context('USERENV','DB_NAME') from dual;
|
|
```
|
|
|
|
Pentru extragere in fisier, `spool` scrie **local**, nu pe server:
|
|
|
|
```sql
|
|
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`).
|
|
|
|
```powershell
|
|
# 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 `romfast` de pe clienti are
|
|
> shell restrans (`netstat`/`ss`/`hostname`/`head` nu 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
|
|
|
|
1. **Doar `SELECT`.** Fara `INSERT`/`UPDATE`/`DELETE`/DDL, fara `ALTER SESSION` care schimba
|
|
comportament persistent. Daca ai nevoie de date de lucru, extrage-le si lucreaza local.
|
|
2. **Fara ghicit parole** - vezi avertismentul de la inceput.
|
|
3. **Interogari marginite**: `fetch first N rows only`, si evita scanarile complete pe tabele
|
|
mari in orele de lucru.
|
|
4. **Inchide tunelul** cand ai terminat; nu-l lasa deschis peste noapte.
|
|
5. **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):
|
|
|
|
```sql
|
|
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:
|
|
|
|
```powershell
|
|
$env:E2E_CODURI = 'D:\ROA\ROADEF\COMUN\utile\Teste\cache_anaf\codes_1000_reale.txt'
|
|
$env:E2E_LIMITA = '1000'
|
|
```
|