/
dynamic_system_view.go
255 lines (215 loc) · 6.92 KB
/
dynamic_system_view.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
package vault
import (
"context"
"fmt"
"time"
"github.com/hashicorp/errwrap"
"github.com/hashicorp/vault/helper/consts"
"github.com/hashicorp/vault/helper/license"
"github.com/hashicorp/vault/helper/namespace"
"github.com/hashicorp/vault/helper/pluginutil"
"github.com/hashicorp/vault/helper/wrapping"
"github.com/hashicorp/vault/logical"
"github.com/hashicorp/vault/version"
)
type dynamicSystemView struct {
core *Core
mountEntry *MountEntry
}
func (d dynamicSystemView) DefaultLeaseTTL() time.Duration {
def, _ := d.fetchTTLs()
return def
}
func (d dynamicSystemView) MaxLeaseTTL() time.Duration {
_, max := d.fetchTTLs()
return max
}
func (d dynamicSystemView) SudoPrivilege(ctx context.Context, path string, token string) bool {
// Resolve the token policy
te, err := d.core.tokenStore.Lookup(ctx, token)
if err != nil {
d.core.logger.Error("failed to lookup token", "error", err)
return false
}
// Ensure the token is valid
if te == nil {
d.core.logger.Error("entry not found for given token")
return false
}
policies := make(map[string][]string)
// Add token policies
policies[te.NamespaceID] = append(policies[te.NamespaceID], te.Policies...)
tokenNS, err := NamespaceByID(ctx, te.NamespaceID, d.core)
if err != nil {
d.core.logger.Error("failed to lookup token namespace", "error", err)
return false
}
if tokenNS == nil {
d.core.logger.Error("failed to lookup token namespace", "error", namespace.ErrNoNamespace)
return false
}
// Add identity policies from all the namespaces
entity, identityPolicies, err := d.core.fetchEntityAndDerivedPolicies(ctx, tokenNS, te.EntityID)
if err != nil {
d.core.logger.Error("failed to fetch identity policies", "error", err)
return false
}
for nsID, nsPolicies := range identityPolicies {
policies[nsID] = append(policies[nsID], nsPolicies...)
}
tokenCtx := namespace.ContextWithNamespace(ctx, tokenNS)
// Construct the corresponding ACL object. Derive and use a new context that
// uses the req.ClientToken's namespace
acl, err := d.core.policyStore.ACL(tokenCtx, entity, policies)
if err != nil {
d.core.logger.Error("failed to retrieve ACL for token's policies", "token_policies", te.Policies, "error", err)
return false
}
// The operation type isn't important here as this is run from a path the
// user has already been given access to; we only care about whether they
// have sudo
req := new(logical.Request)
req.Operation = logical.ReadOperation
req.Path = path
authResults := acl.AllowOperation(ctx, req, true)
return authResults.RootPrivs
}
// TTLsByPath returns the default and max TTLs corresponding to a particular
// mount point, or the system default
func (d dynamicSystemView) fetchTTLs() (def, max time.Duration) {
def = d.core.defaultLeaseTTL
max = d.core.maxLeaseTTL
if d.mountEntry != nil {
if d.mountEntry.Config.DefaultLeaseTTL != 0 {
def = d.mountEntry.Config.DefaultLeaseTTL
}
if d.mountEntry.Config.MaxLeaseTTL != 0 {
max = d.mountEntry.Config.MaxLeaseTTL
}
}
return
}
// Tainted indicates that the mount is in the process of being removed
func (d dynamicSystemView) Tainted() bool {
return d.mountEntry.Tainted
}
// CachingDisabled indicates whether to use caching behavior
func (d dynamicSystemView) CachingDisabled() bool {
return d.core.cachingDisabled || (d.mountEntry != nil && d.mountEntry.Config.ForceNoCache)
}
func (d dynamicSystemView) LocalMount() bool {
return d.mountEntry != nil && d.mountEntry.Local
}
// Checks if this is a primary Vault instance. Caller should hold the stateLock
// in read mode.
func (d dynamicSystemView) ReplicationState() consts.ReplicationState {
state := d.core.ReplicationState()
if d.core.perfStandby {
state |= consts.ReplicationPerformanceStandby
}
return state
}
func (d dynamicSystemView) HasFeature(feature license.Features) bool {
return d.core.HasFeature(feature)
}
// ResponseWrapData wraps the given data in a cubbyhole and returns the
// token used to unwrap.
func (d dynamicSystemView) ResponseWrapData(ctx context.Context, data map[string]interface{}, ttl time.Duration, jwt bool) (*wrapping.ResponseWrapInfo, error) {
req := &logical.Request{
Operation: logical.CreateOperation,
Path: "sys/wrapping/wrap",
}
resp := &logical.Response{
WrapInfo: &wrapping.ResponseWrapInfo{
TTL: ttl,
},
Data: data,
}
if jwt {
resp.WrapInfo.Format = "jwt"
}
_, err := d.core.wrapInCubbyhole(ctx, req, resp, nil)
if err != nil {
return nil, err
}
return resp.WrapInfo, nil
}
// LookupPlugin looks for a plugin with the given name in the plugin catalog. It
// returns a PluginRunner or an error if no plugin was found.
func (d dynamicSystemView) LookupPlugin(ctx context.Context, name string, pluginType consts.PluginType) (*pluginutil.PluginRunner, error) {
if d.core == nil {
return nil, fmt.Errorf("system view core is nil")
}
if d.core.pluginCatalog == nil {
return nil, fmt.Errorf("system view core plugin catalog is nil")
}
r, err := d.core.pluginCatalog.Get(ctx, name, pluginType)
if err != nil {
return nil, err
}
if r == nil {
return nil, errwrap.Wrapf(fmt.Sprintf("{{err}}: %s", name), ErrPluginNotFound)
}
return r, nil
}
// MlockEnabled returns the configuration setting for enabling mlock on plugins.
func (d dynamicSystemView) MlockEnabled() bool {
return d.core.enableMlock
}
func (d dynamicSystemView) EntityInfo(entityID string) (*logical.Entity, error) {
// Requests from token created from the token backend will not have entity information.
// Return missing entity instead of error when requesting from MemDB.
if entityID == "" {
return nil, nil
}
if d.core == nil {
return nil, fmt.Errorf("system view core is nil")
}
if d.core.identityStore == nil {
return nil, fmt.Errorf("system view identity store is nil")
}
// Retrieve the entity from MemDB
entity, err := d.core.identityStore.MemDBEntityByID(entityID, false)
if err != nil {
return nil, err
}
if entity == nil {
return nil, nil
}
// Return a subset of the data
ret := &logical.Entity{
ID: entity.ID,
Name: entity.Name,
}
if entity.Metadata != nil {
ret.Metadata = make(map[string]string, len(entity.Metadata))
for k, v := range entity.Metadata {
ret.Metadata[k] = v
}
}
aliases := make([]*logical.Alias, len(entity.Aliases))
for i, a := range entity.Aliases {
alias := &logical.Alias{
MountAccessor: a.MountAccessor,
Name: a.Name,
}
// MountType is not stored with the entity and must be looked up
if mount := d.core.router.validateMountByAccessor(a.MountAccessor); mount != nil {
alias.MountType = mount.MountType
}
if a.Metadata != nil {
alias.Metadata = make(map[string]string, len(a.Metadata))
for k, v := range a.Metadata {
alias.Metadata[k] = v
}
}
aliases[i] = alias
}
ret.Aliases = aliases
return ret, nil
}
func (d dynamicSystemView) PluginEnv(_ context.Context) (*logical.PluginEnvironment, error) {
return &logical.PluginEnvironment{
VaultVersion: version.GetVersion().Version,
}, nil
}