Skip to content

Commit

Permalink
Added a frontend config wizard. (#1018)
Browse files Browse the repository at this point in the history
The same configuration can be used for multiple frontends by selecting
the frontend name.
  • Loading branch information
scudette authored Apr 14, 2021
1 parent f684ef8 commit ce78aec
Show file tree
Hide file tree
Showing 12 changed files with 223 additions and 167 deletions.
7 changes: 4 additions & 3 deletions api/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ package api
import (
"crypto/tls"
"crypto/x509"
"errors"
"fmt"
"net"
"net/http"
Expand All @@ -30,6 +29,8 @@ import (
"sync"
"time"

errors "github.com/pkg/errors"

"github.com/Velocidex/ordereddict"
"github.com/golang/protobuf/ptypes/empty"
"github.com/prometheus/client_golang/prometheus/promhttp"
Expand Down Expand Up @@ -1128,15 +1129,15 @@ func startAPIServer(

lis, err := net.Listen(config_obj.API.BindScheme, bind_addr)
if err != nil {
return err
return errors.WithStack(err)
}

// Use the server certificate to secure the gRPC connection.
cert, err := tls.X509KeyPair(
[]byte(config_obj.Frontend.Certificate),
[]byte(config_obj.Frontend.PrivateKey))
if err != nil {
return err
return errors.WithStack(err)
}

// Authenticate API clients using certificates.
Expand Down
6 changes: 3 additions & 3 deletions api/builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -333,7 +333,7 @@ func StartFrontendHttps(

err := server.ListenAndServeTLS("", "")
if err != nil && err != http.ErrServerClosed {
server_obj.Error("Frontend server error %v", err)
server_obj.Fatal("Frontend server error %v", err)
}
}()

Expand Down Expand Up @@ -398,7 +398,7 @@ func StartFrontendPlainHttp(

err := server.ListenAndServe()
if err != nil && err != http.ErrServerClosed {
server_obj.Error("Frontend server error %v", err)
server_obj.Fatal("Frontend server error %v", err)
}
}()

Expand Down Expand Up @@ -487,7 +487,7 @@ func StartFrontendWithAutocert(

err := server.ListenAndServeTLS("", "")
if err != nil && err != http.ErrServerClosed {
server_obj.Error("Frontend server error", err)
server_obj.Fatal("Frontend server error", err)
}
}()

Expand Down
13 changes: 11 additions & 2 deletions bin/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,10 @@ var (
"config", "Manipulate the configuration.")
config_show_command = config_command.Command(
"show", "Show the current config.")

config_show_command_json = config_show_command.Flag(
"json", "Show the config as JSON").Bool()

config_client_command = config_command.Command(
"client", "Dump the client's config file.")

Expand Down Expand Up @@ -89,11 +93,16 @@ func doShowConfig() {
config_obj, err := DefaultConfigLoader.LoadAndValidate()
kingpin.FatalIfError(err, "Unable to load config.")

res, err := yaml.Marshal(config_obj)
if err != nil {
if *config_show_command_json {
serialized, err := json.Marshal(config_obj)
kingpin.FatalIfError(err, "Unable to encode config.")
fmt.Printf("%v", string(serialized))
return
}

res, err := yaml.Marshal(config_obj)
kingpin.FatalIfError(err, "Unable to encode config.")

fmt.Printf("%v", string(res))
}

Expand Down
80 changes: 52 additions & 28 deletions bin/config_frontend.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,9 @@
package main

import (
"os"
"fmt"

"github.com/Velocidex/survey"
"github.com/Velocidex/yaml/v2"
proto "github.com/golang/protobuf/proto"
kingpin "gopkg.in/alecthomas/kingpin.v2"
config_proto "www.velocidex.com/golang/velociraptor/config/proto"
)
Expand All @@ -17,36 +15,76 @@ var (
"frontend", "Add a new frontend configuration")
)

func reportCurrentSetup(config_obj *config_proto.Config) string {
result := fmt.Sprintf("Master frontend is at %v:%v \n\n",
config_obj.Frontend.Hostname, config_obj.Frontend.BindPort)

if len(config_obj.ExtraFrontends) > 0 {
result += fmt.Sprintf("Currently configured %v minion frontends\n\n", len(config_obj.ExtraFrontends))
for idx, frontend := range config_obj.ExtraFrontends {
result += fmt.Sprintf("Minion %v: %v:%v\n", idx+1,
frontend.Hostname, frontend.BindPort)
}
}

return result + "\n"
}

func doConfigFrontend() {
config_obj, err := DefaultConfigLoader.WithRequiredFrontend().LoadAndValidate()
kingpin.FatalIfError(err, "Unable to load config.")

/*
if config_obj.Datastore.Implementation == "FileBaseDataStore" {
kingpin.Fatalf("Current FileStore implementation is %v which does not support multiple frontends.", config_obj.Datastore.Implementation)
}
*/
doit := false
kingpin.FatalIfError(survey.AskOne(&survey.Confirm{
Message: `
Welcome to the Velociraptor configuration generator
---------------------------------------------------
I will be adding an extra minion frontend to the configuration. I will not be changing the master frontend configuration at all.
` + reportCurrentSetup(config_obj) + `
frontend_config := &config_proto.FrontendConfig{}
proto.Merge(frontend_config, config_obj.Frontend)
Do you wish to continue?`,
},
&doit, survey.WithValidator(survey.Required)), "")

if !doit {
return
}

// Create a new frontend config
frontend_config := &config_proto.FrontendConfig{
BindAddress: "0.0.0.0",
}

// Set a better default for the url question
url_question.Default = config_obj.Frontend.Hostname

// Figure out the install type
if config_obj.Client.UseSelfSignedSsl {
kingpin.FatalIfError(survey.Ask([]*survey.Question{
{Name: "Hostname", Prompt: url_question},
{Name: "BindPort", Prompt: port_question},
{Name: "Hostname", Prompt: &survey.Input{
Message: "What is the public name of the minion frontend (e.g. 192.168.1.22 or ns2.example.com)",
Default: config_obj.Frontend.Hostname,
}},
{Name: "BindPort", Prompt: &survey.Input{
Message: "What port should this frontend listen on?",
Default: fmt.Sprintf("%v", config_obj.Frontend.BindPort),
}},
}, frontend_config, survey.WithValidator(survey.Required)), "")

// Add the frontend into the client's configuration.
config_obj.Client.ServerUrls = append(config_obj.Client.ServerUrls,
fmt.Sprintf("https://%v:%v/", frontend_config.Hostname,
frontend_config.BindPort))
} else {
kingpin.FatalIfError(survey.AskOne(url_question, &frontend_config.Hostname,
survey.WithValidator(survey.Required)), "")
}

if config_obj.Frontend.DynDns != nil &&
config_obj.Frontend.DynDns.DdnsUsername != "" {
kingpin.FatalIfError(dynDNSConfig(config_obj), "")
kingpin.FatalIfError(dynDNSConfig(config_obj.Frontend), "")
}

// Add the additional frontend.
Expand All @@ -57,19 +95,5 @@ func doConfigFrontend() {
// call it.
config_obj.API.BindAddress = "0.0.0.0"

path := ""
err = survey.AskOne(output_question, &path,
survey.WithValidator(survey.Required))
kingpin.FatalIfError(err, "Question")

res, err := yaml.Marshal(config_obj)
if err != nil {
kingpin.FatalIfError(err, "Unable to encode config.")
}

fd, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600)
kingpin.FatalIfError(err, "Open file %s", path)
_, err = fd.Write(res)
kingpin.FatalIfError(err, "Write file %s", path)
fd.Close()
storeServerConfig(config_obj)
}
Loading

0 comments on commit ce78aec

Please sign in to comment.