Переглянути джерело

create crowdsec group for metabase and crowdsec.db (#606)

AlteredCoder 4 роки тому
батько
коміт
dae4458a6f
5 змінених файлів з 123 додано та 66 видалено
  1. 63 7
      cmd/crowdsec-cli/dashboard.go
  2. 1 1
      pkg/database/database.go
  3. 23 21
      pkg/metabase/container.go
  4. 18 16
      pkg/metabase/metabase.go
  5. 18 21
      wizard.sh

+ 63 - 7
cmd/crowdsec-cli/dashboard.go

@@ -3,7 +3,10 @@ package main
 import (
 	"fmt"
 	"os"
+	"os/exec"
+	"os/user"
 	"path/filepath"
+	"strconv"
 
 	"github.com/AlecAivazis/survey/v2"
 	"github.com/crowdsecurity/crowdsec/pkg/metabase"
@@ -24,6 +27,7 @@ var (
 	metabaseListenAddress = "127.0.0.1"
 	metabaseListenPort    = "3000"
 	metabaseContainerID   = "/crowdsec-metabase"
+	crowdsecGroup         = "crowdsec"
 
 	forceYes bool
 
@@ -72,7 +76,48 @@ cscli dashboard setup -l 0.0.0.0 -p 443 --password <password>
 			if metabasePassword == "" {
 				metabasePassword = generatePassword(16)
 			}
-			mb, err := metabase.SetupMetabase(csConfig.API.Server.DbConfig, metabaseListenAddress, metabaseListenPort, metabaseUser, metabasePassword, metabaseDbPath)
+			var answer bool
+			groupExist := false
+			dockerGroup, err := user.LookupGroup(crowdsecGroup)
+			if err == nil {
+				groupExist = true
+			}
+			if !forceYes && !groupExist {
+				prompt := &survey.Confirm{
+					Message: fmt.Sprintf("For metabase docker to be able to access SQLite file we need to add a new group called '%s' to the system, is it ok for you ?", crowdsecGroup),
+					Default: true,
+				}
+				if err := survey.AskOne(prompt, &answer); err != nil {
+					log.Fatalf("unable to ask to force: %s", err)
+				}
+			}
+			if !answer && !forceYes && !groupExist {
+				log.Fatalf("unable to continue without creating '%s' group", crowdsecGroup)
+			}
+			if !groupExist {
+				groupAddCmd, err := exec.LookPath("groupadd")
+				if err != nil {
+					log.Fatalf("unable to find 'groupadd' command, can't continue")
+				}
+
+				groupAdd := &exec.Cmd{Path: groupAddCmd, Args: []string{groupAddCmd, crowdsecGroup}}
+				if err := groupAdd.Run(); err != nil {
+					log.Fatalf("unable to add group '%s': %s", dockerGroup, err)
+				}
+				dockerGroup, err = user.LookupGroup(crowdsecGroup)
+				if err != nil {
+					log.Fatalf("unable to lookup '%s' group: %+v", dockerGroup, err)
+				}
+			}
+			intID, err := strconv.Atoi(dockerGroup.Gid)
+			if err != nil {
+				log.Fatalf("unable to convert group ID to int: %s", err)
+			}
+			if err := os.Chown(csConfig.DbConfig.DbPath, 0, intID); err != nil {
+				log.Fatalf("unable to chown sqlite db file '%s': %s", csConfig.DbConfig.DbPath, err)
+			}
+
+			mb, err := metabase.SetupMetabase(csConfig.API.Server.DbConfig, metabaseListenAddress, metabaseListenPort, metabaseUser, metabasePassword, metabaseDbPath, dockerGroup.Gid)
 			if err != nil {
 				log.Fatalf(err.Error())
 			}
@@ -92,6 +137,7 @@ cscli dashboard setup -l 0.0.0.0 -p 443 --password <password>
 	cmdDashSetup.Flags().StringVarP(&metabaseDbPath, "dir", "d", "", "Shared directory with metabase container.")
 	cmdDashSetup.Flags().StringVarP(&metabaseListenAddress, "listen", "l", metabaseListenAddress, "Listen address of container")
 	cmdDashSetup.Flags().StringVarP(&metabaseListenPort, "port", "p", metabaseListenPort, "Listen port of container")
+	cmdDashSetup.Flags().BoolVarP(&forceYes, "yes", "y", false, "force  yes")
 	//cmdDashSetup.Flags().StringVarP(&metabaseUser, "user", "u", "crowdsec@crowdsec.net", "metabase user")
 	cmdDashSetup.Flags().StringVar(&metabasePassword, "password", "", "metabase password")
 
@@ -149,34 +195,44 @@ cscli dashboard remove --force
 					log.Fatalf("unable to ask to force: %s", err)
 				}
 			}
-
 			if answer {
 				if metabase.IsContainerExist(metabaseContainerID) {
 					log.Debugf("Stopping container %s", metabaseContainerID)
 					if err := metabase.StopContainer(metabaseContainerID); err != nil {
 						log.Warningf("unable to stop container '%s': %s", metabaseContainerID, err)
 					}
+					dockerGroup, err := user.LookupGroup(crowdsecGroup)
+					if err == nil { // if group exist, remove it
+						groupDelCmd, err := exec.LookPath("groupdel")
+						if err != nil {
+							log.Fatalf("unable to find 'groupdel' command, can't continue")
+						}
+
+						groupDel := &exec.Cmd{Path: groupDelCmd, Args: []string{groupDelCmd, crowdsecGroup}}
+						if err := groupDel.Run(); err != nil {
+							log.Errorf("unable to delete group '%s': %s", dockerGroup, err)
+						}
+					}
 					log.Debugf("Removing container %s", metabaseContainerID)
 					if err := metabase.RemoveContainer(metabaseContainerID); err != nil {
 						log.Warningf("unable to remove container '%s': %s", metabaseContainerID, err)
 					}
 					log.Infof("container %s stopped & removed", metabaseContainerID)
 				}
-				log.Debugf("Removing database %s", csConfig.ConfigPaths.DataDir)
+				log.Debugf("Removing metabase db %s", csConfig.ConfigPaths.DataDir)
 				if err := metabase.RemoveDatabase(csConfig.ConfigPaths.DataDir); err != nil {
 					log.Warningf("failed to remove metabase internal db : %s", err)
 				}
 				if force {
-					log.Debugf("Removing image %s", metabaseImage)
-					if err := metabase.RemoveImageContainer(metabaseImage); err != nil {
-            log.Warningf("Failed to remove metabase container %s : %s", metabaseImage, err)
+					if err := metabase.RemoveImageContainer(); err != nil {
+						log.Fatalf("removing docker image: %s", err)
 
 					}
 				}
 			}
 		},
 	}
