Display secret to user in inpspect

It is pretty complicated to display the secret on the host, but is
not really secured. This patch makes it easier to examine the secret.

Partial fix for https://github.com/containers/podman/issues/18667

Signed-off-by: Daniel J Walsh <dwalsh@redhat.com>
This commit is contained in:
Daniel J Walsh
2023-06-27 09:28:16 -04:00
parent 2ac2ba9e4f
commit bf60bb0731
13 changed files with 95 additions and 14 deletions

View File

@ -43,6 +43,8 @@ Created at: {{.CreatedAt}}
Updated at: {{.UpdatedAt}}`
)
var inspectOpts = entities.SecretInspectOptions{}
func init() {
registry.Commands = append(registry.Commands, registry.CliCommand{
Command: inspectCmd,
@ -53,12 +55,14 @@ func init() {
flags.StringVarP(&format, formatFlagName, "f", "", "Format inspect output using Go template")
_ = inspectCmd.RegisterFlagCompletionFunc(formatFlagName, common.AutocompleteFormat(&entities.SecretInfoReport{}))
flags.BoolVar(&inspectOpts.ShowSecret, "showsecret", false, "Display the secret")
prettyFlagName := "pretty"
flags.BoolVar(&pretty, prettyFlagName, false, "Print inspect output in human-readable format")
}
func inspect(cmd *cobra.Command, args []string) error {
inspected, errs, _ := registry.ContainerEngine().SecretInspect(context.Background(), args)
inspected, errs, _ := registry.ContainerEngine().SecretInspect(context.Background(), args, inspectOpts)
// always print valid list
if len(inspected) == 0 {

View File

@ -23,6 +23,7 @@ Format secret output using Go template.
|--------------------------|-------------------------------------------------------------------|
| .CreatedAt | When secret was created (relative timestamp, human-readable) |
| .ID | ID of secret |
| .SecretData | Secret Data (Displayed only with --showsecret option) |
| .Spec ... | Details of secret |
| .Spec.Driver | Driver info |
| .Spec.Driver.Name | Driver name (string) |
@ -39,12 +40,16 @@ Print usage statement.
Print inspect output in human-readable format
#### **--showsecret**
Display secret data
## EXAMPLES
```
$ podman secret inspect mysecret
$ podman secret inspect --format "{{.Name} {{.Scope}}" mysecret
$ podman secret inspect --showsecret --format "{{.Name} {{.SecretData}}" mysecret
```
## SEE ALSO

View File

@ -34,6 +34,7 @@ Valid placeholders for the Go template are listed below:
| ------------------------ | ----------------------------------------------------------------- |
| .CreatedAt | When secret was created (relative timestamp, human-readable) |
| .ID | ID of secret |
| .SecretData | Secret Data (Displayed only with --showsecret option) |
| .Spec ... | Details of secret |
| .Spec.Driver | Driver info |
| .Spec.Driver.Name | Driver name (string) |

View File

@ -15,6 +15,7 @@ import (
"github.com/containers/podman/v4/pkg/domain/entities"
"github.com/containers/podman/v4/pkg/domain/infra/abi"
"github.com/containers/podman/v4/pkg/util"
"github.com/gorilla/schema"
)
func ListSecrets(w http.ResponseWriter, r *http.Request) {
@ -51,11 +52,25 @@ func ListSecrets(w http.ResponseWriter, r *http.Request) {
}
func InspectSecret(w http.ResponseWriter, r *http.Request) {
decoder := r.Context().Value(api.DecoderKey).(*schema.Decoder)
runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime)
name := utils.GetName(r)
names := []string{name}
query := struct {
ShowSecret bool `schema:"showsecret"`
}{
// override any golang type defaults
}
if err := decoder.Decode(&query, r.URL.Query()); err != nil {
utils.Error(w, http.StatusBadRequest, fmt.Errorf("failed to parse parameters for %s: %w", r.URL.String(), err))
return
}
ic := abi.ContainerEngine{Libpod: runtime}
reports, errs, err := ic.SecretInspect(r.Context(), names)
opts := entities.SecretInspectOptions{}
opts.ShowSecret = query.ShowSecret
reports, errs, err := ic.SecretInspect(r.Context(), names, opts)
if err != nil {
utils.InternalServerError(w, err)
return

View File

@ -79,6 +79,11 @@ func (s *APIServer) registerSecretHandlers(r *mux.Router) error {
// type: string
// required: true
// description: the name or ID of the secret
// - in: query
// name: showsecret
// type: boolean
// description: Display Secret
// default: false
// produces:
// - application/json
// responses:

View File

@ -33,6 +33,9 @@ func List(ctx context.Context, options *ListOptions) ([]*entities.SecretInfoRepo
// Inspect returns low-level information about a secret.
func Inspect(ctx context.Context, nameOrID string, options *InspectOptions) (*entities.SecretInfoReport, error) {
if options == nil {
options = new(InspectOptions)
}
var (
inspect *entities.SecretInfoReport
)
@ -40,12 +43,15 @@ func Inspect(ctx context.Context, nameOrID string, options *InspectOptions) (*en
if err != nil {
return nil, err
}
response, err := conn.DoRequest(ctx, nil, http.MethodGet, "/secrets/%s/json", nil, nil, nameOrID)
params, err := options.ToParams()
if err != nil {
return nil, err
}
response, err := conn.DoRequest(ctx, nil, http.MethodGet, "/secrets/%s/json", params, nil, nameOrID)
if err != nil {
return inspect, err
}
defer response.Body.Close()
return inspect, response.Process(&inspect)
}

View File

@ -11,6 +11,7 @@ type ListOptions struct {
//
//go:generate go run ../generator/generator.go InspectOptions
type InspectOptions struct {
ShowSecret *bool
}
// RemoveOptions are optional options for removing secrets

View File

@ -16,3 +16,18 @@ func (o *InspectOptions) Changed(fieldName string) bool {
func (o *InspectOptions) ToParams() (url.Values, error) {
return util.ToParams(o)
}
// WithShowSecret set field ShowSecret to given value
func (o *InspectOptions) WithShowSecret(value bool) *InspectOptions {
o.ShowSecret = &value
return o
}
// GetShowSecret returns value of field ShowSecret
func (o *InspectOptions) GetShowSecret() bool {
if o.ShowSecret == nil {
var z bool
return z
}
return *o.ShowSecret
}

View File

@ -93,7 +93,7 @@ type ContainerEngine interface { //nolint:interfacebloat
PodUnpause(ctx context.Context, namesOrIds []string, options PodunpauseOptions) ([]*PodUnpauseReport, error)
SetupRootless(ctx context.Context, noMoveProcess bool) error
SecretCreate(ctx context.Context, name string, reader io.Reader, options SecretCreateOptions) (*SecretCreateReport, error)
SecretInspect(ctx context.Context, nameOrIDs []string) ([]*SecretInfoReport, []error, error)
SecretInspect(ctx context.Context, nameOrIDs []string, options SecretInspectOptions) ([]*SecretInfoReport, []error, error)
SecretList(ctx context.Context, opts SecretListRequest) ([]*SecretInfoReport, error)
SecretRm(ctx context.Context, nameOrID []string, opts SecretRmOptions) ([]*SecretRmReport, error)
SecretExists(ctx context.Context, nameOrID string) (*BoolReport, error)

View File

@ -16,6 +16,10 @@ type SecretCreateOptions struct {
Labels map[string]string
}
type SecretInspectOptions struct {
ShowSecret bool
}
type SecretListRequest struct {
Filters map[string][]string
}
@ -38,10 +42,11 @@ type SecretRmReport struct {
}
type SecretInfoReport struct {
ID string
CreatedAt time.Time
UpdatedAt time.Time
Spec SecretSpec
ID string
CreatedAt time.Time
UpdatedAt time.Time
Spec SecretSpec
SecretData string `json:"SecretData,omitempty"`
}
type SecretInfoReportCompat struct {

View File

@ -58,7 +58,11 @@ func (ic *ContainerEngine) SecretCreate(ctx context.Context, name string, reader
}, nil
}
func (ic *ContainerEngine) SecretInspect(ctx context.Context, nameOrIDs []string) ([]*entities.SecretInfoReport, []error, error) {
func (ic *ContainerEngine) SecretInspect(ctx context.Context, nameOrIDs []string, options entities.SecretInspectOptions) ([]*entities.SecretInfoReport, []error, error) {
var (
secret *secrets.Secret
data []byte
)
manager, err := ic.Libpod.SecretsManager()
if err != nil {
return nil, nil, err
@ -66,7 +70,11 @@ func (ic *ContainerEngine) SecretInspect(ctx context.Context, nameOrIDs []string
errs := make([]error, 0, len(nameOrIDs))
reports := make([]*entities.SecretInfoReport, 0, len(nameOrIDs))
for _, nameOrID := range nameOrIDs {
secret, err := manager.Lookup(nameOrID)
if options.ShowSecret {
secret, data, err = manager.LookupSecretData(nameOrID)
} else {
secret, err = manager.Lookup(nameOrID)
}
if err != nil {
if strings.Contains(err.Error(), "no such secret") {
errs = append(errs, err)
@ -90,6 +98,7 @@ func (ic *ContainerEngine) SecretInspect(ctx context.Context, nameOrIDs []string
},
Labels: secret.Labels,
},
SecretData: string(data),
}
reports = append(reports, report)
}

View File

@ -23,11 +23,14 @@ func (ic *ContainerEngine) SecretCreate(ctx context.Context, name string, reader
return created, nil
}
func (ic *ContainerEngine) SecretInspect(ctx context.Context, nameOrIDs []string) ([]*entities.SecretInfoReport, []error, error) {
func (ic *ContainerEngine) SecretInspect(ctx context.Context, nameOrIDs []string, options entities.SecretInspectOptions) ([]*entities.SecretInfoReport, []error, error) {
allInspect := make([]*entities.SecretInfoReport, 0, len(nameOrIDs))
errs := make([]error, 0, len(nameOrIDs))
opts := new(secrets.InspectOptions).
WithShowSecret(options.ShowSecret)
for _, name := range nameOrIDs {
inspected, err := secrets.Inspect(ic.ClientCtx, name, nil)
inspected, err := secrets.Inspect(ic.ClientCtx, name, opts)
if err != nil {
errModel, ok := err.(*errorhandling.ErrorModel)
if !ok {

View File

@ -6,6 +6,7 @@ import (
"path/filepath"
. "github.com/containers/podman/v4/test/utils"
"github.com/containers/storage/pkg/stringid"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
. "github.com/onsi/gomega/gexec"
@ -48,8 +49,9 @@ var _ = Describe("Podman secret", func() {
})
It("podman secret inspect", func() {
random := stringid.GenerateRandomID()
secretFilePath := filepath.Join(podmanTest.TempDir, "secret")
err := os.WriteFile(secretFilePath, []byte("mysecret"), 0755)
err := os.WriteFile(secretFilePath, []byte(random), 0755)
Expect(err).ToNot(HaveOccurred())
session := podmanTest.Podman([]string{"secret", "create", "a", secretFilePath})
@ -61,6 +63,16 @@ var _ = Describe("Podman secret", func() {
inspect.WaitWithDefaultTimeout()
Expect(inspect).Should(Exit(0))
Expect(inspect.OutputToString()).To(BeValidJSON())
inspect = podmanTest.Podman([]string{"secret", "inspect", "--format", "{{ .SecretData }}", secrID})
inspect.WaitWithDefaultTimeout()
Expect(inspect).Should(Exit(0))
Expect(inspect.OutputToString()).To(Equal(""))
inspect = podmanTest.Podman([]string{"secret", "inspect", "--showsecret", "--format", "{{ .SecretData }}", secrID})
inspect.WaitWithDefaultTimeout()
Expect(inspect).Should(Exit(0))
Expect(inspect.OutputToString()).To(Equal(random))
})
It("podman secret inspect with --format", func() {