Introduce data expiry in Redis
Signed-off-by: Knut Ahlers <knut@ahlers.me>
This commit is contained in:
parent
1dcc254a17
commit
0aba89834c
2 changed files with 71 additions and 6 deletions
|
@ -23,7 +23,10 @@ For a better setup you can choose the backend which is used to store the secrets
|
||||||
- `mem` - In memory storage (wiped on restart of the daemon)
|
- `mem` - In memory storage (wiped on restart of the daemon)
|
||||||
- `redis` - Storing the secrets in a hash under one key
|
- `redis` - Storing the secrets in a hash under one key
|
||||||
- `REDIS_URL` - Redis connection string `tcp://auth:PWD@HOST:PORT/DB`
|
- `REDIS_URL` - Redis connection string `tcp://auth:PWD@HOST:PORT/DB`
|
||||||
- `REDIS_KEY` - Key to store the hash in (Default `io.luzifer.ots`)
|
- `REDIS_EXPIRY` - Expiry of the keys in seconds (Default `0` = no expiry)
|
||||||
|
- `REDIS_KEY` - Key prefix to store the keys under (Default `io.luzifer.ots`)
|
||||||
|
|
||||||
|
**Hint:** Starting in `v0.7.0` the secrets in Redis are no longer stored in a hash but in own keys. This allows for individual expiry. At the first start of `v0.7.0` the old data will be migrated automatically and afterwards be subject of expiry if you set `REDIS_EXPIRY`. My hosted instance uses an expiry of 90d (= 7776000s).
|
||||||
|
|
||||||
## Localize to your own language
|
## Localize to your own language
|
||||||
|
|
||||||
|
|
|
@ -3,8 +3,11 @@ package main
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/satori/uuid"
|
"github.com/satori/uuid"
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
"github.com/xuyu/goredis"
|
"github.com/xuyu/goredis"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -22,9 +25,57 @@ func newStorageRedis() (storage, error) {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return &storageRedis{
|
s := &storageRedis{
|
||||||
conn: c,
|
conn: c,
|
||||||
}, nil
|
}
|
||||||
|
|
||||||
|
if err := s.migrate(); err != nil { // Move from the old to the new storage format
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return s, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s storageRedis) migrate() error {
|
||||||
|
t, err := s.conn.Type(s.redisKey())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Printf("Key %q type: %s", s.redisKey(), t)
|
||||||
|
|
||||||
|
if t == "hash" {
|
||||||
|
hashs, err := s.conn.HGetAll(s.redisKey())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
for k, v := range hashs {
|
||||||
|
if err := s.writeKey(k, v); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err = s.conn.Del(s.redisKey()); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s storageRedis) redisExpiry() int {
|
||||||
|
exp := os.Getenv("REDIS_EXPIRY")
|
||||||
|
if exp == "" {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
e, err := strconv.ParseInt(exp, 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
return int(e)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s storageRedis) redisKey() string {
|
func (s storageRedis) redisKey() string {
|
||||||
|
@ -38,13 +89,13 @@ func (s storageRedis) redisKey() string {
|
||||||
|
|
||||||
func (s storageRedis) Create(secret string) (string, error) {
|
func (s storageRedis) Create(secret string) (string, error) {
|
||||||
id := uuid.NewV4().String()
|
id := uuid.NewV4().String()
|
||||||
_, err := s.conn.HSet(s.redisKey(), id, secret)
|
err := s.writeKey(id, secret)
|
||||||
|
|
||||||
return id, err
|
return id, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s storageRedis) ReadAndDestroy(id string) (string, error) {
|
func (s storageRedis) ReadAndDestroy(id string) (string, error) {
|
||||||
secret, err := s.conn.HGet(s.redisKey(), id)
|
secret, err := s.conn.Get(strings.Join([]string{s.redisKey(), id}, ":"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
@ -53,6 +104,17 @@ func (s storageRedis) ReadAndDestroy(id string) (string, error) {
|
||||||
return "", errSecretNotFound
|
return "", errSecretNotFound
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = s.conn.HDel(s.redisKey(), id)
|
_, err = s.conn.Del(strings.Join([]string{s.redisKey(), id}, ":"))
|
||||||
return string(secret), err
|
return string(secret), err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s storageRedis) writeKey(id, value string) error {
|
||||||
|
return s.conn.Set(
|
||||||
|
strings.Join([]string{s.redisKey(), id}, ":"), // Key
|
||||||
|
value, // Secret
|
||||||
|
s.redisExpiry(), // Expiry in seconds
|
||||||
|
0, // Expiry milliseconds
|
||||||
|
false, // MustExist
|
||||||
|
true, // MustNotExist
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue