mirror of https://github.com/containers/podman.git
Merge pull request #19011 from rhatdan/secret1
Display secret to user in inspect
This commit is contained in:
commit
455c7c8264
|
|
@ -43,6 +43,8 @@ Created at: {{.CreatedAt}}
|
||||||
Updated at: {{.UpdatedAt}}`
|
Updated at: {{.UpdatedAt}}`
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var inspectOpts = entities.SecretInspectOptions{}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
registry.Commands = append(registry.Commands, registry.CliCommand{
|
registry.Commands = append(registry.Commands, registry.CliCommand{
|
||||||
Command: inspectCmd,
|
Command: inspectCmd,
|
||||||
|
|
@ -53,12 +55,14 @@ func init() {
|
||||||
flags.StringVarP(&format, formatFlagName, "f", "", "Format inspect output using Go template")
|
flags.StringVarP(&format, formatFlagName, "f", "", "Format inspect output using Go template")
|
||||||
_ = inspectCmd.RegisterFlagCompletionFunc(formatFlagName, common.AutocompleteFormat(&entities.SecretInfoReport{}))
|
_ = inspectCmd.RegisterFlagCompletionFunc(formatFlagName, common.AutocompleteFormat(&entities.SecretInfoReport{}))
|
||||||
|
|
||||||
|
flags.BoolVar(&inspectOpts.ShowSecret, "showsecret", false, "Display the secret")
|
||||||
|
|
||||||
prettyFlagName := "pretty"
|
prettyFlagName := "pretty"
|
||||||
flags.BoolVar(&pretty, prettyFlagName, false, "Print inspect output in human-readable format")
|
flags.BoolVar(&pretty, prettyFlagName, false, "Print inspect output in human-readable format")
|
||||||
}
|
}
|
||||||
|
|
||||||
func inspect(cmd *cobra.Command, args []string) error {
|
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
|
// always print valid list
|
||||||
if len(inspected) == 0 {
|
if len(inspected) == 0 {
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,7 @@ Format secret output using Go template.
|
||||||
|--------------------------|-------------------------------------------------------------------|
|
|--------------------------|-------------------------------------------------------------------|
|
||||||
| .CreatedAt | When secret was created (relative timestamp, human-readable) |
|
| .CreatedAt | When secret was created (relative timestamp, human-readable) |
|
||||||
| .ID | ID of secret |
|
| .ID | ID of secret |
|
||||||
|
| .SecretData | Secret Data (Displayed only with --showsecret option) |
|
||||||
| .Spec ... | Details of secret |
|
| .Spec ... | Details of secret |
|
||||||
| .Spec.Driver | Driver info |
|
| .Spec.Driver | Driver info |
|
||||||
| .Spec.Driver.Name | Driver name (string) |
|
| .Spec.Driver.Name | Driver name (string) |
|
||||||
|
|
@ -39,12 +40,16 @@ Print usage statement.
|
||||||
|
|
||||||
Print inspect output in human-readable format
|
Print inspect output in human-readable format
|
||||||
|
|
||||||
|
#### **--showsecret**
|
||||||
|
|
||||||
|
Display secret data
|
||||||
|
|
||||||
## EXAMPLES
|
## EXAMPLES
|
||||||
|
|
||||||
```
|
```
|
||||||
$ podman secret inspect mysecret
|
$ podman secret inspect mysecret
|
||||||
$ podman secret inspect --format "{{.Name} {{.Scope}}" mysecret
|
$ podman secret inspect --format "{{.Name} {{.Scope}}" mysecret
|
||||||
|
$ podman secret inspect --showsecret --format "{{.Name} {{.SecretData}}" mysecret
|
||||||
```
|
```
|
||||||
|
|
||||||
## SEE ALSO
|
## SEE ALSO
|
||||||
|
|
|
||||||
|
|
@ -34,6 +34,7 @@ Valid placeholders for the Go template are listed below:
|
||||||
| ------------------------ | ----------------------------------------------------------------- |
|
| ------------------------ | ----------------------------------------------------------------- |
|
||||||
| .CreatedAt | When secret was created (relative timestamp, human-readable) |
|
| .CreatedAt | When secret was created (relative timestamp, human-readable) |
|
||||||
| .ID | ID of secret |
|
| .ID | ID of secret |
|
||||||
|
| .SecretData | Secret Data (Displayed only with --showsecret option) |
|
||||||
| .Spec ... | Details of secret |
|
| .Spec ... | Details of secret |
|
||||||
| .Spec.Driver | Driver info |
|
| .Spec.Driver | Driver info |
|
||||||
| .Spec.Driver.Name | Driver name (string) |
|
| .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/entities"
|
||||||
"github.com/containers/podman/v4/pkg/domain/infra/abi"
|
"github.com/containers/podman/v4/pkg/domain/infra/abi"
|
||||||
"github.com/containers/podman/v4/pkg/util"
|
"github.com/containers/podman/v4/pkg/util"
|
||||||
|
"github.com/gorilla/schema"
|
||||||
)
|
)
|
||||||
|
|
||||||
func ListSecrets(w http.ResponseWriter, r *http.Request) {
|
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) {
|
func InspectSecret(w http.ResponseWriter, r *http.Request) {
|
||||||
|
decoder := r.Context().Value(api.DecoderKey).(*schema.Decoder)
|
||||||
runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime)
|
runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime)
|
||||||
name := utils.GetName(r)
|
name := utils.GetName(r)
|
||||||
names := []string{name}
|
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}
|
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 {
|
if err != nil {
|
||||||
utils.InternalServerError(w, err)
|
utils.InternalServerError(w, err)
|
||||||
return
|
return
|
||||||
|
|
|
||||||
|
|
@ -79,6 +79,11 @@ func (s *APIServer) registerSecretHandlers(r *mux.Router) error {
|
||||||
// type: string
|
// type: string
|
||||||
// required: true
|
// required: true
|
||||||
// description: the name or ID of the secret
|
// description: the name or ID of the secret
|
||||||
|
// - in: query
|
||||||
|
// name: showsecret
|
||||||
|
// type: boolean
|
||||||
|
// description: Display Secret
|
||||||
|
// default: false
|
||||||
// produces:
|
// produces:
|
||||||
// - application/json
|
// - application/json
|
||||||
// responses:
|
// responses:
|
||||||
|
|
|
||||||
|
|
@ -33,6 +33,9 @@ func List(ctx context.Context, options *ListOptions) ([]*entities.SecretInfoRepo
|
||||||
|
|
||||||
// Inspect returns low-level information about a secret.
|
// Inspect returns low-level information about a secret.
|
||||||
func Inspect(ctx context.Context, nameOrID string, options *InspectOptions) (*entities.SecretInfoReport, error) {
|
func Inspect(ctx context.Context, nameOrID string, options *InspectOptions) (*entities.SecretInfoReport, error) {
|
||||||
|
if options == nil {
|
||||||
|
options = new(InspectOptions)
|
||||||
|
}
|
||||||
var (
|
var (
|
||||||
inspect *entities.SecretInfoReport
|
inspect *entities.SecretInfoReport
|
||||||
)
|
)
|
||||||
|
|
@ -40,12 +43,15 @@ func Inspect(ctx context.Context, nameOrID string, options *InspectOptions) (*en
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
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 {
|
if err != nil {
|
||||||
return inspect, err
|
return inspect, err
|
||||||
}
|
}
|
||||||
defer response.Body.Close()
|
defer response.Body.Close()
|
||||||
|
|
||||||
return inspect, response.Process(&inspect)
|
return inspect, response.Process(&inspect)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@ type ListOptions struct {
|
||||||
//
|
//
|
||||||
//go:generate go run ../generator/generator.go InspectOptions
|
//go:generate go run ../generator/generator.go InspectOptions
|
||||||
type InspectOptions struct {
|
type InspectOptions struct {
|
||||||
|
ShowSecret *bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// RemoveOptions are optional options for removing secrets
|
// 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) {
|
func (o *InspectOptions) ToParams() (url.Values, error) {
|
||||||
return util.ToParams(o)
|
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)
|
PodUnpause(ctx context.Context, namesOrIds []string, options PodunpauseOptions) ([]*PodUnpauseReport, error)
|
||||||
SetupRootless(ctx context.Context, noMoveProcess bool) error
|
SetupRootless(ctx context.Context, noMoveProcess bool) error
|
||||||
SecretCreate(ctx context.Context, name string, reader io.Reader, options SecretCreateOptions) (*SecretCreateReport, 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)
|
SecretList(ctx context.Context, opts SecretListRequest) ([]*SecretInfoReport, error)
|
||||||
SecretRm(ctx context.Context, nameOrID []string, opts SecretRmOptions) ([]*SecretRmReport, error)
|
SecretRm(ctx context.Context, nameOrID []string, opts SecretRmOptions) ([]*SecretRmReport, error)
|
||||||
SecretExists(ctx context.Context, nameOrID string) (*BoolReport, error)
|
SecretExists(ctx context.Context, nameOrID string) (*BoolReport, error)
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,10 @@ type SecretCreateOptions struct {
|
||||||
Labels map[string]string
|
Labels map[string]string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type SecretInspectOptions struct {
|
||||||
|
ShowSecret bool
|
||||||
|
}
|
||||||
|
|
||||||
type SecretListRequest struct {
|
type SecretListRequest struct {
|
||||||
Filters map[string][]string
|
Filters map[string][]string
|
||||||
}
|
}
|
||||||
|
|
@ -38,10 +42,11 @@ type SecretRmReport struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
type SecretInfoReport struct {
|
type SecretInfoReport struct {
|
||||||
ID string
|
ID string
|
||||||
CreatedAt time.Time
|
CreatedAt time.Time
|
||||||
UpdatedAt time.Time
|
UpdatedAt time.Time
|
||||||
Spec SecretSpec
|
Spec SecretSpec
|
||||||
|
SecretData string `json:"SecretData,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type SecretInfoReportCompat struct {
|
type SecretInfoReportCompat struct {
|
||||||
|
|
|
||||||
|
|
@ -58,7 +58,11 @@ func (ic *ContainerEngine) SecretCreate(ctx context.Context, name string, reader
|
||||||
}, nil
|
}, 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()
|
manager, err := ic.Libpod.SecretsManager()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
|
@ -66,7 +70,11 @@ func (ic *ContainerEngine) SecretInspect(ctx context.Context, nameOrIDs []string
|
||||||
errs := make([]error, 0, len(nameOrIDs))
|
errs := make([]error, 0, len(nameOrIDs))
|
||||||
reports := make([]*entities.SecretInfoReport, 0, len(nameOrIDs))
|
reports := make([]*entities.SecretInfoReport, 0, len(nameOrIDs))
|
||||||
for _, nameOrID := range 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 err != nil {
|
||||||
if strings.Contains(err.Error(), "no such secret") {
|
if strings.Contains(err.Error(), "no such secret") {
|
||||||
errs = append(errs, err)
|
errs = append(errs, err)
|
||||||
|
|
@ -90,6 +98,7 @@ func (ic *ContainerEngine) SecretInspect(ctx context.Context, nameOrIDs []string
|
||||||
},
|
},
|
||||||
Labels: secret.Labels,
|
Labels: secret.Labels,
|
||||||
},
|
},
|
||||||
|
SecretData: string(data),
|
||||||
}
|
}
|
||||||
reports = append(reports, report)
|
reports = append(reports, report)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -23,11 +23,14 @@ func (ic *ContainerEngine) SecretCreate(ctx context.Context, name string, reader
|
||||||
return created, nil
|
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))
|
allInspect := make([]*entities.SecretInfoReport, 0, len(nameOrIDs))
|
||||||
errs := make([]error, 0, len(nameOrIDs))
|
errs := make([]error, 0, len(nameOrIDs))
|
||||||
|
opts := new(secrets.InspectOptions).
|
||||||
|
WithShowSecret(options.ShowSecret)
|
||||||
|
|
||||||
for _, name := range nameOrIDs {
|
for _, name := range nameOrIDs {
|
||||||
inspected, err := secrets.Inspect(ic.ClientCtx, name, nil)
|
inspected, err := secrets.Inspect(ic.ClientCtx, name, opts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errModel, ok := err.(*errorhandling.ErrorModel)
|
errModel, ok := err.(*errorhandling.ErrorModel)
|
||||||
if !ok {
|
if !ok {
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ import (
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
||||||
. "github.com/containers/podman/v4/test/utils"
|
. "github.com/containers/podman/v4/test/utils"
|
||||||
|
"github.com/containers/storage/pkg/stringid"
|
||||||
. "github.com/onsi/ginkgo/v2"
|
. "github.com/onsi/ginkgo/v2"
|
||||||
. "github.com/onsi/gomega"
|
. "github.com/onsi/gomega"
|
||||||
. "github.com/onsi/gomega/gexec"
|
. "github.com/onsi/gomega/gexec"
|
||||||
|
|
@ -48,8 +49,9 @@ var _ = Describe("Podman secret", func() {
|
||||||
})
|
})
|
||||||
|
|
||||||
It("podman secret inspect", func() {
|
It("podman secret inspect", func() {
|
||||||
|
random := stringid.GenerateRandomID()
|
||||||
secretFilePath := filepath.Join(podmanTest.TempDir, "secret")
|
secretFilePath := filepath.Join(podmanTest.TempDir, "secret")
|
||||||
err := os.WriteFile(secretFilePath, []byte("mysecret"), 0755)
|
err := os.WriteFile(secretFilePath, []byte(random), 0755)
|
||||||
Expect(err).ToNot(HaveOccurred())
|
Expect(err).ToNot(HaveOccurred())
|
||||||
|
|
||||||
session := podmanTest.Podman([]string{"secret", "create", "a", secretFilePath})
|
session := podmanTest.Podman([]string{"secret", "create", "a", secretFilePath})
|
||||||
|
|
@ -61,6 +63,16 @@ var _ = Describe("Podman secret", func() {
|
||||||
inspect.WaitWithDefaultTimeout()
|
inspect.WaitWithDefaultTimeout()
|
||||||
Expect(inspect).Should(Exit(0))
|
Expect(inspect).Should(Exit(0))
|
||||||
Expect(inspect.OutputToString()).To(BeValidJSON())
|
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() {
|
It("podman secret inspect with --format", func() {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue