Implement Secrets

Implement podman secret create, inspect, ls, rm
Implement podman run/create --secret
Secrets are blobs of data that are sensitive.
Currently, the only secret driver supported is filedriver, which means creating a secret stores it in base64 unencrypted in a file.
After creating a secret, a user can use the --secret flag to expose the secret inside the container at /run/secrets/[secretname]
This secret will not be commited to an image on a podman commit

Signed-off-by: Ashley Cui <acui@redhat.com>
This commit is contained in:
Ashley Cui
2021-01-15 01:27:23 -05:00
parent 2aaf631586
commit 832a69b0be
58 changed files with 2962 additions and 7 deletions

View File

@@ -0,0 +1,158 @@
package filedriver
import (
"encoding/json"
"io/ioutil"
"os"
"path/filepath"
"sort"
"github.com/containers/storage/pkg/lockfile"
"github.com/pkg/errors"
)
// secretsDataFile is the file where secrets data/payload will be stored
var secretsDataFile = "secretsdata.json"
// errNoSecretData indicates that there is not data associated with an id
var errNoSecretData = errors.New("no secret data with ID")
// errNoSecretData indicates that there is secret data already associated with an id
var errSecretIDExists = errors.New("secret data with ID already exists")
// Driver is the filedriver object
type Driver struct {
// secretsDataFilePath is the path to the secretsfile
secretsDataFilePath string
// lockfile is the filedriver lockfile
lockfile lockfile.Locker
}
// NewDriver creates a new file driver.
// rootPath is the directory where the secrets data file resides.
func NewDriver(rootPath string) (*Driver, error) {
fileDriver := new(Driver)
fileDriver.secretsDataFilePath = filepath.Join(rootPath, secretsDataFile)
// the lockfile functions requre that the rootPath dir is executable
if err := os.MkdirAll(rootPath, 0700); err != nil {
return nil, err
}
lock, err := lockfile.GetLockfile(filepath.Join(rootPath, "secretsdata.lock"))
if err != nil {
return nil, err
}
fileDriver.lockfile = lock
return fileDriver, nil
}
// List returns all secret IDs
func (d *Driver) List() ([]string, error) {
d.lockfile.Lock()
defer d.lockfile.Unlock()
secretData, err := d.getAllData()
if err != nil {
return nil, err
}
var allID []string
for k := range secretData {
allID = append(allID, k)
}
sort.Strings(allID)
return allID, err
}
// Lookup returns the bytes associated with a secret ID
func (d *Driver) Lookup(id string) ([]byte, error) {
d.lockfile.Lock()
defer d.lockfile.Unlock()
secretData, err := d.getAllData()
if err != nil {
return nil, err
}
if data, ok := secretData[id]; ok {
return data, nil
}
return nil, errors.Wrapf(errNoSecretData, "%s", id)
}
// Store stores the bytes associated with an ID. An error is returned if the ID arleady exists
func (d *Driver) Store(id string, data []byte) error {
d.lockfile.Lock()
defer d.lockfile.Unlock()
secretData, err := d.getAllData()
if err != nil {
return err
}
if _, ok := secretData[id]; ok {
return errors.Wrapf(errSecretIDExists, "%s", id)
}
secretData[id] = data
marshalled, err := json.MarshalIndent(secretData, "", " ")
if err != nil {
return err
}
err = ioutil.WriteFile(d.secretsDataFilePath, marshalled, 0600)
if err != nil {
return err
}
return nil
}
// Delete deletes the secret associated with the specified ID. An error is returned if no matching secret is found.
func (d *Driver) Delete(id string) error {
d.lockfile.Lock()
defer d.lockfile.Unlock()
secretData, err := d.getAllData()
if err != nil {
return err
}
if _, ok := secretData[id]; ok {
delete(secretData, id)
} else {
return errors.Wrap(errNoSecretData, id)
}
marshalled, err := json.MarshalIndent(secretData, "", " ")
if err != nil {
return err
}
err = ioutil.WriteFile(d.secretsDataFilePath, marshalled, 0600)
if err != nil {
return err
}
return nil
}
// getAllData reads the data file and returns all data
func (d *Driver) getAllData() (map[string][]byte, error) {
// check if the db file exists
_, err := os.Stat(d.secretsDataFilePath)
if err != nil {
if os.IsNotExist(err) {
// the file will be created later on a store()
return make(map[string][]byte), nil
} else {
return nil, err
}
}
file, err := os.Open(d.secretsDataFilePath)
if err != nil {
return nil, err
}
defer file.Close()
byteValue, err := ioutil.ReadAll(file)
if err != nil {
return nil, err
}
secretData := new(map[string][]byte)
err = json.Unmarshal([]byte(byteValue), secretData)
if err != nil {
return nil, err
}
return *secretData, nil
}