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

MFA #14049

Merged
merged 29 commits into from
Feb 17, 2022
Merged

MFA #14049

Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
d9e35b9
adds development workflow to mirage config
zofskeez Dec 20, 2021
b7a8a66
adds mirage handler and factory for mfa workflow
zofskeez Dec 21, 2021
f9ea459
adds mfa handling to auth service and cluster adapter
zofskeez Dec 21, 2021
7a7f64d
moves auth success logic from form to controller
zofskeez Dec 22, 2021
b3489ea
adds mfa form component
zofskeez Dec 22, 2021
bcc225e
shows delayed auth message for all methods
zofskeez Jan 5, 2022
7f48e20
adds new code delay to mfa form
zofskeez Jan 5, 2022
b3a5b6c
adds error views
zofskeez Jan 7, 2022
89919f4
merges main and fixes conflicts
zofskeez Jan 11, 2022
2fb047d
Merge branch 'main' into ui/mfa
zofskeez Jan 12, 2022
e88666f
fixes merge conflict
zofskeez Jan 12, 2022
c56c6a3
adds integration tests for mfa-form component
zofskeez Jan 12, 2022
dd90674
fixes auth tests
zofskeez Jan 13, 2022
8292e99
updates mfa response handling to align with backend
zofskeez Feb 14, 2022
f793811
updates mfa-form to handle multiple methods and constraints
zofskeez Feb 14, 2022
578e698
adds noDefault arg to Select component
zofskeez Feb 14, 2022
c20e907
updates mirage mfa handler to align with backend and adds generator f…
zofskeez Feb 14, 2022
5c5befc
adds tests
zofskeez Feb 14, 2022
2fd4556
flaky test fix attempt
zofskeez Feb 14, 2022
0115d22
reverts test fix attempt
zofskeez Feb 14, 2022
4a15e4b
adds changelog entry
zofskeez Feb 14, 2022
34d1776
updates comments for todo items
zofskeez Feb 16, 2022
ad50474
Merge branch 'main' into ui/mfa
zofskeez Feb 16, 2022
73a0e52
removes faker from mfa mirage factory and handler
zofskeez Feb 16, 2022
5dffb1e
adds number to word helper
zofskeez Feb 16, 2022
2428dd6
fixes tests
zofskeez Feb 16, 2022
8ee6a6a
Merge branch 'main' into ui/mfa
zofskeez Feb 16, 2022
5a8a801
Revert "Merge branch 'main' into ui/mfa"
zofskeez Feb 17, 2022
94f5100
format-ttl helper fix from main
zofskeez Feb 17, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 2 additions & 4 deletions api/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,6 @@ const (
EnvRateLimit = "VAULT_RATE_LIMIT"
EnvHTTPProxy = "VAULT_HTTP_PROXY"
HeaderIndex = "X-Vault-Index"
HeaderForward = "X-Vault-Forward"
HeaderInconsistent = "X-Vault-Inconsistent"
)

