summaryrefslogtreecommitdiff
path: root/vendor/github.com
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com')
-rw-r--r--vendor/github.com/containers/image/docker/archive/dest.go12
-rw-r--r--vendor/github.com/containers/image/docker/archive/transport.go6
-rw-r--r--vendor/github.com/containers/image/docker/daemon/daemon_transport.go51
-rw-r--r--vendor/github.com/containers/image/docker/docker_image.go78
-rw-r--r--vendor/github.com/containers/image/docker/tarfile/dest.go75
-rw-r--r--vendor/github.com/containers/image/internal/tmpdir/tmpdir.go12
-rw-r--r--vendor/github.com/containers/image/oci/layout/oci_dest.go14
-rw-r--r--vendor/github.com/containers/image/oci/layout/oci_transport.go4
-rw-r--r--vendor/github.com/containers/image/pkg/docker/config/config.go8
-rw-r--r--vendor/github.com/containers/image/storage/storage_image.go5
-rw-r--r--vendor/github.com/containers/image/types/types.go3
11 files changed, 191 insertions, 77 deletions
diff --git a/vendor/github.com/containers/image/docker/archive/dest.go b/vendor/github.com/containers/image/docker/archive/dest.go
index 12bbfa747..c88aea3b3 100644
--- a/vendor/github.com/containers/image/docker/archive/dest.go
+++ b/vendor/github.com/containers/image/docker/archive/dest.go
@@ -16,11 +16,7 @@ type archiveImageDestination struct {
writer io.Closer
}
-func newImageDestination(ref archiveReference) (types.ImageDestination, error) {
- if ref.destinationRef == nil {
- return nil, errors.Errorf("docker-archive: destination reference not supplied (must be of form <path>:<reference:tag>)")
- }
-
+func newImageDestination(sys *types.SystemContext, ref archiveReference) (types.ImageDestination, error) {
// ref.path can be either a pipe or a regular file
// in the case of a pipe, we require that we can open it for write
// in the case of a regular file, we don't want to overwrite any pre-existing file
@@ -40,8 +36,12 @@ func newImageDestination(ref archiveReference) (types.ImageDestination, error) {
return nil, errors.New("docker-archive doesn't support modifying existing images")
}
+ tarDest := tarfile.NewDestination(fh, ref.destinationRef)
+ if sys != nil && sys.DockerArchiveAdditionalTags != nil {
+ tarDest.AddRepoTags(sys.DockerArchiveAdditionalTags)
+ }
return &archiveImageDestination{
- Destination: tarfile.NewDestination(fh, ref.destinationRef),
+ Destination: tarDest,
ref: ref,
writer: fh,
}, nil
diff --git a/vendor/github.com/containers/image/docker/archive/transport.go b/vendor/github.com/containers/image/docker/archive/transport.go
index 169a2b2e2..f345b343c 100644
--- a/vendor/github.com/containers/image/docker/archive/transport.go
+++ b/vendor/github.com/containers/image/docker/archive/transport.go
@@ -41,7 +41,9 @@ func (t archiveTransport) ValidatePolicyConfigurationScope(scope string) error {
// archiveReference is an ImageReference for Docker images.
type archiveReference struct {
- destinationRef reference.NamedTagged // only used for destinations
+ // only used for destinations
+ // archiveReference.destinationRef is optional and can be nil for destinations as well.
+ destinationRef reference.NamedTagged
path string
}
@@ -148,7 +150,7 @@ func (ref archiveReference) NewImageSource(ctx context.Context, sys *types.Syste
// NewImageDestination returns a types.ImageDestination for this reference.
// The caller must call .Close() on the returned ImageDestination.
func (ref archiveReference) NewImageDestination(ctx context.Context, sys *types.SystemContext) (types.ImageDestination, error) {
- return newImageDestination(ref)
+ return newImageDestination(sys, ref)
}
// DeleteImage deletes the named image from the registry, if supported.
diff --git a/vendor/github.com/containers/image/docker/daemon/daemon_transport.go b/vendor/github.com/containers/image/docker/daemon/daemon_transport.go
index b083628ae..1a265bf76 100644
--- a/vendor/github.com/containers/image/docker/daemon/daemon_transport.go
+++ b/vendor/github.com/containers/image/docker/daemon/daemon_transport.go
@@ -2,13 +2,15 @@ package daemon
import (
"context"
- "github.com/pkg/errors"
+ "fmt"
+ "github.com/containers/image/docker/policyconfiguration"
"github.com/containers/image/docker/reference"
"github.com/containers/image/image"
"github.com/containers/image/transports"
"github.com/containers/image/types"
"github.com/opencontainers/go-digest"
+ "github.com/pkg/errors"
)
func init() {
@@ -35,8 +37,15 @@ func (t daemonTransport) ParseReference(reference string) (types.ImageReference,
// It is acceptable to allow an invalid value which will never be matched, it can "only" cause user confusion.
// scope passed to this function will not be "", that value is always allowed.
func (t daemonTransport) ValidatePolicyConfigurationScope(scope string) error {
- // See the explanation in daemonReference.PolicyConfigurationIdentity.
- return errors.New(`docker-daemon: does not support any scopes except the default "" one`)
+ // ID values cannot be effectively namespaced, and are clearly invalid host:port values.
+ if _, err := digest.Parse(scope); err == nil {
+ return errors.Errorf(`docker-daemon: can not use algo:digest value %s as a namespace`, scope)
+ }
+
+ // FIXME? We could be verifying the various character set and length restrictions
+ // from docker/distribution/reference.regexp.go, but other than that there
+ // are few semantically invalid strings.
+ return nil
}
// daemonReference is an ImageReference for images managed by a local Docker daemon
@@ -88,6 +97,8 @@ func NewReference(id digest.Digest, ref reference.Named) (types.ImageReference,
// A github.com/distribution/reference value can have a tag and a digest at the same time!
// Most versions of docker/reference do not handle that (ignoring the tag), so reject such input.
// This MAY be accepted in the future.
+ // (Even if it were supported, the semantics of policy namespaces are unclear - should we drop
+ // the tag or the digest first?)
_, isTagged := ref.(reference.NamedTagged)
_, isDigested := ref.(reference.Canonical)
if isTagged && isDigested {
@@ -137,9 +148,28 @@ func (ref daemonReference) DockerReference() reference.Named {
// Returns "" if configuration identities for these references are not supported.
func (ref daemonReference) PolicyConfigurationIdentity() string {
// We must allow referring to images in the daemon by image ID, otherwise untagged images would not be accessible.
- // But the existence of image IDs means that we can’t truly well namespace the input; the untagged images would have to fall into the default policy,
- // which can be unexpected. So, punt.
- return "" // This still allows using the default "" scope to define a policy for this transport.
+ // But the existence of image IDs means that we can’t truly well namespace the input:
+ // a single image can be namespaced either using the name or the ID depending on how it is named.
+ //
+ // That’s fairly unexpected, but we have to cope somehow.
+ //
+ // So, use the ordinary docker/policyconfiguration namespacing for named images.
+ // image IDs all fall into the root namespace.
+ // Users can set up the root namespace to be either untrusted or rejected,
+ // and to set up specific trust for named namespaces. This allows verifying image
+ // identity when a name is known, and unnamed images would be untrusted or rejected.
+ switch {
+ case ref.id != "":
+ return "" // This still allows using the default "" scope to define a global policy for ID-identified images.
+ case ref.ref != nil:
+ res, err := policyconfiguration.DockerReferenceIdentity(ref.ref)
+ if res == "" || err != nil { // Coverage: Should never happen, NewReference above should refuse values which could cause a failure.
+ panic(fmt.Sprintf("Internal inconsistency: policyconfiguration.DockerReferenceIdentity returned %#v, %v", res, err))
+ }
+ return res
+ default: // Coverage: Should never happen, NewReference above should refuse such values.
+ panic("Internal inconsistency: daemonReference has empty id and nil ref")
+ }
}
// PolicyConfigurationNamespaces returns a list of other policy configuration namespaces to search
@@ -149,7 +179,14 @@ func (ref daemonReference) PolicyConfigurationIdentity() string {
// and each following element to be a prefix of the element preceding it.
func (ref daemonReference) PolicyConfigurationNamespaces() []string {
// See the explanation in daemonReference.PolicyConfigurationIdentity.
- return []string{}
+ switch {
+ case ref.id != "":
+ return []string{}
+ case ref.ref != nil:
+ return policyconfiguration.DockerReferenceNamespaces(ref.ref)
+ default: // Coverage: Should never happen, NewReference above should refuse such values.
+ panic("Internal inconsistency: daemonReference has empty id and nil ref")
+ }
}
// NewImage returns a types.ImageCloser for this reference, possibly specialized for this ImageTransport.
diff --git a/vendor/github.com/containers/image/docker/docker_image.go b/vendor/github.com/containers/image/docker/docker_image.go
index 7f8ad858e..010791241 100644
--- a/vendor/github.com/containers/image/docker/docker_image.go
+++ b/vendor/github.com/containers/image/docker/docker_image.go
@@ -5,6 +5,8 @@ import (
"encoding/json"
"fmt"
"net/http"
+ "net/url"
+ "strings"
"github.com/containers/image/docker/reference"
"github.com/containers/image/image"
@@ -39,25 +41,67 @@ func (i *Image) SourceRefFullName() string {
return i.src.ref.ref.Name()
}
-// GetRepositoryTags list all tags available in the repository. Note that this has no connection with the tag(s) used for this specific image, if any.
+// GetRepositoryTags list all tags available in the repository. The tag
+// provided inside the ImageReference will be ignored. (This is a
+// backward-compatible shim method which calls the module-level
+// GetRepositoryTags)
func (i *Image) GetRepositoryTags(ctx context.Context) ([]string, error) {
- path := fmt.Sprintf(tagsPath, reference.Path(i.src.ref.ref))
- // FIXME: Pass the context.Context
- res, err := i.src.c.makeRequest(ctx, "GET", path, nil, nil)
- if err != nil {
- return nil, err
- }
- defer res.Body.Close()
- if res.StatusCode != http.StatusOK {
- // print url also
- return nil, errors.Errorf("Invalid status code returned when fetching tags list %d", res.StatusCode)
+ return GetRepositoryTags(ctx, i.src.c.sys, i.src.ref)
+}
+
+// GetRepositoryTags list all tags available in the repository. The tag
+// provided inside the ImageReference will be ignored.
+func GetRepositoryTags(ctx context.Context, sys *types.SystemContext, ref types.ImageReference) ([]string, error) {
+ dr, ok := ref.(dockerReference)
+ if !ok {
+ return nil, errors.Errorf("ref must be a dockerReference")
}
- type tagsRes struct {
- Tags []string
+
+ path := fmt.Sprintf(tagsPath, reference.Path(dr.ref))
+ client, err := newDockerClientFromRef(sys, dr, false, "pull")
+ if err != nil {
+ return nil, errors.Wrap(err, "failed to create client")
}
- tags := &tagsRes{}
- if err := json.NewDecoder(res.Body).Decode(tags); err != nil {
- return nil, err
+
+ tags := make([]string, 0)
+
+ for {
+ res, err := client.makeRequest(ctx, "GET", path, nil, nil)
+ if err != nil {
+ return nil, err
+ }
+ defer res.Body.Close()
+ if res.StatusCode != http.StatusOK {
+ // print url also
+ return nil, errors.Errorf("Invalid status code returned when fetching tags list %d", res.StatusCode)
+ }
+
+ var tagsHolder struct {
+ Tags []string
+ }
+ if err = json.NewDecoder(res.Body).Decode(&tagsHolder); err != nil {
+ return nil, err
+ }
+ tags = append(tags, tagsHolder.Tags...)
+
+ link := res.Header.Get("Link")
+ if link == "" {
+ break
+ }
+
+ linkURLStr := strings.Trim(strings.Split(link, ";")[0], "<>")
+ linkURL, err := url.Parse(linkURLStr)
+ if err != nil {
+ return tags, err
+ }
+
+ // can be relative or absolute, but we only want the path (and I
+ // guess we're in trouble if it forwards to a new place...)
+ path = linkURL.Path
+ if linkURL.RawQuery != "" {
+ path += "?"
+ path += linkURL.RawQuery
+ }
}
- return tags.Tags, nil
+ return tags, nil
}
diff --git a/vendor/github.com/containers/image/docker/tarfile/dest.go b/vendor/github.com/containers/image/docker/tarfile/dest.go
index 2bbaf4320..b6791af95 100644
--- a/vendor/github.com/containers/image/docker/tarfile/dest.go
+++ b/vendor/github.com/containers/image/docker/tarfile/dest.go
@@ -23,9 +23,9 @@ import (
// Destination is a partial implementation of types.ImageDestination for writing to an io.Writer.
type Destination struct {
- writer io.Writer
- tar *tar.Writer
- reference reference.NamedTagged
+ writer io.Writer
+ tar *tar.Writer
+ repoTags []reference.NamedTagged
// Other state.
blobs map[digest.Digest]types.BlobInfo // list of already-sent blobs
config []byte
@@ -33,14 +33,23 @@ type Destination struct {
// NewDestination returns a tarfile.Destination for the specified io.Writer.
func NewDestination(dest io.Writer, ref reference.NamedTagged) *Destination {
+ repoTags := []reference.NamedTagged{}
+ if ref != nil {
+ repoTags = append(repoTags, ref)
+ }
return &Destination{
- writer: dest,
- tar: tar.NewWriter(dest),
- reference: ref,
- blobs: make(map[digest.Digest]types.BlobInfo),
+ writer: dest,
+ tar: tar.NewWriter(dest),
+ repoTags: repoTags,
+ blobs: make(map[digest.Digest]types.BlobInfo),
}
}
+// AddRepoTags adds the specified tags to the destination's repoTags.
+func (d *Destination) AddRepoTags(tags []reference.NamedTagged) {
+ d.repoTags = append(d.repoTags, tags...)
+}
+
// SupportedManifestMIMETypes tells which manifest mime types the destination supports
// If an empty slice or nil it's returned, then any mime type can be tried to upload
func (d *Destination) SupportedManifestMIMETypes() []string {
@@ -161,8 +170,15 @@ func (d *Destination) ReapplyBlob(ctx context.Context, info types.BlobInfo) (typ
}
func (d *Destination) createRepositoriesFile(rootLayerID string) error {
- repositories := map[string]map[string]string{
- d.reference.Name(): {d.reference.Tag(): rootLayerID}}
+ repositories := map[string]map[string]string{}
+ for _, repoTag := range d.repoTags {
+ if val, ok := repositories[repoTag.Name()]; ok {
+ val[repoTag.Tag()] = rootLayerID
+ } else {
+ repositories[repoTag.Name()] = map[string]string{repoTag.Tag(): rootLayerID}
+ }
+ }
+
b, err := json.Marshal(repositories)
if err != nil {
return errors.Wrap(err, "Error marshaling repositories")
@@ -199,26 +215,31 @@ func (d *Destination) PutManifest(ctx context.Context, m []byte) error {
}
}
- // For github.com/docker/docker consumers, this works just as well as
- // refString := ref.String()
- // because when reading the RepoTags strings, github.com/docker/docker/reference
- // normalizes both of them to the same value.
- //
- // Doing it this way to include the normalized-out `docker.io[/library]` does make
- // a difference for github.com/projectatomic/docker consumers, with the
- // “Add --add-registry and --block-registry options to docker daemon” patch.
- // These consumers treat reference strings which include a hostname and reference
- // strings without a hostname differently.
- //
- // Using the host name here is more explicit about the intent, and it has the same
- // effect as (docker pull) in projectatomic/docker, which tags the result using
- // a hostname-qualified reference.
- // See https://github.com/containers/image/issues/72 for a more detailed
- // analysis and explanation.
- refString := fmt.Sprintf("%s:%s", d.reference.Name(), d.reference.Tag())
+ repoTags := []string{}
+ for _, tag := range d.repoTags {
+ // For github.com/docker/docker consumers, this works just as well as
+ // refString := ref.String()
+ // because when reading the RepoTags strings, github.com/docker/docker/reference
+ // normalizes both of them to the same value.
+ //
+ // Doing it this way to include the normalized-out `docker.io[/library]` does make
+ // a difference for github.com/projectatomic/docker consumers, with the
+ // “Add --add-registry and --block-registry options to docker daemon” patch.
+ // These consumers treat reference strings which include a hostname and reference
+ // strings without a hostname differently.
+ //
+ // Using the host name here is more explicit about the intent, and it has the same
+ // effect as (docker pull) in projectatomic/docker, which tags the result using
+ // a hostname-qualified reference.
+ // See https://github.com/containers/image/issues/72 for a more detailed
+ // analysis and explanation.
+ refString := fmt.Sprintf("%s:%s", tag.Name(), tag.Tag())
+ repoTags = append(repoTags, refString)
+ }
+
items := []ManifestItem{{
Config: man.ConfigDescriptor.Digest.Hex() + ".json",
- RepoTags: []string{refString},
+ RepoTags: repoTags,
Layers: layerPaths,
Parent: "",
LayerSources: nil,
diff --git a/vendor/github.com/containers/image/internal/tmpdir/tmpdir.go b/vendor/github.com/containers/image/internal/tmpdir/tmpdir.go
index a28020edc..8c776929c 100644
--- a/vendor/github.com/containers/image/internal/tmpdir/tmpdir.go
+++ b/vendor/github.com/containers/image/internal/tmpdir/tmpdir.go
@@ -5,6 +5,16 @@ import (
"runtime"
)
+// unixTempDirForBigFiles is the directory path to store big files on non Windows systems.
+// You can override this at build time with
+// -ldflags '-X github.com/containers/image/internal/tmpdir.unixTempDirForBigFiles=$your_path'
+var unixTempDirForBigFiles = builtinUnixTempDirForBigFiles
+
+// builtinUnixTempDirForBigFiles is the directory path to store big files.
+// Do not use the system default of os.TempDir(), usually /tmp, because with systemd it could be a tmpfs.
+// DO NOT change this, instead see unixTempDirForBigFiles above.
+const builtinUnixTempDirForBigFiles = "/var/tmp"
+
// TemporaryDirectoryForBigFiles returns a directory for temporary (big) files.
// On non Windows systems it avoids the use of os.TempDir(), because the default temporary directory usually falls under /tmp
// which on systemd based systems could be the unsuitable tmpfs filesystem.
@@ -13,7 +23,7 @@ func TemporaryDirectoryForBigFiles() string {
if runtime.GOOS == "windows" {
temporaryDirectoryForBigFiles = os.TempDir()
} else {
- temporaryDirectoryForBigFiles = "/var/tmp"
+ temporaryDirectoryForBigFiles = unixTempDirForBigFiles
}
return temporaryDirectoryForBigFiles
}
diff --git a/vendor/github.com/containers/image/oci/layout/oci_dest.go b/vendor/github.com/containers/image/oci/layout/oci_dest.go
index e0761c693..a1e27b5d5 100644
--- a/vendor/github.com/containers/image/oci/layout/oci_dest.go
+++ b/vendor/github.com/containers/image/oci/layout/oci_dest.go
@@ -25,10 +25,6 @@ type ociImageDestination struct {
// newImageDestination returns an ImageDestination for writing to an existing directory.
func newImageDestination(sys *types.SystemContext, ref ociReference) (types.ImageDestination, error) {
- if ref.image == "" {
- return nil, errors.Errorf("cannot save image with empty reference name (syntax must be of form <transport>:<path>:<reference>)")
- }
-
var index *imgspecv1.Index
if indexExists(ref) {
var err error
@@ -217,13 +213,11 @@ func (d *ociImageDestination) PutManifest(ctx context.Context, m []byte) error {
return err
}
- if d.ref.image == "" {
- return errors.Errorf("cannot save image with empyt image.ref.name")
+ if d.ref.image != "" {
+ annotations := make(map[string]string)
+ annotations["org.opencontainers.image.ref.name"] = d.ref.image
+ desc.Annotations = annotations
}
-
- annotations := make(map[string]string)
- annotations["org.opencontainers.image.ref.name"] = d.ref.image
- desc.Annotations = annotations
desc.Platform = &imgspecv1.Platform{
Architecture: runtime.GOARCH,
OS: runtime.GOOS,
diff --git a/vendor/github.com/containers/image/oci/layout/oci_transport.go b/vendor/github.com/containers/image/oci/layout/oci_transport.go
index 95a9def2c..857c80818 100644
--- a/vendor/github.com/containers/image/oci/layout/oci_transport.go
+++ b/vendor/github.com/containers/image/oci/layout/oci_transport.go
@@ -55,7 +55,9 @@ type ociReference struct {
// (But in general, we make no attempt to be completely safe against concurrent hostile filesystem modifications.)
dir string // As specified by the user. May be relative, contain symlinks, etc.
resolvedDir string // Absolute path with no symlinks, at least at the time of its creation. Primarily used for policy namespaces.
- image string // If image=="", it means the only image in the index.json is used
+ // If image=="", it means the "only image" in the index.json is used in the case it is a source
+ // for destinations, the image name annotation "image.ref.name" is not added to the index.json
+ image string
}
// ParseReference converts a string, which should not start with the ImageTransport.Name prefix, into an OCI ImageReference.
diff --git a/vendor/github.com/containers/image/pkg/docker/config/config.go b/vendor/github.com/containers/image/pkg/docker/config/config.go
index 3edab0539..724aef4e6 100644
--- a/vendor/github.com/containers/image/pkg/docker/config/config.go
+++ b/vendor/github.com/containers/image/pkg/docker/config/config.go
@@ -149,13 +149,15 @@ func getPathToAuth(sys *types.SystemContext) (string, error) {
runtimeDir := os.Getenv("XDG_RUNTIME_DIR")
if runtimeDir != "" {
+ // This function does not in general need to separately check that the returned path exists; that’s racy, and callers will fail accessing the file anyway.
+ // We are checking for os.IsNotExist here only to give the user better guidance what to do in this special case.
_, err := os.Stat(runtimeDir)
if os.IsNotExist(err) {
// This means the user set the XDG_RUNTIME_DIR variable and either forgot to create the directory
- // or made a typo while setting the environment variable
- // so we log the error and return an empty string as the path
+ // or made a typo while setting the environment variable,
+ // so return an error referring to $XDG_RUNTIME_DIR instead of …/authCfgFileName inside.
return "", errors.Wrapf(err, "%q directory set by $XDG_RUNTIME_DIR does not exist. Either create the directory or unset $XDG_RUNTIME_DIR.", runtimeDir)
- }
+ } // else ignore err and let the caller fail accessing …/authCfgFileName.
runtimeDir = filepath.Join(runtimeDir, authCfg)
} else {
runtimeDir = filepath.Join(defaultPath, authCfg, strconv.Itoa(os.Getuid()))
diff --git a/vendor/github.com/containers/image/storage/storage_image.go b/vendor/github.com/containers/image/storage/storage_image.go
index e6e759456..b43caf1a0 100644
--- a/vendor/github.com/containers/image/storage/storage_image.go
+++ b/vendor/github.com/containers/image/storage/storage_image.go
@@ -14,6 +14,7 @@ import (
"sync/atomic"
"github.com/containers/image/image"
+ "github.com/containers/image/internal/tmpdir"
"github.com/containers/image/manifest"
"github.com/containers/image/types"
"github.com/containers/storage"
@@ -25,8 +26,6 @@ import (
"github.com/sirupsen/logrus"
)
-const temporaryDirectoryForBigFiles = "/var/tmp" // Do not use the system default of os.TempDir(), usually /tmp, because with systemd it could be a tmpfs.
-
var (
// ErrBlobDigestMismatch is returned when PutBlob() is given a blob
// with a digest-based name that doesn't match its contents.
@@ -240,7 +239,7 @@ func (s *storageImageSource) GetSignatures(ctx context.Context, instanceDigest *
// newImageDestination sets us up to write a new image, caching blobs in a temporary directory until
// it's time to Commit() the image
func newImageDestination(imageRef storageReference) (*storageImageDestination, error) {
- directory, err := ioutil.TempDir(temporaryDirectoryForBigFiles, "storage")
+ directory, err := ioutil.TempDir(tmpdir.TemporaryDirectoryForBigFiles(), "storage")
if err != nil {
return nil, errors.Wrapf(err, "error creating a temporary directory")
}
diff --git a/vendor/github.com/containers/image/types/types.go b/vendor/github.com/containers/image/types/types.go
index 62aa78ddd..2823a05c0 100644
--- a/vendor/github.com/containers/image/types/types.go
+++ b/vendor/github.com/containers/image/types/types.go
@@ -347,6 +347,9 @@ type SystemContext struct {
// If not "", overrides the use of platform.GOOS when choosing an image or verifying OS match.
OSChoice string
+ // Additional tags when creating or copying a docker-archive.
+ DockerArchiveAdditionalTags []reference.NamedTagged
+
// === OCI.Transport overrides ===
// If not "", a directory containing a CA certificate (ending with ".crt"),
// a client certificate (ending with ".cert") and a client ceritificate key