Skip to content

Commit

Permalink
HCK-6145: PostgreSQL - Fix ssh tunneling (#104)
Browse files Browse the repository at this point in the history
  • Loading branch information
AlikRakhmonov authored Jun 7, 2024
1 parent 6d12726 commit 5ae9d93
Show file tree
Hide file tree
Showing 5 changed files with 50 additions and 110 deletions.
41 changes: 3 additions & 38 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

11 changes: 2 additions & 9 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -71,13 +71,6 @@
"simple-git-hooks": "2.11.1"
},
"dependencies": {
"pg": "^8.7.1",
"tunnel-ssh": "^4.1.6"
},
"overrides": {
"tunnel-ssh": {
"cpu-features": "npm:-@0.0.1",
"ssh2": "npm:-@0.0.1"
}
"pg": "^8.7.1"
}
}
}
23 changes: 16 additions & 7 deletions reverse_engineering/api.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,15 @@ const postgresService = require('./helpers/postgresService');

module.exports = {
async disconnect(connectionInfo, logger, callback, app) {
await postgresService.disconnect();
const sshService = app.require('@hackolade/ssh-service');
await postgresService.disconnect(sshService);

callback();
},

async testConnection(connectionInfo, logger, callback, app) {
const sshService = app.require('@hackolade/ssh-service');

try {
logInfo('Test connection', connectionInfo, logger);

Expand All @@ -21,19 +24,21 @@ module.exports = {
});

postgresService.setDependencies(app);
await postgresService.connect(connectionInfo, postgresLogger);
await postgresService.connect(connectionInfo, sshService, postgresLogger);
await postgresService.pingDb();
await postgresService.logVersion();
callback();
} catch (error) {
logger.log('error', prepareError(error), 'Test connection instance log');
callback(prepareError(error));
} finally {
await postgresService.disconnect();
await postgresService.disconnect(sshService);
}
},

