• Stars
    star
    173
  • Rank 220,124 (Top 5 %)
  • Language
    TypeScript
  • License
    MIT License
  • Created over 2 years ago
  • Updated over 1 year ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

Basti is a CLI tool for securely accessing your DB instances and other AWS resources in private networks at almost no cost.

Basti


Basti (from Bastion Host) is a CLI tool for securely accessing your DB instances and other AWS resources in private networks at almost no cost.

💵 No idle costs. 🔑 No SSH keys. 🔒 Fully IAM-driven.

Demo

Table of contents


Why Basti?

With Basti, you can securely connect to your RDS/Aurora/Elasticache/EC2 instances in private VPC subnets from a local machine or CI/CD pipeline almost for free!

How it works

  • 🏰 Basti sets up a so called bastion EC2 instance in the connection target's VPC.

  • 🧑‍💻 The bastion instance is used with AWS Session Manager port forwarding capability to make the target available on your localhost.

  • 💵 Basti takes care of keeping the bastion instance stopped when it's not used to make the solution cost as low as ≈ 0.01 USD per hour of connection plus ≈ 0.80 USD per month of maintaining the instance in a stopped state.

  • 🔒 Security completely relies on AWS Session Manager and IAM policies. The bastion instance is not accessible from the Internet and no SSH keys are used.

Installation

npm install --global basti

Other, NodeJS-independent, installation options are coming soon!

Basic usage

Basti uses AWS SDK and relies on credentials to be configured in your system. You can use any of the methods supported by AWS SDK to configure credentials.

💡 You can expect Basti to work if you can use AWS CLI in your terminal.

Initialize target

First, initialize your target for use with Basti. The following command will set up all the infrastructure required to start a connection. You only need to do this once.

basti init

You will be prompted for a target to initialize and a public VPC subnet to create the bastion EC2 instance in.

Connect to target

Now, you can start the connection. This command will establish a secure port forwarding session and make the target available on your localhost.

basti connect

You will be prompted for the target to connect to as well as the local port to forward the connection to.

Use target on localhost

Finally, you can use the target same way as it was running on your localhost and port you specified in the previous step.

psql -h localhost -p 5432

💡 psql, the PostgreSQL client, is used as an example here. Basti can be used to connect to any type of database or other services as long as the communication is done over TCP.

Cleanup (optional)

You can remove all the resources created by Basti in you AWS account.

basti cleanup

The list of resources will be displayed and you will be prompted to confirm the cleanup.

Custom connection targets

Basti provides first class support for RDS instances and Aurora clusters. However, you can use Basti to connect to any other target in your AWS VPC (e.g. Elasticache instance, EC2 instance, etc.).

To connect to a custom target, select the Custom option when prompted for a target to initialize or connect to. You will be prompted for the target's VPC, IP address and port.

🤝 Feel free to open an issue or a pull request if you want to extend the list of natively supported targets

Basti in CI/CD pipelines

Using interactive mode is convenient when you're getting used to Basti. However, in Continuous Integration and Continuous Delivery (CI/CD) pipelines, you will probably want to disable interactivity and pass all the options as command line arguments.

Take for example the non-interactive form of the basti connect command:

basti connect --rds-instance your-instance-id --local-port your-port

Use basti <command> --help to see all the available options for basti connect and other commands.

Basti configuration file

When dealing with multiple connection targets, it becomes convenient to store their configurations and other Basti settings in a dedicated configuration file. To facilitate this, Basti automatically searches for the configuration file in the current directory and its parent directories. The supported file names are .basti.yaml, .basti.yml, and .basti.json.

You can quickly start a connection defined in the configuration file by passing its name to the basti connect command:

basti connect your-connection
Configuration file example

This example uses YAML format. The same configuration can be written in JSON.

# - Connections are used with the `basti connect <connection>` command
# - Targets' fields are the same as the options for the `basti connect` command
connections:
  database-dev:
    target:
      rdsInstance: my-dev-database
      awsProfile: dev
    localPort: 5432

  database-prod:
    target:
      rdsInstance: my-prod-database
      awsProfile: prod
    localPort: 5432

  # Default AWS profile and region are used if not specified in the target
  aurora-cluster-dev:
    target:
      rdsCluster: my-prod-aurora-cluster
    localPort: 5432

  custom-target:
    target: custom-target
    localPort: 4647

  # Same target but with different local port
  custom-target-local:
    target: custom-target
    localPort: 4646

