Scaling Privileged Access for Modern Infrastructure: Real-World Insights
Apr 25
Virtual
Register Today
Teleport logoTry For Free
Fork me on GitHub

Teleport

Machine ID with Database Access

This version of the guide uses the v2 tbot configuration. This version is only supported by Teleport 14 and beyond. Change the selected version of the documentation to view the guide for previous Teleport versions.

Teleport protects and controls access to databases. Machine ID can be used to grant machines secure, short-lived access to these databases.

In this guide, you will configure tbot to produce credentials that can be used to access a database configured in Teleport.

Machine ID and Database Access Deployment
Machine ID and Database Access Deployment

Prerequisites

  • A running Teleport cluster. If you want to get started with Teleport, sign up for a free trial or set up a demo environment.

  • The tctl admin tool and tsh client tool version >= 15.2.2.

    On Teleport Enterprise, you must use the Enterprise version of tctl, which you can download from your Teleport account workspace. Otherwise, visit Installation for instructions on downloading tctl and tsh for Teleport Community Edition.

  • If you have not already put your database behind the Teleport Database Service, follow the database access getting started guide. The Teleport Database Service supports databases like PostgreSQL, MongoDB, Redis, and much more. See our database access guides for a complete list.
  • To check that you can connect to your Teleport cluster, sign in with tsh login, then verify that you can run tctl commands using your current credentials. tctl is supported on macOS and Linux machines. For example:
    tsh login --proxy=teleport.example.com --user=[email protected]
    tctl status

    Cluster teleport.example.com

    Version 15.2.2

    CA pin sha256:abdc1245efgh5678abdc1245efgh5678abdc1245efgh5678abdc1245efgh5678

    If you can connect to the cluster and run the tctl status command, you can use your current credentials to run subsequent tctl commands from your workstation. If you host your own Teleport cluster, you can also run tctl commands on the computer that hosts the Teleport Auth Service for full permissions.
  • The tsh binary must be installed on the machine that will access the database. Depending on how tbot was installed, this may already be installed. If it is not, see Installation for details.
  • tbot must already be installed and configured on the machine that will access the database. For more information, see the deployment guides.

Step 1/4. Configure RBAC

First, Teleport must be configured to allow the credentials produced by the bot to access the database server and database. This is done by creating a role that grants the necessary permissions and then assigning this role to a Bot.

Create a file called role.yaml with the following content:

kind: role
version: v6
metadata:
  name: example-role
spec:
  allow:
    db_labels:
      '*': '*'
    db_names: [example-db]
    db_users: [alice]
    rules:
      - resources: [db_server, db]
        verbs: [read, list]

Replace:

  • example-role with a descriptive name related to your use case.
  • example-db with the name of the database which the bot will be used to access.
  • alice with the name of the user which the bot will use when connecting to the database.

Use tctl create -f ./role.yaml to create the role.

Now, use tctl bots update to add the role to the Bot. Replace example with the name of the Bot you created in the deployment guide and example-role with the name of the role you just created:

$ tctl bots update example --add-roles example-role

This rule will allow the bot to do two things:

  • Access the database example on any database server (due to the '*': '*' label selector) as the user alice.
  • Discover information about database resources in Teleport.

The '*': '*' label selector grants access to any database server configured in Teleport. In production, consider restricting the bot's access using a more specific label selector; see the Database Access RBAC guide for a full reference of database-related role options.

Step 2/4. Configure a database tbot output

Now, tbot needs to be configured with an output that will produce the credentials needed for database access. To do this, the database output type is used.

The database you wish to generate credentials for is configured as part of the database output. This is controlled using three fields:

  • service specifies the Database Service as named in the Teleport configuration that the credentials will grant access to.
  • database specifies the database on the Database Service that the credentials will grant access to.
  • username specifies the user on the database that the credentials will grant access to. This field does not need to be specified for all types of database.

In addition, the format field in the database output controls the format of the generated credentials. This allows for compatibility with clients that expect a specific format. When this field is not specified, a sensible default option is used that is compatible with most clients. The full list of supported format options is below:

ClientformatDescription
DefaultUnspecifiedProvides a certificate in tlscert, a private key in key and the CA in teleport-database-ca.crt. This is compatible with most clients.
MongoDBmongoProvides mongo.crt and mongo.cas.
CockroachDBcockroachProvides cockroach/node.key, cockroach/node.crt, and cockroach/ca.crt.
Generic TLStlsProvides tls.key, tls.crt, and tls.cas for generic clients that require specific file extensions.

Outputs must be configured with a destination. In this example, the directory destination will be used. This will write artifacts to a specified directory on disk. Ensure that this directory can be written to by the Linux user that tbot runs as, and that it can be read by the Linux user that will be accessing applications.

Modify your tbot configuration to add a database output:

outputs:
- type: database
  destination:
    type: directory
    path: /opt/machine-id
  # Specify the details of the database you wish to connect to.
  service: example-server
  database: example
  username: alice
  # Specify a format to use for the output credentials. For most databases,
  # this configuration field can be omitted.
  # format: mongo

If operating tbot as a background service, restart it. If running tbot in one-shot mode, it must be executed before you attempt to execute the Ansible playbook.

Step 3/4. Configure the local database access proxy

Now that tbot has produced the database access credentials, a local proxy should be set up to forward the database connections from your database client to the Teleport Proxy Service through a TLS connection. This is necessary as the TLS connection allows the Teleport Proxy Service to identify the protocol and intended recipient.

