mirror of
https://github.com/containers/podman.git
synced 2025-07-02 00:30:00 +08:00
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:
@ -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 {
|
||||
|
@ -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
|
||||
|
@ -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) |
|
||||
|
@ -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
|
||||
|
@ -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:
|
||||
|
@ -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)
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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 {
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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() {
|
||||
|
Reference in New Issue
Block a user