feat(mongo): Add MongoDB connection support for Boundary CLI PORT (#6069)

* feat(mongo): add mongo for boundary cli

* Fix: remove changes added by mistake during testing

* feat(enos): install MongoDB client (mongosh) in e2e Docker test runtime

* Remove explicit Boundary DB/server startup from target_tcp_connect_mongo_test.go

* Add a full stop and trigger CI

* feat: add mongosh support to boundary connect mongo

* feat: add mongosh support to boundary connect mongo

* feat: add auth-source flag for MongoDB connect command

- Add -auth-source flag with default value 'admin' for root users
- Add BOUNDARY_CONNECT_MONGO_AUTH_SOURCE environment variable support
- Update documentation with new flag and correct mongosh default
- Improve MongoDB test with explicit auth-source parameter
- Fix Docker configuration formatting for better readability

This ensures MongoDB root users authenticate against the admin database
by default while allowing flexibility for custom authentication databases.

* test(e2e): Fixed false positive in Mongo connect test; use UriNetwork and container hostname

* make gen

* fix: Removed unnecessary variable containerName from the MongoDB E2E test

* feat(connect/mongo): Build URI compatible with mongosh and set default authSource

* feat(connect/mongo): use mongosh flags following PostgreSQL pattern

* remove 'connection string' from auth-source usage text

* Update internal/cmd/commands/connect/mongo.go

Co-authored-by: Bharath Gajjala <120367134+bgajjala8@users.noreply.github.com>

* remove 'connection string' from auth-source usage text

* refacotr: replace authSource flag with authenticationDatabase

---------

Co-authored-by: Enbiya <100806254+enbiyagoral@users.noreply.github.com>
Co-authored-by: Johan Brandhorst-Satzkorn <johan.brandhorst@gmail.com>
This commit is contained in:
authored and GitHub committed 2025-10-09 11:13:47 -05:00
1 parent b006c47887
commit 4078838f56
10 files changed
+435 -10

No files matched your search

+6
View File
@@ -417,6 +417,12 @@ func initCommands(ui, serverCmdUi cli.Ui, runOpts *RunOptions) {
Func: "mysql",
}
}),
"connect mongo": wrapper.Wrap(func() wrapper.WrappableCommand {
return &connect.Command{
Command: base.NewCommand(ui, opts...),
Func: "mongo",
}
}),
"connect cassandra": wrapper.Wrap(func() wrapper.WrappableCommand {
return &connect.Command{
Command: base.NewCommand(ui, opts...),
+30 -9
View File
@@ -59,15 +59,16 @@ var (
type Command struct {
*base.Command
flagAuthzToken string
flagListenAddr string
flagListenPort int64
flagTargetId string
flagTargetName string
flagHostId string
flagExec string
flagUsername string
flagDbname string
flagAuthzToken string
flagListenAddr string
flagListenPort int64
flagTargetId string
flagTargetName string
flagHostId string
flagExec string
flagUsername string
flagDbname string
flagMongoDbAuthenticationDatabase string
// HTTP
httpFlags
@@ -81,6 +82,9 @@ type Command struct {
// MySQL
mysqlFlags
// MongoDB
mongoFlags
// Cassandra
cassandraFlags
@@ -115,6 +119,8 @@ func (c *Command) Synopsis() string {
return postgresSynopsis
case "mysql":
return mysqlSynopsis
case "mongo":
return mongoSynopsis
case "cassandra":
return cassandraSynopsis
case "redis":
@@ -241,6 +247,9 @@ func (c *Command) Flags() *base.FlagSets {
case "mysql":
mysqlOptions(c, set)
case "mongo":
mongoOptions(c, set)
case "cassandra":
cassandraOptions(c, set)
@@ -336,6 +345,8 @@ func (c *Command) Run(args []string) (retCode int) {
c.flagExec = c.postgresFlags.defaultExec()
case "mysql":
c.flagExec = c.mysqlFlags.defaultExec()
case "mongo":
c.flagExec = c.mongoFlags.defaultExec()
case "cassandra":
c.flagExec = c.cassandraFlags.defaultExec()
case "redis":
@@ -714,6 +725,16 @@ func (c *Command) handleExec(clientProxy *apiproxy.ClientProxy, passthroughArgs
envs = append(envs, mysqlEnvs...)
creds = mysqlCreds
case "mongo":
mongoArgs, mongoEnvs, mongoCreds, mongoErr := c.mongoFlags.buildArgs(c, port, host, addr, creds)
if mongoErr != nil {
argsErr = mongoErr
break
}
args = append(args, mongoArgs...)
envs = append(envs, mongoEnvs...)
creds = mongoCreds
case "cassandra":
cassandraArgs, cassandraEnvs, cassandraCreds, cassandraErr := c.cassandraFlags.buildArgs(c, port, host, addr, creds)
if cassandraErr != nil {
+110
View File
@@ -0,0 +1,110 @@
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: BUSL-1.1
package connect
import (
"fmt"
"strings"
"github.com/hashicorp/boundary/api/proxy"
"github.com/hashicorp/boundary/internal/cmd/base"
"github.com/posener/complete"
)
const (
mongoSynopsis = "Authorize a session against a target and invoke a MongoDB client to connect"
)
func mongoOptions(c *Command, set *base.FlagSets) {
f := set.NewFlagSet("MongoDB Options")
f.StringVar(&base.StringVar{
Name: "style",
Target: &c.flagMongoStyle,
EnvVar: "BOUNDARY_CONNECT_MONGO_STYLE",
Completion: complete.PredictSet("mongosh"),
Default: "mongosh",
Usage: `Specifies how the CLI will attempt to invoke a MongoDB client. Currently only "mongosh" is supported.`,
})
f.StringVar(&base.StringVar{
Name: "username",
Target: &c.flagUsername,
EnvVar: "BOUNDARY_CONNECT_USERNAME",
Completion: complete.PredictNothing,
Usage: `Specifies the username to pass through to the client. May be overridden by credentials sourced from a credential store.`,
})
f.StringVar(&base.StringVar{
Name: "dbname",
Target: &c.flagDbname,
EnvVar: "BOUNDARY_CONNECT_DBNAME",
Completion: complete.PredictNothing,
Usage: `Specifies the database name to pass through to the client.`,
})
f.StringVar(&base.StringVar{
Name: "authentication-database",
Target: &c.flagMongoDbAuthenticationDatabase,
EnvVar: "BOUNDARY_CONNECT_MONGO_AUTHENTICATION_DATABASE",
Completion: complete.PredictNothing,
Default: "",
Usage: `Specifies the authentication database for MongoDB. If omitted, mongosh defaults authSource to the database name (dbname); if none is specified, it defaults to "admin".`,
})
}
type mongoFlags struct {
flagMongoStyle string
}
func (m *mongoFlags) defaultExec() string {
return strings.ToLower(m.flagMongoStyle)
}
func (m *mongoFlags) buildArgs(c *Command, port, ip, _ string, creds proxy.Credentials) (args, envs []string, retCreds proxy.Credentials, retErr error) {
var username, password string
retCreds = creds
if len(retCreds.UsernamePassword) > 0 {
// Mark credential as consumed so it is not printed to user
retCreds.UsernamePassword[0].Consumed = true
// For now just grab the first username password credential brokered
username = retCreds.UsernamePassword[0].Username
password = retCreds.UsernamePassword[0].Password
}
switch m.flagMongoStyle {
case "mongosh":
if port != "" {
args = append(args, "--port", port)
}
args = append(args, "--host", ip)
if c.flagDbname != "" {
args = append(args, c.flagDbname)
}
switch {
case username != "":
args = append(args, "-u", username)
case c.flagUsername != "":
args = append(args, "-u", c.flagUsername)
}
if password != "" {
args = append(args, "-p", password)
if c.flagDbname == "" {
c.UI.Warn("Credentials are being brokered but no -dbname parameter provided. mongosh will default the database to 'test'.")
}
}
if c.flagMongoDbAuthenticationDatabase != "" {
args = append(args, "--authenticationDatabase", c.flagMongoDbAuthenticationDatabase)
}
default:
return nil, nil, proxy.Credentials{}, fmt.Errorf("unsupported MongoDB style: %s", m.flagMongoStyle)
}
return args, envs, retCreds, retErr
}