Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support reading Raft TLS flags from file #9060

Merged
merged 11 commits into from
May 23, 2020
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ CHANGES:

IMPROVEMENTS:

* cli: Support reading TLS parameters from file for the `vault operator raft join` command. [[GH-9060](https://github.com/hashicorp/vault/pull/9060)]
* plugin: Add SDK method, `Sys.ReloadPlugin`, and CLI command, `vault plugin reload`,
for reloading plugins. [[GH-8777](https://github.com/hashicorp/vault/pull/8777)]
* sdk/framework: Support accepting TypeFloat parameters over the API [[GH-8923](https://github.com/hashicorp/vault/pull/8923)]
Expand Down
19 changes: 19 additions & 0 deletions command/base_helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,11 @@ import (
"encoding/json"
"fmt"
"io"
"io/ioutil"
"strings"
"time"

"github.com/hashicorp/errwrap"
"github.com/hashicorp/vault/api"
kvbuilder "github.com/hashicorp/vault/internalshared/kv-builder"
"github.com/kr/text"
Expand Down Expand Up @@ -273,3 +275,20 @@ func humanDurationInt(i interface{}) interface{} {
// If we don't know what type it is, just return the original value
return i
}

// parseFlagFile accepts a flag value returns the contets of that value. If the
// value starts with '@', that indicates the value is a file and its content
// should be read and returned. Otherwise, the raw value is returned.
func parseFlagFile(raw string) (string, error) {
// check if the provided argument should be read from file
if len(raw) > 0 && raw[0] == '@' {
contents, err := ioutil.ReadFile(raw[1:])
if err != nil {
return "", errwrap.Wrapf("error reading file: {{err}}", err)
}

return string(contents), nil
}

return raw, nil
}
49 changes: 49 additions & 0 deletions command/base_helpers_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -160,3 +160,52 @@ func TestTruncateToSeconds(t *testing.T) {
})
}
}

func TestParseFlagFile(t *testing.T) {
t.Parallel()

content := "some raw content"
tmpFile, err := ioutil.TempFile(os.TempDir(), "TestParseFlagFile")
if err != nil {
t.Fatalf("failed to create temporary file: %v", err)
}

defer os.Remove(tmpFile.Name())

if _, err := tmpFile.WriteString(content); err != nil {
t.Fatalf("failed to write to temporary file: %v", err)
}

cases := []struct {
value string
exp string
}{
{
"",
"",
},
{
content,
content,
},
{
fmt.Sprintf("@%s", tmpFile.Name()),
content,
},
}

for _, tc := range cases {
tc := tc

t.Run(tc.value, func(t *testing.T) {
content, err := parseFlagFile(tc.value)
if err != nil {
t.Fatalf("unexpected error parsing flag value: %v", err)
}

if content != tc.exp {
t.Fatalf("expected %s to be %s", content, tc.exp)
}
})
}
}
41 changes: 34 additions & 7 deletions command/operator_raft_join.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,15 @@ Usage: vault operator raft join [options] <leader-api-addr>
Join the current node as a peer to the Raft cluster by providing the address
of the Raft leader node.

$ vault operator raft join "http://127.0.0.2:8200"
$ vault operator raft join "http://127.0.0.2:8200"

TLS certificate data can also be consumed from a file on disk by prefixing with
the "@" symbol. For example:

$ vault operator raft join "http://127.0.0.2:8200" \
-leader-ca-cert=@leader_ca.crt \
-leader-client-cert=@leader_client.crt \
-leader-client-key=@leader.key

` + c.Flags().Help()

Expand Down Expand Up @@ -114,6 +122,24 @@ func (c *OperatorRaftJoinCommand) Run(args []string) int {
return 1
}

leaderCACert, err := parseFlagFile(c.flagLeaderCACert)
if err != nil {
c.UI.Error(fmt.Sprintf("Failed to parse leader CA certificate: %s", err))
return 1
}

leaderClientCert, err := parseFlagFile(c.flagLeaderClientCert)
if err != nil {
c.UI.Error(fmt.Sprintf("Failed to parse leader client certificate: %s", err))
return 1
}

leaderClientKey, err := parseFlagFile(c.flagLeaderClientKey)
if err != nil {
c.UI.Error(fmt.Sprintf("Failed to parse leader client key: %s", err))
return 1
}

client, err := c.Client()
if err != nil {
c.UI.Error(err.Error())
Expand All @@ -122,9 +148,9 @@ func (c *OperatorRaftJoinCommand) Run(args []string) int {

resp, err := client.Sys().RaftJoin(&api.RaftJoinRequest{
LeaderAPIAddr: leaderAPIAddr,
LeaderCACert: c.flagLeaderCACert,
LeaderClientCert: c.flagLeaderClientCert,
LeaderClientKey: c.flagLeaderClientKey,
LeaderCACert: leaderCACert,
LeaderClientCert: leaderClientCert,
LeaderClientKey: leaderClientKey,
Retry: c.flagRetry,
NonVoter: c.flagNonVoter,
})
Expand All @@ -139,9 +165,10 @@ func (c *OperatorRaftJoinCommand) Run(args []string) int {
return OutputData(c.UI, resp)
}

out := []string{}
out = append(out, "Key | Value")
out = append(out, fmt.Sprintf("Joined | %t", resp.Joined))
out := []string{
"Key | Value",
fmt.Sprintf("Joined | %t", resp.Joined),
}
c.UI.Output(tableOutput(out, nil))

return 0
Expand Down