The local proxy needs to be running as long as the client needs to make connections to the database or as long as a connection is still open. One way to keep this local proxy running in the background is to use a systemd service. This is demonstrated in the remainder of this step, but a different service manager could be used or a number of other techniques could be used to run the local proxy whilst the client is running.

The local proxy opens a specified port on the local loopback interface. Clients must then be configured to connect to this port on localhost. As the port is opened on the local loopback interface, it means that the local proxy must be running on the same host as the client which wants to connect to the database.

By default, database clients must also be configured to use the credentials when connecting to the local port. This ensures no other users of the host can access the database via the local port, and ensures the connection between your database client and server is never unencrypted, even over localhost.

To create a systemd service for this purpose, create a unit file at /etc/systemd/system/tbot-db-proxy.service:

[Unit]
Description=Teleport Machine ID Proxy Service
After=network.target
# If you have followed a previous guide and configured tbot itself as a systemd
# service, uncomment the following line to create a dependency between the two
# services.
# Requires=tbot.service

[Service]
Type=simple
# Ensure that the teleport user/group exists and has read access to the
# destination directory.
User=teleport
Group=teleport
Restart=on-failure
# Adjust `12345` to any port of your choosing that is free on the local loopback
# interface. Adjust `example-server` to the name of the Database Service in
# teleport.
ExecStart=/usr/local/bin/tbot -c /etc/tbot.yaml proxy --proxy=proxy.example.com:3080 --destination-dir=/opt/machine-id db --port=12345 example-server
ExecReload=/bin/kill -HUP $MAINPID
PIDFile=/run/tbot-db-proxy.pid
LimitNOFILE=8192

[Install]
WantedBy=multi-user.target

This will start a local proxy on port 12345 that can be used to connect to the example-server database server. Be sure to customize the tbot parameters as necessary for your local setup.

Finally, run the following commands to enable and start the local proxy service:

sudo systemctl enable tbot-db-proxy
sudo systemctl start tbot-db-proxy
sudo systemctl status tbot-db-proxy

Authenticated tunnel

Whilst the default behaviour requires the client to use client certificate authentication, it is possible to configure an authenticated tunnel. This will automatically attach credentials to any incoming connection to the local port. Whilst this is less secure, it can be necessary if the client you wish to use with your database does not support client certificate authentication.

If you wish to enable the authenticated tunnel mode, --tunnel flag is used with tbot proxy db....

If you are executing this in the foreground, provide the --tunnel flag. If you are using a systemd service, add --tunnel to the ExecStart in machine-id-proxy.service and then reload the unit.

Once this has been enabled, you do not need to specify a password or TLS certificates and certificate authorities in the client when connecting to the configured port.

Step 4/4. Configure the client to connect to the database

With the credentials generated and the local proxy running, you can now configure your client to use the local proxy with the credentials.

Refer to these sample Go programs for using the credentials:

// This example program demonstrates how to connect to a Postgres database
// using certificates issued by Teleport Machine ID.

package main

import (
	"database/sql"
	"fmt"
	"log"

	_ "github.com/jackc/pgx/v4/stdlib"
)

func main() {
	// Open connection to database.
	db, err := sql.Open("pgx", fmt.Sprint(
		"host=localhost ",
		"port=1234 ",
		"dbname=example ",
		"user=alice ",
		// The next four options should be omitted if the local proxy has been
		// placed in "authenticated tunnel" mode.
		"sslmode=verify-full ",
		"sslrootcert=/opt/machine-id/teleport-host-ca.crt ",
		"sslkey=/opt/machine-id/key ",
		"sslcert=/opt/machine-id/tlscert ",
	))
	if err != nil {
		log.Fatalf("Failed to open database: %v.", err)
	}

	defer db.Close()

	// Call "Ping" to test connectivity.
	err = db.Ping()
	if err != nil {
		log.Fatalf("Failed to Ping database: %v.", err)
	}

	log.Printf("Successfully connected to PostgreSQL.")
}

// This example program demonstrates how to connect to a MongoDB database
// using certificates issued by Teleport Machine ID.

package main

import (
	"context"
	"fmt"
	"log"
	"time"

	"go.mongodb.org/mongo-driver/bson"
	"go.mongodb.org/mongo-driver/mongo"
	"go.mongodb.org/mongo-driver/mongo/options"
)

func main() {
	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
	defer cancel()

	// Create client and connect to MongoDB. Make sure to modify the host,
	// port, and certificate paths.
	uri := fmt.Sprintf(
		"mongodb://localhost:1234/?tlsCAFile=%s&tlsCertificateKeyFile=%s",
		"/opt/machine-id/mongo.cas",
		"/opt/machine-id/mongo.crt",
	)
	client, err := mongo.NewClient(options.Client().ApplyURI(uri))
	if err != nil {
		log.Fatalf("Failed to create database client: %v.", err)
	}
	err = client.Connect(ctx)
	if err != nil {
		log.Fatalf("Failed to connect to database: %v.", err)
	}

	defer client.Disconnect(ctx)

	log.Printf("Successfully connected to MongoDB.")

	// List databases to test connectivity.
	databases, err := client.ListDatabaseNames(ctx, bson.M{})
	if err != nil {
		log.Fatalf("Failed to list databases: %v.", err)
	}
	log.Println(databases)

}

You're all set. You have provided your application with short-lived certificates tied to a machine identity that can access your database, be rotated, and audited, all while being controlled with all the familiar Teleport access controls.

Next steps