github.imxd.top/hashicorp/consul@v1.4.5/agent/consul/kvs_endpoint.go (about) 1 package consul 2 3 import ( 4 "fmt" 5 "time" 6 7 "github.com/armon/go-metrics" 8 "github.com/hashicorp/consul/acl" 9 "github.com/hashicorp/consul/agent/consul/state" 10 "github.com/hashicorp/consul/agent/structs" 11 "github.com/hashicorp/consul/api" 12 "github.com/hashicorp/consul/sentinel" 13 "github.com/hashicorp/go-memdb" 14 ) 15 16 // KVS endpoint is used to manipulate the Key-Value store 17 type KVS struct { 18 srv *Server 19 } 20 21 // preApply does all the verification of a KVS update that is performed BEFORE 22 // we submit as a Raft log entry. This includes enforcing the lock delay which 23 // must only be done on the leader. 24 func kvsPreApply(srv *Server, rule acl.Authorizer, op api.KVOp, dirEnt *structs.DirEntry) (bool, error) { 25 // Verify the entry. 26 27 if dirEnt.Key == "" && op != api.KVDeleteTree { 28 return false, fmt.Errorf("Must provide key") 29 } 30 31 // Apply the ACL policy if any. 32 if rule != nil { 33 switch op { 34 case api.KVDeleteTree: 35 if !rule.KeyWritePrefix(dirEnt.Key) { 36 return false, acl.ErrPermissionDenied 37 } 38 39 case api.KVGet, api.KVGetTree: 40 // Filtering for GETs is done on the output side. 41 42 case api.KVCheckSession, api.KVCheckIndex: 43 // These could reveal information based on the outcome 44 // of the transaction, and they operate on individual 45 // keys so we check them here. 46 if !rule.KeyRead(dirEnt.Key) { 47 return false, acl.ErrPermissionDenied 48 } 49 50 default: 51 scope := func() map[string]interface{} { 52 return sentinel.ScopeKVUpsert(dirEnt.Key, dirEnt.Value, dirEnt.Flags) 53 } 54 if !rule.KeyWrite(dirEnt.Key, scope) { 55 return false, acl.ErrPermissionDenied 56 } 57 } 58 } 59 60 // If this is a lock, we must check for a lock-delay. Since lock-delay 61 // is based on wall-time, each peer would expire the lock-delay at a slightly 62 // different time. This means the enforcement of lock-delay cannot be done 63 // after the raft log is committed as it would lead to inconsistent FSMs. 64 // Instead, the lock-delay must be enforced before commit. This means that 65 // only the wall-time of the leader node is used, preventing any inconsistencies. 66 if op == api.KVLock { 67 state := srv.fsm.State() 68 expires := state.KVSLockDelay(dirEnt.Key) 69 if expires.After(time.Now()) { 70 srv.logger.Printf("[WARN] consul.kvs: Rejecting lock of %s due to lock-delay until %v", 71 dirEnt.Key, expires) 72 return false, nil 73 } 74 } 75 76 return true, nil 77 } 78 79 // Apply is used to apply a KVS update request to the data store. 80 func (k *KVS) Apply(args *structs.KVSRequest, reply *bool) error { 81 if done, err := k.srv.forward("KVS.Apply", args, args, reply); done { 82 return err 83 } 84 defer metrics.MeasureSince([]string{"kvs", "apply"}, time.Now()) 85 86 // Perform the pre-apply checks. 87 acl, err := k.srv.ResolveToken(args.Token) 88 if err != nil { 89 return err 90 } 91 ok, err := kvsPreApply(k.srv, acl, args.Op, &args.DirEnt) 92 if err != nil { 93 return err 94 } 95 if !ok { 96 *reply = false 97 return nil 98 } 99 100 // Apply the update. 101 resp, err := k.srv.raftApply(structs.KVSRequestType, args) 102 if err != nil { 103 k.srv.logger.Printf("[ERR] consul.kvs: Apply failed: %v", err) 104 return err 105 } 106 if respErr, ok := resp.(error); ok { 107 return respErr 108 } 109 110 // Check if the return type is a bool. 111 if respBool, ok := resp.(bool); ok { 112 *reply = respBool 113 } 114 return nil 115 } 116 117 // Get is used to lookup a single key. 118 func (k *KVS) Get(args *structs.KeyRequest, reply *structs.IndexedDirEntries) error { 119 if done, err := k.srv.forward("KVS.Get", args, args, reply); done { 120 return err 121 } 122 123 aclRule, err := k.srv.ResolveToken(args.Token) 124 if err != nil { 125 return err 126 } 127 return k.srv.blockingQuery( 128 &args.QueryOptions, 129 &reply.QueryMeta, 130 func(ws memdb.WatchSet, state *state.Store) error { 131 index, ent, err := state.KVSGet(ws, args.Key) 132 if err != nil { 133 return err 134 } 135 if aclRule != nil && !aclRule.KeyRead(args.Key) { 136 return acl.ErrPermissionDenied 137 } 138 139 if ent == nil { 140 // Must provide non-zero index to prevent blocking 141 // Index 1 is impossible anyways (due to Raft internals) 142 if index == 0 { 143 reply.Index = 1 144 } else { 145 reply.Index = index 146 } 147 reply.Entries = nil 148 } else { 149 reply.Index = ent.ModifyIndex 150 reply.Entries = structs.DirEntries{ent} 151 } 152 return nil 153 }) 154 } 155 156 // List is used to list all keys with a given prefix. 157 func (k *KVS) List(args *structs.KeyRequest, reply *structs.IndexedDirEntries) error { 158 if done, err := k.srv.forward("KVS.List", args, args, reply); done { 159 return err 160 } 161 162 aclToken, err := k.srv.ResolveToken(args.Token) 163 if err != nil { 164 return err 165 } 166 167 if aclToken != nil && k.srv.config.ACLEnableKeyListPolicy && !aclToken.KeyList(args.Key) { 168 return acl.ErrPermissionDenied 169 } 170 171 return k.srv.blockingQuery( 172 &args.QueryOptions, 173 &reply.QueryMeta, 174 func(ws memdb.WatchSet, state *state.Store) error { 175 index, ent, err := state.KVSList(ws, args.Key) 176 if err != nil { 177 return err 178 } 179 if aclToken != nil { 180 ent = FilterDirEnt(aclToken, ent) 181 } 182 183 if len(ent) == 0 { 184 // Must provide non-zero index to prevent blocking 185 // Index 1 is impossible anyways (due to Raft internals) 186 if index == 0 { 187 reply.Index = 1 188 } else { 189 reply.Index = index 190 } 191 reply.Entries = nil 192 } else { 193 reply.Index = index 194 reply.Entries = ent 195 } 196 return nil 197 }) 198 } 199 200 // ListKeys is used to list all keys with a given prefix to a separator. 201 func (k *KVS) ListKeys(args *structs.KeyListRequest, reply *structs.IndexedKeyList) error { 202 if done, err := k.srv.forward("KVS.ListKeys", args, args, reply); done { 203 return err 204 } 205 206 aclToken, err := k.srv.ResolveToken(args.Token) 207 if err != nil { 208 return err 209 } 210 211 if aclToken != nil && k.srv.config.ACLEnableKeyListPolicy && !aclToken.KeyList(args.Prefix) { 212 return acl.ErrPermissionDenied 213 } 214 215 return k.srv.blockingQuery( 216 &args.QueryOptions, 217 &reply.QueryMeta, 218 func(ws memdb.WatchSet, state *state.Store) error { 219 index, keys, err := state.KVSListKeys(ws, args.Prefix, args.Seperator) 220 if err != nil { 221 return err 222 } 223 224 // Must provide non-zero index to prevent blocking 225 // Index 1 is impossible anyways (due to Raft internals) 226 if index == 0 { 227 reply.Index = 1 228 } else { 229 reply.Index = index 230 } 231 232 if aclToken != nil { 233 keys = FilterKeys(aclToken, keys) 234 } 235 reply.Keys = keys 236 return nil 237 }) 238 }