summaryrefslogtreecommitdiff
path: root/pkg/api
diff options
context:
space:
mode:
authorAshley Cui <acui@redhat.com>2021-01-15 01:27:23 -0500
committerAshley Cui <acui@redhat.com>2021-02-09 09:13:21 -0500
commit832a69b0bee6ec289521fbd59ddd480372493ee3 (patch)
tree4c8a14b7fad879dc454c37f8b59120cf74ceafd1 /pkg/api
parent2aaf631586e82192e6b7b992e6b5c8717eb792d7 (diff)
downloadpodman-832a69b0bee6ec289521fbd59ddd480372493ee3.tar.gz
podman-832a69b0bee6ec289521fbd59ddd480372493ee3.tar.bz2
podman-832a69b0bee6ec289521fbd59ddd480372493ee3.zip
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>
Diffstat (limited to 'pkg/api')
-rw-r--r--pkg/api/handlers/compat/secrets.go121
-rw-r--r--pkg/api/handlers/libpod/secrets.go40
-rw-r--r--pkg/api/handlers/utils/errors.go8
-rw-r--r--pkg/api/server/register_secrets.go194
-rw-r--r--pkg/api/server/server.go1
-rw-r--r--pkg/api/tags.yaml4
6 files changed, 368 insertions, 0 deletions
diff --git a/pkg/api/handlers/compat/secrets.go b/pkg/api/handlers/compat/secrets.go
new file mode 100644
index 000000000..ea2dfc707
--- /dev/null
+++ b/pkg/api/handlers/compat/secrets.go
@@ -0,0 +1,121 @@
+package compat
+
+import (
+ "bytes"
+ "encoding/base64"
+ "encoding/json"
+ "fmt"
+ "net/http"
+
+ "github.com/containers/podman/v2/libpod"
+ "github.com/containers/podman/v2/pkg/api/handlers/utils"
+ "github.com/containers/podman/v2/pkg/domain/entities"
+ "github.com/containers/podman/v2/pkg/domain/infra/abi"
+ "github.com/gorilla/schema"
+ "github.com/pkg/errors"
+)
+
+func ListSecrets(w http.ResponseWriter, r *http.Request) {
+ var (
+ runtime = r.Context().Value("runtime").(*libpod.Runtime)
+ decoder = r.Context().Value("decoder").(*schema.Decoder)
+ )
+ query := struct {
+ Filters map[string][]string `schema:"filters"`
+ }{}
+
+ if err := decoder.Decode(&query, r.URL.Query()); err != nil {
+ utils.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest,
+ errors.Wrapf(err, "failed to parse parameters for %s", r.URL.String()))
+ return
+ }
+ if len(query.Filters) > 0 {
+ utils.Error(w, "filters not supported", http.StatusBadRequest, errors.New("bad parameter"))
+ }
+ ic := abi.ContainerEngine{Libpod: runtime}
+ reports, err := ic.SecretList(r.Context())
+ if err != nil {
+ utils.InternalServerError(w, err)
+ return
+ }
+ utils.WriteResponse(w, http.StatusOK, reports)
+}
+
+func InspectSecret(w http.ResponseWriter, r *http.Request) {
+ var (
+ runtime = r.Context().Value("runtime").(*libpod.Runtime)
+ )
+ name := utils.GetName(r)
+ names := []string{name}
+ ic := abi.ContainerEngine{Libpod: runtime}
+ reports, errs, err := ic.SecretInspect(r.Context(), names)
+ if err != nil {
+ utils.InternalServerError(w, err)
+ return
+ }
+ if len(errs) > 0 {
+ utils.SecretNotFound(w, name, errs[0])
+ return
+ }
+ utils.WriteResponse(w, http.StatusOK, reports[0])
+
+}
+
+func RemoveSecret(w http.ResponseWriter, r *http.Request) {
+ var (
+ runtime = r.Context().Value("runtime").(*libpod.Runtime)
+ )
+
+ opts := entities.SecretRmOptions{}
+ name := utils.GetName(r)
+ ic := abi.ContainerEngine{Libpod: runtime}
+ reports, err := ic.SecretRm(r.Context(), []string{name}, opts)
+ if err != nil {
+ utils.InternalServerError(w, err)
+ return
+ }
+ if reports[0].Err != nil {
+ utils.SecretNotFound(w, name, reports[0].Err)
+ return
+ }
+ utils.WriteResponse(w, http.StatusNoContent, nil)
+}
+
+func CreateSecret(w http.ResponseWriter, r *http.Request) {
+ var (
+ runtime = r.Context().Value("runtime").(*libpod.Runtime)
+ )
+ opts := entities.SecretCreateOptions{}
+ createParams := struct {
+ *entities.SecretCreateRequest
+ Labels map[string]string `schema:"labels"`
+ }{}
+
+ if err := json.NewDecoder(r.Body).Decode(&createParams); err != nil {
+ utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "Decode()"))
+ return
+ }
+ if len(createParams.Labels) > 0 {
+ utils.Error(w, "labels not supported", http.StatusBadRequest, errors.New("bad parameter"))
+ }
+
+ decoded, _ := base64.StdEncoding.DecodeString(createParams.Data)
+ reader := bytes.NewReader(decoded)
+ opts.Driver = createParams.Driver.Name
+
+ ic := abi.ContainerEngine{Libpod: runtime}
+ report, err := ic.SecretCreate(r.Context(), createParams.Name, reader, opts)
+ if err != nil {
+ if errors.Cause(err).Error() == "secret name in use" {
+ utils.Error(w, "name conflicts with an existing object", http.StatusConflict, err)
+ return
+ }
+ utils.InternalServerError(w, err)
+ return
+ }
+ utils.WriteResponse(w, http.StatusOK, report)
+}
+
+func UpdateSecret(w http.ResponseWriter, r *http.Request) {
+ utils.Error(w, fmt.Sprintf("unsupported endpoint: %v", r.Method), http.StatusNotImplemented, errors.New("update is not supported"))
+}
diff --git a/pkg/api/handlers/libpod/secrets.go b/pkg/api/handlers/libpod/secrets.go
new file mode 100644
index 000000000..447a5d021
--- /dev/null
+++ b/pkg/api/handlers/libpod/secrets.go
@@ -0,0 +1,40 @@
+package libpod
+
+import (
+ "net/http"
+
+ "github.com/containers/podman/v2/libpod"
+ "github.com/containers/podman/v2/pkg/api/handlers/utils"
+ "github.com/containers/podman/v2/pkg/domain/entities"
+ "github.com/containers/podman/v2/pkg/domain/infra/abi"
+ "github.com/gorilla/schema"
+ "github.com/pkg/errors"
+)
+
+func CreateSecret(w http.ResponseWriter, r *http.Request) {
+ var (
+ runtime = r.Context().Value("runtime").(*libpod.Runtime)
+ decoder = r.Context().Value("decoder").(*schema.Decoder)
+ )
+ query := struct {
+ Name string `schema:"name"`
+ Driver string `schema:"driver"`
+ }{
+ // override any golang type defaults
+ }
+ opts := entities.SecretCreateOptions{}
+ if err := decoder.Decode(&query, r.URL.Query()); err != nil {
+ utils.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest,
+ errors.Wrapf(err, "failed to parse parameters for %s", r.URL.String()))
+ return
+ }
+ opts.Driver = query.Driver
+
+ ic := abi.ContainerEngine{Libpod: runtime}
+ report, err := ic.SecretCreate(r.Context(), query.Name, r.Body, opts)
+ if err != nil {
+ utils.InternalServerError(w, err)
+ return
+ }
+ utils.WriteResponse(w, http.StatusOK, report)
+}
diff --git a/pkg/api/handlers/utils/errors.go b/pkg/api/handlers/utils/errors.go
index e2c287c45..c8785fb89 100644
--- a/pkg/api/handlers/utils/errors.go
+++ b/pkg/api/handlers/utils/errors.go
@@ -80,6 +80,14 @@ func SessionNotFound(w http.ResponseWriter, name string, err error) {
Error(w, msg, http.StatusNotFound, err)
}
+func SecretNotFound(w http.ResponseWriter, nameOrID string, err error) {
+ if errors.Cause(err).Error() != "no such secret" {
+ InternalServerError(w, err)
+ }
+ msg := fmt.Sprintf("No such secret: %s", nameOrID)
+ Error(w, msg, http.StatusNotFound, err)
+}
+
func ContainerNotRunning(w http.ResponseWriter, containerID string, err error) {
msg := fmt.Sprintf("Container %s is not running", containerID)
Error(w, msg, http.StatusConflict, err)
diff --git a/pkg/api/server/register_secrets.go b/pkg/api/server/register_secrets.go
new file mode 100644
index 000000000..95abf83e8
--- /dev/null
+++ b/pkg/api/server/register_secrets.go
@@ -0,0 +1,194 @@
+package server
+
+import (
+ "net/http"
+
+ "github.com/containers/podman/v2/pkg/api/handlers/compat"
+ "github.com/containers/podman/v2/pkg/api/handlers/libpod"
+ "github.com/gorilla/mux"
+)
+
+func (s *APIServer) registerSecretHandlers(r *mux.Router) error {
+ // swagger:operation POST /libpod/secrets/create libpod libpodCreateSecret
+ // ---
+ // tags:
+ // - secrets
+ // summary: Create a secret
+ // parameters:
+ // - in: query
+ // name: name
+ // type: string
+ // description: User-defined name of the secret.
+ // required: true
+ // - in: query
+ // name: driver
+ // type: string
+ // description: Secret driver
+ // default: "file"
+ // - in: body
+ // name: request
+ // description: Secret
+ // schema:
+ // type: string
+ // produces:
+ // - application/json
+ // responses:
+ // '201':
+ // $ref: "#/responses/SecretCreateResponse"
+ // '500':
+ // "$ref": "#/responses/InternalError"
+ r.Handle(VersionedPath("/libpod/secrets/create"), s.APIHandler(libpod.CreateSecret)).Methods(http.MethodPost)
+ // swagger:operation GET /libpod/secrets/json libpod libpodListSecret
+ // ---
+ // tags:
+ // - secrets
+ // summary: List secrets
+ // description: Returns a list of secrets
+ // produces:
+ // - application/json
+ // parameters:
+ // responses:
+ // '200':
+ // "$ref": "#/responses/SecretListResponse"
+ // '500':
+ // "$ref": "#/responses/InternalError"
+ r.Handle(VersionedPath("/libpod/secrets/json"), s.APIHandler(compat.ListSecrets)).Methods(http.MethodGet)
+ // swagger:operation GET /libpod/secrets/{name}/json libpod libpodInspectSecret
+ // ---
+ // tags:
+ // - secrets
+ // summary: Inspect secret
+ // parameters:
+ // - in: path
+ // name: name
+ // type: string
+ // required: true
+ // description: the name or ID of the secret
+ // produces:
+ // - application/json
+ // responses:
+ // '200':
+ // "$ref": "#/responses/SecretInspectResponse"
+ // '404':
+ // "$ref": "#/responses/NoSuchSecret"
+ // '500':
+ // "$ref": "#/responses/InternalError"
+ r.Handle(VersionedPath("/libpod/secrets/{name}/json"), s.APIHandler(compat.InspectSecret)).Methods(http.MethodGet)
+ // swagger:operation DELETE /libpod/secrets/{name} libpod libpodRemoveSecret
+ // ---
+ // tags:
+ // - secrets
+ // summary: Remove secret
+ // parameters:
+ // - in: path
+ // name: name
+ // type: string
+ // required: true
+ // description: the name or ID of the secret
+ // - in: query
+ // name: all
+ // type: boolean
+ // description: Remove all secrets
+ // default: false
+ // produces:
+ // - application/json
+ // responses:
+ // '204':
+ // description: no error
+ // '404':
+ // "$ref": "#/responses/NoSuchSecret"
+ // '500':
+ // "$ref": "#/responses/InternalError"
+ r.Handle(VersionedPath("/libpod/secrets/{name}"), s.APIHandler(compat.RemoveSecret)).Methods(http.MethodDelete)
+
+ /*
+ * Docker compatibility endpoints
+ */
+ // swagger:operation GET /secrets compat ListSecret
+ // ---
+ // tags:
+ // - secrets (compat)
+ // summary: List secrets
+ // description: Returns a list of secrets
+ // produces:
+ // - application/json
+ // parameters:
+ // responses:
+ // '200':
+ // "$ref": "#/responses/SecretListResponse"
+ // '500':
+ // "$ref": "#/responses/InternalError"
+ r.Handle(VersionedPath("/secrets"), s.APIHandler(compat.ListSecrets)).Methods(http.MethodGet)
+ r.Handle("/secrets", s.APIHandler(compat.ListSecrets)).Methods(http.MethodGet)
+ // swagger:operation POST /secrets/create compat CreateSecret
+ // ---
+ // tags:
+ // - secrets (compat)
+ // summary: Create a secret
+ // parameters:
+ // - in: body
+ // name: create
+ // description: |
+ // attributes for creating a secret
+ // schema:
+ // $ref: "#/definitions/SecretCreate"
+ // produces:
+ // - application/json
+ // responses:
+ // '201':
+ // $ref: "#/responses/SecretCreateResponse"
+ // '409':
+ // "$ref": "#/responses/SecretInUse"
+ // '500':
+ // "$ref": "#/responses/InternalError"
+ r.Handle(VersionedPath("/secrets/create"), s.APIHandler(compat.CreateSecret)).Methods(http.MethodPost)
+ r.Handle("/secrets/create", s.APIHandler(compat.CreateSecret)).Methods(http.MethodPost)
+ // swagger:operation GET /secrets/{name} compat InspectSecret
+ // ---
+ // tags:
+ // - secrets (compat)
+ // summary: Inspect secret
+ // parameters:
+ // - in: path
+ // name: name
+ // type: string
+ // required: true
+ // description: the name or ID of the secret
+ // produces:
+ // - application/json
+ // responses:
+ // '200':
+ // "$ref": "#/responses/SecretInspectResponse"
+ // '404':
+ // "$ref": "#/responses/NoSuchSecret"
+ // '500':
+ // "$ref": "#/responses/InternalError"
+ r.Handle(VersionedPath("/secrets/{name}"), s.APIHandler(compat.InspectSecret)).Methods(http.MethodGet)
+ r.Handle("/secrets/{name}", s.APIHandler(compat.InspectSecret)).Methods(http.MethodGet)
+ // swagger:operation DELETE /secrets/{name} compat RemoveSecret
+ // ---
+ // tags:
+ // - secrets (compat)
+ // summary: Remove secret
+ // parameters:
+ // - in: path
+ // name: name
+ // type: string
+ // required: true
+ // description: the name or ID of the secret
+ // produces:
+ // - application/json
+ // responses:
+ // '204':
+ // description: no error
+ // '404':
+ // "$ref": "#/responses/NoSuchSecret"
+ // '500':
+ // "$ref": "#/responses/InternalError"
+ r.Handle(VersionedPath("/secrets/{name}"), s.APIHandler(compat.RemoveSecret)).Methods(http.MethodDelete)
+ r.Handle("/secret/{name}", s.APIHandler(compat.RemoveSecret)).Methods(http.MethodDelete)
+
+ r.Handle(VersionedPath("/secrets/{name}/update"), s.APIHandler(compat.UpdateSecret)).Methods(http.MethodPost)
+ r.Handle("/secrets/{name}/update", s.APIHandler(compat.UpdateSecret)).Methods(http.MethodPost)
+ return nil
+}
diff --git a/pkg/api/server/server.go b/pkg/api/server/server.go
index d612041f6..6926eda62 100644
--- a/pkg/api/server/server.go
+++ b/pkg/api/server/server.go
@@ -124,6 +124,7 @@ func newServer(runtime *libpod.Runtime, duration time.Duration, listener *net.Li
server.registerPlayHandlers,
server.registerPluginsHandlers,
server.registerPodsHandlers,
+ server.registerSecretHandlers,
server.RegisterSwaggerHandlers,
server.registerSwarmHandlers,
server.registerSystemHandlers,
diff --git a/pkg/api/tags.yaml b/pkg/api/tags.yaml
index 0cfb3f440..bb56098eb 100644
--- a/pkg/api/tags.yaml
+++ b/pkg/api/tags.yaml
@@ -13,6 +13,8 @@ tags:
description: Actions related to pods
- name: volumes
description: Actions related to volumes
+ - name: secrets
+ description: Actions related to secrets
- name: system
description: Actions related to Podman engine
- name: containers (compat)
@@ -25,5 +27,7 @@ tags:
description: Actions related to compatibility networks
- name: volumes (compat)
description: Actions related to volumes for the compatibility endpoints
+ - name: secrets (compat)
+ description: Actions related to secrets for the compatibility endpoints
- name: system (compat)
description: Actions related to Podman and compatibility engines