Machine ID with Database Access
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.
Prerequisites
-
A running Teleport cluster version 15.4.22 or above. If you want to get started with Teleport, sign up for a free trial or set up a demo environment.
-
The
tctl
admin tool andtsh
client tool.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 downloadingtctl
andtsh
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 runtctl
commands using your current credentials.tctl
is supported on macOS and Linux machines. For example:If you can connect to the cluster and run the$ tsh login --proxy=teleport.example.com [email protected]
$ tctl status
# Cluster teleport.example.com
# Version 15.4.22
# CA pin sha256:abdc1245efgh5678abdc1245efgh5678abdc1245efgh5678abdc1245efgh5678tctl status
command, you can use your current credentials to run subsequenttctl
commands from your workstation. If you host your own Teleport cluster, you can also runtctl
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 howtbot
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 useralice
. - 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:
Client | format | Description |
---|---|---|
Default | Unspecified | Provides a certificate in tlscert , a private key in key and the CA in teleport-database-ca.crt . This is compatible with most clients. |
MongoDB | mongo | Provides mongo.crt and mongo.cas . |
CockroachDB | cockroach | Provides cockroach/node.key , cockroach/node.crt , and cockroach/ca.crt . |
Generic TLS | tls | Provides 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=always
RestartSec=5
# 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:
- Self-Hosted PostgreSQL
- Self-Hosted MongoDB
// 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
- Read the configuration reference to explore all the available configuration options.