mirror of
https://github.com/executeautomation/mcp-database-server.git
synced 2025-12-09 21:12:57 +08:00
feat: Add AWS IAM authentication support for MySQL
- Add @aws-sdk/rds-signer dependency for RDS auth token generation - Extend CLI arguments with --aws-iam-auth and --aws-region options - Implement automatic AWS RDS auth token generation in MySQL adapter - Auto-enable SSL for AWS IAM authentication (required by RDS) - Add comprehensive error handling for AWS credential issues - Update documentation with AWS IAM authentication examples - Maintain backward compatibility with existing authentication methods Resolves the need for secure AWS RDS connections without hardcoded passwords.
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
import { DbAdapter } from "./adapter.js";
|
||||
import mysql from "mysql2/promise";
|
||||
import { Signer } from "@aws-sdk/rds-signer";
|
||||
|
||||
/**
|
||||
* MySQL database adapter implementation
|
||||
@@ -9,6 +10,8 @@ export class MysqlAdapter implements DbAdapter {
|
||||
private config: mysql.ConnectionOptions;
|
||||
private host: string;
|
||||
private database: string;
|
||||
private awsIamAuth: boolean;
|
||||
private awsRegion?: string;
|
||||
|
||||
constructor(connectionInfo: {
|
||||
host: string;
|
||||
@@ -18,9 +21,13 @@ export class MysqlAdapter implements DbAdapter {
|
||||
port?: number;
|
||||
ssl?: boolean | object;
|
||||
connectionTimeout?: number;
|
||||
awsIamAuth?: boolean;
|
||||
awsRegion?: string;
|
||||
}) {
|
||||
this.host = connectionInfo.host;
|
||||
this.database = connectionInfo.database;
|
||||
this.awsIamAuth = connectionInfo.awsIamAuth || false;
|
||||
this.awsRegion = connectionInfo.awsRegion;
|
||||
this.config = {
|
||||
host: connectionInfo.host,
|
||||
database: connectionInfo.database,
|
||||
@@ -33,7 +40,14 @@ export class MysqlAdapter implements DbAdapter {
|
||||
if (typeof connectionInfo.ssl === 'object' || typeof connectionInfo.ssl === 'string') {
|
||||
this.config.ssl = connectionInfo.ssl;
|
||||
} else if (connectionInfo.ssl === true) {
|
||||
this.config.ssl = {};
|
||||
// For AWS IAM authentication, configure SSL appropriately for RDS
|
||||
if (this.awsIamAuth) {
|
||||
this.config.ssl = {
|
||||
rejectUnauthorized: false // AWS RDS handles certificate validation
|
||||
};
|
||||
} else {
|
||||
this.config.ssl = {};
|
||||
}
|
||||
}
|
||||
// Validate port
|
||||
if (connectionInfo.port && typeof connectionInfo.port !== 'number') {
|
||||
@@ -47,17 +61,74 @@ export class MysqlAdapter implements DbAdapter {
|
||||
console.error(`[DEBUG] MySQL connection will use port: ${this.config.port}`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate AWS RDS authentication token
|
||||
*/
|
||||
private async generateAwsAuthToken(): Promise<string> {
|
||||
if (!this.awsRegion) {
|
||||
throw new Error("AWS region is required for IAM authentication");
|
||||
}
|
||||
|
||||
if (!this.config.user) {
|
||||
throw new Error("AWS username is required for IAM authentication");
|
||||
}
|
||||
|
||||
try {
|
||||
console.error(`[INFO] Generating AWS auth token for region: ${this.awsRegion}, host: ${this.host}, user: ${this.config.user}`);
|
||||
|
||||
const signer = new Signer({
|
||||
region: this.awsRegion,
|
||||
hostname: this.host,
|
||||
port: this.config.port || 3306,
|
||||
username: this.config.user,
|
||||
});
|
||||
|
||||
const token = await signer.getAuthToken();
|
||||
console.error(`[INFO] AWS auth token generated successfully`);
|
||||
return token;
|
||||
} catch (err) {
|
||||
console.error(`[ERROR] Failed to generate AWS auth token: ${(err as Error).message}`);
|
||||
throw new Error(`AWS IAM authentication failed: ${(err as Error).message}. Please check your AWS credentials and IAM permissions.`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize MySQL connection
|
||||
*/
|
||||
async init(): Promise<void> {
|
||||
try {
|
||||
console.error(`[INFO] Connecting to MySQL: ${this.host}, Database: ${this.database}`);
|
||||
this.connection = await mysql.createConnection(this.config);
|
||||
|
||||
// Handle AWS IAM authentication
|
||||
if (this.awsIamAuth) {
|
||||
console.error(`[INFO] Using AWS IAM authentication for user: ${this.config.user}`);
|
||||
|
||||
try {
|
||||
const authToken = await this.generateAwsAuthToken();
|
||||
|
||||
// Create a new config with the generated token as password
|
||||
const awsConfig = {
|
||||
...this.config,
|
||||
password: authToken
|
||||
};
|
||||
|
||||
this.connection = await mysql.createConnection(awsConfig);
|
||||
} catch (err) {
|
||||
console.error(`[ERROR] AWS IAM authentication failed: ${(err as Error).message}`);
|
||||
throw new Error(`AWS IAM authentication failed: ${(err as Error).message}`);
|
||||
}
|
||||
} else {
|
||||
this.connection = await mysql.createConnection(this.config);
|
||||
}
|
||||
|
||||
console.error(`[INFO] MySQL connection established successfully`);
|
||||
} catch (err) {
|
||||
console.error(`[ERROR] MySQL connection error: ${(err as Error).message}`);
|
||||
throw new Error(`Failed to connect to MySQL: ${(err as Error).message}`);
|
||||
if (this.awsIamAuth) {
|
||||
throw new Error(`Failed to connect to MySQL with AWS IAM authentication: ${(err as Error).message}. Please verify your AWS credentials, IAM permissions, and RDS configuration.`);
|
||||
} else {
|
||||
throw new Error(`Failed to connect to MySQL: ${(err as Error).message}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
24
src/index.ts
24
src/index.ts
@@ -46,6 +46,7 @@ if (args.length === 0) {
|
||||
logger.error("Usage for SQL Server: node index.js --sqlserver --server <server> --database <database> [--user <user> --password <password>]");
|
||||
logger.error("Usage for PostgreSQL: node index.js --postgresql --host <host> --database <database> [--user <user> --password <password> --port <port>]");
|
||||
logger.error("Usage for MySQL: node index.js --mysql --host <host> --database <database> [--user <user> --password <password> --port <port>]");
|
||||
logger.error("Usage for MySQL with AWS IAM: node index.js --mysql --aws-iam-auth --host <rds-endpoint> --database <database> --user <aws-username> --aws-region <region>");
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
@@ -132,7 +133,9 @@ else if (args.includes('--mysql')) {
|
||||
password: undefined,
|
||||
port: undefined,
|
||||
ssl: undefined,
|
||||
connectionTimeout: undefined
|
||||
connectionTimeout: undefined,
|
||||
awsIamAuth: false,
|
||||
awsRegion: undefined
|
||||
};
|
||||
// Parse MySQL connection parameters
|
||||
for (let i = 0; i < args.length; i++) {
|
||||
@@ -153,6 +156,10 @@ else if (args.includes('--mysql')) {
|
||||
else connectionInfo.ssl = sslVal;
|
||||
} else if (args[i] === '--connection-timeout' && i + 1 < args.length) {
|
||||
connectionInfo.connectionTimeout = parseInt(args[i + 1], 10);
|
||||
} else if (args[i] === '--aws-iam-auth') {
|
||||
connectionInfo.awsIamAuth = true;
|
||||
} else if (args[i] === '--aws-region' && i + 1 < args.length) {
|
||||
connectionInfo.awsRegion = args[i + 1];
|
||||
}
|
||||
}
|
||||
// Validate MySQL connection info
|
||||
@@ -160,6 +167,21 @@ else if (args.includes('--mysql')) {
|
||||
logger.error("Error: MySQL requires --host and --database parameters");
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
// Additional validation for AWS IAM authentication
|
||||
if (connectionInfo.awsIamAuth) {
|
||||
if (!connectionInfo.user) {
|
||||
logger.error("Error: AWS IAM authentication requires --user parameter");
|
||||
process.exit(1);
|
||||
}
|
||||
if (!connectionInfo.awsRegion) {
|
||||
logger.error("Error: AWS IAM authentication requires --aws-region parameter");
|
||||
process.exit(1);
|
||||
}
|
||||
// Automatically enable SSL for AWS IAM authentication (required)
|
||||
connectionInfo.ssl = true;
|
||||
logger.info("AWS IAM authentication enabled - SSL automatically configured");
|
||||
}
|
||||
} else {
|
||||
// SQLite mode (default)
|
||||
dbType = 'sqlite';
|
||||
|
||||
Reference in New Issue
Block a user