Skip to content

Commit

Permalink
Merge branch 'main' into 15730-ui-show-task-events-in-the-sidebar
Browse files Browse the repository at this point in the history
  • Loading branch information
philrenaud committed Jan 10, 2023
2 parents 13f45c1 + 17531ca commit 5840333
Show file tree
Hide file tree
Showing 27 changed files with 233 additions and 55 deletions.
3 changes: 3 additions & 0 deletions .changelog/15455.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
```release-note:improvement
scheduler: allow using device IDs in `affinity` and `constraint`
```
3 changes: 3 additions & 0 deletions .changelog/15732.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
```release-note:bug
docker: configure restart policy for bridge network pause container
```
3 changes: 3 additions & 0 deletions .changelog/15735.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
```release-note:improvement
ui: Add a button for expanding the Task sidebar to full width
```
3 changes: 3 additions & 0 deletions .changelog/15745.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
```release-note:improvement
vault: configure Nomad User-Agent on vault clients
```
6 changes: 4 additions & 2 deletions .semgrep/rpc_endpoint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -65,9 +65,11 @@ rules:
# Pattern used by Authenticate method.
# TODO: add authorization steps as well.
- pattern-not-inside: |
authErr := $A.$B.Authenticate($A.ctx, args)
...
... := $A.$B.Authenticate($A.ctx, args.AuthToken)
...
if authErr != nil {
return authErr
}
- metavariable-pattern:
metavariable: $METHOD
patterns:
Expand Down
6 changes: 3 additions & 3 deletions client/fingerprint/vault.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (

log "github.com/hashicorp/go-hclog"
"github.com/hashicorp/nomad/helper"
"github.com/hashicorp/nomad/helper/useragent"
vapi "github.com/hashicorp/vault/api"
)

Expand Down Expand Up @@ -35,18 +36,17 @@ func (f *VaultFingerprint) Fingerprint(req *FingerprintRequest, resp *Fingerprin
return nil
}

// Only create the client once to avoid creating too many connections to
// Vault.
// Only create the client once to avoid creating too many connections to Vault
if f.client == nil {
vaultConfig, err := config.VaultConfig.ApiConfig()
if err != nil {
return fmt.Errorf("Failed to initialize the Vault client config: %v", err)
}

f.client, err = vapi.NewClient(vaultConfig)
if err != nil {
return fmt.Errorf("Failed to initialize Vault client: %s", err)
}
useragent.SetHeaders(f.client)
}

