From 862ce88aaf99f22dd54364387dd355f1932be326 Mon Sep 17 00:00:00 2001 From: Silvan Kaiser Date: Fri, 15 Dec 2017 15:23:55 +0100 Subject: [PATCH 1/2] Read params via environment instead of cli flags Instead of reading the parameters to the docker-quobyte-plugin service via cli flags they are now read directly from the environment. This prevents credential leakage for the API password. A tenant_id is now provided via the configuration (not via cli param). --- README.md | 55 ++++++++++++++------------ main.go | 57 ++++++++++++++++----------- quobyte_driver.go | 14 ++----- systemd/docker-quobyte-plugin.service | 2 +- systemd/docker-quobyte.env.sample | 16 +++++++- 5 files changed, 83 insertions(+), 61 deletions(-) diff --git a/README.md b/README.md index ff1eac6..9842392 100644 --- a/README.md +++ b/README.md @@ -47,33 +47,36 @@ $ systemctl enable docker-quobyte-plugin $ systemctl status docker-quobyte-plugin ``` +### Configuration + +Configuration is done mainly through the systemd environment file: + +``` +# Maximum number of filesystem checks when a Volume is created before returning an error +MAX_FS_CHECKS=5 +# Maximum wait time for filesystem checks to complete when a Volume is created before returning an error +MAX_WAIT_TIME=30 +# Group to create the unix socket +SOCKET_GROUP=root +QUOBYTE_API_URL=http://localhost:7860 +QUOBYTE_API_PASSWORD=quobyte +QUOBYTE_API_USER=admin +QUOBYTE_MOUNT_PATH=/run/docker/quobyte/mnt +QUOBYTE_MOUNT_OPTIONS=-o user_xattr +QUOBYTE_REGISTRY=localhost:7861 +# ID of the Quobyte tenant in whose domain volumes are managed by this plugin +QUOBYTE_TENANT_ID=replace_me +# Default volume config for new volumes, can be overridden via --opt flag 'configuration_name' +QUOBYTE_VOLUME_CONFIG_NAME=BASE +``` + ### Usage +The cli is faily simple: + ``` $ bin/docker-quobyte-plugin -h Usage of bin/docker-quobyte-plugin: - -api string - URL to the API server(s) in the form http(s)://host[:port][,host:port] or SRV record name (default "http://localhost:7860") - -configuration_name string - Name of the volume configuration of new volumes (default "BASE") - -group string - Group to create the unix socket (default "root") - -max-fs-checks int - Maximimum number of filesystem checks when a Volume is created before returning an error (default 5) - -max-wait-time float - Maximimum wait time for filesystem checks to complete when a Volume is created before returning an error (default 30) - -options string - Fuse options to be used when Quobyte is mounted (default "-o user_xattr") - -password string - Password for the user to connect to the Quobyte API server (default "quobyte") - -path string - Path where Quobyte is mounted on the host (default "/run/docker/quobyte/mnt") - -registry string - URL to the registry server(s) in the form of host[:port][,host:port] or SRV record name (default "localhost:7861") - -tenant_id string - Id of the Quobyte tenant in whose domain the operation takes place (default "no default") - -user string - User to connect to the Quobyte API server (default "root") -version Shows version string ``` @@ -83,13 +86,15 @@ Usage of bin/docker-quobyte-plugin: ### Create a volume ``` -$ docker volume create --driver quobyte --name --opt tenant_id= -# Set user and group of the volume -$ docker volume create --driver quobyte --name --opt user=docker --opt group=docker --opt tenant_id= +$ docker volume create --driver quobyte --name +# Set user, group and specific volume configuration for the new volume +$ docker volume create --driver quobyte --name --opt user=docker --opt group=docker --opt configuration_name=SSD_ONLY ``` ### Delete a volume +__Important__: Be careful when using this. The volume removal allows removing any volume accessible in the configured tenant! + ``` $ docker volume rm ``` diff --git a/main.go b/main.go index 6e94daa..599e7a5 100644 --- a/main.go +++ b/main.go @@ -4,11 +4,14 @@ import ( "flag" "log" "os" + "strconv" "github.com/docker/go-plugins-helpers/volume" ) -const quobyteID string = "quobyte" +const ( + quobyteID string = "quobyte" +) var ( version string @@ -16,42 +19,52 @@ var ( ) func main() { - quobyteMountPath := flag.String("path", "/run/docker/quobyte/mnt", "Path where Quobyte is mounted on the host") - quobyteMountOptions := flag.String("options", "-o user_xattr", "Fuse options to be used when Quobyte is mounted") - - quobyteUser := flag.String("user", "root", "User to connect to the Quobyte API server") - quobytePassword := flag.String("password", "quobyte", "Password for the user to connect to the Quobyte API server") - quobyteConfigName := flag.String("configuration_name", "BASE", "Name of the volume configuration of new volumes") - quobyteAPIURL := flag.String("api", "http://localhost:7860", "URL to the API server(s) in the form http(s)://host[:port][,host:port] or SRV record name") - quobyteRegistry := flag.String("registry", "localhost:7861", "URL to the registry server(s) in the form of host[:port][,host:port] or SRV record name") - quobyteTenantId := flag.String("tenant_id", "no default", "Id of the Quobyte tenant in whose domain the operation takes place") - - group := flag.String("group", "root", "Group to create the unix socket") - maxWaitTime := flag.Float64("max-wait-time", 30, "Maximimum wait time for filesystem checks to complete when a Volume is created before returning an error") - maxFSChecks := flag.Int("max-fs-checks", 5, "Maximimum number of filesystem checks when a Volume is created before returning an error") showVersion := flag.Bool("version", false, "Shows version string") + flag.Parse() + maxFSChecks, _ := strconv.Atoi(os.Getenv("MAX_FS_CHECKS")) + maxWaitTime, _ := strconv.ParseFloat(os.Getenv("MAX_WAIT_TIME"), 64) + socketGroup := os.Getenv("SOCKET_GROUP") + + quobyteAPIURL := os.Getenv("QUOBYTE_API_URL") + quobyteAPIPassword := os.Getenv("QUOBYTE_API_PASSWORD") + quobyteAPIUser := os.Getenv("QUOBYTE_API_USER") + quobyteMountPath := os.Getenv("QUOBYTE_MOUNT_PATH") + quobyteMountOptions := os.Getenv("QUOBYTE_MOUNT_OPTIONS") + quobyteRegistry := os.Getenv("QUOBYTE_REGISTRY") + quobyteTenantID := os.Getenv("QUOBYTE_TENANT_ID") + quobyteVolConfigName := os.Getenv("QUOBYTE_VOLUME_CONFIG_NAME") + log.Printf("\nVariables read from environment:\n"+ + "MAX_FS_CHECKS: %v\nMAX_WAIT_TIME: %v\nSOCKET_GROUP: %s\n"+ + "QUOBYTE_API_URL: %s\nQUOBYTE_API_USER: %s\nQUOBYTE_MOUNT_PATH:"+ + " %s\nQUOBYTE_MOUNT_OPTIONS: %s\nQUOBYTE_REGISTRY: %s\nQUOBYTE_TENANT_ID: "+ + " %s\nQUOBYTE_VOLUME_CONFIG_NAME: %s\n", maxFSChecks, maxWaitTime, + socketGroup, quobyteAPIURL, quobyteAPIUser, + quobyteMountPath, quobyteMountOptions, quobyteRegistry, quobyteTenantID, + quobyteVolConfigName) + if *showVersion { - log.Printf("Version: %s - Revision: %s\n", version, revision) + log.Printf("\nVersion: %s - Revision: %s\n", version, revision) return } - if err := validateAPIURL(*quobyteAPIURL); err != nil { + if err := validateAPIURL(quobyteAPIURL); err != nil { log.Fatalln(err) } - if err := os.MkdirAll(*quobyteMountPath, 0555); err != nil { + if err := os.MkdirAll(quobyteMountPath, 0555); err != nil { log.Println(err.Error()) } - if !isMounted(*quobyteMountPath) { - log.Printf("Mounting Quobyte namespace in %s", *quobyteMountPath) - mountAll(*quobyteMountOptions, *quobyteRegistry, *quobyteMountPath) + if !isMounted(quobyteMountPath) { + log.Printf("Mounting Quobyte namespace in %s", quobyteMountPath) + mountAll(quobyteMountOptions, quobyteRegistry, quobyteMountPath) } - qDriver := newQuobyteDriver(*quobyteAPIURL, *quobyteUser, *quobytePassword, *quobyteMountPath, *maxFSChecks, *maxWaitTime, *quobyteConfigName, *quobyteTenantId) + qDriver := newQuobyteDriver(quobyteAPIURL, quobyteAPIUser, quobyteAPIPassword, + quobyteMountPath, maxFSChecks, maxWaitTime, quobyteVolConfigName, quobyteTenantID) handler := volume.NewHandler(qDriver) - log.Println(handler.ServeUnix(*group, quobyteID)) + log.Println(handler.ServeUnix(socketGroup, quobyteID)) } diff --git a/quobyte_driver.go b/quobyte_driver.go index bf6553e..8aa2aa9 100644 --- a/quobyte_driver.go +++ b/quobyte_driver.go @@ -44,28 +44,20 @@ func (driver quobyteDriver) Create(request volume.Request) volume.Response { defer driver.m.Unlock() user, group := "root", "root" - configurationName := "BASE" + configurationName := driver.configName retryPolicy := "INTERACTIVE" - tenantID := "default" + tenantID := driver.tenantID if usr, ok := request.Options["user"]; ok { user = usr } - if grp, ok := request.Options["group"]; ok { group = grp } - if conf, ok := request.Options["configuration_name"]; ok { configurationName = conf } - if tenant, ok := request.Options["tenant_id"]; ok { - tenantID = tenant - } else { - return volume.Response{Err: "No tenant_id given, cannot create a new volume."} - } - if _, err := driver.client.CreateVolume(&quobyte_api.CreateVolumeRequest{ Name: request.Name, RootUserID: user, @@ -122,7 +114,7 @@ func (driver quobyteDriver) Remove(request volume.Request) volume.Response { driver.m.Lock() defer driver.m.Unlock() - if err := driver.client.DeleteVolumeByName(request.Name, ""); err != nil { + if err := driver.client.DeleteVolumeByName(request.Name, driver.tenantID); err != nil { log.Println(err) return volume.Response{Err: err.Error()} } diff --git a/systemd/docker-quobyte-plugin.service b/systemd/docker-quobyte-plugin.service index d58d6c7..17fa943 100644 --- a/systemd/docker-quobyte-plugin.service +++ b/systemd/docker-quobyte-plugin.service @@ -7,7 +7,7 @@ Requires=docker.service [Service] EnvironmentFile=/etc/quobyte/docker-quobyte.env -ExecStart=/usr/local/bin/docker-quobyte-plugin --user ${QUOBYTE_API_USER} --password ${QUOBYTE_API_PASSWORD} --api ${QUOBYTE_API_URL} --registry ${QUOBYTE_REGISTRY} --configuration_name ${QUOBYTE_CONFIGURATION_NAME} +ExecStart=/usr/local/bin/docker-quobyte-plugin [Install] WantedBy=multi-user.target diff --git a/systemd/docker-quobyte.env.sample b/systemd/docker-quobyte.env.sample index 43aca89..b121064 100644 --- a/systemd/docker-quobyte.env.sample +++ b/systemd/docker-quobyte.env.sample @@ -1,4 +1,16 @@ -QUOBYTE_API_USER=admin -QUOBYTE_API_PASSWORD=quobyte +# Maximum number of filesystem checks when a Volume is created before returning an error +MAX_FS_CHECKS=5 +# Maximum wait time for filesystem checks to complete when a Volume is created before returning an error +MAX_WAIT_TIME=30 +# Group to create the unix socket +SOCKET_GROUP=root QUOBYTE_API_URL=http://localhost:7860 +QUOBYTE_API_PASSWORD=quobyte +QUOBYTE_API_USER=admin +QUOBYTE_MOUNT_PATH=/run/docker/quobyte/mnt +QUOBYTE_MOUNT_OPTIONS=-o user_xattr QUOBYTE_REGISTRY=localhost:7861 +# ID of the Quobyte tenant in whose domain volumes are managed by this plugin +QUOBYTE_TENANT_ID=replace_me +# Default volume config for new volumes, can be overridden via --opt flag 'configuration_name' +QUOBYTE_VOLUME_CONFIG_NAME=BASE From 17251f806fee98baa55388f896c4ada3a9b59520 Mon Sep 17 00:00:00 2001 From: Silvan Kaiser Date: Wed, 20 Dec 2017 09:47:38 +0100 Subject: [PATCH 2/2] Reintroduces parameters for backward compatibility. --- README.md | 32 +++++++++++++++++++--- main.go | 79 +++++++++++++++++++++++++++++++++++++------------------ 2 files changed, 82 insertions(+), 29 deletions(-) diff --git a/README.md b/README.md index 9842392..799cd44 100644 --- a/README.md +++ b/README.md @@ -72,14 +72,38 @@ QUOBYTE_VOLUME_CONFIG_NAME=BASE ### Usage -The cli is faily simple: +The cli allows passing all options.: ``` -$ bin/docker-quobyte-plugin -h +$ bin/docker-quobyte-plugin -h Usage of bin/docker-quobyte-plugin: + -api string + URL to the API server(s) in the form http(s)://host[:port][,host:port] or SRV record name (default "http://localhost:7860") + -configuration_name string + Name of the volume configuration of new volumes (default "BASE") + -group string + Group to create the unix socket (default "root") + -max-fs-checks int + Maximimum number of filesystem checks when a Volume is created before returning an error (default 5) + -max-wait-time float + Maximimum wait time for filesystem checks to complete when a Volume is created before returning an error (default 64) + -options string + Fuse options to be used when Quobyte is mounted (default "-o user_xattr") + -password string + Password for the user to connect to the Quobyte API server (default "quobyte") + -path string + Path where Quobyte is mounted on the host (default "/run/docker/quobyte/mnt") + -registry string + URL to the registry server(s) in the form of host[:port][,host:port] or SRV record name (default "localhost:7861") + -tenant_id string + Id of the Quobyte tenant in whose domain the operation takes place (default "NO-DEFAULT-CHANGE-ME") + -user string + User to connect to the Quobyte API server (default "admin") -version - Shows version string + Shows version string ``` + __Please note__ that using the environment file for setting the password is strongly encouraged over using the cli parameter. + ## Examples @@ -87,7 +111,7 @@ Usage of bin/docker-quobyte-plugin: ``` $ docker volume create --driver quobyte --name -# Set user, group and specific volume configuration for the new volume +# Set user, group and a non default volume configuration for the new volume $ docker volume create --driver quobyte --name --opt user=docker --opt group=docker --opt configuration_name=SSD_ONLY ``` diff --git a/main.go b/main.go index 599e7a5..aed3dc5 100644 --- a/main.go +++ b/main.go @@ -18,53 +18,82 @@ var ( revision string ) +func getEnvWithDefault(key, fallback string) string { + if value, ok := os.LookupEnv(key); ok { + return value + } + return fallback +} + func main() { + + maxFSChecksDefaultStr := getEnvWithDefault("MAX_FS_CHECKS", "5") + maxFSChecksDefault, _ := strconv.Atoi(maxFSChecksDefaultStr) + maxWaitTimeDefaultStr := getEnvWithDefault("MAX_WAIT_TIME", "64") + maxWaitTimeDefault, _ := strconv.ParseFloat(maxWaitTimeDefaultStr, 64) + quobyteAPIURLDefault := getEnvWithDefault("QUOBYTE_API_URL", "http://localhost:7860") + quobyteAPIPasswordDefault := getEnvWithDefault("QUOBYTE_API_PASSWORD", "quobyte") + quobyteAPIUserDefault := getEnvWithDefault("QUOBYTE_API_USER", "admin") + quobyteMountPathDefault := getEnvWithDefault("QUOBYTE_MOUNT_PATH", "/run/docker/quobyte/mnt") + quobyteMountOptionsDefault := getEnvWithDefault("QUOBYTE_MOUNT_OPTIONS", "-o user_xattr") + quobyteRegistryDefault := getEnvWithDefault("QUOBYTE_REGISTRY", "localhost:7861") + quobyteTenantIDDefault := getEnvWithDefault("QUOBYTE_TENANT_ID", "NO-DEFAULT-CHANGE-ME") + quobyteVolConfigNameDefault := getEnvWithDefault("QUOBYTE_VOLUME_CONFIG_NAME", "BASE") + socketGroupDefault := getEnvWithDefault("SOCKET_GROUP", "root") + + maxFSChecks := flag.Int("max-fs-checks", maxFSChecksDefault, + "Maximimum number of filesystem checks when a Volume is created before returning an error") + maxWaitTime := flag.Float64("max-wait-time", maxWaitTimeDefault, + "Maximimum wait time for filesystem checks to complete when a Volume is created before returning an error") + quobyteAPIUser := flag.String("user", quobyteAPIUserDefault, "User to connect to the Quobyte API server") + quobyteAPIPassword := flag.String("password", quobyteAPIPasswordDefault, + "Password for the user to connect to the Quobyte API server") + quobyteAPIURL := flag.String("api", quobyteAPIURLDefault, + "URL to the API server(s) in the form http(s)://host[:port][,host:port] or SRV record name") + quobyteMountPath := flag.String("path", quobyteMountPathDefault, "Path where Quobyte is mounted on the host") + quobyteMountOptions := flag.String("options", quobyteMountOptionsDefault, + "Fuse options to be used when Quobyte is mounted") + quobyteRegistry := flag.String("registry", quobyteRegistryDefault, + "URL to the registry server(s) in the form of host[:port][,host:port] or SRV record name") + quobyteTenantID := flag.String("tenant_id", quobyteTenantIDDefault, + "Id of the Quobyte tenant in whose domain the operation takes place") + quobyteVolConfigName := flag.String("configuration_name", quobyteVolConfigNameDefault, + "Name of the volume configuration of new volumes") + socketGroup := flag.String("group", socketGroupDefault, "Group to create the unix socket") showVersion := flag.Bool("version", false, "Shows version string") flag.Parse() - maxFSChecks, _ := strconv.Atoi(os.Getenv("MAX_FS_CHECKS")) - maxWaitTime, _ := strconv.ParseFloat(os.Getenv("MAX_WAIT_TIME"), 64) - socketGroup := os.Getenv("SOCKET_GROUP") - - quobyteAPIURL := os.Getenv("QUOBYTE_API_URL") - quobyteAPIPassword := os.Getenv("QUOBYTE_API_PASSWORD") - quobyteAPIUser := os.Getenv("QUOBYTE_API_USER") - quobyteMountPath := os.Getenv("QUOBYTE_MOUNT_PATH") - quobyteMountOptions := os.Getenv("QUOBYTE_MOUNT_OPTIONS") - quobyteRegistry := os.Getenv("QUOBYTE_REGISTRY") - quobyteTenantID := os.Getenv("QUOBYTE_TENANT_ID") - quobyteVolConfigName := os.Getenv("QUOBYTE_VOLUME_CONFIG_NAME") - log.Printf("\nVariables read from environment:\n"+ + log.Printf("\nVariables read:\n"+ "MAX_FS_CHECKS: %v\nMAX_WAIT_TIME: %v\nSOCKET_GROUP: %s\n"+ "QUOBYTE_API_URL: %s\nQUOBYTE_API_USER: %s\nQUOBYTE_MOUNT_PATH:"+ " %s\nQUOBYTE_MOUNT_OPTIONS: %s\nQUOBYTE_REGISTRY: %s\nQUOBYTE_TENANT_ID: "+ - " %s\nQUOBYTE_VOLUME_CONFIG_NAME: %s\n", maxFSChecks, maxWaitTime, - socketGroup, quobyteAPIURL, quobyteAPIUser, - quobyteMountPath, quobyteMountOptions, quobyteRegistry, quobyteTenantID, - quobyteVolConfigName) + " %s\nQUOBYTE_VOLUME_CONFIG_NAME: %s\n", *maxFSChecks, *maxWaitTime, + *socketGroup, *quobyteAPIURL, *quobyteAPIUser, + *quobyteMountPath, *quobyteMountOptions, *quobyteRegistry, *quobyteTenantID, + *quobyteVolConfigName) if *showVersion { log.Printf("\nVersion: %s - Revision: %s\n", version, revision) return } - if err := validateAPIURL(quobyteAPIURL); err != nil { + if err := validateAPIURL(*quobyteAPIURL); err != nil { log.Fatalln(err) } - if err := os.MkdirAll(quobyteMountPath, 0555); err != nil { + if err := os.MkdirAll(*quobyteMountPath, 0555); err != nil { log.Println(err.Error()) } - if !isMounted(quobyteMountPath) { - log.Printf("Mounting Quobyte namespace in %s", quobyteMountPath) - mountAll(quobyteMountOptions, quobyteRegistry, quobyteMountPath) + if !isMounted(*quobyteMountPath) { + log.Printf("Mounting Quobyte namespace in %s", *quobyteMountPath) + mountAll(*quobyteMountOptions, *quobyteRegistry, *quobyteMountPath) } - qDriver := newQuobyteDriver(quobyteAPIURL, quobyteAPIUser, quobyteAPIPassword, - quobyteMountPath, maxFSChecks, maxWaitTime, quobyteVolConfigName, quobyteTenantID) + qDriver := newQuobyteDriver(*quobyteAPIURL, *quobyteAPIUser, *quobyteAPIPassword, + *quobyteMountPath, *maxFSChecks, *maxWaitTime, *quobyteVolConfigName, *quobyteTenantID) handler := volume.NewHandler(qDriver) - log.Println(handler.ServeUnix(socketGroup, quobyteID)) + log.Println(handler.ServeUnix(*socketGroup, quobyteID)) }