mirror of
https://github.com/executeautomation/mcp-database-server.git
synced 2025-12-09 21:12:57 +08:00
Merge pull request #12 from fabrizio11/feature/aws-iam-authentication
Feature/aws iam authentication
This commit is contained in:
@@ -75,18 +75,35 @@ node dist/src/index.js --postgresql --host dbserver.example.com --database sampl
|
|||||||
| `--mysql` | Specifies MySQL mode | - | Yes |
|
| `--mysql` | Specifies MySQL mode | - | Yes |
|
||||||
| `--host` | MySQL hostname or IP | - | Yes |
|
| `--host` | MySQL hostname or IP | - | Yes |
|
||||||
| `--database` | Database name | - | Yes |
|
| `--database` | Database name | - | Yes |
|
||||||
| `--user` | MySQL username | - | No |
|
| `--user` | MySQL username | - | No* |
|
||||||
| `--password` | MySQL password | - | No |
|
| `--password` | MySQL password | - | No* |
|
||||||
| `--port` | MySQL port | 3306 | No |
|
| `--port` | MySQL port | 3306 | No |
|
||||||
| `--ssl` | Use SSL connection (true/false or object) | false | No |
|
| `--ssl` | Use SSL connection (true/false or object) | false | No |
|
||||||
| `--connection-timeout` | Connection timeout in ms | 30000 | No |
|
| `--connection-timeout` | Connection timeout in ms | 30000 | No |
|
||||||
|
| `--aws-iam-auth` | Enable AWS IAM authentication | false | No |
|
||||||
|
| `--aws-region` | AWS region for RDS IAM auth | - | No** |
|
||||||
|
|
||||||
### Example
|
*Required for standard authentication
|
||||||
|
**Required when using `--aws-iam-auth`
|
||||||
|
|
||||||
|
### Standard Authentication Example
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
node dist/src/index.js --mysql --host localhost --database sample_db --port 3306 --user root --password secret
|
node dist/src/index.js --mysql --host localhost --database sample_db --port 3306 --user root --password secret
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### AWS IAM Authentication Example
|
||||||
|
|
||||||
|
**Prerequisites:** AWS credentials must be configured using the default credential provider chain:
|
||||||
|
- `aws configure` (default profile)
|
||||||
|
- `AWS_PROFILE=myprofile` environment variable
|
||||||
|
- `AWS_ACCESS_KEY_ID` and `AWS_SECRET_ACCESS_KEY` environment variables
|
||||||
|
- IAM roles (if running on EC2)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
node dist/src/index.js --mysql --aws-iam-auth --host rds-endpoint.region.rds.amazonaws.com --database sample_db --user aws-username --aws-region us-east-1
|
||||||
|
```
|
||||||
|
|
||||||
## Environment Variables
|
## Environment Variables
|
||||||
|
|
||||||
Instead of specifying sensitive credentials on the command line, you can use environment variables:
|
Instead of specifying sensitive credentials on the command line, you can use environment variables:
|
||||||
|
|||||||
1309
package-lock.json
generated
1309
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -23,6 +23,7 @@
|
|||||||
"clean": "rimraf dist"
|
"clean": "rimraf dist"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@aws-sdk/rds-signer": "^3.0.0",
|
||||||
"@modelcontextprotocol/sdk": "1.9.0",
|
"@modelcontextprotocol/sdk": "1.9.0",
|
||||||
"mssql": "11.0.1",
|
"mssql": "11.0.1",
|
||||||
"mysql2": "^3.14.1",
|
"mysql2": "^3.14.1",
|
||||||
|
|||||||
52
readme.md
52
readme.md
@@ -94,6 +94,8 @@ Optional parameters:
|
|||||||
|
|
||||||
### MySQL Database
|
### MySQL Database
|
||||||
|
|
||||||
|
#### Standard Authentication
|
||||||
|
|
||||||
To use with a MySQL database:
|
To use with a MySQL database:
|
||||||
|
|
||||||
```
|
```
|
||||||
@@ -111,6 +113,31 @@ Optional parameters:
|
|||||||
- `--ssl`: Enable SSL connection (true/false or object)
|
- `--ssl`: Enable SSL connection (true/false or object)
|
||||||
- `--connection-timeout`: Connection timeout in milliseconds (default: 30000)
|
- `--connection-timeout`: Connection timeout in milliseconds (default: 30000)
|
||||||
|
|
||||||
|
#### AWS IAM Authentication
|
||||||
|
|
||||||
|
For Amazon RDS MySQL instances with IAM database authentication:
|
||||||
|
|
||||||
|
**Prerequisites:**
|
||||||
|
- AWS credentials must be configured (the RDS Signer uses the default credential provider chain)
|
||||||
|
- Configure using one of these methods:
|
||||||
|
- `aws configure` (uses default profile)
|
||||||
|
- `AWS_PROFILE=myprofile` environment variable
|
||||||
|
- `AWS_ACCESS_KEY_ID` and `AWS_SECRET_ACCESS_KEY` environment variables
|
||||||
|
- IAM roles (if running on EC2)
|
||||||
|
|
||||||
|
```
|
||||||
|
node dist/src/index.js --mysql --aws-iam-auth --host <rds-endpoint> --database <database-name> --user <aws-username> --aws-region <region>
|
||||||
|
```
|
||||||
|
|
||||||
|
Required parameters:
|
||||||
|
- `--host`: RDS endpoint hostname
|
||||||
|
- `--database`: Name of the database
|
||||||
|
- `--aws-iam-auth`: Enable AWS IAM authentication
|
||||||
|
- `--user`: AWS IAM username (also the database user)
|
||||||
|
- `--aws-region`: AWS region where RDS instance is located
|
||||||
|
|
||||||
|
Note: SSL is automatically enabled for AWS IAM authentication
|
||||||
|
|
||||||
## Configuring Claude Desktop
|
## Configuring Claude Desktop
|
||||||
|
|
||||||
### Direct Usage Configuration
|
### Direct Usage Configuration
|
||||||
@@ -164,6 +191,19 @@ If you installed the package globally, configure Claude Desktop with:
|
|||||||
"--user", "your-username",
|
"--user", "your-username",
|
||||||
"--password", "your-password"
|
"--password", "your-password"
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
"mysql-aws": {
|
||||||
|
"command": "npx",
|
||||||
|
"args": [
|
||||||
|
"-y",
|
||||||
|
"@executeautomation/database-server",
|
||||||
|
"--mysql",
|
||||||
|
"--aws-iam-auth",
|
||||||
|
"--host", "your-rds-endpoint.region.rds.amazonaws.com",
|
||||||
|
"--database", "your-database-name",
|
||||||
|
"--user", "your-aws-username",
|
||||||
|
"--aws-region", "us-east-1"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -216,6 +256,18 @@ For local development, configure Claude Desktop to use your locally built versio
|
|||||||
"--user", "your-username",
|
"--user", "your-username",
|
||||||
"--password", "your-password"
|
"--password", "your-password"
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
"mysql-aws": {
|
||||||
|
"command": "node",
|
||||||
|
"args": [
|
||||||
|
"/absolute/path/to/mcp-database-server/dist/src/index.js",
|
||||||
|
"--mysql",
|
||||||
|
"--aws-iam-auth",
|
||||||
|
"--host", "your-rds-endpoint.region.rds.amazonaws.com",
|
||||||
|
"--database", "your-database-name",
|
||||||
|
"--user", "your-aws-username",
|
||||||
|
"--aws-region", "us-east-1"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { DbAdapter } from "./adapter.js";
|
import { DbAdapter } from "./adapter.js";
|
||||||
import mysql from "mysql2/promise";
|
import mysql from "mysql2/promise";
|
||||||
|
import { Signer } from "@aws-sdk/rds-signer";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* MySQL database adapter implementation
|
* MySQL database adapter implementation
|
||||||
@@ -9,6 +10,8 @@ export class MysqlAdapter implements DbAdapter {
|
|||||||
private config: mysql.ConnectionOptions;
|
private config: mysql.ConnectionOptions;
|
||||||
private host: string;
|
private host: string;
|
||||||
private database: string;
|
private database: string;
|
||||||
|
private awsIamAuth: boolean;
|
||||||
|
private awsRegion?: string;
|
||||||
|
|
||||||
constructor(connectionInfo: {
|
constructor(connectionInfo: {
|
||||||
host: string;
|
host: string;
|
||||||
@@ -18,9 +21,13 @@ export class MysqlAdapter implements DbAdapter {
|
|||||||
port?: number;
|
port?: number;
|
||||||
ssl?: boolean | object;
|
ssl?: boolean | object;
|
||||||
connectionTimeout?: number;
|
connectionTimeout?: number;
|
||||||
|
awsIamAuth?: boolean;
|
||||||
|
awsRegion?: string;
|
||||||
}) {
|
}) {
|
||||||
this.host = connectionInfo.host;
|
this.host = connectionInfo.host;
|
||||||
this.database = connectionInfo.database;
|
this.database = connectionInfo.database;
|
||||||
|
this.awsIamAuth = connectionInfo.awsIamAuth || false;
|
||||||
|
this.awsRegion = connectionInfo.awsRegion;
|
||||||
this.config = {
|
this.config = {
|
||||||
host: connectionInfo.host,
|
host: connectionInfo.host,
|
||||||
database: connectionInfo.database,
|
database: connectionInfo.database,
|
||||||
@@ -33,8 +40,15 @@ export class MysqlAdapter implements DbAdapter {
|
|||||||
if (typeof connectionInfo.ssl === 'object' || typeof connectionInfo.ssl === 'string') {
|
if (typeof connectionInfo.ssl === 'object' || typeof connectionInfo.ssl === 'string') {
|
||||||
this.config.ssl = connectionInfo.ssl;
|
this.config.ssl = connectionInfo.ssl;
|
||||||
} else if (connectionInfo.ssl === true) {
|
} else if (connectionInfo.ssl === true) {
|
||||||
|
// 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 = {};
|
this.config.ssl = {};
|
||||||
}
|
}
|
||||||
|
}
|
||||||
// Validate port
|
// Validate port
|
||||||
if (connectionInfo.port && typeof connectionInfo.port !== 'number') {
|
if (connectionInfo.port && typeof connectionInfo.port !== 'number') {
|
||||||
const parsedPort = parseInt(connectionInfo.port as any, 10);
|
const parsedPort = parseInt(connectionInfo.port as any, 10);
|
||||||
@@ -47,19 +61,76 @@ export class MysqlAdapter implements DbAdapter {
|
|||||||
console.error(`[DEBUG] MySQL connection will use port: ${this.config.port}`);
|
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.info(`[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.info(`[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
|
* Initialize MySQL connection
|
||||||
*/
|
*/
|
||||||
async init(): Promise<void> {
|
async init(): Promise<void> {
|
||||||
try {
|
try {
|
||||||
console.error(`[INFO] Connecting to MySQL: ${this.host}, Database: ${this.database}`);
|
console.info(`[INFO] Connecting to MySQL: ${this.host}, Database: ${this.database}`);
|
||||||
|
|
||||||
|
// Handle AWS IAM authentication
|
||||||
|
if (this.awsIamAuth) {
|
||||||
|
console.info(`[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);
|
this.connection = await mysql.createConnection(this.config);
|
||||||
console.error(`[INFO] MySQL connection established successfully`);
|
}
|
||||||
|
|
||||||
|
console.info(`[INFO] MySQL connection established successfully`);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error(`[ERROR] MySQL connection error: ${(err as Error).message}`);
|
console.error(`[ERROR] MySQL connection error: ${(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}`);
|
throw new Error(`Failed to connect to MySQL: ${(err as Error).message}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Execute a SQL query and get all results
|
* Execute a SQL query and get all results
|
||||||
|
|||||||
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 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 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: 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);
|
process.exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -132,7 +133,9 @@ else if (args.includes('--mysql')) {
|
|||||||
password: undefined,
|
password: undefined,
|
||||||
port: undefined,
|
port: undefined,
|
||||||
ssl: undefined,
|
ssl: undefined,
|
||||||
connectionTimeout: undefined
|
connectionTimeout: undefined,
|
||||||
|
awsIamAuth: false,
|
||||||
|
awsRegion: undefined
|
||||||
};
|
};
|
||||||
// Parse MySQL connection parameters
|
// Parse MySQL connection parameters
|
||||||
for (let i = 0; i < args.length; i++) {
|
for (let i = 0; i < args.length; i++) {
|
||||||
@@ -153,6 +156,10 @@ else if (args.includes('--mysql')) {
|
|||||||
else connectionInfo.ssl = sslVal;
|
else connectionInfo.ssl = sslVal;
|
||||||
} else if (args[i] === '--connection-timeout' && i + 1 < args.length) {
|
} else if (args[i] === '--connection-timeout' && i + 1 < args.length) {
|
||||||
connectionInfo.connectionTimeout = parseInt(args[i + 1], 10);
|
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
|
// Validate MySQL connection info
|
||||||
@@ -160,6 +167,21 @@ else if (args.includes('--mysql')) {
|
|||||||
logger.error("Error: MySQL requires --host and --database parameters");
|
logger.error("Error: MySQL requires --host and --database parameters");
|
||||||
process.exit(1);
|
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 {
|
} else {
|
||||||
// SQLite mode (default)
|
// SQLite mode (default)
|
||||||
dbType = 'sqlite';
|
dbType = 'sqlite';
|
||||||
|
|||||||
Reference in New Issue
Block a user