-	cmdDashRemove.Flags().BoolVarP(&force, "force", "f", false, "Force remove : stop the container if running and remove.")
+	cmdDashRemove.Flags().BoolVarP(&force, "force", "f", false, "Remove also the metabase image")
 	cmdDashRemove.Flags().BoolVarP(&forceYes, "yes", "y", false, "force  yes")
 	cmdDashboard.AddCommand(cmdDashRemove)
 

+ 1 - 1
pkg/database/database.go

@@ -42,7 +42,7 @@ func NewClient(config *csconfig.DatabaseCfg) (*Client, error) {
 				return &Client{}, errors.Wrapf(err, "failed to create SQLite database file %q", config.DbPath)
 			}
 		} else { /*ensure file perms*/
-			if err := os.Chmod(config.DbPath, 0600); err != nil {
+			if err := os.Chmod(config.DbPath, 0660); err != nil {
 				return &Client{}, fmt.Errorf("unable to set perms on %s: %v", config.DbPath, err)
 			}
 		}

+ 23 - 21
pkg/metabase/container.go

@@ -16,29 +16,31 @@ import (
 )
 
 type Container struct {
-	ListenAddr   string
-	ListenPort   string
-	SharedFolder string
-	Image        string
-	Name         string
-	ID           string
-	CLI          *client.Client
-	MBDBUri      string
+	ListenAddr    string
+	ListenPort    string
+	SharedFolder  string
+	Image         string
+	Name          string
+	ID            string
+	CLI           *client.Client
+	MBDBUri       string
+	DockerGroupID string
 }
 
-func NewContainer(listenAddr string, listenPort string, sharedFolder string, name string, image string, mbDBURI string) (*Container, error) {
+func NewContainer(listenAddr string, listenPort string, sharedFolder string, name string, image string, mbDBURI string, dockerGroupID string) (*Container, error) {
 	cli, err := client.NewClientWithOpts(client.FromEnv, client.WithAPIVersionNegotiation())
 	if err != nil {
 		return nil, fmt.Errorf("failed to create docker client : %s", err)
 	}
 	return &Container{
-		ListenAddr:   listenAddr,
-		ListenPort:   listenPort,
-		SharedFolder: sharedFolder,
-		Image:        image,
-		Name:         name,
-		CLI:          cli,
-		MBDBUri:      mbDBURI,
+		ListenAddr:    listenAddr,
+		ListenPort:    listenPort,
+		SharedFolder:  sharedFolder,
+		Image:         image,
+		Name:          name,
+		CLI:           cli,
+		MBDBUri:       mbDBURI,
+		DockerGroupID: dockerGroupID,
 	}, nil
 }
 
@@ -84,12 +86,12 @@ func (c *Container) Create() error {
 		env = append(env, c.MBDBUri)
 	}
 
+	env = append(env, fmt.Sprintf("MGID=%s", c.DockerGroupID))
 	dockerConfig := &container.Config{
 		Image: c.Image,
 		Tty:   true,
 		Env:   env,
 	}
-
 	os := runtime.GOOS
 	switch os {
 	case "linux":
@@ -158,15 +160,15 @@ func RemoveContainer(name string) error {
 	return nil
 }
 
-func RemoveImageContainer(image string) error {
+func RemoveImageContainer() error {
 	cli, err := client.NewClientWithOpts(client.FromEnv, client.WithAPIVersionNegotiation())
 	if err != nil {
 		return fmt.Errorf("failed to create docker client : %s", err)
 	}
 	ctx := context.Background()
-	log.Printf("Removing docker metabase %s", image)
-	if err := cli.ContainerRemove(ctx, image, types.ContainerRemoveOptions{}); err != nil {
-		return fmt.Errorf("failed remove container %s : %s", image, err)
+	log.Printf("Removing docker image '%s'", metabaseImage)
+	if _, err := cli.ImageRemove(ctx, metabaseImage, types.ImageRemoveOptions{}); err != nil {
+		return fmt.Errorf("failed remove image container %s : %s", metabaseImage, err)
 	}
 	return nil
 }

+ 18 - 16
pkg/metabase/metabase.go

@@ -28,13 +28,14 @@ type Metabase struct {
 }
 
 type Config struct {
-	Database   *csconfig.DatabaseCfg `yaml:"database"`
-	ListenAddr string                `yaml:"listen_addr"`
-	ListenPort string                `yaml:"listen_port"`
-	ListenURL  string                `yaml:"listen_url"`
-	Username   string                `yaml:"username"`
-	Password   string                `yaml:"password"`
-	DBPath     string                `yaml:"metabase_db_path"`
+	Database      *csconfig.DatabaseCfg `yaml:"database"`
+	ListenAddr    string                `yaml:"listen_addr"`
+	ListenPort    string                `yaml:"listen_port"`
+	ListenURL     string                `yaml:"listen_url"`
+	Username      string                `yaml:"username"`
+	Password      string                `yaml:"password"`
+	DBPath        string                `yaml:"metabase_db_path"`
+	DockerGroupID string                `yaml:"-"`
 }
 
 var (
@@ -70,7 +71,7 @@ func (m *Metabase) Init() error {
 	}
 	m.Database, err = NewDatabase(m.Config.Database, m.Client, remoteDBAddr)
 
-	m.Container, err = NewContainer(m.Config.ListenAddr, m.Config.ListenPort, m.Config.DBPath, containerName, metabaseImage, DBConnectionURI)
+	m.Container, err = NewContainer(m.Config.ListenAddr, m.Config.ListenPort, m.Config.DBPath, containerName, metabaseImage, DBConnectionURI, m.Config.DockerGroupID)
 	if err != nil {
 		return errors.Wrap(err, "container init")
 	}
@@ -123,16 +124,17 @@ func (m *Metabase) LoadConfig(configPath string) error {
 
 }
 
-func SetupMetabase(dbConfig *csconfig.DatabaseCfg, listenAddr string, listenPort string, username string, password string, mbDBPath string) (*Metabase, error) {
+func SetupMetabase(dbConfig *csconfig.DatabaseCfg, listenAddr string, listenPort string, username string, password string, mbDBPath string, dockerGroupID string) (*Metabase, error) {
 	metabase := &Metabase{
 		Config: &Config{
-			Database:   dbConfig,
-			ListenAddr: listenAddr,
-			ListenPort: listenPort,
-			Username:   username,
-			Password:   password,
-			ListenURL:  fmt.Sprintf("http://%s:%s", listenAddr, listenPort),
-			DBPath:     mbDBPath,
+			Database:      dbConfig,
+			ListenAddr:    listenAddr,
+			ListenPort:    listenPort,
+			Username:      username,
+			Password:      password,
+			ListenURL:     fmt.Sprintf("http://%s:%s", listenAddr, listenPort),
+			DBPath:        mbDBPath,
+			DockerGroupID: dockerGroupID,
 		},
 	}
 	if err := metabase.Init(); err != nil {

+ 18 - 21
wizard.sh

@@ -302,47 +302,44 @@ check_cs_version () {
     NEW_PATCH_VERSION=$(echo $NEW_CS_VERSION | cut -d'.' -f3)
 
     if [[ $NEW_MAJOR_VERSION -gt $CURRENT_MAJOR_VERSION ]]; then
-        log_warn "new version ($NEW_CS_VERSION) is a major, you need to follow documentation to upgrade !"
-        echo ""
-        echo "Please follow : https://docs.crowdsec.net/Crowdsec/v1/migration/"
         if [[ ${FORCE_MODE} == "false" ]]; then
+            log_warn "new version ($NEW_CS_VERSION) is a major, you need to follow documentation to upgrade !"
+            echo ""
+            echo "Please follow : https://docs.crowdsec.net/Crowdsec/v1/migration/"
             exit 1
         fi
     elif [[ $NEW_MINOR_VERSION -gt $CURRENT_MINOR_VERSION ]] ; then
         log_warn "new version ($NEW_CS_VERSION) is a minor upgrade !"
-
         if [[ $ACTION != "upgrade" ]] ; then 
-            echo ""
-            echo "We recommand to upgrade with : sudo ./wizard.sh --upgrade "
-            echo "If you want to $ACTION anyway, please use '--force'."
-            echo ""
-            echo "Run : sudo ./wizard.sh --$ACTION --force"
             if [[ ${FORCE_MODE} == "false" ]]; then
+                echo ""
+                echo "We recommand to upgrade with : sudo ./wizard.sh --upgrade "
+                echo "If you want to $ACTION anyway, please use '--force'."
+                echo ""
+                echo "Run : sudo ./wizard.sh --$ACTION --force"
                 exit 1
             fi
         fi
     elif [[ $NEW_PATCH_VERSION -gt $CURRENT_PATCH_VERSION ]] ; then
         log_warn "new version ($NEW_CS_VERSION) is a patch !"
-
         if [[ $ACTION != "binupgrade" ]] ; then 
-            echo ""
-            echo "We recommand to upgrade binaries only : sudo ./wizard.sh --binupgrade "
-            echo "If you want to $ACTION anyway, please use '--force'."
-            echo ""
-            echo "Run : sudo ./wizard.sh --$ACTION --force"
             if [[ ${FORCE_MODE} == "false" ]]; then
+                echo ""
+                echo "We recommand to upgrade binaries only : sudo ./wizard.sh --binupgrade "
+                echo "If you want to $ACTION anyway, please use '--force'."
+                echo ""
+                echo "Run : sudo ./wizard.sh --$ACTION --force"
                 exit 1
             fi
         fi
     elif [[ $NEW_MINOR_VERSION -eq $CURRENT_MINOR_VERSION ]]; then
         log_warn "new version ($NEW_CS_VERSION) is same as current version ($CURRENT_CS_VERSION) !"
-
-        echo ""
-        echo "We recommand to $ACTION only if it's an higher version. "
-        echo "If it's an RC version (vX.X.X-rc) you can upgrade it using '--force'."
-        echo ""
-        echo "Run : sudo ./wizard.sh --$ACTION --force"
         if [[ ${FORCE_MODE} == "false" ]]; then
+            echo ""
+            echo "We recommand to $ACTION only if it's an higher version. "
+            echo "If it's an RC version (vX.X.X-rc) you can upgrade it using '--force'."
+            echo ""
+            echo "Run : sudo ./wizard.sh --$ACTION --force"
             exit 1
         fi
     fi