Files
redoctober/cmd/ro/main.go
Kyle Isom cb16b159f3 Add a status endpoint to the server.
This pull request adds a status endpoint to the Red October server; as
of this pull request, the status endpoint only returns the current
delegation persistence state. The HTTP UI has not been updated, as
this is scoped out for a future request; however, the CLI utility now
features a status command to fetch this information.
2016-07-13 10:20:06 -07:00

313 lines
7.5 KiB
Go

package main
import (
"encoding/base64"
"encoding/json"
"flag"
"fmt"
"io/ioutil"
"log"
"os"
"strings"
"time"
"github.com/cloudflare/redoctober/client"
"github.com/cloudflare/redoctober/cmd/ro/gopass"
"github.com/cloudflare/redoctober/core"
"github.com/cloudflare/redoctober/order"
)
var action, user, pswd, userEnv, pswdEnv, server, caPath string
var owners, lefters, righters, inPath, labels, outPath, outEnv string
var uses, minUsers int
var duration, users string
var pollInterval time.Duration
type command struct {
Run func()
Desc string
}
var roServer *client.RemoteServer
var commandSet = map[string]command{
"create": command{Run: runCreate, Desc: "create a user account"},
"summary": command{Run: runSummary, Desc: "list the user and delegation summary"},
"delegate": command{Run: runDelegate, Desc: "do decryption delegation"},
"encrypt": command{Run: runEncrypt, Desc: "encrypt a file"},
"decrypt": command{Run: runDecrypt, Desc: "decrypt a file"},
"re-encrypt": command{Run: runReEncrypt, Desc: "re-encrypt a file"},
"order": command{Run: runOrder, Desc: "place an order for delegations"},
"owners": command{Run: runOwner, Desc: "show owners list"},
"status": command{Run: runStatus, Desc: "show Red October persistent delegation state"},
}
func registerFlags() {
flag.StringVar(&server, "server", "localhost:8080", "server address")
flag.StringVar(&caPath, "ca", "", "ca file path")
flag.StringVar(&owners, "owners", "", "comma separated owner list")
flag.StringVar(&users, "users", "", "comma separated user list")
flag.IntVar(&uses, "uses", 0, "number of delegated key uses")
flag.IntVar(&minUsers, "minUsers", 2, "minimum number of delegations")
flag.StringVar(&duration, "time", "0h", "duration of delegated key uses")
flag.StringVar(&lefters, "left", "", "comma separated left owners")
flag.StringVar(&righters, "right", "", "comma separated right owners")
flag.StringVar(&labels, "labels", "", "comma separated labels")
flag.StringVar(&inPath, "in", "", "input data file")
flag.StringVar(&outPath, "out", "", "output data file")
flag.StringVar(&outEnv, "outenv", "", "env variable for output data")
flag.StringVar(&user, "user", "", "username")
flag.StringVar(&pswd, "password", "", "password")
flag.StringVar(&userEnv, "userenv", "RO_USER", "env variable for user name")
flag.StringVar(&pswdEnv, "pswdenv", "RO_PASS", "env variable for user password")
flag.DurationVar(&pollInterval, "poll-interval", time.Second, "interval for polling an outstanding order (set 0 to disable polling)")
}
func getUserCredentials() {
user = os.Getenv(userEnv)
pswd = os.Getenv(pswdEnv)
if user == "" || pswd == "" {
fmt.Print("Username:")
fmt.Scan(&user)
var err error
pswd, err = gopass.GetPass("Password:")
processError(err)
}
}
func processError(err error) {
if err != nil {
log.Fatal("error:", err)
}
}
func processCSL(s string) []string {
if s == "" {
return nil
}
return strings.Split(s, ",")
}
func runCreate() {
req := core.CreateRequest{
Name: user,
Password: pswd,
}
resp, err := roServer.Create(req)
processError(err)
fmt.Println(resp.Status)
}
func runDelegate() {
req := core.DelegateRequest{
Name: user,
Password: pswd,
Uses: uses,
Time: duration,
Users: processCSL(users),
Labels: processCSL(labels),
}
resp, err := roServer.Delegate(req)
processError(err)
fmt.Println(resp.Status)
}
// TODO: summary response needs better formatting
func runSummary() {
req := core.SummaryRequest{
Name: user,
Password: pswd,
}
resp, err := roServer.Summary(req)
processError(err)
fmt.Println(resp)
}
func runEncrypt() {
inBytes, err := ioutil.ReadFile(inPath)
processError(err)
req := core.EncryptRequest{
Name: user,
Password: pswd,
Minimum: minUsers,
Owners: processCSL(owners),
LeftOwners: processCSL(lefters),
RightOwners: processCSL(righters),
Labels: processCSL(labels),
Data: inBytes,
}
resp, err := roServer.Encrypt(req)
processError(err)
if resp.Status != "ok" {
log.Fatal("response status error:", resp.Status)
return
}
fmt.Println("Response Status:", resp.Status)
outBytes := []byte(base64.StdEncoding.EncodeToString(resp.Response))
ioutil.WriteFile(outPath, outBytes, 0644)
}
func runReEncrypt() {
inBytes, err := ioutil.ReadFile(inPath)
processError(err)
// base64 decode the input
encBytes, err := base64.StdEncoding.DecodeString(string(inBytes))
if err != nil {
log.Println("fail to base64 decode the data, proceed with raw data")
encBytes = inBytes
}
req := core.ReEncryptRequest{
Name: user,
Password: pswd,
Owners: processCSL(owners),
Minimum: minUsers,
LeftOwners: processCSL(lefters),
RightOwners: processCSL(righters),
Labels: processCSL(labels),
Data: encBytes,
}
resp, err := roServer.ReEncrypt(req)
processError(err)
if resp.Status != "ok" {
log.Fatal("response status error:", resp.Status)
return
}
fmt.Println("Response Status:", resp.Status)
outBytes := []byte(base64.StdEncoding.EncodeToString(resp.Response))
ioutil.WriteFile(outPath, outBytes, 0644)
}
func runDecrypt() {
inBytes, err := ioutil.ReadFile(inPath)
processError(err)
// base64 decode the input
encBytes, err := base64.StdEncoding.DecodeString(string(inBytes))
if err != nil {
log.Println("fail to base64 decode the data, proceed with raw data")
encBytes = inBytes
}
req := core.DecryptRequest{
Name: user,
Password: pswd,
Data: encBytes,
}
resp, err := roServer.Decrypt(req)
processError(err)
if resp.Status != "ok" {
log.Fatal("response status error:", resp.Status)
return
}
fmt.Println("Response Status:", resp.Status)
var msg core.DecryptWithDelegates
err = json.Unmarshal(resp.Response, &msg)
processError(err)
fmt.Println("Secure:", msg.Secure)
fmt.Println("Delegates:", msg.Delegates)
ioutil.WriteFile(outPath, msg.Data, 0644)
}
func runOrder() {
req := core.OrderRequest{
Name: user,
Password: pswd,
Uses: uses,
Duration: duration,
Labels: processCSL(labels),
Users: processCSL(users),
}
resp, err := roServer.Order(req)
processError(err)
var o order.Order
err = json.Unmarshal(resp.Response, &o)
processError(err)
if pollInterval > 0 {
for o.Delegated < 2 {
time.Sleep(pollInterval)
resp, err = roServer.OrderInfo(core.OrderInfoRequest{Name: user, Password: pswd, OrderNum: o.Num})
processError(err)
err = json.Unmarshal(resp.Response, &o)
processError(err)
}
}
fmt.Println(resp.Status)
}
func runOwner() {
inBytes, err := ioutil.ReadFile(inPath)
processError(err)
req := core.OwnersRequest{
Data: inBytes,
}
resp, err := roServer.Owners(req)
processError(err)
fmt.Println(resp.Status)
fmt.Println(resp)
}
func runStatus() {
req := core.StatusRequest{
Name: user,
Password: pswd,
}
resp, err := roServer.Status(req)
processError(err)
fmt.Println(resp.Status)
fmt.Println(resp)
}
func main() {
flag.Usage = func() {
fmt.Println("Usage: ro [options] subcommand")
fmt.Println("Currently supported subcommands are:")
for key := range commandSet {
fmt.Println("\t", key, ":", commandSet[key].Desc)
}
fmt.Println("Options:")
flag.PrintDefaults()
}
registerFlags()
flag.Parse()
if flag.NArg() != 1 {
flag.Usage()
os.Exit(1)
}
action := flag.Arg(0)
cmd, found := commandSet[action]
if !found {
fmt.Println("Unsupported subcommand:", action)
flag.Usage()
os.Exit(1)
} else {
var err error
roServer, err = client.NewRemoteServer(server, caPath)
processError(err)
getUserCredentials()
cmd.Run()
}
}