// Connect to vault and parse its information
Expand Down
16 changes: 7 additions & 9 deletions client/vaultclient/vaultclient.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,13 @@ import (
"container/heap"
"fmt"
"math/rand"
"net/http"
"strings"
"sync"
"time"

metrics "github.com/armon/go-metrics"
hclog "github.com/hashicorp/go-hclog"
"github.com/hashicorp/nomad/helper/useragent"
"github.com/hashicorp/nomad/nomad/structs"
"github.com/hashicorp/nomad/nomad/structs/config"
vaultapi "github.com/hashicorp/vault/api"
Expand All @@ -21,7 +21,7 @@ import (
// wrapped tokens will be unwrapped using the vault API client.
type TokenDeriverFunc func(*structs.Allocation, []string, *vaultapi.Client) (map[string]string, error)

// The interface which nomad client uses to interact with vault and
// VaultClient is the interface which nomad client uses to interact with vault and
// periodically renews the tokens and secrets.
type VaultClient interface {
// Start initiates the renewal loop of tokens and secrets
Expand Down Expand Up @@ -151,9 +151,8 @@ func NewVaultClient(config *config.VaultConfig, logger hclog.Logger, tokenDerive
return nil, err
}

client.SetHeaders(http.Header{
"User-Agent": []string{"hashicorp/nomad"},
})
// Set our Nomad user agent
useragent.SetHeaders(client)

// SetHeaders above will replace all headers, make this call second
if config.Namespace != "" {
Expand Down Expand Up @@ -193,7 +192,7 @@ func (c *vaultClient) isRunning() bool {
return c.running
}

// Starts the renewal loop of vault client
// Start starts the renewal loop of vault client
func (c *vaultClient) Start() {
c.lock.Lock()
defer c.lock.Unlock()
Expand All @@ -207,7 +206,7 @@ func (c *vaultClient) Start() {
go c.run()
}

// Stops the renewal loop of vault client
// Stop stops the renewal loop of vault client
func (c *vaultClient) Stop() {
c.lock.Lock()
defer c.lock.Unlock()
Expand Down Expand Up @@ -353,8 +352,7 @@ func (c *vaultClient) renew(req *vaultClientRenewalRequest) error {
var renewalErr error
leaseDuration := req.increment
if req.isToken {
// Set the token in the API client to the one that needs
// renewal
// Set the token in the API client to the one that needs renewal
c.client.SetToken(req.id)

// Renew the token
Expand Down
16 changes: 16 additions & 0 deletions client/vaultclient/vaultclient_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,13 @@ import (

"github.com/hashicorp/nomad/ci"
"github.com/hashicorp/nomad/client/config"
"github.com/hashicorp/nomad/helper/pointer"
"github.com/hashicorp/nomad/helper/testlog"
"github.com/hashicorp/nomad/helper/useragent"
"github.com/hashicorp/nomad/testutil"
vaultapi "github.com/hashicorp/vault/api"
vaultconsts "github.com/hashicorp/vault/sdk/helper/consts"
"github.com/shoenig/test/must"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
Expand Down Expand Up @@ -354,3 +357,16 @@ func TestVaultClient_RenewalTime_Short(t *testing.T) {
assert.Equal(t, 15*time.Second, renewalTime(dice, 30))
assert.Equal(t, 1*time.Second, renewalTime(dice, 2))
}

func TestVaultClient_SetUserAgent(t *testing.T) {
ci.Parallel(t)

conf := config.DefaultConfig()
conf.VaultConfig.Enabled = pointer.Of(true)
logger := testlog.HCLogger(t)
c, err := NewVaultClient(conf.VaultConfig, logger, nil)
must.NoError(t, err)

ua := c.client.Headers().Get("User-Agent")
must.Eq(t, useragent.String(), ua)
}
6 changes: 6 additions & 0 deletions drivers/docker/network.go
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,12 @@ func (d *Driver) createSandboxContainerConfig(allocID string, createSpec *driver
// Set the network mode to none which creates a network namespace
// with only a loopback interface.
NetworkMode: "none",

// Set the restart policy to unless-stopped. The pause container should
// never not be running until Nomad issues a stop.
//
// https://docs.docker.com/engine/reference/run/#restart-policies---restart
RestartPolicy: docker.RestartUnlessStopped(),
},
}, nil
}
Expand Down
6 changes: 4 additions & 2 deletions drivers/docker/network_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,8 @@ func TestDriver_createSandboxContainerConfig(t *testing.T) {
Image: "gcr.io/google_containers/pause-amd64:3.1",
},
HostConfig: &docker.HostConfig{
NetworkMode: "none",
NetworkMode: "none",
RestartPolicy: docker.RestartUnlessStopped(),
},
},
name: "no input hostname",
Expand All @@ -45,7 +46,8 @@ func TestDriver_createSandboxContainerConfig(t *testing.T) {
Hostname: "linux",
},
HostConfig: &docker.HostConfig{
NetworkMode: "none",
NetworkMode: "none",
RestartPolicy: docker.RestartUnlessStopped(),
},
},
name: "supplied input hostname",
Expand Down
11 changes: 6 additions & 5 deletions e2e/e2eutil/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,31 +5,32 @@ import (

capi "github.com/hashicorp/consul/api"
napi "github.com/hashicorp/nomad/api"
"github.com/hashicorp/nomad/helper/useragent"
vapi "github.com/hashicorp/vault/api"

"github.com/stretchr/testify/require"
"github.com/shoenig/test/must"
)

// NomadClient creates a default Nomad client based on the env vars
// from the test environment. Fails the test if it can't be created
func NomadClient(t *testing.T) *napi.Client {
client, err := napi.NewClient(napi.DefaultConfig())
require.NoError(t, err, "could not create Nomad client")
must.NoError(t, err)
return client
}

// ConsulClient creates a default Consul client based on the env vars
// from the test environment. Fails the test if it can't be created
func ConsulClient(t *testing.T) *capi.Client {
client, err := capi.NewClient(capi.DefaultConfig())
require.NoError(t, err, "could not create Consul client")
must.NoError(t, err)
return client
}

// VaultClient creates a default Vault client based on the env vars
// from the test environment. Fails the test if it can't be created
func VaultClient(t *testing.T) *vapi.Client {
client, err := vapi.NewClient(vapi.DefaultConfig())
require.NoError(t, err, "could not create Vault client")
useragent.SetHeaders(client)
must.NoError(t, err)
return client
}
2 changes: 2 additions & 0 deletions e2e/framework/provisioner.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (

capi "github.com/hashicorp/consul/api"
napi "github.com/hashicorp/nomad/api"
"github.com/hashicorp/nomad/helper/useragent"
"github.com/hashicorp/nomad/helper/uuid"
vapi "github.com/hashicorp/vault/api"
)
Expand Down Expand Up @@ -115,6 +116,7 @@ func (p *singleClusterProvisioner) SetupTestCase(t *testing.T, opts SetupOptions
if err != nil && opts.ExpectVault {
return nil, err
}
useragent.SetHeaders(vaultClient)
info.VaultClient = vaultClient
} else if opts.ExpectVault {
return nil, fmt.Errorf("vault client expected but environment variable %s not set",
Expand Down
13 changes: 13 additions & 0 deletions helper/useragent/useragent.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package useragent

import (
"fmt"
"net/http"
"runtime"

"github.com/hashicorp/nomad/version"
Expand All @@ -27,3 +28,15 @@ func String() string {
return fmt.Sprintf("Nomad/%s (+%s; %s)",
versionFunc(), projectURL, rt)
}

// HeaderSetter is anything that implements SetHeaders(http.Header).
type HeaderSetter interface {
SetHeaders(http.Header)
}

// SetHeaders configures the User-Agent http.Header for the client.
func SetHeaders(client HeaderSetter) {
client.SetHeaders(http.Header{
"User-Agent": []string{String()},
})
}
56 changes: 35 additions & 21 deletions nomad/acl.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,32 +15,31 @@ import (
)

// Authenticate extracts an AuthenticatedIdentity from the request context or
// provided token. The caller can extract an acl.ACL, WorkloadIdentity, or other
// identifying token to use for authorization.
// provided token and sets the identity on the request. The caller can extract
// an acl.ACL, WorkloadIdentity, or other identifying tokens to use for
// authorization. Keeping these fields independent rather than merging them into
// an ephemeral ACLToken makes the original of the credential clear to RPC
// handlers, who may have different behavior for internal vs external origins.
//
// Note: when called on the follower we'll be making stale queries, so it's
// possible if the follower is behind that the leader will get a different value
// if an ACL token or allocation's WI has just been created.
func (s *Server) Authenticate(ctx *RPCContext, secretID string) (*structs.AuthenticatedIdentity, error) {

// Previously-connected clients will have a NodeID set and will be a large
// number of the RPCs sent, so we can fast path this case
if ctx != nil && ctx.NodeID != "" {
return &structs.AuthenticatedIdentity{ClientID: ctx.NodeID}, nil
}
func (s *Server) Authenticate(ctx *RPCContext, args structs.RequestWithIdentity) error {

// get the user ACLToken or anonymous token
secretID := args.GetAuthToken()
aclToken, err := s.ResolveSecretToken(secretID)

switch {
case err == nil:
// If ACLs are disabled or we have a non-anonymous token, return that.
if aclToken == nil || aclToken != structs.AnonymousACLToken {
return &structs.AuthenticatedIdentity{ACLToken: aclToken}, nil
args.SetIdentity(&structs.AuthenticatedIdentity{ACLToken: aclToken})
return nil
}

case errors.Is(err, structs.ErrTokenExpired):
return nil, err
return err

case errors.Is(err, structs.ErrTokenInvalid):
// if it's not a UUID it might be an identity claim
Expand All @@ -49,10 +48,11 @@ func (s *Server) Authenticate(ctx *RPCContext, secretID string) (*structs.Authen
// we already know the token wasn't valid for an ACL in the state
// store, so if we get an error at this point we have an invalid
// token and there are no other options but to bail out
return nil, err
return err
}

return &structs.AuthenticatedIdentity{Claims: claims}, nil
args.SetIdentity(&structs.AuthenticatedIdentity{Claims: claims})
return nil

case errors.Is(err, structs.ErrTokenNotFound):
// Check if the secret ID is the leader's secret ID, in which case treat
Expand All @@ -66,26 +66,39 @@ func (s *Server) Authenticate(ctx *RPCContext, secretID string) (*structs.Authen
node, err := s.State().NodeBySecretID(nil, secretID)
if err != nil {
// this is a go-memdb error; shouldn't happen
return nil, fmt.Errorf("could not resolve node secret: %w", err)
return fmt.Errorf("could not resolve node secret: %w", err)
}
if node != nil {
return &structs.AuthenticatedIdentity{ClientID: node.ID}, nil
args.SetIdentity(&structs.AuthenticatedIdentity{ClientID: node.ID})
return nil
}
}

default: // any other error
return nil, fmt.Errorf("could not resolve user: %w", err)
return fmt.Errorf("could not resolve user: %w", err)

}

// If there's no context we're in a "static" handler which only happens for
// cases where the leader is making RPCs internally (volumewatcher and
// deploymentwatcher)
if ctx == nil {
return &structs.AuthenticatedIdentity{ACLToken: aclToken}, nil
args.SetIdentity(&structs.AuthenticatedIdentity{ACLToken: aclToken})
return nil
}

// At this point we either have an anonymous token or an invalid one.

// Previously-connected clients will have a NodeID set on the context, which
// is available for all yamux streams over the same yamux session (and TCP
// connection). This will be a large portion of the RPCs sent, but we can't
// fast-path this at the top of the method, because authenticated HTTP
// requests to the clients will come in over to the same session.
if ctx.NodeID != "" {
args.SetIdentity(&structs.AuthenticatedIdentity{ClientID: ctx.NodeID})
return nil
}

// Unlike clients that provide their Node ID on first connection, server
// RPCs don't include an ID for the server so we identify servers by cert
// and IP address.
Expand All @@ -99,22 +112,23 @@ func (s *Server) Authenticate(ctx *RPCContext, secretID string) (*structs.Authen
if ctx.Session != nil {
remoteAddr, ok = ctx.Session.RemoteAddr().(*net.TCPAddr)
if !ok {
return nil, errors.New("session address was not a TCP address")
return errors.New("session address was not a TCP address")
}
}
if remoteAddr == nil && ctx.Conn != nil {
remoteAddr, ok = ctx.Conn.RemoteAddr().(*net.TCPAddr)
if !ok {
return nil, errors.New("session address was not a TCP address")
return errors.New("session address was not a TCP address")
}
}
if remoteAddr != nil {
identity.RemoteIP = remoteAddr.IP
return identity, nil
args.SetIdentity(identity)
return nil
}

s.logger.Error("could not authenticate RPC request or determine remote address")
return nil, structs.ErrPermissionDenied
return structs.ErrPermissionDenied
}

func (s *Server) ResolveACL(aclToken *structs.ACLToken) (*acl.ACL, error) {
Expand Down
Loading

0 comments on commit 5840333

Please sign in to comment.