Skip to content

Commit

Permalink
Merge branch 'master' into ui/replication-status-discoverability
Browse files Browse the repository at this point in the history
  • Loading branch information
Noelle Daley authored Jun 1, 2020
2 parents 6fa0f54 + 7f14810 commit bd59ffc
Show file tree
Hide file tree
Showing 574 changed files with 21,847 additions and 14,119 deletions.
39 changes: 35 additions & 4 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,30 +2,43 @@

CHANGES:

* storage/raft: The storage configuration now accepts a new `max_entry_size` config that will limit
the total size in bytes of any entry committed via raft. It defaults to `"1048576"` (1MiB). [[GH-9027](https://github.com/hashicorp/vault/pull/9027)]
* token: Token creation with custom token ID via `id` will no longer allow periods (`.`) as part of the input string.
The final generated token value may contain periods, such as the `s.` prefix for service token
indication. [[GH-8646](https://github.com/hashicorp/vault/pull/8646/files)]
* token: Token renewals will now return token policies within the `token_policies` , identity policies within `identity_policies`, and the full policy set within `policies`. [[GH-8535](https://github.com/hashicorp/vault/pull/8535)]
* cubbyhole: Reject reads and writes to an empty ("") path. [[GH-8971](https://github.com/hashicorp/vault/pull/8971)]
* core: Remove the addition of newlines to parsed configuration when using integer/boolean values [[GH-8928](https://github.com/hashicorp/vault/pull/8928)]
* audit: Token TTL and issue time are now provided in the auth portion of audit logs. [[GH-9091](https://github.com/hashicorp/vault/pull/9091)]

IMPROVEMENTS:

* secrets/transit: Transit requests that make use of keys now include a new field `key_version` in their responses [[GH-9100](https://github.com/hashicorp/vault/pull/9100)]
* core: Add the Go version used to build a Vault binary to the server message output. [[GH-9078](https://github.com/hashicorp/vault/pull/9078)]
* core: Added Password Policies for user-configurable password generation [[GH-8637](https://github.com/hashicorp/vault/pull/8637)]
* 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)]
* secrets/database: Add static role rotation for MongoDB Atlas database plugin [[GH-11](https://github.com/hashicorp/vault-plugin-database-mongodbatlas/pull/11)]
* secrets/gcp: Support BigQuery dataset ACLs in absence of IAM endpoints [[GH-78](https://github.com/hashicorp/vault-plugin-secrets-gcp/pull/78)]
* sentinel: Add a sentinel config section, and "additional_enabled_modules", a list of Sentinel modules that may be imported in addition to the defaults.
* ui: Update TTL picker styling on SSH secret engine [[GH-8891](https://github.com/hashicorp/vault/pull/8891)]
* ui: Only render the JWT input field of the Vault login form on mounts configured for JWT auth [[GH-8952](https://github.com/hashicorp/vault/pull/8952)]
* cli: Add a new subcommand, `vault monitor`, for tailing server logs in the console. [[GH-8477](https://github.com/hashicorp/vault/pull/8477)]

BUG FIXES:

* secrets/database: Fix issue where rotating root database credentials while Vault's storage backend is unavailable causes Vault to lose access to the database [[GH-8782](https://github.com/hashicorp/vault/pull/8782)]
* secrets/kv: Return the value of delete_version_after when reading kv/config, even if it is set to the default. [[GH-42](https://github.com/hashicorp/vault-plugin-secrets-kv/pull/42)]
* ui: Fix snowman that appears when namespaces have more than one period [[GH-8910](https://github.com/hashicorp/vault/pull/8910)]
* ui: Add Toggle component into core addon so it is available in KMIP and other Ember Engines.[[GH-8913]](https://github.com/hashicorp/vault/pull/8913)

## 1.4.2 (TBD)
## 1.4.2 (May 21st, 2020)

SECURITY:
* core: Proxy environment variables are now redacted before being logged, in case the URLs include a username:password. This vulnerability, CVE-2020-13223, is fixed in 1.3.6 and 1.4.2, but affects 1.4.0 and 1.4.1, as well as older versions of Vault [[GH-9022](https://github.com/hashicorp/vault/pull/9022)]
* secrets/gcp: Fix a regression in 1.4.0 where the system TTLs were being used instead of the configured backend TTLs for dynamic service accounts. This vulnerability is CVE-2020-12757. [[GH-85](https://github.com/hashicorp/vault-plugin-secrets-gcp/pull/85)]

IMPROVEMENTS:

Expand All @@ -38,16 +51,24 @@ IMPROVEMENTS:

BUG FIXES:

* auth/aws: Fix token renewal issues caused by the metadata changes in 1.4.1 [[GH-8991](https://github.com/hashicorp/vault/pull/8991)]
* auth/ldap: Fix 1.4.0 regression that could result in auth failures when LDAP auth config includes upndomain. [[GH-9041](https://github.com/hashicorp/vault/pull/9041)]
* secrets/ad: Forward rotation requests from standbys to active clusters [[GH-66](https://github.com/hashicorp/vault-plugin-secrets-ad/pull/66)]
* secrets/database: Prevent generation of usernames that are not allowed by the MongoDB Atlas API [[GH-9](https://github.com/hashicorp/vault-plugin-database-mongodbatlas/pull/9)]
* secrets/database: Return an error if a manual rotation of static account credentials fails [[GH-9035](https://github.com/hashicorp/vault/pull/9035)]
* secrets/openldap: Forward all rotation requests from standbys to active clusters [[GH-9028](https://github.com/hashicorp/vault/pull/9028)]
* secrets/transform (enterprise): Fix panic that could occur when accessing cached template entries, such as a requests
that accessed templates directly or indirectly from a performance standby node.
* serviceregistration: Fix a regression for Consul service registration that ignored using the listener address as
the redirect address unless api_addr was provided. It now properly uses the same redirect address as the one
used by Vault's Core object. [[GH-8976](https://github.com/hashicorp/vault/pull/8976)]
* secrets/openldap: Forward all rotation requests from standbys to active clusters [[GH-9028](https://github.com/hashicorp/vault/pull/9028)]
used by Vault's Core object. [[GH-8976](https://github.com/hashicorp/vault/pull/8976)]
* storage/raft: Advertise the configured cluster address to the rest of the nodes in the raft cluster. This fixes
an issue where a node advertising 0.0.0.0 is not using a unique hostname. [[GH-9008](https://github.com/hashicorp/vault/pull/9008)]
* storage/raft: Fix panic when multiple nodes attempt to join the cluster at once. [[GH-9008](https://github.com/hashicorp/vault/pull/9008)]
* sys: The path provided in `sys/internal/ui/mounts/:path` is now namespace-aware. This fixes an issue
with `vault kv` subcommands that had namespaces provided in the path returning permission denied all the time.
[[GH-8962](https://github.com/hashicorp/vault/pull/8962)]
* ui: Fix snowman that appears when namespaces have more than one period [[GH-8910](https://github.com/hashicorp/vault/pull/8910)]

## 1.4.1 (April 30th, 2020)

Expand Down Expand Up @@ -201,6 +222,16 @@ BUG FIXES:
* ui: Update CLI to accept empty strings as param value to reset previously-set values
* ui: Fix bug where error states don't clear when moving between action tabs on Transit [[GH-8354](https://github.com/hashicorp/vault/pull/8354)]

## 1.3.6 (May 21st, 2020)

SECURITY:
* core: proxy environment variables are now redacted before being logged, in case the URLs include a username:password. This vulnerability, CVE-2020-13223, is fixed in 1.3.6 and 1.4.2, but affects 1.4 and 1.4.1, as well as older versions of Vault [[GH-9022](https://github.com/hashicorp/vault/pull/9022)]

BUG FIXES:

* auth/aws: Fix token renewal issues caused by the metadata changes in 1.3.5 [[GH-8991](https://github.com/hashicorp/vault/pull/8991)]
* replication: Fix mount filter bug that allowed replication filters to hide local mounts on a performance secondary

## 1.3.5 (April 28th, 2020)

CHANGES:
Expand Down
2 changes: 1 addition & 1 deletion api/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ require (
github.com/hashicorp/go-rootcerts v1.0.1
github.com/hashicorp/hcl v1.0.0
github.com/hashicorp/vault/sdk v0.1.14-0.20200514144402-4bfac290c352
github.com/mitchellh/mapstructure v1.1.2
github.com/mitchellh/mapstructure v1.2.2
golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4
gopkg.in/square/go-jose.v2 v2.3.1
Expand Down
9 changes: 7 additions & 2 deletions api/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMT
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ=
github.com/armon/go-metrics v0.3.0/go.mod h1:zXjbSimjXTd7vOpY8B0/2LpvNvDoXBuplAD+gJD3GYs=
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310 h1:BUAU3CGlLvorLI26FmByPp2eC2qla6E1Tw+scpcg/to=
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
github.com/aws/aws-sdk-go v1.25.37/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
github.com/bgentry/speakeasy v0.1.0 h1:ByYyxL9InA1OWqxJqqp2A5pYHUrCiAL6K3J+LKSsQkY=
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6Dob7S7YxXgwXpfOuvO54S+tGdZdw9fuRZt25Ag=
github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I=
Expand Down Expand Up @@ -65,15 +67,16 @@ github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hd
github.com/mattn/go-isatty v0.0.10 h1:qxFzApOv4WsAL965uUPIsXzAKCZxN2p9UqdhFS4ZW10=
github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/mitchellh/cli v1.0.0 h1:iGBIsUe3+HZ/AD/Vd7DErOt5sU9fa8Uj7A2s1aggv1Y=
github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=
github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw=
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/mitchellh/go-testing-interface v0.0.0-20171004221916-a61a99592b77/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo=
github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE=
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/mitchellh/mapstructure v1.2.2 h1:dxe5oCinTXiTIcfgmZecdCzPmAJKd46KsCWc35r0TV4=
github.com/mitchellh/mapstructure v1.2.2/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA=
github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
Expand All @@ -82,6 +85,7 @@ github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/posener/complete v1.1.1 h1:ccV59UEOTzVDnDUEFdT95ZzHVZ+5+158q8+SJb2QV5w=
github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
github.com/prometheus/client_golang v0.9.2/go.mod h1:OsXs2jCmiKlQ1lTBmv21f2mNfw4xf/QclQDMrYNZzcM=
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
Expand All @@ -91,6 +95,7 @@ github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFo
github.com/ryanuber/go-glob v1.0.0 h1:iQh3xXAumdQ+4Ufa5b25cRpC5TYKlno6hsv6Cb3pkBk=
github.com/ryanuber/go-glob v1.0.0/go.mod h1:807d1WSdnB0XRJzKNil9Om6lcp/3a0v4qIHxIXzX/Yc=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
Expand Down
2 changes: 1 addition & 1 deletion api/sys_audit.go
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ func (c *Sys) DisableAudit(path string) error {
return err
}

// Structures for the requests/resposne are all down here. They aren't
// Structures for the requests/response are all down here. They aren't
// individually documented because the map almost directly to the raw HTTP API
// documentation. Please refer to that documentation for more details.

Expand Down
64 changes: 64 additions & 0 deletions api/sys_monitor.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
package api

import (
"bufio"
"context"
"fmt"
)

// Monitor returns a channel that outputs strings containing the log messages
// coming from the server.
func (c *Sys) Monitor(ctx context.Context, logLevel string) (chan string, error) {
r := c.c.NewRequest("GET", "/v1/sys/monitor")

if logLevel == "" {
r.Params.Add("log_level", "info")
} else {
r.Params.Add("log_level", logLevel)
}

resp, err := c.c.RawRequestWithContext(ctx, r)
if err != nil {
return nil, err
}

logCh := make(chan string, 64)

go func() {
scanner := bufio.NewScanner(resp.Body)
droppedCount := 0

defer close(logCh)
defer resp.Body.Close()

for {
if ctx.Err() != nil {
return
}

if !scanner.Scan() {
return
}

logMessage := scanner.Text()

if droppedCount > 0 {
select {
case logCh <- fmt.Sprintf("Monitor dropped %d logs during monitor request\n", droppedCount):
droppedCount = 0
default:
droppedCount++
continue
}
}

select {
case logCh <- logMessage:
default:
droppedCount++
}
}
}()

return logCh, nil
}
15 changes: 15 additions & 0 deletions audit/format.go
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@ func (f *AuditFormatter) FormatRequest(ctx context.Context, w io.Writer, config
EntityID: auth.EntityID,
RemainingUses: req.ClientTokenRemainingUses,
TokenType: auth.TokenType.String(),
TokenTTL: int64(auth.TTL.Seconds()),
},

Request: &AuditRequest{
Expand All @@ -127,6 +128,10 @@ func (f *AuditFormatter) FormatRequest(ctx context.Context, w io.Writer, config
},
}

if !auth.IssueTime.IsZero() {
reqEntry.Auth.TokenIssueTime = auth.IssueTime.Format(time.RFC3339)
}

if req.WrapInfo != nil {
reqEntry.Request.WrapTTL = int(req.WrapInfo.TTL / time.Second)
}
Expand Down Expand Up @@ -212,6 +217,10 @@ func (f *AuditFormatter) FormatResponse(ctx context.Context, w io.Writer, config
NumUses: resp.Auth.NumUses,
EntityID: resp.Auth.EntityID,
TokenType: resp.Auth.TokenType.String(),
TokenTTL: int64(resp.Auth.TTL.Seconds()),
}
if !resp.Auth.IssueTime.IsZero() {
respAuth.TokenIssueTime = resp.Auth.IssueTime.Format(time.RFC3339)
}
}

Expand Down Expand Up @@ -258,6 +267,7 @@ func (f *AuditFormatter) FormatResponse(ctx context.Context, w io.Writer, config
RemainingUses: req.ClientTokenRemainingUses,
EntityID: auth.EntityID,
TokenType: auth.TokenType.String(),
TokenTTL: int64(auth.TTL.Seconds()),
},

Request: &AuditRequest{
Expand Down Expand Up @@ -289,6 +299,9 @@ func (f *AuditFormatter) FormatResponse(ctx context.Context, w io.Writer, config
},
}

if !auth.IssueTime.IsZero() {
respEntry.Auth.TokenIssueTime = auth.IssueTime.Format(time.RFC3339)
}
if req.WrapInfo != nil {
respEntry.Request.WrapTTL = int(req.WrapInfo.TTL / time.Second)
}
Expand Down Expand Up @@ -359,6 +372,8 @@ type AuditAuth struct {
RemainingUses int `json:"remaining_uses,omitempty"`
EntityID string `json:"entity_id,omitempty"`
TokenType string `json:"token_type,omitempty"`
TokenTTL int64 `json:"token_ttl,omitempty"`
TokenIssueTime string `json:"token_issue_time,omitempty"`
}

type AuditSecret struct {
Expand Down
11 changes: 10 additions & 1 deletion audit/format_json_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ func TestFormatJSON_formatRequest(t *testing.T) {

expectedResultStr := fmt.Sprintf(testFormatJSONReqBasicStrFmt, salter.GetIdentifiedHMAC("foo"))

issueTime, _ := time.Parse(time.RFC3339, "2020-05-28T13:40:18-05:00")
cases := map[string]struct {
Auth *logical.Auth
Req *logical.Request
Expand All @@ -45,6 +46,10 @@ func TestFormatJSON_formatRequest(t *testing.T) {
NoDefaultPolicy: true,
Policies: []string{"root"},
TokenType: logical.TokenTypeService,
LeaseOptions: logical.LeaseOptions{
TTL: time.Hour * 4,
IssueTime: issueTime,
},
},
&logical.Request{
Operation: logical.UpdateOperation,
Expand Down Expand Up @@ -72,6 +77,10 @@ func TestFormatJSON_formatRequest(t *testing.T) {
NoDefaultPolicy: true,
Policies: []string{"root"},
TokenType: logical.TokenTypeService,
LeaseOptions: logical.LeaseOptions{
TTL: time.Hour * 4,
IssueTime: issueTime,
},
},
&logical.Request{
Operation: logical.UpdateOperation,
Expand Down Expand Up @@ -143,5 +152,5 @@ func TestFormatJSON_formatRequest(t *testing.T) {
}
}

const testFormatJSONReqBasicStrFmt = `{"time":"2015-08-05T13:45:46Z","type":"request","auth":{"client_token":"%s","accessor":"bar","display_name":"testtoken","policies":["root"],"no_default_policy":true,"metadata":null,"entity_id":"foobarentity","token_type":"service"},"request":{"operation":"update","path":"/foo","data":null,"wrap_ttl":60,"remote_address":"127.0.0.1","headers":{"foo":["bar"]}},"error":"this is an error"}
const testFormatJSONReqBasicStrFmt = `{"time":"2015-08-05T13:45:46Z","type":"request","auth":{"client_token":"%s","accessor":"bar","display_name":"testtoken","policies":["root"],"no_default_policy":true,"metadata":null,"entity_id":"foobarentity","token_type":"service", "token_ttl": 14400, "token_issue_time": "2020-05-28T13:40:18-05:00"},"request":{"operation":"update","path":"/foo","data":null,"wrap_ttl":60,"remote_address":"127.0.0.1","headers":{"foo":["bar"]}},"error":"this is an error"}
`
Loading

0 comments on commit bd59ffc

Please sign in to comment.