async getDatabases(connectionInfo, logger, cb, app) {
const sshService = app.require('@hackolade/ssh-service');

try {
logInfo('Get databases', connectionInfo, logger);

Expand All @@ -44,7 +49,7 @@ module.exports = {
});

postgresService.setDependencies(app);
await postgresService.connect(connectionInfo, postgresLogger);
await postgresService.connect(connectionInfo, sshService, postgresLogger);
await postgresService.logVersion();

const dbs = await postgresService.getDatabaseNames();
Expand All @@ -61,6 +66,8 @@ module.exports = {
},

async getDbCollectionsNames(connectionInfo, logger, callback, app) {
const sshService = app.require('@hackolade/ssh-service');

try {
logInfo('Get DB table names', connectionInfo, logger);

Expand All @@ -71,7 +78,7 @@ module.exports = {
});

postgresService.setDependencies(app);
await postgresService.connect(connectionInfo, postgresLogger);
await postgresService.connect(connectionInfo, sshService, postgresLogger);
await postgresService.logVersion();
const schemasNames = await postgresService.getAllSchemasNames();

Expand Down Expand Up @@ -102,11 +109,13 @@ module.exports = {
} catch (error) {
logger.log('error', prepareError(error), 'Get DB collections names');
callback(prepareError(error));
await postgresService.disconnect();
await postgresService.disconnect(sshService);
}
},

async getDbCollectionsData(data, logger, callback, app) {
const sshService = app.require('@hackolade/ssh-service');

try {
logger.log('info', data, 'Retrieve tables data:', data.hiddenKeys);

Expand Down Expand Up @@ -214,7 +223,7 @@ module.exports = {
logger.log('error', prepareError(error), 'Retrieve tables data');
callback(prepareError(error));
} finally {
await postgresService.disconnect();
await postgresService.disconnect(sshService);
}
},
};
Expand Down
67 changes: 20 additions & 47 deletions reverse_engineering/helpers/connectionHelper.js
Original file line number Diff line number Diff line change
@@ -1,50 +1,9 @@
const fs = require('fs');
const ssh = require('tunnel-ssh');
const pg = require('pg');

const SSL_NOT_SUPPORTED_MESSAGE = 'The server does not support SSL connections';
const POSTGRES_SSL_REQUIRED_ERROR_CODE = '28000';

const getSshConfig = info => {
const config = {
username: info.ssh_user,
host: info.ssh_host,
port: info.ssh_port,
dstHost: info.host,
dstPort: info.port,
localHost: '127.0.0.1',
localPort: info.port,
keepAlive: true,
};

if (info.ssh_method === 'privateKey') {
return Object.assign({}, config, {
privateKey: fs.readFileSync(info.ssh_key_file),
passphrase: info.ssh_key_passphrase,
});
} else {
return Object.assign({}, config, {
password: info.ssh_password,
});
}
};

const connectViaSsh = info =>
new Promise((resolve, reject) => {
ssh(getSshConfig(info), (err, tunnel) => {
if (err) {
reject(err);
} else {
resolve({
tunnel,
info: Object.assign({}, info, {
host: '127.0.0.1',
}),
});
}
});
});

const getSslOptions = (connectionInfo, logger) => {
const sslType = connectionInfo.sslType;

Expand Down Expand Up @@ -98,13 +57,27 @@ const mapSslType = sslType => {
return oldToNewSslType[sslType] || sslType;
};

const createClient = async (connectionInfo, logger) => {
let sshTunnel = null;
const createClient = async (connectionInfo, sshService, logger) => {
let isSshTunnel = false;

if (connectionInfo.ssh) {
const { info, tunnel } = await connectViaSsh(connectionInfo);
sshTunnel = tunnel;
connectionInfo = info;
const { options } = await sshService.openTunnel({
sshAuthMethod: connectionInfo.ssh_method === 'privateKey' ? 'IDENTITY_FILE' : 'USER_PASSWORD',
sshTunnelHostname: connectionInfo.ssh_host,
sshTunnelPort: connectionInfo.ssh_port,
sshTunnelUsername: connectionInfo.ssh_user,
sshTunnelPassword: connectionInfo.ssh_password,
sshTunnelIdentityFile: connectionInfo.ssh_key_file,
sshTunnelPassphrase: connectionInfo.ssh_key_passphrase,
host: connectionInfo.host,
port: connectionInfo.port,
});

connectionInfo = {
...connectionInfo,
...options,
};
isSshTunnel = true;
}

connectionInfo = Object.assign({}, connectionInfo, { sslType: mapSslType(connectionInfo.sslType) });
Expand All @@ -127,7 +100,7 @@ const createClient = async (connectionInfo, logger) => {

const client = await createConnectionPool(config, logger);

return { client, sshTunnel };
return { client, isSshTunnel };
};

const retryOnSslError = (config, logger, error) => {
Expand Down
18 changes: 9 additions & 9 deletions reverse_engineering/helpers/postgresService.js
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ const queryConstants = require('./queryConstants');
const { reorganizeConstraints } = require('./postgresHelpers/reorganizeConstraints');
const { mapSequenceData } = require('./postgresHelpers/sequenceHelper');

let currentSshTunnel = null;
let useSshTunnel = false;
let _ = null;
let logger = null;
let version = 14;
Expand All @@ -66,23 +66,23 @@ module.exports = {
setDependenciesInTriggerHelper(app);
},

async connect(connectionInfo, specificLogger) {
async connect(connectionInfo, sshService, specificLogger) {
if (db.isClientInitialized()) {
await this.disconnect();
await this.disconnect(sshService);
}

const { client, sshTunnel } = await createClient(connectionInfo, specificLogger);
const { client, isSshTunnel } = await createClient(connectionInfo, sshService, specificLogger);

db.initializeClient(client, specificLogger);
currentSshTunnel = sshTunnel;
useSshTunnel = isSshTunnel;
logger = specificLogger;
version = await this._getServerVersion();
},

async disconnect() {
if (currentSshTunnel) {
currentSshTunnel.close();
currentSshTunnel = null;
async disconnect(sshService) {
if (useSshTunnel) {
useSshTunnel = false;
await sshService.closeConsumer();
}

await db.releaseClient();
Expand Down

0 comments on commit 5ae9d93

Please sign in to comment.