// Deprecated values
Expand Down Expand Up @@ -1397,7 +1395,7 @@ func ParseReplicationState(raw string, hmacKey []byte) (*logical.WALState, error
// conjunction with RequireState.
func ForwardInconsistent() RequestCallback {
return func(req *Request) {
req.Headers.Set(HeaderInconsistent, "forward-active-node")
req.Headers.Set("X-Vault-Inconsistent", "forward-active-node")
}
}

Expand All @@ -1406,7 +1404,7 @@ func ForwardInconsistent() RequestCallback {
// This feature must be enabled in Vault's configuration.
func ForwardAlways() RequestCallback {
return func(req *Request) {
req.Headers.Set(HeaderForward, "active-node")
req.Headers.Set("X-Vault-Forward", "active-node")
}
}

Expand Down
91 changes: 4 additions & 87 deletions api/sys_mounts.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import (
"context"
"errors"
"fmt"
"time"

"github.com/mitchellh/mapstructure"
)
Expand Down Expand Up @@ -66,91 +65,24 @@ func (c *Sys) Unmount(path string) error {
return err
}

// Remount kicks off a remount operation, polls the status endpoint using
// the migration ID till either success or failure state is observed
func (c *Sys) Remount(from, to string) error {
remountResp, err := c.StartRemount(from, to)
if err != nil {
return err
}

for {
remountStatusResp, err := c.RemountStatus(remountResp.MigrationID)
if err != nil {
return err
}
if remountStatusResp.MigrationInfo.MigrationStatus == "success" {
return nil
}
if remountStatusResp.MigrationInfo.MigrationStatus == "failure" {
return fmt.Errorf("Failure! Error encountered moving mount %s to %s, with migration ID %s", from, to, remountResp.MigrationID)
}
time.Sleep(1 * time.Second)
}
}

// StartRemount kicks off a mount migration and returns a response with the migration ID
func (c *Sys) StartRemount(from, to string) (*MountMigrationOutput, error) {
body := map[string]interface{}{
"from": from,
"to": to,
}

r := c.c.NewRequest("POST", "/v1/sys/remount")
if err := r.SetJSONBody(body); err != nil {
return nil, err
return err
}

ctx, cancelFunc := context.WithCancel(context.Background())
defer cancelFunc()
resp, err := c.c.RawRequestWithContext(ctx, r)
if err != nil {
return nil, err
}
defer resp.Body.Close()
secret, err := ParseSecret(resp.Body)
if err != nil {
return nil, err
}
if secret == nil || secret.Data == nil {
return nil, errors.New("data from server response is empty")
}

var result MountMigrationOutput
err = mapstructure.Decode(secret.Data, &result)
if err != nil {
return nil, err
}

return &result, err
}

// RemountStatus checks the status of a mount migration operation with the provided ID
func (c *Sys) RemountStatus(migrationID string) (*MountMigrationStatusOutput, error) {
r := c.c.NewRequest("GET", fmt.Sprintf("/v1/sys/remount/status/%s", migrationID))

ctx, cancelFunc := context.WithCancel(context.Background())
defer cancelFunc()
resp, err := c.c.RawRequestWithContext(ctx, r)
if err != nil {
return nil, err
}
defer resp.Body.Close()
secret, err := ParseSecret(resp.Body)
if err != nil {
return nil, err
}
if secret == nil || secret.Data == nil {
return nil, errors.New("data from server response is empty")
}

var result MountMigrationStatusOutput
err = mapstructure.Decode(secret.Data, &result)
if err != nil {
return nil, err
if err == nil {
defer resp.Body.Close()
}

return &result, err
return err
}

func (c *Sys) TuneMount(path string, config MountConfigInput) error {
Expand Down Expand Up @@ -255,18 +187,3 @@ type MountConfigOutput struct {
// Deprecated: This field will always be blank for newer server responses.
PluginName string `json:"plugin_name,omitempty" mapstructure:"plugin_name"`
}

type MountMigrationOutput struct {
MigrationID string `mapstructure:"migration_id"`
}

type MountMigrationStatusOutput struct {
MigrationID string `mapstructure:"migration_id"`
MigrationInfo *MountMigrationStatusInfo `mapstructure:"migration_info"`
}

type MountMigrationStatusInfo struct {
SourceMount string `mapstructure:"source_mount"`
TargetMount string `mapstructure:"target_mount"`
MigrationStatus string `mapstructure:"status"`
}
9 changes: 3 additions & 6 deletions builtin/credential/approle/path_login.go
Original file line number Diff line number Diff line change
Expand Up @@ -178,14 +178,11 @@ func (b *backend) pathLoginUpdate(ctx context.Context, req *logical.Request, dat
}

belongs, err := cidrutil.IPBelongsToCIDRBlocksSlice(req.Connection.RemoteAddr, entry.CIDRList)
if err != nil {
return logical.ErrorResponse(err.Error()), logical.ErrInvalidRequest
}

if !belongs {
if !belongs || err != nil {
return logical.ErrorResponse(fmt.Errorf(
"source address %q unauthorized through CIDR restrictions on the secret ID",
"source address %q unauthorized through CIDR restrictions on the secret ID: %w",
req.Connection.RemoteAddr,
err,
).Error()), nil
}
}
Expand Down
9 changes: 3 additions & 6 deletions builtin/logical/ssh/path_config_ca.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,13 @@ import (
"crypto/rsa"
"crypto/x509"
"encoding/pem"
"errors"
"fmt"
"io"

multierror "github.com/hashicorp/go-multierror"
"github.com/hashicorp/vault/sdk/framework"
"github.com/hashicorp/vault/sdk/logical"
"golang.org/x/crypto/ssh"

"github.com/mikesmitty/edkey"
)

const (
Expand Down Expand Up @@ -360,9 +357,9 @@ func generateSSHKeyPair(randomSource io.Reader, keyType string, keyBits int) (st
return "", "", err
}

marshalled := edkey.MarshalED25519PrivateKey(privateSeed)
if marshalled == nil {
return "", "", errors.New("unable to marshal ed25519 private key")
marshalled, err := x509.MarshalPKCS8PrivateKey(privateSeed)
if err != nil {
return "", "", err
}

privateBlock = &pem.Block{
Expand Down
36 changes: 2 additions & 34 deletions builtin/logical/ssh/path_config_ca_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -191,31 +191,17 @@ func createDeleteHelper(t *testing.T, b logical.Backend, config *logical.Backend
}
resp, err := b.HandleRequest(context.Background(), caReq)
if err != nil || (resp != nil && resp.IsError()) {
t.Fatalf("bad case %v: err: %v, resp: %v", index, err, resp)
t.Fatalf("bad case %v: err: %v, resp:%v", index, err, resp)
}
if !strings.Contains(resp.Data["public_key"].(string), caReq.Data["key_type"].(string)) {
t.Fatalf("bad case %v: expected public key of type %v but was %v", index, caReq.Data["key_type"], resp.Data["public_key"])
}

issueOptions := map[string]interface{}{
"public_key": testCAPublicKeyEd25519,
}
issueReq := &logical.Request{
Path: "sign/ca-issuance",
Operation: logical.UpdateOperation,
Storage: config.StorageView,
Data: issueOptions,
}
resp, err = b.HandleRequest(context.Background(), issueReq)
if err != nil || (resp != nil && resp.IsError()) {
t.Fatalf("bad case %v: err: %v, resp: %v", index, err, resp)
}

// Delete the configured keys
caReq.Operation = logical.DeleteOperation
resp, err = b.HandleRequest(context.Background(), caReq)
if err != nil || (resp != nil && resp.IsError()) {
t.Fatalf("bad case %v: err: %v, resp: %v", index, err, resp)
t.Fatalf("bad case %v: err: %v, resp:%v", index, err, resp)
}
}

Expand Down Expand Up @@ -249,24 +235,6 @@ func TestSSH_ConfigCAKeyTypes(t *testing.T) {
{"ed25519", 0},
}

// Create a role for ssh signing.
roleOptions := map[string]interface{}{
"allow_user_certificates": true,
"allowed_users": "*",
"key_type": "ca",
"ttl": "30s",
}
roleReq := &logical.Request{
Operation: logical.UpdateOperation,
Path: "roles/ca-issuance",
Data: roleOptions,
Storage: config.StorageView,
}
_, err = b.HandleRequest(context.Background(), roleReq)
if err != nil {
t.Fatalf("Cannot create role to issue against: %s", err)
}

for index, scenario := range cases {
createDeleteHelper(t, b, config, index, scenario.keyType, scenario.keyBits)
}
Expand Down
8 changes: 4 additions & 4 deletions builtin/logical/transit/backend.go
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,7 @@ func (b *backend) periodicFunc(ctx context.Context, req *logical.Request) error
}

// autoRotateKeys retrieves all transit keys and rotates those which have an
// auto rotate period defined which has passed. This operation only happens
// auto rotate interval defined which has passed. This operation only happens
// on primary nodes and performance secondary nodes which have a local mount.
func (b *backend) autoRotateKeys(ctx context.Context, req *logical.Request) error {
// Only check for autorotation once an hour to avoid unnecessarily iterating
Expand Down Expand Up @@ -247,15 +247,15 @@ func (b *backend) rotateIfRequired(ctx context.Context, req *logical.Request, ke
}
defer p.Unlock()

// If the policy's automatic rotation period is 0, it should not
// If the policy's automatic rotation interval is 0, it should not
// automatically rotate.
if p.AutoRotatePeriod == 0 {
if p.AutoRotateInterval == 0 {
return nil
}

// Retrieve the latest version of the policy and determine if it is time to rotate.
latestKey := p.Keys[strconv.Itoa(p.LatestVersion)]
if time.Now().After(latestKey.CreationTime.Add(p.AutoRotatePeriod)) {
if time.Now().After(latestKey.CreationTime.Add(p.AutoRotateInterval)) {
if b.Logger().IsDebug() {
b.Logger().Debug("automatically rotating key", "key", key)
}
Expand Down
6 changes: 3 additions & 3 deletions builtin/logical/transit/backend_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1607,7 +1607,7 @@ func TestTransit_AutoRotateKeys(t *testing.T) {
Operation: logical.UpdateOperation,
Path: "keys/test2",
Data: map[string]interface{}{
"auto_rotate_period": 24 * time.Hour,
"auto_rotate_interval": 24 * time.Hour,
},
}
resp, err = b.HandleRequest(context.Background(), req)
Expand Down Expand Up @@ -1651,7 +1651,7 @@ func TestTransit_AutoRotateKeys(t *testing.T) {
t.Fatalf("incorrect latest_version found, got: %d, want: %d", resp.Data["latest_version"], 1)
}

// Update auto rotate period on one key to be one nanosecond
// Update auto rotate interval on one key to be one nanosecond
p, _, err := b.GetPolicy(context.Background(), keysutil.PolicyRequest{
Storage: storage,
Name: "test2",
Expand All @@ -1662,7 +1662,7 @@ func TestTransit_AutoRotateKeys(t *testing.T) {
if p == nil {
t.Fatal("expected non-nil policy")
}
p.AutoRotatePeriod = time.Nanosecond
p.AutoRotateInterval = time.Nanosecond
err = p.Persist(context.Background(), storage)
if err != nil {
t.Fatal(err)
Expand Down
14 changes: 7 additions & 7 deletions builtin/logical/transit/path_config.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ the latest version of the key is allowed.`,
Description: `Enables taking a backup of the named key in plaintext format. Once set, this cannot be disabled.`,
},

"auto_rotate_period": {
"auto_rotate_interval": {
Type: framework.TypeDurationSecond,
Description: `Amount of time the key should live before
being automatically rotated. A value of 0
Expand Down Expand Up @@ -193,19 +193,19 @@ func (b *backend) pathConfigWrite(ctx context.Context, req *logical.Request, d *
}
}

autoRotatePeriodRaw, ok, err := d.GetOkErr("auto_rotate_period")
autoRotateIntervalRaw, ok, err := d.GetOkErr("auto_rotate_interval")
if err != nil {
return nil, err
}
if ok {
autoRotatePeriod := time.Second * time.Duration(autoRotatePeriodRaw.(int))
autoRotateInterval := time.Second * time.Duration(autoRotateIntervalRaw.(int))
// Provided value must be 0 to disable or at least an hour
if autoRotatePeriod != 0 && autoRotatePeriod < time.Hour {
return logical.ErrorResponse("auto rotate period must be 0 to disable or at least an hour"), nil
if autoRotateInterval != 0 && autoRotateInterval < time.Hour {
return logical.ErrorResponse("auto rotate interval must be 0 to disable or at least an hour"), nil
}

if autoRotatePeriod != p.AutoRotatePeriod {
p.AutoRotatePeriod = autoRotatePeriod
if autoRotateInterval != p.AutoRotateInterval {
p.AutoRotateInterval = autoRotateInterval
persistNeeded = true
}
}
Expand Down
Loading