Skip to content

Commit

Permalink
Add remote_port in the audit logs when it is available (hashicorp#12790)
Browse files Browse the repository at this point in the history
* Add remote_port in the audit logs when it is available

The `request.remote_port` field is now present in the audit log when it
is available:

```
{
  "time": "2021-10-10T13:53:51.760039Z",
  "type": "response",
  "auth": {
    "client_token": "hmac-sha256:1304aab0ac65747684e1b58248cc16715fa8f558f8d27e90fcbcb213220c0edf",
    "accessor": "hmac-sha256:f8cf0601dadd19aac84f205ded44c62898e3746a42108a51105a92ccc39baa43",
    "display_name": "root",
    "policies": [
      "root"
    ],
    "token_policies": [
      "root"
    ],
    "token_type": "service",
    "token_issue_time": "2021-10-10T15:53:44+02:00"
  },
  "request": {
    "id": "829c04a1-0352-2d9d-9bc9-00b928d33df5",
    "operation": "update",
    "mount_type": "system",
    "client_token": "hmac-sha256:1304aab0ac65747684e1b58248cc16715fa8f558f8d27e90fcbcb213220c0edf",
    "client_token_accessor": "hmac-sha256:f8cf0601dadd19aac84f205ded44c62898e3746a42108a51105a92ccc39baa43",
    "namespace": {
      "id": "root"
    },
    "path": "sys/audit/file",
    "data": {
      "description": "hmac-sha256:321a1d105f8c6fd62be4f34c4da4f0e6d1cdee9eb2ff4af0b59e1410950fe86b",
      "local": false,
      "options": {
        "file_path": "hmac-sha256:2421b5bf8dab1f9775b2e6e66e58d7bca99ab729f3f311782fda50717eee55b3"
      },
      "type": "hmac-sha256:30dff9607b4087e3ae6808b4a3aa395b1fc064e467748c55c25ddf0e9b150fcc"
    },
    "remote_address": "127.0.0.1",
    "remote_port": 54798
  },
  "response": {
    "mount_type": "system"
  }
}
```

Closes hashicorp#7716

* Add changelog entry

* Empty commit to trigger CI

* Add test and explicit error handling

* Change temporary file pattern in test
  • Loading branch information
remilapeyre authored and Artem Alexandrov committed Feb 4, 2022
1 parent ebb02ad commit 9a4d935
Show file tree
Hide file tree
Showing 7 changed files with 269 additions and 141 deletions.
11 changes: 11 additions & 0 deletions audit/format.go
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,7 @@ func (f *AuditFormatter) FormatRequest(ctx context.Context, w io.Writer, config
Data: req.Data,
PolicyOverride: req.PolicyOverride,
RemoteAddr: getRemoteAddr(req),
RemotePort: getRemotePort(req),
ReplicationCluster: req.ReplicationCluster,
Headers: req.Headers,
ClientCertificateSerialNumber: getClientCertificateSerialNumber(connState),
Expand Down Expand Up @@ -285,6 +286,7 @@ func (f *AuditFormatter) FormatResponse(ctx context.Context, w io.Writer, config
Data: req.Data,
PolicyOverride: req.PolicyOverride,
RemoteAddr: getRemoteAddr(req),
RemotePort: getRemotePort(req),
ClientCertificateSerialNumber: getClientCertificateSerialNumber(connState),
ReplicationCluster: req.ReplicationCluster,
Headers: req.Headers,
Expand Down Expand Up @@ -348,6 +350,7 @@ type AuditRequest struct {
Data map[string]interface{} `json:"data,omitempty"`
PolicyOverride bool `json:"policy_override,omitempty"`
RemoteAddr string `json:"remote_address,omitempty"`
RemotePort int `json:"remote_port,omitempty"`
WrapTTL int `json:"wrap_ttl,omitempty"`
Headers map[string][]string `json:"headers,omitempty"`
ClientCertificateSerialNumber string `json:"client_certificate_serial_number,omitempty"`
Expand Down Expand Up @@ -408,6 +411,14 @@ func getRemoteAddr(req *logical.Request) string {
return ""
}

// getRemotePort safely gets the remote port avoiding a nil pointer
func getRemotePort(req *logical.Request) int {
if req != nil && req.Connection != nil {
return req.Connection.RemotePort
}
return 0
}

func getClientCertificateSerialNumber(connState *tls.ConnectionState) string {
if connState == nil || len(connState.VerifiedChains) == 0 || len(connState.VerifiedChains[0]) == 0 {
return ""
Expand Down
3 changes: 3 additions & 0 deletions changelog/12790.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
```release-note:improvement
audit: The audit logs now contain the port used by the client
```
9 changes: 8 additions & 1 deletion http/logical.go
Original file line number Diff line number Diff line change
Expand Up @@ -530,14 +530,21 @@ WRITE_RESPONSE:
// attaching to a logical request
func getConnection(r *http.Request) (connection *logical.Connection) {
var remoteAddr string
var remotePort int

remoteAddr, _, err := net.SplitHostPort(r.RemoteAddr)
remoteAddr, port, err := net.SplitHostPort(r.RemoteAddr)
if err != nil {
remoteAddr = ""
} else {
remotePort, err = strconv.Atoi(port)
if err != nil {
remotePort = 0
}
}

connection = &logical.Connection{
RemoteAddr: remoteAddr,
RemotePort: remotePort,
ConnState: r.TLS,
}
return
Expand Down
90 changes: 90 additions & 0 deletions http/logical_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ import (
"testing"
"time"

kv "github.com/hashicorp/vault-plugin-secrets-kv"
"github.com/hashicorp/vault/api"
auditFile "github.com/hashicorp/vault/builtin/audit/file"
"github.com/hashicorp/vault/internalshared/configutil"
"github.com/hashicorp/vault/sdk/helper/consts"
"github.com/hashicorp/vault/sdk/helper/logging"
Expand Down Expand Up @@ -500,3 +503,90 @@ func TestLogical_ShouldParseForm(t *testing.T) {
}
}
}

func TestLogical_AuditPort(t *testing.T) {
coreConfig := &vault.CoreConfig{
LogicalBackends: map[string]logical.Factory{
"kv": kv.VersionedKVFactory,
},
AuditBackends: map[string]audit.Factory{
"file": auditFile.Factory,
},
}

cluster := vault.NewTestCluster(t, coreConfig, &vault.TestClusterOptions{
HandlerFunc: Handler,
})

cluster.Start()
defer cluster.Cleanup()

cores := cluster.Cores

core := cores[0].Core
c := cluster.Cores[0].Client
vault.TestWaitActive(t, core)

if err := c.Sys().Mount("kv/", &api.MountInput{
Type: "kv-v2",
}); err != nil {
t.Fatalf("kv-v2 mount attempt failed - err: %#v\n", err)
}

auditLogFile, err := ioutil.TempFile("", "auditport")
if err != nil {
t.Fatal(err)
}

err = c.Sys().EnableAuditWithOptions("file", &api.EnableAuditOptions{
Type: "file",
Options: map[string]string{
"file_path": auditLogFile.Name(),
},
})
if err != nil {
t.Fatalf("failed to enable audit file, err: %#v\n", err)
}

writeData := map[string]interface{}{
"data": map[string]interface{}{
"bar": "a",
},
}

resp, err := c.Logical().Write("kv/data/foo", writeData)
if err != nil {
t.Fatalf("write request failed, err: %#v, resp: %#v\n", err, resp)
}

decoder := json.NewDecoder(auditLogFile)

var auditRecord map[string]interface{}
count := 0
for decoder.Decode(&auditRecord) == nil {
count += 1

// Skip the first line
if count == 1 {
continue
}

auditRequest := map[string]interface{}{}

if req, ok := auditRecord["request"]; ok {
auditRequest = req.(map[string]interface{})
}

if _, ok := auditRequest["remote_address"].(string); !ok {
t.Fatalf("remote_port should be a number, not %T", auditRequest["remote_address"])
}

if _, ok := auditRequest["remote_port"].(float64); !ok {
t.Fatalf("remote_port should be a number, not %T", auditRequest["remote_port"])
}
}

if count != 4 {
t.Fatalf("wrong number of audit entries: %d", count)
}
}
3 changes: 3 additions & 0 deletions sdk/logical/connection.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ type Connection struct {
// RemoteAddr is the network address that sent the request.
RemoteAddr string `json:"remote_addr"`

// RemotePort is the network port that sent the request.
RemotePort int `json:"remote_port"`

// ConnState is the TLS connection state if applicable.
ConnState *tls.ConnectionState `sentinel:""`
}

0 comments on commit 9a4d935

Please sign in to comment.