# Targets can be extracted and reused in multiple connections
# with different local ports
targets:
  custom-target:
    customTargetVpc: vpc-1234567890
    customTargetHost: 10.0.1.1
    customTargetPort: 4646
    awsProfile: prod
    awsRegion: us-east-1

Basti in teams and organizations

Basti was designed with organizational usage patterns in mind. The bastion instance and other infrastructure created by Basti is reused across all the users in your organization.

Minimal IAM permissions

Basti commands require different sets of IAM permissions. basti init needs broad permissions to set up all the infrastructure required to start a connection. basti connect, on the other hand, requires only minimal permissions to start a connection. This means that the AWS account administrator can run the basti init command once and then grant the minimal permissions to the IAM users who need to start connections.

Minimal IAM policy for connection

The following command is optimized for minimal permissions required to start a connection. It doesn't need to retrieve the target information as it's passed as command line arguments.

basti connect --custom-target-vpc your-vpc-id --custom-target-host your-target-host --custom-target-port your-target-port --local-port your-local-port

Minimal policy:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": "ec2:DescribeInstances",
      "Resource": "*"
    },
    {
      "Effect": "Allow",
      "Action": "ec2:StartInstances",
      "Resource": "arn:aws:ec2:<your-region>:<your-account-id>:instance/<your-basti-instance-id>"
    },
    {
      "Effect": "Allow",
      "Action": "ec2:CreateTags",
      "Resource": "arn:aws:ec2:<your-region>:<your-account-id>:instance/<your-basti-instance-id>"
    },
    {
      "Effect": "Allow",
      "Action": "ssm:StartSession",
      "Resource": [
        "arn:aws:ssm:*:*:document/AWS-StartPortForwardingSessionToRemoteHost",
        "arn:aws:ec2:<your-region>:<your-account-id>:instance/<your-basti-instance-id>"
      ]
    }
  ]
}

Usage audit

Since Basti uses IAM for access control, the connection history, along with the responsible IAM user and all the connection details, can be audited using AWS CloudTrail by filtering on the "StartSession" event. Please, refer to the AWS CloudTrail documentation for more details.

A simple connections history can also be found in the AWS Session Manager history. See AWS Session Manager documentation for more details.

Shared configuration

The Basti configuration file file can be shared across your organization, making it easy for all developers to connect to the project's cloud infrastructure. A recommended practice is to store the configuration file in the root of your project's repository. This ensures that the configuration is readily accessible to all team members, enabling quick and seamless connections to the required cloud resources.

Security

Security is a top priority for Basti. The following sections describe the security measures taken by Basti.

Network

The bastion EC2 instance reachability from the Internet is completely disabled with AWS Security Groups configuration. No ports are open for inbound traffic. The bastion instance is only accessible through AWS Session Manager.

Basti automatically adjusts the target's Security Group to allow inbound traffic from the bastion instance's Security Group.

Access control

AWS Session Manager, which is used by Basti to establish a port forwarding session, doesn't use SSH keys for access control. Instead, it relies on AWS IAM users and their permissions in your AWS account. This also means that AWS CloudTrail could be used to audit Basti usage.

Software

Basti uses the latest Amazon Linux 2 - Kernel 5.10 AMI available at the initialization time (basti init command) for the bastion instance.

The bastion instance is being stopped when it's not used for some short period of time. These shutdowns are also used to update the bastion instance's software packages and OS kernel. By default, the updates happen once a day but not more often than the bastion instance is used.

Development

First of all, thank you for your interest in contributing to Basti! 🎉

The following section describes how to run your local version of Basti as you make changes to the code.

Build

Before proceeding to development, it's recommended to run the full build. This requires Docker to be installed on your machine and may take a couple of minutes.

npm run build

Full Basti build consists of two parts:

  1. Compiling Basti TypeScript code. The code has to be compiled after each change.
    npm run build-src
    
    # Or, if you want to automatically recompile on each change:
    npm run build-src-watch
  2. Building non-NodeJS dependencies (AWS session-manger-plugin). This step is only required after the first checkout or in a rare case when the dependencies are updated.
    npm run build-deps

Run

After the build is complete, you can run Basti:

npm run start -- <command> <options>

Alternatively, you can link-install the local version of Basti in your system and use it as you would usually use Basti:

# Link-install the local version of Basti
npm link

# Run Basti
basti <command> <options>

Test

Before submitting a pull request, please make sure that all the tests and checks pass:

npm run test

License

Usage is provided under the MIT License. See LICENSE for the full details.