mirror of
https://github.com/docker/cli.git
synced 2026-11-13 16:23:41 -04:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
370c28948e | ||
|
|
dc017bdda3 | ||
|
|
feb6f439e3 | ||
|
|
8bc4062fc0 | ||
|
|
84cc7d87cc | ||
|
|
c1c3d3b3aa | ||
|
|
048a846146 | ||
|
|
33dacda24f | ||
|
|
fcc05e5ea1 | ||
|
|
58061d25f6 | ||
|
|
55b2abb0ca | ||
|
|
4c3b87d922 | ||
|
|
e77605c83d | ||
|
|
0196098721 | ||
|
|
6ebf765040 | ||
|
|
f508ce9db7 | ||
|
|
897293c7c7 | ||
|
|
2c04354315 | ||
|
|
9c8a91d22b | ||
|
|
ff945151ef | ||
|
|
4571d90f20 | ||
|
|
55c4c88966 | ||
|
|
f33a69f6ee | ||
|
|
d3cb89ee53 | ||
|
|
433014bcfb | ||
|
|
61cb016851 | ||
|
|
53c4602909 | ||
|
|
830d6595e7 | ||
|
|
13048444cc | ||
|
|
3e293e6bd1 | ||
|
|
41b3ea7e47 | ||
|
|
d6eeeb6259 | ||
|
|
3e157d5293 | ||
|
|
1fdf84b8e9 | ||
|
|
376b99c6d6 | ||
|
|
0de4e6e9a7 | ||
|
|
3c87f01b18 | ||
|
|
de40c2b172 | ||
|
|
d513e46bfc | ||
|
|
2b74b90efb | ||
|
|
05343b36a2 | ||
|
|
f90db254d7 | ||
|
|
0dcfdde336 | ||
|
|
03cd1dc50a | ||
|
|
42811a7eb9 | ||
|
|
be966aa194 | ||
|
|
b22fe0fb14 | ||
|
|
4eb050071e | ||
|
|
08c4fdfa7a | ||
|
|
6aa1b37c8d | ||
|
|
e82920d76d | ||
|
|
82123939f7 | ||
|
|
48d30b5b32 | ||
|
|
5941f4104a | ||
|
|
18f33b337d | ||
|
|
9ecc69d17e | ||
|
|
6f49197cab |
+9
-15
@@ -42,6 +42,7 @@ jobs:
|
|||||||
docker: [{image: 'docker:19.03-git'}]
|
docker: [{image: 'docker:19.03-git'}]
|
||||||
environment:
|
environment:
|
||||||
DOCKER_BUILDKIT: 1
|
DOCKER_BUILDKIT: 1
|
||||||
|
BUILDX_VERSION: "v0.5.1"
|
||||||
parallelism: 3
|
parallelism: 3
|
||||||
steps:
|
steps:
|
||||||
- checkout
|
- checkout
|
||||||
@@ -55,21 +56,14 @@ jobs:
|
|||||||
- run:
|
- run:
|
||||||
name: "Docker info"
|
name: "Docker info"
|
||||||
command: docker info
|
command: docker info
|
||||||
- run:
|
- run: apk add make curl
|
||||||
name: "Cross - build image"
|
- run: mkdir -vp ~/.docker/cli-plugins/
|
||||||
command: |
|
- run: curl -fsSL --output ~/.docker/cli-plugins/docker-buildx https://github.com/docker/buildx/releases/download/${BUILDX_VERSION}/buildx-${BUILDX_VERSION}.linux-amd64
|
||||||
docker build --progress=plain -f dockerfiles/Dockerfile.cross --tag cli-builder:$CIRCLE_BUILD_NUM .
|
- run: chmod a+x ~/.docker/cli-plugins/docker-buildx
|
||||||
- run:
|
- run: docker buildx version
|
||||||
name: "Cross"
|
- run: docker context create buildctx
|
||||||
command: |
|
- run: docker buildx create --use buildctx && docker buildx inspect --bootstrap
|
||||||
name=cross-$CIRCLE_BUILD_NUM-$CIRCLE_NODE_INDEX
|
- run: GROUP_INDEX=$CIRCLE_NODE_INDEX GROUP_TOTAL=$CIRCLE_NODE_TOTAL docker buildx bake cross --progress=plain
|
||||||
docker run \
|
|
||||||
-e CROSS_GROUP=$CIRCLE_NODE_INDEX \
|
|
||||||
--name $name cli-builder:$CIRCLE_BUILD_NUM \
|
|
||||||
make cross
|
|
||||||
docker cp \
|
|
||||||
$name:/go/src/github.com/docker/cli/build \
|
|
||||||
/work/build
|
|
||||||
- store_artifacts:
|
- store_artifacts:
|
||||||
path: /work/build
|
path: /work/build
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
.circleci
|
.circleci
|
||||||
.dockerignore
|
.dockerignore
|
||||||
.git
|
|
||||||
.github
|
.github
|
||||||
.gitignore
|
.gitignore
|
||||||
appveyor.yml
|
appveyor.yml
|
||||||
|
|||||||
+1
-2
@@ -8,8 +8,7 @@
|
|||||||
Thumbs.db
|
Thumbs.db
|
||||||
.editorconfig
|
.editorconfig
|
||||||
/build/
|
/build/
|
||||||
cli/winresources/rsrc_386.syso
|
cli/winresources/rsrc_*.syso
|
||||||
cli/winresources/rsrc_amd64.syso
|
|
||||||
/man/man1/
|
/man/man1/
|
||||||
/man/man5/
|
/man/man5/
|
||||||
/man/man8/
|
/man/man8/
|
||||||
|
|||||||
+61
@@ -0,0 +1,61 @@
|
|||||||
|
#syntax=docker/dockerfile:1.2
|
||||||
|
|
||||||
|
ARG BASE_VARIANT=alpine
|
||||||
|
ARG GO_VERSION=1.13.15
|
||||||
|
|
||||||
|
FROM --platform=$BUILDPLATFORM golang:${GO_VERSION}-${BASE_VARIANT} AS gostable
|
||||||
|
FROM --platform=$BUILDPLATFORM golang:1.16-${BASE_VARIANT} AS golatest
|
||||||
|
|
||||||
|
FROM gostable AS go-linux
|
||||||
|
FROM golatest AS go-darwin
|
||||||
|
FROM golatest AS go-windows-amd64
|
||||||
|
FROM golatest AS go-windows-386
|
||||||
|
FROM golatest AS go-windows-arm
|
||||||
|
FROM --platform=$BUILDPLATFORM tonistiigi/golang:497feff1-${BASE_VARIANT} AS go-windows-arm64
|
||||||
|
FROM go-windows-${TARGETARCH} AS go-windows
|
||||||
|
|
||||||
|
FROM --platform=$BUILDPLATFORM tonistiigi/xx@sha256:620d36a9d7f1e3b102a5c7e8eff12081ac363828b3a44390f24fa8da2d49383d AS xx
|
||||||
|
|
||||||
|
FROM go-${TARGETOS} AS build-base-alpine
|
||||||
|
COPY --from=xx / /
|
||||||
|
RUN apk add --no-cache clang lld llvm file git
|
||||||
|
WORKDIR /go/src/github.com/docker/cli
|
||||||
|
|
||||||
|
FROM build-base-alpine AS build-alpine
|
||||||
|
ARG TARGETPLATFORM
|
||||||
|
# gcc is installed for libgcc only
|
||||||
|
RUN xx-apk add --no-cache musl-dev gcc
|
||||||
|
|
||||||
|
FROM go-${TARGETOS} AS build-base-buster
|
||||||
|
COPY --from=xx / /
|
||||||
|
RUN apt-get update && apt-get install --no-install-recommends -y clang lld file
|
||||||
|
WORKDIR /go/src/github.com/docker/cli
|
||||||
|
|
||||||
|
FROM build-base-buster AS build-buster
|
||||||
|
ARG TARGETPLATFORM
|
||||||
|
RUN xx-apt install --no-install-recommends -y libc6-dev libgcc-8-dev
|
||||||
|
|
||||||
|
FROM build-${BASE_VARIANT} AS build
|
||||||
|
# GO_LINKMODE defines if static or dynamic binary should be produced
|
||||||
|
ARG GO_LINKMODE=static
|
||||||
|
# GO_BUILDTAGS defines additional build tags
|
||||||
|
ARG GO_BUILDTAGS
|
||||||
|
# GO_STRIP strips debugging symbols if set
|
||||||
|
ARG GO_STRIP
|
||||||
|
# CGO_ENABLED manually sets if cgo is used
|
||||||
|
ARG CGO_ENABLED
|
||||||
|
# VERSION sets the version for the produced binary
|
||||||
|
ARG VERSION
|
||||||
|
RUN --mount=ro --mount=type=cache,target=/root/.cache \
|
||||||
|
--mount=from=dockercore/golang-cross:xx-sdk-extras,target=/xx-sdk,src=/xx-sdk \
|
||||||
|
--mount=type=tmpfs,target=cli/winresources \
|
||||||
|
xx-go --wrap && \
|
||||||
|
# export GOCACHE=$(go env GOCACHE)/$(xx-info)$([ -f /etc/alpine-release ] && echo "alpine") && \
|
||||||
|
TARGET=/out ./scripts/build/binary && \
|
||||||
|
xx-verify $([ "$GO_LINKMODE" = "static" ] && echo "--static") /out/docker
|
||||||
|
|
||||||
|
FROM build-base-${BASE_VARIANT} AS dev
|
||||||
|
COPY . .
|
||||||
|
|
||||||
|
FROM scratch AS binary
|
||||||
|
COPY --from=build /out .
|
||||||
@@ -30,8 +30,7 @@ lint: ## run all the lint tools
|
|||||||
gometalinter --config gometalinter.json ./...
|
gometalinter --config gometalinter.json ./...
|
||||||
|
|
||||||
.PHONY: binary
|
.PHONY: binary
|
||||||
binary: ## build executable for Linux
|
binary:
|
||||||
@echo "WARNING: binary creates a Linux executable. Use cross for macOS or Windows."
|
|
||||||
./scripts/build/binary
|
./scripts/build/binary
|
||||||
|
|
||||||
.PHONY: plugins
|
.PHONY: plugins
|
||||||
@@ -39,28 +38,20 @@ plugins: ## build example CLI plugins
|
|||||||
./scripts/build/plugins
|
./scripts/build/plugins
|
||||||
|
|
||||||
.PHONY: cross
|
.PHONY: cross
|
||||||
cross: ## build executable for macOS and Windows
|
cross:
|
||||||
./scripts/build/cross
|
./scripts/build/binary
|
||||||
|
|
||||||
.PHONY: binary-windows
|
|
||||||
binary-windows: ## build executable for Windows
|
|
||||||
./scripts/build/windows
|
|
||||||
|
|
||||||
.PHONY: plugins-windows
|
.PHONY: plugins-windows
|
||||||
plugins-windows: ## build example CLI plugins for Windows
|
plugins-windows: ## build example CLI plugins for Windows
|
||||||
./scripts/build/plugins-windows
|
./scripts/build/plugins-windows
|
||||||
|
|
||||||
.PHONY: binary-osx
|
|
||||||
binary-osx: ## build executable for macOS
|
|
||||||
./scripts/build/osx
|
|
||||||
|
|
||||||
.PHONY: plugins-osx
|
.PHONY: plugins-osx
|
||||||
plugins-osx: ## build example CLI plugins for macOS
|
plugins-osx: ## build example CLI plugins for macOS
|
||||||
./scripts/build/plugins-osx
|
./scripts/build/plugins-osx
|
||||||
|
|
||||||
.PHONY: dynbinary
|
.PHONY: dynbinary
|
||||||
dynbinary: ## build dynamically linked binary
|
dynbinary: ## build dynamically linked binary
|
||||||
./scripts/build/dynbinary
|
GO_LINKMODE=dynamic ./scripts/build/binary
|
||||||
|
|
||||||
vendor: vendor.conf ## check that vendor matches vendor.conf
|
vendor: vendor.conf ## check that vendor matches vendor.conf
|
||||||
rm -rf vendor
|
rm -rf vendor
|
||||||
|
|||||||
@@ -12,18 +12,31 @@ Development
|
|||||||
|
|
||||||
`docker/cli` is developed using Docker.
|
`docker/cli` is developed using Docker.
|
||||||
|
|
||||||
Build a linux binary:
|
Build CLI from source:
|
||||||
|
|
||||||
```
|
```
|
||||||
$ make -f docker.Makefile binary
|
$ docker buildx bake
|
||||||
```
|
```
|
||||||
|
|
||||||
Build binaries for all supported platforms:
|
Build binaries for all supported platforms:
|
||||||
|
|
||||||
```
|
```
|
||||||
$ make -f docker.Makefile cross
|
$ docker buildx bake cross
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Build for a specific platform:
|
||||||
|
|
||||||
|
```
|
||||||
|
$ docker buildx bake --set binary.platform=linux/arm64
|
||||||
|
```
|
||||||
|
|
||||||
|
Build dynamic binary for glibc or musl:
|
||||||
|
|
||||||
|
```
|
||||||
|
$ USE_GLIBC=1 docker buildx bake dynbinary
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
Run all linting:
|
Run all linting:
|
||||||
|
|
||||||
```
|
```
|
||||||
@@ -44,12 +57,6 @@ Start an interactive development environment:
|
|||||||
$ make -f docker.Makefile shell
|
$ make -f docker.Makefile shell
|
||||||
```
|
```
|
||||||
|
|
||||||
In the development environment you can run many tasks, including build binaries:
|
|
||||||
|
|
||||||
```
|
|
||||||
$ make binary
|
|
||||||
```
|
|
||||||
|
|
||||||
Legal
|
Legal
|
||||||
=====
|
=====
|
||||||
*Brought to you courtesy of our legal counsel. For more context,
|
*Brought to you courtesy of our legal counsel. For more context,
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package manager
|
package manager
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"os/exec"
|
exec "golang.org/x/sys/execabs"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Candidate represents a possible plugin candidate, for mocking purposes
|
// Candidate represents a possible plugin candidate, for mocking purposes
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ package manager
|
|||||||
import (
|
import (
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
@@ -12,6 +11,7 @@ import (
|
|||||||
"github.com/docker/cli/cli/config"
|
"github.com/docker/cli/cli/config"
|
||||||
"github.com/fvbommel/sortorder"
|
"github.com/fvbommel/sortorder"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
exec "golang.org/x/sys/execabs"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ReexecEnvvar is the name of an ennvar which is set to the command
|
// ReexecEnvvar is the name of an ennvar which is set to the command
|
||||||
|
|||||||
@@ -1,10 +1,6 @@
|
|||||||
package context
|
package context
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"regexp"
|
|
||||||
|
|
||||||
"github.com/docker/cli/cli"
|
"github.com/docker/cli/cli"
|
||||||
"github.com/docker/cli/cli/command"
|
"github.com/docker/cli/cli/command"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
@@ -30,20 +26,3 @@ func NewContextCommand(dockerCli command.Cli) *cobra.Command {
|
|||||||
)
|
)
|
||||||
return cmd
|
return cmd
|
||||||
}
|
}
|
||||||
|
|
||||||
const restrictedNamePattern = "^[a-zA-Z0-9][a-zA-Z0-9_.+-]+$"
|
|
||||||
|
|
||||||
var restrictedNameRegEx = regexp.MustCompile(restrictedNamePattern)
|
|
||||||
|
|
||||||
func validateContextName(name string) error {
|
|
||||||
if name == "" {
|
|
||||||
return errors.New("context name cannot be empty")
|
|
||||||
}
|
|
||||||
if name == "default" {
|
|
||||||
return errors.New(`"default" is a reserved context name`)
|
|
||||||
}
|
|
||||||
if !restrictedNameRegEx.MatchString(name) {
|
|
||||||
return fmt.Errorf("context name %q is invalid, names are validated against regexp %q", name, restrictedNamePattern)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -137,7 +137,7 @@ func createNewContext(o *CreateOptions, stackOrchestrator command.Orchestrator,
|
|||||||
}
|
}
|
||||||
|
|
||||||
func checkContextNameForCreation(s store.Reader, name string) error {
|
func checkContextNameForCreation(s store.Reader, name string) error {
|
||||||
if err := validateContextName(name); err != nil {
|
if err := store.ValidateContextName(name); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if _, err := s.GetMetadata(name); !store.IsErrContextDoesNotExist(err) {
|
if _, err := s.GetMetadata(name); !store.IsErrContextDoesNotExist(err) {
|
||||||
|
|||||||
@@ -77,7 +77,7 @@ func writeTo(dockerCli command.Cli, reader io.Reader, dest string) error {
|
|||||||
|
|
||||||
// RunExport exports a Docker context
|
// RunExport exports a Docker context
|
||||||
func RunExport(dockerCli command.Cli, opts *ExportOptions) error {
|
func RunExport(dockerCli command.Cli, opts *ExportOptions) error {
|
||||||
if err := validateContextName(opts.ContextName); err != nil && opts.ContextName != command.DefaultContextName {
|
if err := store.ValidateContextName(opts.ContextName); err != nil && opts.ContextName != command.DefaultContextName {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
ctxMeta, err := dockerCli.ContextStore().GetMetadata(opts.ContextName)
|
ctxMeta, err := dockerCli.ContextStore().GetMetadata(opts.ContextName)
|
||||||
|
|||||||
@@ -68,7 +68,7 @@ func newUpdateCommand(dockerCli command.Cli) *cobra.Command {
|
|||||||
|
|
||||||
// RunUpdate updates a Docker context
|
// RunUpdate updates a Docker context
|
||||||
func RunUpdate(cli command.Cli, o *UpdateOptions) error {
|
func RunUpdate(cli command.Cli, o *UpdateOptions) error {
|
||||||
if err := validateContextName(o.Name); err != nil {
|
if err := store.ValidateContextName(o.Name); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
s := cli.ContextStore()
|
s := cli.ContextStore()
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
|
|
||||||
"github.com/docker/cli/cli/command"
|
"github.com/docker/cli/cli/command"
|
||||||
|
"github.com/docker/cli/cli/context/store"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -23,7 +24,7 @@ func newUseCommand(dockerCli command.Cli) *cobra.Command {
|
|||||||
|
|
||||||
// RunUse set the current Docker context
|
// RunUse set the current Docker context
|
||||||
func RunUse(dockerCli command.Cli, name string) error {
|
func RunUse(dockerCli command.Cli, name string) error {
|
||||||
if err := validateContextName(name); err != nil && name != "default" {
|
if err := store.ValidateContextName(name); err != nil && name != "default" {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if _, err := dockerCli.ContextStore().GetMetadata(name); err != nil && name != "default" {
|
if _, err := dockerCli.ContextStore().GetMetadata(name); err != nil && name != "default" {
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ import (
|
|||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"runtime"
|
"runtime"
|
||||||
"strings"
|
"strings"
|
||||||
@@ -24,6 +23,7 @@ import (
|
|||||||
"github.com/docker/docker/pkg/streamformatter"
|
"github.com/docker/docker/pkg/streamformatter"
|
||||||
"github.com/docker/docker/pkg/stringid"
|
"github.com/docker/docker/pkg/stringid"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
|
exec "golang.org/x/sys/execabs"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
|||||||
@@ -78,7 +78,7 @@ func runBuildBuildKit(dockerCli command.Cli, options buildOptions) error {
|
|||||||
if options.dockerfileFromStdin() {
|
if options.dockerfileFromStdin() {
|
||||||
return errStdinConflict
|
return errStdinConflict
|
||||||
}
|
}
|
||||||
rc, isArchive, err := build.DetectArchiveReader(os.Stdin)
|
rc, isArchive, err := build.DetectArchiveReader(dockerCli.In())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -98,7 +98,7 @@ func runBuildBuildKit(dockerCli command.Cli, options buildOptions) error {
|
|||||||
case isLocalDir(options.context):
|
case isLocalDir(options.context):
|
||||||
contextDir = options.context
|
contextDir = options.context
|
||||||
if options.dockerfileFromStdin() {
|
if options.dockerfileFromStdin() {
|
||||||
dockerfileReader = os.Stdin
|
dockerfileReader = dockerCli.In()
|
||||||
} else if options.dockerfileName != "" {
|
} else if options.dockerfileName != "" {
|
||||||
dockerfileName = filepath.Base(options.dockerfileName)
|
dockerfileName = filepath.Base(options.dockerfileName)
|
||||||
dockerfileDir = filepath.Dir(options.dockerfileName)
|
dockerfileDir = filepath.Dir(options.dockerfileName)
|
||||||
|
|||||||
@@ -63,6 +63,9 @@ func RegistryAuthenticationPrivilegedFunc(cli Cli, index *registrytypes.IndexInf
|
|||||||
indexServer := registry.GetAuthConfigKey(index)
|
indexServer := registry.GetAuthConfigKey(index)
|
||||||
isDefaultRegistry := indexServer == ElectAuthServer(context.Background(), cli)
|
isDefaultRegistry := indexServer == ElectAuthServer(context.Background(), cli)
|
||||||
authConfig, err := GetDefaultAuthConfig(cli, true, indexServer, isDefaultRegistry)
|
authConfig, err := GetDefaultAuthConfig(cli, true, indexServer, isDefaultRegistry)
|
||||||
|
if authConfig == nil {
|
||||||
|
authConfig = &types.AuthConfig{}
|
||||||
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Fprintf(cli.Err(), "Unable to retrieve stored credentials for %s, error: %s.\n", indexServer, err)
|
fmt.Fprintf(cli.Err(), "Unable to retrieve stored credentials for %s, error: %s.\n", indexServer, err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -111,11 +111,12 @@ func runLogin(dockerCli command.Cli, opts loginOptions) error { //nolint: gocycl
|
|||||||
serverAddress = authServer
|
serverAddress = authServer
|
||||||
}
|
}
|
||||||
|
|
||||||
var err error
|
|
||||||
var authConfig *types.AuthConfig
|
|
||||||
var response registrytypes.AuthenticateOKBody
|
var response registrytypes.AuthenticateOKBody
|
||||||
isDefaultRegistry := serverAddress == authServer
|
isDefaultRegistry := serverAddress == authServer
|
||||||
authConfig, err = command.GetDefaultAuthConfig(dockerCli, opts.user == "" && opts.password == "", serverAddress, isDefaultRegistry)
|
authConfig, err := command.GetDefaultAuthConfig(dockerCli, opts.user == "" && opts.password == "", serverAddress, isDefaultRegistry)
|
||||||
|
if authConfig == nil {
|
||||||
|
authConfig = &types.AuthConfig{}
|
||||||
|
}
|
||||||
if err == nil && authConfig.Username != "" && authConfig.Password != "" {
|
if err == nil && authConfig.Username != "" && authConfig.Password != "" {
|
||||||
response, err = loginWithCredStoreCreds(ctx, dockerCli, authConfig)
|
response, err = loginWithCredStoreCreds(ctx, dockerCli, authConfig)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -165,7 +165,7 @@ func updateConfigFromDefaults(defaultUpdateConfig *api.UpdateConfig) *swarm.Upda
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (opts updateOptions) updateConfig(flags *pflag.FlagSet) *swarm.UpdateConfig {
|
func (opts updateOptions) updateConfig(flags *pflag.FlagSet) *swarm.UpdateConfig {
|
||||||
if !anyChanged(flags, flagUpdateParallelism, flagUpdateDelay, flagUpdateMonitor, flagUpdateFailureAction, flagUpdateMaxFailureRatio) {
|
if !anyChanged(flags, flagUpdateParallelism, flagUpdateDelay, flagUpdateMonitor, flagUpdateFailureAction, flagUpdateMaxFailureRatio, flagUpdateOrder) {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -194,7 +194,7 @@ func (opts updateOptions) updateConfig(flags *pflag.FlagSet) *swarm.UpdateConfig
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (opts updateOptions) rollbackConfig(flags *pflag.FlagSet) *swarm.UpdateConfig {
|
func (opts updateOptions) rollbackConfig(flags *pflag.FlagSet) *swarm.UpdateConfig {
|
||||||
if !anyChanged(flags, flagRollbackParallelism, flagRollbackDelay, flagRollbackMonitor, flagRollbackFailureAction, flagRollbackMaxFailureRatio) {
|
if !anyChanged(flags, flagRollbackParallelism, flagRollbackDelay, flagRollbackMonitor, flagRollbackFailureAction, flagRollbackMaxFailureRatio, flagRollbackOrder) {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -289,6 +289,22 @@ func TestToServiceUpdateRollback(t *testing.T) {
|
|||||||
assert.Check(t, is.DeepEqual(service.RollbackConfig, expected.RollbackConfig))
|
assert.Check(t, is.DeepEqual(service.RollbackConfig, expected.RollbackConfig))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestToServiceUpdateRollbackOrder(t *testing.T) {
|
||||||
|
flags := newCreateCommand(nil).Flags()
|
||||||
|
flags.Set("update-order", "start-first")
|
||||||
|
flags.Set("rollback-order", "start-first")
|
||||||
|
|
||||||
|
o := newServiceOptions()
|
||||||
|
o.mode = "replicated"
|
||||||
|
o.update = updateOptions{order: "start-first"}
|
||||||
|
o.rollback = updateOptions{order: "start-first"}
|
||||||
|
|
||||||
|
service, err := o.ToService(context.Background(), &fakeClient{}, flags)
|
||||||
|
assert.NilError(t, err)
|
||||||
|
assert.Check(t, is.Equal(service.UpdateConfig.Order, o.update.order))
|
||||||
|
assert.Check(t, is.Equal(service.RollbackConfig.Order, o.rollback.order))
|
||||||
|
}
|
||||||
|
|
||||||
func TestToServiceMaxReplicasGlobalModeConflict(t *testing.T) {
|
func TestToServiceMaxReplicasGlobalModeConflict(t *testing.T) {
|
||||||
opt := serviceOptions{
|
opt := serviceOptions{
|
||||||
mode: "global",
|
mode: "global",
|
||||||
|
|||||||
@@ -99,6 +99,7 @@ func ServiceProgress(ctx context.Context, client client.APIClient, serviceID str
|
|||||||
convergedAt time.Time
|
convergedAt time.Time
|
||||||
monitor = 5 * time.Second
|
monitor = 5 * time.Second
|
||||||
rollback bool
|
rollback bool
|
||||||
|
message *progress.Progress
|
||||||
)
|
)
|
||||||
|
|
||||||
for {
|
for {
|
||||||
@@ -140,8 +141,9 @@ func ServiceProgress(ctx context.Context, client client.APIClient, serviceID str
|
|||||||
return fmt.Errorf("service rollback paused: %s", service.UpdateStatus.Message)
|
return fmt.Errorf("service rollback paused: %s", service.UpdateStatus.Message)
|
||||||
case swarm.UpdateStateRollbackCompleted:
|
case swarm.UpdateStateRollbackCompleted:
|
||||||
if !converged {
|
if !converged {
|
||||||
return fmt.Errorf("service rolled back: %s", service.UpdateStatus.Message)
|
message = &progress.Progress{ID: "rollback", Message: service.UpdateStatus.Message}
|
||||||
}
|
}
|
||||||
|
rollback = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if converged && time.Since(convergedAt) >= monitor {
|
if converged && time.Since(convergedAt) >= monitor {
|
||||||
@@ -149,7 +151,9 @@ func ServiceProgress(ctx context.Context, client client.APIClient, serviceID str
|
|||||||
ID: "verify",
|
ID: "verify",
|
||||||
Action: "Service converged",
|
Action: "Service converged",
|
||||||
})
|
})
|
||||||
|
if message != nil {
|
||||||
|
progressOut.WriteProgress(*message)
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+33
-7
@@ -24,17 +24,38 @@ const (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
initConfigDir sync.Once
|
initConfigDir = new(sync.Once)
|
||||||
configDir string
|
configDir string
|
||||||
|
homeDir string
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// resetHomeDir is used in testing to reset the "homeDir" package variable to
|
||||||
|
// force re-lookup of the home directory between tests.
|
||||||
|
func resetHomeDir() {
|
||||||
|
homeDir = ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func getHomeDir() string {
|
||||||
|
if homeDir == "" {
|
||||||
|
homeDir = homedir.Get()
|
||||||
|
}
|
||||||
|
return homeDir
|
||||||
|
}
|
||||||
|
|
||||||
|
// resetConfigDir is used in testing to reset the "configDir" package variable
|
||||||
|
// and its sync.Once to force re-lookup between tests.
|
||||||
|
func resetConfigDir() {
|
||||||
|
configDir = ""
|
||||||
|
initConfigDir = new(sync.Once)
|
||||||
|
}
|
||||||
|
|
||||||
func setConfigDir() {
|
func setConfigDir() {
|
||||||
if configDir != "" {
|
if configDir != "" {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
configDir = os.Getenv("DOCKER_CONFIG")
|
configDir = os.Getenv("DOCKER_CONFIG")
|
||||||
if configDir == "" {
|
if configDir == "" {
|
||||||
configDir = filepath.Join(homedir.Get(), configFileDir)
|
configDir = filepath.Join(getHomeDir(), configFileDir)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -83,10 +104,15 @@ func LoadFromReader(configData io.Reader) (*configfile.ConfigFile, error) {
|
|||||||
return &configFile, err
|
return &configFile, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO remove this temporary hack, which is used to warn about the deprecated ~/.dockercfg file
|
||||||
|
var printLegacyFileWarning bool
|
||||||
|
|
||||||
// Load reads the configuration files in the given directory, and sets up
|
// Load reads the configuration files in the given directory, and sets up
|
||||||
// the auth config information and returns values.
|
// the auth config information and returns values.
|
||||||
// FIXME: use the internal golang config parser
|
// FIXME: use the internal golang config parser
|
||||||
func Load(configDir string) (*configfile.ConfigFile, error) {
|
func Load(configDir string) (*configfile.ConfigFile, error) {
|
||||||
|
printLegacyFileWarning = false
|
||||||
|
|
||||||
if configDir == "" {
|
if configDir == "" {
|
||||||
configDir = Dir()
|
configDir = Dir()
|
||||||
}
|
}
|
||||||
@@ -109,12 +135,9 @@ func Load(configDir string) (*configfile.ConfigFile, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Can't find latest config file so check for the old one
|
// Can't find latest config file so check for the old one
|
||||||
home, err := os.UserHomeDir()
|
filename = filepath.Join(getHomeDir(), oldConfigfile)
|
||||||
if err != nil {
|
|
||||||
return configFile, errors.Wrap(err, oldConfigfile)
|
|
||||||
}
|
|
||||||
filename = filepath.Join(home, oldConfigfile)
|
|
||||||
if file, err := os.Open(filename); err == nil {
|
if file, err := os.Open(filename); err == nil {
|
||||||
|
printLegacyFileWarning = true
|
||||||
defer file.Close()
|
defer file.Close()
|
||||||
if err := configFile.LegacyLoadFromReader(file); err != nil {
|
if err := configFile.LegacyLoadFromReader(file); err != nil {
|
||||||
return configFile, errors.Wrap(err, filename)
|
return configFile, errors.Wrap(err, filename)
|
||||||
@@ -130,6 +153,9 @@ func LoadDefaultConfigFile(stderr io.Writer) *configfile.ConfigFile {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Fprintf(stderr, "WARNING: Error loading config file: %v\n", err)
|
fmt.Fprintf(stderr, "WARNING: Error loading config file: %v\n", err)
|
||||||
}
|
}
|
||||||
|
if printLegacyFileWarning {
|
||||||
|
_, _ = fmt.Fprintln(stderr, "WARNING: Support for the legacy ~/.dockercfg configuration file and file-format is deprecated and will be removed in an upcoming release")
|
||||||
|
}
|
||||||
if !configFile.ContainsAuth() {
|
if !configFile.ContainsAuth() {
|
||||||
configFile.CredentialsStore = credentials.DetectDefaultStore(configFile.CredentialsStore)
|
configFile.CredentialsStore = credentials.DetectDefaultStore(configFile.CredentialsStore)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,9 +12,11 @@ import (
|
|||||||
|
|
||||||
"github.com/docker/cli/cli/config/configfile"
|
"github.com/docker/cli/cli/config/configfile"
|
||||||
"github.com/docker/cli/cli/config/credentials"
|
"github.com/docker/cli/cli/config/credentials"
|
||||||
|
"github.com/docker/cli/cli/config/types"
|
||||||
"gotest.tools/v3/assert"
|
"gotest.tools/v3/assert"
|
||||||
is "gotest.tools/v3/assert/cmp"
|
is "gotest.tools/v3/assert/cmp"
|
||||||
"gotest.tools/v3/env"
|
"gotest.tools/v3/env"
|
||||||
|
"gotest.tools/v3/fs"
|
||||||
)
|
)
|
||||||
|
|
||||||
var homeKey = "HOME"
|
var homeKey = "HOME"
|
||||||
@@ -115,6 +117,7 @@ password`: "Invalid Auth config file",
|
|||||||
email`: "Invalid auth configuration file",
|
email`: "Invalid auth configuration file",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
resetHomeDir()
|
||||||
tmpHome, err := ioutil.TempDir("", "config-test")
|
tmpHome, err := ioutil.TempDir("", "config-test")
|
||||||
assert.NilError(t, err)
|
assert.NilError(t, err)
|
||||||
defer os.RemoveAll(tmpHome)
|
defer os.RemoveAll(tmpHome)
|
||||||
@@ -131,6 +134,7 @@ email`: "Invalid auth configuration file",
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestOldValidAuth(t *testing.T) {
|
func TestOldValidAuth(t *testing.T) {
|
||||||
|
resetHomeDir()
|
||||||
tmpHome, err := ioutil.TempDir("", "config-test")
|
tmpHome, err := ioutil.TempDir("", "config-test")
|
||||||
assert.NilError(t, err)
|
assert.NilError(t, err)
|
||||||
defer os.RemoveAll(tmpHome)
|
defer os.RemoveAll(tmpHome)
|
||||||
@@ -165,6 +169,7 @@ func TestOldValidAuth(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestOldJSONInvalid(t *testing.T) {
|
func TestOldJSONInvalid(t *testing.T) {
|
||||||
|
resetHomeDir()
|
||||||
tmpHome, err := ioutil.TempDir("", "config-test")
|
tmpHome, err := ioutil.TempDir("", "config-test")
|
||||||
assert.NilError(t, err)
|
assert.NilError(t, err)
|
||||||
defer os.RemoveAll(tmpHome)
|
defer os.RemoveAll(tmpHome)
|
||||||
@@ -184,6 +189,7 @@ func TestOldJSONInvalid(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestOldJSON(t *testing.T) {
|
func TestOldJSON(t *testing.T) {
|
||||||
|
resetHomeDir()
|
||||||
tmpHome, err := ioutil.TempDir("", "config-test")
|
tmpHome, err := ioutil.TempDir("", "config-test")
|
||||||
assert.NilError(t, err)
|
assert.NilError(t, err)
|
||||||
defer os.RemoveAll(tmpHome)
|
defer os.RemoveAll(tmpHome)
|
||||||
@@ -219,6 +225,31 @@ func TestOldJSON(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestOldJSONFallbackDeprecationWarning(t *testing.T) {
|
||||||
|
js := `{"https://index.docker.io/v1/":{"auth":"am9lam9lOmhlbGxv","email":"user@example.com"}}`
|
||||||
|
tmpHome := fs.NewDir(t, t.Name(), fs.WithFile(oldConfigfile, js))
|
||||||
|
defer tmpHome.Remove()
|
||||||
|
defer env.PatchAll(t, map[string]string{homeKey: tmpHome.Path(), "DOCKER_CONFIG": ""})()
|
||||||
|
|
||||||
|
// reset the homeDir, configDir, and its sync.Once, to force them being resolved again
|
||||||
|
resetHomeDir()
|
||||||
|
resetConfigDir()
|
||||||
|
|
||||||
|
buffer := new(bytes.Buffer)
|
||||||
|
configFile := LoadDefaultConfigFile(buffer)
|
||||||
|
expected := configfile.New(tmpHome.Join(configFileDir, ConfigFileName))
|
||||||
|
expected.AuthConfigs = map[string]types.AuthConfig{
|
||||||
|
"https://index.docker.io/v1/": {
|
||||||
|
Username: "joejoe",
|
||||||
|
Password: "hello",
|
||||||
|
Email: "user@example.com",
|
||||||
|
ServerAddress: "https://index.docker.io/v1/",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
assert.Assert(t, strings.Contains(buffer.String(), "WARNING: Support for the legacy ~/.dockercfg configuration file and file-format is deprecated and will be removed in an upcoming release"))
|
||||||
|
assert.Check(t, is.DeepEqual(expected, configFile))
|
||||||
|
}
|
||||||
|
|
||||||
func TestNewJSON(t *testing.T) {
|
func TestNewJSON(t *testing.T) {
|
||||||
tmpHome, err := ioutil.TempDir("", "config-test")
|
tmpHome, err := ioutil.TempDir("", "config-test")
|
||||||
assert.NilError(t, err)
|
assert.NilError(t, err)
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package credentials
|
package credentials
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"os/exec"
|
exec "golang.org/x/sys/execabs"
|
||||||
)
|
)
|
||||||
|
|
||||||
// DetectDefaultStore return the default credentials store for the platform if
|
// DetectDefaultStore return the default credentials store for the platform if
|
||||||
|
|||||||
@@ -20,7 +20,6 @@ import (
|
|||||||
"io"
|
"io"
|
||||||
"net"
|
"net"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
|
||||||
"runtime"
|
"runtime"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
@@ -29,6 +28,7 @@ import (
|
|||||||
|
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
|
exec "golang.org/x/sys/execabs"
|
||||||
)
|
)
|
||||||
|
|
||||||
// New returns net.Conn
|
// New returns net.Conn
|
||||||
|
|||||||
@@ -7,19 +7,24 @@ import (
|
|||||||
"bytes"
|
"bytes"
|
||||||
_ "crypto/sha256" // ensure ids can be computed
|
_ "crypto/sha256" // ensure ids can be computed
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"net/http"
|
"net/http"
|
||||||
"path"
|
"path"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/docker/docker/errdefs"
|
"github.com/docker/docker/errdefs"
|
||||||
digest "github.com/opencontainers/go-digest"
|
digest "github.com/opencontainers/go-digest"
|
||||||
|
"github.com/pkg/errors"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const restrictedNamePattern = "^[a-zA-Z0-9][a-zA-Z0-9_.+-]+$"
|
||||||
|
|
||||||
|
var restrictedNameRegEx = regexp.MustCompile(restrictedNamePattern)
|
||||||
|
|
||||||
// Store provides a context store for easily remembering endpoints configuration
|
// Store provides a context store for easily remembering endpoints configuration
|
||||||
type Store interface {
|
type Store interface {
|
||||||
Reader
|
Reader
|
||||||
@@ -184,6 +189,20 @@ func (s *store) GetStorageInfo(contextName string) StorageInfo {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ValidateContextName checks a context name is valid.
|
||||||
|
func ValidateContextName(name string) error {
|
||||||
|
if name == "" {
|
||||||
|
return errors.New("context name cannot be empty")
|
||||||
|
}
|
||||||
|
if name == "default" {
|
||||||
|
return errors.New(`"default" is a reserved context name`)
|
||||||
|
}
|
||||||
|
if !restrictedNameRegEx.MatchString(name) {
|
||||||
|
return fmt.Errorf("context name %q is invalid, names are validated against regexp %q", name, restrictedNamePattern)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// Export exports an existing namespace into an opaque data stream
|
// Export exports an existing namespace into an opaque data stream
|
||||||
// This stream is actually a tarball containing context metadata and TLS materials, but it does
|
// This stream is actually a tarball containing context metadata and TLS materials, but it does
|
||||||
// not map 1:1 the layout of the context store (don't try to restore it manually without calling store.Import)
|
// not map 1:1 the layout of the context store (don't try to restore it manually without calling store.Import)
|
||||||
@@ -295,6 +314,19 @@ func Import(name string, s Writer, reader io.Reader) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func isValidFilePath(p string) error {
|
||||||
|
if p != metaFile && !strings.HasPrefix(p, "tls/") {
|
||||||
|
return errors.New("unexpected context file")
|
||||||
|
}
|
||||||
|
if path.Clean(p) != p {
|
||||||
|
return errors.New("unexpected path format")
|
||||||
|
}
|
||||||
|
if strings.Contains(p, `\`) {
|
||||||
|
return errors.New(`unexpected '\' in path`)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func importTar(name string, s Writer, reader io.Reader) error {
|
func importTar(name string, s Writer, reader io.Reader) error {
|
||||||
tr := tar.NewReader(&LimitedReader{R: reader, N: maxAllowedFileSizeToImport})
|
tr := tar.NewReader(&LimitedReader{R: reader, N: maxAllowedFileSizeToImport})
|
||||||
tlsData := ContextTLSData{
|
tlsData := ContextTLSData{
|
||||||
@@ -309,10 +341,13 @@ func importTar(name string, s Writer, reader io.Reader) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if hdr.Typeflag == tar.TypeDir {
|
if hdr.Typeflag != tar.TypeReg {
|
||||||
// skip this entry, only taking files into account
|
// skip this entry, only taking files into account
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
if err := isValidFilePath(hdr.Name); err != nil {
|
||||||
|
return errors.Wrap(err, hdr.Name)
|
||||||
|
}
|
||||||
if hdr.Name == metaFile {
|
if hdr.Name == metaFile {
|
||||||
data, err := ioutil.ReadAll(tr)
|
data, err := ioutil.ReadAll(tr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -358,10 +393,13 @@ func importZip(name string, s Writer, reader io.Reader) error {
|
|||||||
var importedMetaFile bool
|
var importedMetaFile bool
|
||||||
for _, zf := range zr.File {
|
for _, zf := range zr.File {
|
||||||
fi := zf.FileInfo()
|
fi := zf.FileInfo()
|
||||||
if fi.IsDir() {
|
if !fi.Mode().IsRegular() {
|
||||||
// skip this entry, only taking files into account
|
// skip this entry, only taking regular files into account
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
if err := isValidFilePath(zf.Name); err != nil {
|
||||||
|
return errors.Wrap(err, zf.Name)
|
||||||
|
}
|
||||||
if zf.Name == metaFile {
|
if zf.Name == metaFile {
|
||||||
f, err := zf.Open()
|
f, err := zf.Open()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -408,6 +446,9 @@ func parseMetadata(data []byte, name string) (Metadata, error) {
|
|||||||
if err := json.Unmarshal(data, &meta); err != nil {
|
if err := json.Unmarshal(data, &meta); err != nil {
|
||||||
return meta, err
|
return meta, err
|
||||||
}
|
}
|
||||||
|
if err := ValidateContextName(name); err != nil {
|
||||||
|
return Metadata{}, err
|
||||||
|
}
|
||||||
meta.Name = name
|
meta.Name = name
|
||||||
return meta, nil
|
return meta, nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -175,7 +175,7 @@ func TestImportTarInvalid(t *testing.T) {
|
|||||||
var r io.Reader = source
|
var r io.Reader = source
|
||||||
s := New(testDir, testCfg)
|
s := New(testDir, testCfg)
|
||||||
err = Import("tarInvalid", s, r)
|
err = Import("tarInvalid", s, r)
|
||||||
assert.ErrorContains(t, err, "invalid context: no metadata found")
|
assert.ErrorContains(t, err, "unexpected context file")
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestImportZip(t *testing.T) {
|
func TestImportZip(t *testing.T) {
|
||||||
@@ -254,5 +254,5 @@ func TestImportZipInvalid(t *testing.T) {
|
|||||||
var r io.Reader = source
|
var r io.Reader = source
|
||||||
s := New(testDir, testCfg)
|
s := New(testDir, testCfg)
|
||||||
err = Import("zipInvalid", s, r)
|
err = Import("zipInvalid", s, r)
|
||||||
assert.ErrorContains(t, err, "invalid context: no metadata found")
|
assert.ErrorContains(t, err, "unexpected context file")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,3 +29,32 @@ func TestConfigModification(t *testing.T) {
|
|||||||
assert.Equal(t, &testEP2{}, cfgCopy.endpointTypes["ep1"]())
|
assert.Equal(t, &testEP2{}, cfgCopy.endpointTypes["ep1"]())
|
||||||
assert.Equal(t, &testEP3{}, cfgCopy.endpointTypes["ep2"]())
|
assert.Equal(t, &testEP3{}, cfgCopy.endpointTypes["ep2"]())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestValidFilePaths(t *testing.T) {
|
||||||
|
paths := map[string]bool{
|
||||||
|
"tls/_/../../something": false,
|
||||||
|
"tls/../../something": false,
|
||||||
|
"../../something": false,
|
||||||
|
"/tls/absolute/unix/path": false,
|
||||||
|
`C:\tls\absolute\windows\path`: false,
|
||||||
|
"C:/tls/absolute/windows/path": false,
|
||||||
|
"tls/kubernetes/key.pem": true,
|
||||||
|
}
|
||||||
|
for p, expectedValid := range paths {
|
||||||
|
err := isValidFilePath(p)
|
||||||
|
assert.Equal(t, err == nil, expectedValid, "%q should report valid as: %v", p, expectedValid)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestValidateContextName(t *testing.T) {
|
||||||
|
names := map[string]bool{
|
||||||
|
"../../invalid/escape": false,
|
||||||
|
"/invalid/absolute": false,
|
||||||
|
`\invalid\windows`: false,
|
||||||
|
"validname": true,
|
||||||
|
}
|
||||||
|
for n, expectedValid := range names {
|
||||||
|
err := ValidateContextName(n)
|
||||||
|
assert.Equal(t, err == nil, expectedValid, "%q should report valid as: %v", n, expectedValid)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ These resources are used to provide
|
|||||||
* An icon
|
* An icon
|
||||||
* A Windows manifest declaring Windows version support
|
* A Windows manifest declaring Windows version support
|
||||||
|
|
||||||
The resource object files are generated with go generate.
|
The resource object files are generated when building with scripts/build/binary .
|
||||||
The resource source files are located in scripts/winresources.
|
The resource source files are located in scripts/winresources.
|
||||||
This occurs automatically when you run scripts/build/windows.
|
This occurs automatically when you run scripts/build/windows.
|
||||||
|
|
||||||
@@ -14,5 +14,3 @@ is included.
|
|||||||
|
|
||||||
*/
|
*/
|
||||||
package winresources
|
package winresources
|
||||||
|
|
||||||
//go:generate ../../scripts/gen/windows-resources
|
|
||||||
|
|||||||
@@ -17,7 +17,9 @@ import (
|
|||||||
func TestClientDebugEnabled(t *testing.T) {
|
func TestClientDebugEnabled(t *testing.T) {
|
||||||
defer debug.Disable()
|
defer debug.Disable()
|
||||||
|
|
||||||
tcmd := newDockerCommand(&command.DockerCli{})
|
cli, err := command.NewDockerCli()
|
||||||
|
assert.NilError(t, err)
|
||||||
|
tcmd := newDockerCommand(cli)
|
||||||
tcmd.SetFlag("debug", "true")
|
tcmd.SetFlag("debug", "true")
|
||||||
cmd, _, err := tcmd.HandleGlobalFlags()
|
cmd, _, err := tcmd.HandleGlobalFlags()
|
||||||
assert.NilError(t, err)
|
assert.NilError(t, err)
|
||||||
|
|||||||
@@ -935,7 +935,7 @@ __docker_complete_log_options() {
|
|||||||
# awslogs does not implement the $common_options2.
|
# awslogs does not implement the $common_options2.
|
||||||
local awslogs_options="$common_options1 awslogs-create-group awslogs-credentials-endpoint awslogs-datetime-format awslogs-group awslogs-multiline-pattern awslogs-region awslogs-stream tag"
|
local awslogs_options="$common_options1 awslogs-create-group awslogs-credentials-endpoint awslogs-datetime-format awslogs-group awslogs-multiline-pattern awslogs-region awslogs-stream tag"
|
||||||
|
|
||||||
local fluentd_options="$common_options1 $common_options2 fluentd-address fluentd-async-connect fluentd-buffer-limit fluentd-retry-wait fluentd-max-retries fluentd-sub-second-precision tag"
|
local fluentd_options="$common_options1 $common_options2 fluentd-address fluentd-async fluentd-buffer-limit fluentd-request-ack fluentd-retry-wait fluentd-max-retries fluentd-sub-second-precision tag"
|
||||||
local gcplogs_options="$common_options1 $common_options2 gcp-log-cmd gcp-meta-id gcp-meta-name gcp-meta-zone gcp-project"
|
local gcplogs_options="$common_options1 $common_options2 gcp-log-cmd gcp-meta-id gcp-meta-name gcp-meta-zone gcp-project"
|
||||||
local gelf_options="$common_options1 $common_options2 gelf-address gelf-compression-level gelf-compression-type gelf-tcp-max-reconnect gelf-tcp-reconnect-delay tag"
|
local gelf_options="$common_options1 $common_options2 gelf-address gelf-compression-level gelf-compression-type gelf-tcp-max-reconnect gelf-tcp-reconnect-delay tag"
|
||||||
local journald_options="$common_options1 $common_options2 tag"
|
local journald_options="$common_options1 $common_options2 tag"
|
||||||
@@ -1955,6 +1955,7 @@ _docker_container_run_and_create() {
|
|||||||
--pid
|
--pid
|
||||||
--pids-limit
|
--pids-limit
|
||||||
--publish -p
|
--publish -p
|
||||||
|
--pull
|
||||||
--restart
|
--restart
|
||||||
--runtime
|
--runtime
|
||||||
--security-opt
|
--security-opt
|
||||||
@@ -2153,6 +2154,10 @@ _docker_container_run_and_create() {
|
|||||||
esac
|
esac
|
||||||
return
|
return
|
||||||
;;
|
;;
|
||||||
|
--pull)
|
||||||
|
COMPREPLY=( $( compgen -W 'always missing never' -- "$cur" ) )
|
||||||
|
return
|
||||||
|
;;
|
||||||
--runtime)
|
--runtime)
|
||||||
__docker_complete_runtimes
|
__docker_complete_runtimes
|
||||||
return
|
return
|
||||||
@@ -2548,6 +2553,7 @@ _docker_daemon() {
|
|||||||
--ip-forward=false
|
--ip-forward=false
|
||||||
--ip-masq=false
|
--ip-masq=false
|
||||||
--iptables=false
|
--iptables=false
|
||||||
|
--ip6tables
|
||||||
--ipv6
|
--ipv6
|
||||||
--live-restore
|
--live-restore
|
||||||
--no-new-privileges
|
--no-new-privileges
|
||||||
@@ -3601,7 +3607,7 @@ _docker_service_ls() {
|
|||||||
return
|
return
|
||||||
;;
|
;;
|
||||||
mode)
|
mode)
|
||||||
COMPREPLY=( $( compgen -W "global replicated" -- "${cur##*=}" ) )
|
COMPREPLY=( $( compgen -W "global global-job replicated replicated-job" -- "${cur##*=}" ) )
|
||||||
return
|
return
|
||||||
;;
|
;;
|
||||||
name)
|
name)
|
||||||
@@ -3731,6 +3737,7 @@ _docker_service_update_and_create() {
|
|||||||
--limit-pids
|
--limit-pids
|
||||||
--log-driver
|
--log-driver
|
||||||
--log-opt
|
--log-opt
|
||||||
|
--max-replicas
|
||||||
--replicas
|
--replicas
|
||||||
--replicas-max-per-node
|
--replicas-max-per-node
|
||||||
--reserve-cpu
|
--reserve-cpu
|
||||||
@@ -3804,7 +3811,7 @@ _docker_service_update_and_create() {
|
|||||||
return
|
return
|
||||||
;;
|
;;
|
||||||
--mode)
|
--mode)
|
||||||
COMPREPLY=( $( compgen -W "global replicated" -- "$cur" ) )
|
COMPREPLY=( $( compgen -W "global global-job replicated replicated-job" -- "$cur" ) )
|
||||||
return
|
return
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
@@ -4348,6 +4355,9 @@ _docker_node_ls() {
|
|||||||
__docker_complete_nodes --cur "${cur##*=}" --id
|
__docker_complete_nodes --cur "${cur##*=}" --id
|
||||||
return
|
return
|
||||||
;;
|
;;
|
||||||
|
label|node.label)
|
||||||
|
return
|
||||||
|
;;
|
||||||
membership)
|
membership)
|
||||||
COMPREPLY=( $( compgen -W "accepted pending" -- "${cur##*=}" ) )
|
COMPREPLY=( $( compgen -W "accepted pending" -- "${cur##*=}" ) )
|
||||||
return
|
return
|
||||||
@@ -4364,7 +4374,7 @@ _docker_node_ls() {
|
|||||||
|
|
||||||
case "$prev" in
|
case "$prev" in
|
||||||
--filter|-f)
|
--filter|-f)
|
||||||
COMPREPLY=( $( compgen -W "id label membership name role" -S = -- "$cur" ) )
|
COMPREPLY=( $( compgen -W "id label membership name node.label role" -S = -- "$cur" ) )
|
||||||
__docker_nospace
|
__docker_nospace
|
||||||
return
|
return
|
||||||
;;
|
;;
|
||||||
|
|||||||
@@ -1343,7 +1343,7 @@ __docker_node_complete_ls_filters() {
|
|||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
else
|
else
|
||||||
opts=('id' 'label' 'membership' 'name' 'role')
|
opts=('id' 'label' 'membership' 'name' 'node.label' 'role')
|
||||||
_describe -t filter-opts "filter options" opts -qS "=" && ret=0
|
_describe -t filter-opts "filter options" opts -qS "=" && ret=0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
@@ -2544,6 +2544,82 @@ __docker_volume_subcommand() {
|
|||||||
|
|
||||||
# EO volume
|
# EO volume
|
||||||
|
|
||||||
|
# BO context
|
||||||
|
|
||||||
|
__docker_complete_contexts() {
|
||||||
|
[[ $PREFIX = -* ]] && return 1
|
||||||
|
integer ret=1
|
||||||
|
declare -a contexts
|
||||||
|
|
||||||
|
contexts=(${(f)${:-"$(_call_program commands docker $docker_options context ls -q)"$'\n'}})
|
||||||
|
|
||||||
|
_describe -t context-list "context" contexts && ret=0
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
__docker_context_commands() {
|
||||||
|
local -a _docker_context_subcommands
|
||||||
|
_docker_context_subcommands=(
|
||||||
|
"create:Create new context"
|
||||||
|
"inspect:Display detailed information on one or more contexts"
|
||||||
|
"list:List available contexts"
|
||||||
|
"rm:Remove one or more contexts"
|
||||||
|
"show:Print the current context"
|
||||||
|
"update:Update a context"
|
||||||
|
"use:Set the default context"
|
||||||
|
)
|
||||||
|
_describe -t docker-context-commands "docker context command" _docker_context_subcommands
|
||||||
|
}
|
||||||
|
|
||||||
|
__docker_context_subcommand() {
|
||||||
|
local -a _command_args opts_help
|
||||||
|
local expl help="--help"
|
||||||
|
integer ret=1
|
||||||
|
|
||||||
|
opts_help=("(: -)--help[Print usage]")
|
||||||
|
|
||||||
|
case "$words[1]" in
|
||||||
|
(create)
|
||||||
|
_arguments $(__docker_arguments) \
|
||||||
|
$opts_help \
|
||||||
|
"($help)--default-stack-orchestrator=[Default orchestrator for stack operations to use with this context]:default-stack-orchestrator:(swarm kubernetes all)" \
|
||||||
|
"($help)--description=[Description of the context]:description:" \
|
||||||
|
"($help)--docker=[Set the docker endpoint]:docker:" \
|
||||||
|
"($help)--kubernetes=[Set the kubernetes endpoint]:kubernetes:" \
|
||||||
|
"($help)--from=[Create context from a named context]:from:__docker_complete_contexts" \
|
||||||
|
"($help -):name: " && ret=0
|
||||||
|
;;
|
||||||
|
(use)
|
||||||
|
_arguments $(__docker_arguments) \
|
||||||
|
$opts_help \
|
||||||
|
"($help -)1:context:__docker_complete_contexts" && ret=0
|
||||||
|
;;
|
||||||
|
(inspect)
|
||||||
|
_arguments $(__docker_arguments) \
|
||||||
|
$opts_help \
|
||||||
|
"($help -)1:context:__docker_complete_contexts" && ret=0
|
||||||
|
;;
|
||||||
|
(rm)
|
||||||
|
_arguments $(__docker_arguments) \
|
||||||
|
$opts_help \
|
||||||
|
"($help -)1:context:__docker_complete_contexts" && ret=0
|
||||||
|
;;
|
||||||
|
(update)
|
||||||
|
_arguments $(__docker_arguments) \
|
||||||
|
$opts_help \
|
||||||
|
"($help)--default-stack-orchestrator=[Default orchestrator for stack operations to use with this context]:default-stack-orchestrator:(swarm kubernetes all)" \
|
||||||
|
"($help)--description=[Description of the context]:description:" \
|
||||||
|
"($help)--docker=[Set the docker endpoint]:docker:" \
|
||||||
|
"($help)--kubernetes=[Set the kubernetes endpoint]:kubernetes:" \
|
||||||
|
"($help -):name:" && ret=0
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
# EO context
|
||||||
|
|
||||||
__docker_caching_policy() {
|
__docker_caching_policy() {
|
||||||
oldp=( "$1"(Nmh+1) ) # 1 hour
|
oldp=( "$1"(Nmh+1) ) # 1 hour
|
||||||
(( $#oldp ))
|
(( $#oldp ))
|
||||||
@@ -2631,6 +2707,23 @@ __docker_subcommand() {
|
|||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
;;
|
;;
|
||||||
|
(context)
|
||||||
|
local curcontext="$curcontext" state
|
||||||
|
_arguments $(__docker_arguments) \
|
||||||
|
$opts_help \
|
||||||
|
"($help -): :->command" \
|
||||||
|
"($help -)*:: :->option-or-argument" && ret=0
|
||||||
|
|
||||||
|
case $state in
|
||||||
|
(command)
|
||||||
|
__docker_context_commands && ret=0
|
||||||
|
;;
|
||||||
|
(option-or-argument)
|
||||||
|
curcontext=${curcontext%:*:*}:docker-${words[-1]}:
|
||||||
|
__docker_context_subcommand && ret=0
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
;;
|
||||||
(daemon)
|
(daemon)
|
||||||
_arguments $(__docker_arguments) \
|
_arguments $(__docker_arguments) \
|
||||||
$opts_help \
|
$opts_help \
|
||||||
|
|||||||
@@ -0,0 +1,63 @@
|
|||||||
|
variable "VERSION" {
|
||||||
|
default = ""
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "USE_GLIBC" {
|
||||||
|
default = ""
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "STRIP_TARGET" {
|
||||||
|
default = ""
|
||||||
|
}
|
||||||
|
|
||||||
|
group "default" {
|
||||||
|
targets = ["binary"]
|
||||||
|
}
|
||||||
|
|
||||||
|
target "binary" {
|
||||||
|
target = "binary"
|
||||||
|
platforms = ["local"]
|
||||||
|
output = ["build"]
|
||||||
|
args = {
|
||||||
|
BASE_VARIANT = USE_GLIBC != "" ? "buster" : "alpine"
|
||||||
|
VERSION = VERSION
|
||||||
|
GO_STRIP = STRIP_TARGET
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
target "dynbinary" {
|
||||||
|
inherits = ["binary"]
|
||||||
|
args = {
|
||||||
|
GO_LINKMODE = "dynamic"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "GROUP_TOTAL" {
|
||||||
|
default = "1"
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "GROUP_INDEX" {
|
||||||
|
default = "0"
|
||||||
|
}
|
||||||
|
|
||||||
|
function "platforms" {
|
||||||
|
params = []
|
||||||
|
result = ["linux/amd64", "linux/386", "linux/arm64", "linux/arm", "linux/ppc64le", "linux/s390x", "darwin/amd64", "darwin/arm64", "windows/amd64"]
|
||||||
|
}
|
||||||
|
|
||||||
|
function "glen" {
|
||||||
|
params = [platforms, GROUP_TOTAL]
|
||||||
|
result = ceil(length(platforms)/GROUP_TOTAL)
|
||||||
|
}
|
||||||
|
|
||||||
|
target "_all_platforms" {
|
||||||
|
platforms = slice(platforms(), GROUP_INDEX*glen(platforms(), GROUP_TOTAL),min(length(platforms()), (GROUP_INDEX+1)*glen(platforms(), GROUP_TOTAL)))
|
||||||
|
}
|
||||||
|
|
||||||
|
target "cross" {
|
||||||
|
inherits = ["binary", "_all_platforms"]
|
||||||
|
}
|
||||||
|
|
||||||
|
target "dynbinary-cross" {
|
||||||
|
inherits = ["dynbinary", "_all_platforms"]
|
||||||
|
}
|
||||||
@@ -38,11 +38,6 @@ build_linter_image:
|
|||||||
# build dockerfile from stdin so that we don't send the build-context; source is bind-mounted in the development environment
|
# build dockerfile from stdin so that we don't send the build-context; source is bind-mounted in the development environment
|
||||||
cat ./dockerfiles/Dockerfile.lint | docker build ${DOCKER_BUILD_ARGS} --build-arg=GO_VERSION -t $(LINTER_IMAGE_NAME) -
|
cat ./dockerfiles/Dockerfile.lint | docker build ${DOCKER_BUILD_ARGS} --build-arg=GO_VERSION -t $(LINTER_IMAGE_NAME) -
|
||||||
|
|
||||||
.PHONY: build_cross_image
|
|
||||||
build_cross_image:
|
|
||||||
# build dockerfile from stdin so that we don't send the build-context; source is bind-mounted in the development environment
|
|
||||||
cat ./dockerfiles/Dockerfile.cross | docker build ${DOCKER_BUILD_ARGS} --build-arg=GO_VERSION -t $(CROSS_IMAGE_NAME) -
|
|
||||||
|
|
||||||
.PHONY: build_shell_validate_image
|
.PHONY: build_shell_validate_image
|
||||||
build_shell_validate_image:
|
build_shell_validate_image:
|
||||||
# build dockerfile from stdin so that we don't send the build-context; source is bind-mounted in the development environment
|
# build dockerfile from stdin so that we don't send the build-context; source is bind-mounted in the development environment
|
||||||
@@ -80,22 +75,10 @@ test-unit: build_docker_image ## run unit tests (using go test)
|
|||||||
.PHONY: test ## run unit and e2e tests
|
.PHONY: test ## run unit and e2e tests
|
||||||
test: test-unit test-e2e
|
test: test-unit test-e2e
|
||||||
|
|
||||||
.PHONY: cross
|
|
||||||
cross: build_cross_image ## build the CLI for macOS and Windows
|
|
||||||
$(DOCKER_RUN) $(CROSS_IMAGE_NAME) make cross
|
|
||||||
|
|
||||||
.PHONY: binary-windows
|
|
||||||
binary-windows: build_cross_image ## build the CLI for Windows
|
|
||||||
$(DOCKER_RUN) $(CROSS_IMAGE_NAME) make $@
|
|
||||||
|
|
||||||
.PHONY: plugins-windows
|
.PHONY: plugins-windows
|
||||||
plugins-windows: build_cross_image ## build the example CLI plugins for Windows
|
plugins-windows: build_cross_image ## build the example CLI plugins for Windows
|
||||||
$(DOCKER_RUN) $(CROSS_IMAGE_NAME) make $@
|
$(DOCKER_RUN) $(CROSS_IMAGE_NAME) make $@
|
||||||
|
|
||||||
.PHONY: binary-osx
|
|
||||||
binary-osx: build_cross_image ## build the CLI for macOS
|
|
||||||
$(DOCKER_RUN) $(CROSS_IMAGE_NAME) make $@
|
|
||||||
|
|
||||||
.PHONY: plugins-osx
|
.PHONY: plugins-osx
|
||||||
plugins-osx: build_cross_image ## build the example CLI plugins for macOS
|
plugins-osx: build_cross_image ## build the example CLI plugins for macOS
|
||||||
$(DOCKER_RUN) $(CROSS_IMAGE_NAME) make $@
|
$(DOCKER_RUN) $(CROSS_IMAGE_NAME) make $@
|
||||||
@@ -120,9 +103,6 @@ fmt: ## run gofmt
|
|||||||
vendor: build_docker_image vendor.conf ## download dependencies (vendor/) listed in vendor.conf
|
vendor: build_docker_image vendor.conf ## download dependencies (vendor/) listed in vendor.conf
|
||||||
$(DOCKER_RUN) -it $(DEV_DOCKER_IMAGE_NAME) make vendor
|
$(DOCKER_RUN) -it $(DEV_DOCKER_IMAGE_NAME) make vendor
|
||||||
|
|
||||||
dynbinary: build_cross_image ## build the CLI dynamically linked
|
|
||||||
$(DOCKER_RUN) -it $(CROSS_IMAGE_NAME) make dynbinary
|
|
||||||
|
|
||||||
.PHONY: authors
|
.PHONY: authors
|
||||||
authors: ## generate AUTHORS file from git history
|
authors: ## generate AUTHORS file from git history
|
||||||
$(DOCKER_RUN) -it $(DEV_DOCKER_IMAGE_NAME) make authors
|
$(DOCKER_RUN) -it $(DEV_DOCKER_IMAGE_NAME) make authors
|
||||||
|
|||||||
@@ -52,6 +52,7 @@ Status | Feature
|
|||||||
-----------|------------------------------------------------------------------------------------------------------------------------------------|------------|------------
|
-----------|------------------------------------------------------------------------------------------------------------------------------------|------------|------------
|
||||||
Deprecated | [Pulling images from non-compliant image registries](#pulling-images-from-non-compliant-image-registries) | v20.10 | -
|
Deprecated | [Pulling images from non-compliant image registries](#pulling-images-from-non-compliant-image-registries) | v20.10 | -
|
||||||
Deprecated | [Linux containers on Windows (LCOW)](#linux-containers-on-windows-lcow-experimental) | v20.10 | -
|
Deprecated | [Linux containers on Windows (LCOW)](#linux-containers-on-windows-lcow-experimental) | v20.10 | -
|
||||||
|
Deprecated | [BLKIO weight options with cgroups v1](#blkio-weight-options–with-cgroups-v1) | v20.10 | -
|
||||||
Deprecated | [Kernel memory limit](#kernel-memory-limit) | v20.10 | -
|
Deprecated | [Kernel memory limit](#kernel-memory-limit) | v20.10 | -
|
||||||
Deprecated | [Classic Swarm and overlay networks using external key/value stores](#classic-swarm-and-overlay-networks-using-cluster-store) | v20.10 | -
|
Deprecated | [Classic Swarm and overlay networks using external key/value stores](#classic-swarm-and-overlay-networks-using-cluster-store) | v20.10 | -
|
||||||
Deprecated | [Support for the legacy `~/.dockercfg` configuration file for authentication](#support-for-legacy-dockercfg-configuration-files) | v20.10 | -
|
Deprecated | [Support for the legacy `~/.dockercfg` configuration file for authentication](#support-for-legacy-dockercfg-configuration-files) | v20.10 | -
|
||||||
@@ -140,6 +141,16 @@ now stopped in favor of running docker natively on Linux in WSL2.
|
|||||||
Developers who want to run Linux workloads on a Windows host are encouraged to use
|
Developers who want to run Linux workloads on a Windows host are encouraged to use
|
||||||
[Docker Desktop with WSL2](https://docs.docker.com/docker-for-windows/wsl/) instead.
|
[Docker Desktop with WSL2](https://docs.docker.com/docker-for-windows/wsl/) instead.
|
||||||
|
|
||||||
|
### BLKIO weight options with cgroups v1
|
||||||
|
|
||||||
|
**Deprecated in Release: v20.10**
|
||||||
|
|
||||||
|
Specifying blkio weight (`docker run --blkio-weight` and `docker run --blkio-weight-device`)
|
||||||
|
is now marked as deprecated when using cgroups v1 because the corresponding features
|
||||||
|
were [removed in Linux kernel v5.0 and up](https://github.com/torvalds/linux/commit/f382fb0bcef4c37dc049e9f6963e3baf204d815c).
|
||||||
|
When using cgroups v2, the `--blkio-weight` options are implemented using
|
||||||
|
[`io.weight](https://github.com/torvalds/linux/blob/v5.0/Documentation/admin-guide/cgroup-v2.rst#io).
|
||||||
|
|
||||||
### Kernel memory limit
|
### Kernel memory limit
|
||||||
|
|
||||||
**Deprecated in Release: v20.10**
|
**Deprecated in Release: v20.10**
|
||||||
|
|||||||
@@ -60,6 +60,7 @@ The currently supported filters are:
|
|||||||
|
|
||||||
* [id](#id)
|
* [id](#id)
|
||||||
* [label](#label)
|
* [label](#label)
|
||||||
|
* [node.label](#nodelabel)
|
||||||
* [membership](#membership)
|
* [membership](#membership)
|
||||||
* [name](#name)
|
* [name](#name)
|
||||||
* [role](#role)
|
* [role](#role)
|
||||||
@@ -77,7 +78,10 @@ ID HOSTNAME STATUS AVAILABILITY MANAGER STATUS
|
|||||||
|
|
||||||
#### label
|
#### label
|
||||||
|
|
||||||
The `label` filter matches nodes based on engine labels and on the presence of a `label` alone or a `label` and a value. Node labels are currently not used for filtering.
|
The `label` filter matches nodes based on engine labels and on the presence of a
|
||||||
|
`label` alone or a `label` and a value. Engine labels are configured in
|
||||||
|
the [daemon configuration](dockerd.md#daemon-configuration-file). To filter on
|
||||||
|
Swarm `node` labels, use [`node.label` instead](#nodelabel).
|
||||||
|
|
||||||
The following filter matches nodes with the `foo` label regardless of its value.
|
The following filter matches nodes with the `foo` label regardless of its value.
|
||||||
|
|
||||||
@@ -88,6 +92,42 @@ ID HOSTNAME STATUS AVAILABILITY MANAGER STATUS
|
|||||||
1bcef6utixb0l0ca7gxuivsj0 swarm-worker2 Ready Active
|
1bcef6utixb0l0ca7gxuivsj0 swarm-worker2 Ready Active
|
||||||
```
|
```
|
||||||
|
|
||||||
|
#### node.label
|
||||||
|
|
||||||
|
The `node.label` filter matches nodes based on node labels and on the presence
|
||||||
|
of a `node.label` alone or a `node.label` and a value.
|
||||||
|
|
||||||
|
The following filter updates nodes to have a `region` node label:
|
||||||
|
|
||||||
|
```console
|
||||||
|
$ docker node update --label-add region=region-a swarm-test-01
|
||||||
|
$ docker node update --label-add region=region-a swarm-test-02
|
||||||
|
$ docker node update --label-add region=region-b swarm-test-03
|
||||||
|
$ docker node update --label-add region=region-b swarm-test-04
|
||||||
|
```
|
||||||
|
|
||||||
|
Show all nodes that have a `region` node label set:
|
||||||
|
|
||||||
|
```console
|
||||||
|
$ docker node ls --filter node.label=region
|
||||||
|
|
||||||
|
ID HOSTNAME STATUS AVAILABILITY MANAGER STATUS ENGINE VERSION
|
||||||
|
yg550ettvsjn6g6t840iaiwgb * swarm-test-01 Ready Active Leader 20.10.2
|
||||||
|
2lm9w9kbepgvkzkkeyku40e65 swarm-test-02 Ready Active Reachable 20.10.2
|
||||||
|
hc0pu7ntc7s4uvj4pv7z7pz15 swarm-test-03 Ready Active Reachable 20.10.2
|
||||||
|
n41b2cijmhifxxvz56vwrs12q swarm-test-04 Ready Active 20.10.2
|
||||||
|
```
|
||||||
|
|
||||||
|
Show all nodes that have a `region` node label, with value `region-a`:
|
||||||
|
|
||||||
|
```console
|
||||||
|
$ docker node ls --filter node.label=region=region-a
|
||||||
|
|
||||||
|
ID HOSTNAME STATUS AVAILABILITY MANAGER STATUS ENGINE VERSION
|
||||||
|
yg550ettvsjn6g6t840iaiwgb * swarm-test-01 Ready Active Leader 20.10.2
|
||||||
|
2lm9w9kbepgvkzkkeyku40e65 swarm-test-02 Ready Active Reachable 20.10.2
|
||||||
|
```
|
||||||
|
|
||||||
#### membership
|
#### membership
|
||||||
|
|
||||||
The `membership` filter matches nodes based on the presence of a `membership` and a value
|
The `membership` filter matches nodes based on the presence of a `membership` and a value
|
||||||
|
|||||||
@@ -76,7 +76,7 @@ listed.
|
|||||||
|
|
||||||
### Push all tags of an image
|
### Push all tags of an image
|
||||||
|
|
||||||
Use the `-a` (or `--all-tags`) option to push To push all tags of a local image.
|
Use the `-a` (or `--all-tags`) option to push all tags of a local image.
|
||||||
|
|
||||||
The following example creates multiple tags for an image, and pushes all those
|
The following example creates multiple tags for an image, and pushes all those
|
||||||
tags to Docker Hub.
|
tags to Docker Hub.
|
||||||
|
|||||||
@@ -58,13 +58,33 @@ The main process inside the container referenced under the link `redis` will rec
|
|||||||
|
|
||||||
### Remove all stopped containers
|
### Remove all stopped containers
|
||||||
|
|
||||||
```bash
|
Use the [`docker container prune`](container_prune.md) command to remove all
|
||||||
$ docker rm $(docker ps -a -q)
|
stopped containers, or refer to the [`docker system prune`](system_prune.md)
|
||||||
|
command to remove unused containers in addition to other Docker resources, such
|
||||||
|
as (unused) images and networks.
|
||||||
|
|
||||||
|
Alternatively, you can use the `docker ps` with the `-q` / `--quiet` option to
|
||||||
|
generate a list of container IDs to remove, and use that list as argument for
|
||||||
|
the `docker rm` command.
|
||||||
|
|
||||||
|
Combining commands can be more flexible, but is less portable as it depends
|
||||||
|
on features provided by the shell, and the exact syntax may differ depending on
|
||||||
|
what shell is used. To use this approach on Windows, consider using PowerShell
|
||||||
|
or Bash.
|
||||||
|
|
||||||
|
The example below uses `docker ps -q` to print the IDs of all containers that
|
||||||
|
have exited (`--filter status=exited`), and removes those containers with
|
||||||
|
the `docker rm` command:
|
||||||
|
|
||||||
|
```console
|
||||||
|
$ docker rm $(docker ps --filter status=exited -q)
|
||||||
```
|
```
|
||||||
|
|
||||||
This command deletes all stopped containers. The command
|
Or, using the `xargs` Linux utility;
|
||||||
`docker ps -a -q` above returns all existing container IDs and passes them to
|
|
||||||
the `rm` command which deletes them. Running containers are not deleted.
|
```console
|
||||||
|
$ docker ps --filter status=exited -q | xargs docker rm
|
||||||
|
```
|
||||||
|
|
||||||
### Remove a container and its volumes
|
### Remove a container and its volumes
|
||||||
|
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ ID NAME MODE REPLICAS IMAGE
|
|||||||
c8wgl7q4ndfd frontend replicated 5/5 nginx:alpine
|
c8wgl7q4ndfd frontend replicated 5/5 nginx:alpine
|
||||||
dmu1ept4cxcf redis replicated 3/3 redis:3.0.6
|
dmu1ept4cxcf redis replicated 3/3 redis:3.0.6
|
||||||
iwe3278osahj mongo global 7/7 mongo:3.3
|
iwe3278osahj mongo global 7/7 mongo:3.3
|
||||||
hh08h9uu8uwr job replicated-job 1/1 (3/5 completed) nginx:latest
|
hh08h9uu8uwr job replicated-job 1/1 (3/5 completed) nginx:latest
|
||||||
```
|
```
|
||||||
|
|
||||||
The `REPLICAS` column shows both the *actual* and *desired* number of tasks for
|
The `REPLICAS` column shows both the *actual* and *desired* number of tasks for
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
---
|
---
|
||||||
description: "Configure containers at runtime"
|
description: "Configure containers at runtime"
|
||||||
keywords: "docker, run, configure, runtime"
|
keywords: "docker, run, configure, runtime"
|
||||||
|
redirect_from:
|
||||||
|
- /reference/run/
|
||||||
---
|
---
|
||||||
|
|
||||||
<!-- This file is maintained within the docker/cli GitHub
|
<!-- This file is maintained within the docker/cli GitHub
|
||||||
@@ -1738,7 +1740,7 @@ volume mounted on the host).
|
|||||||
|
|
||||||
The `container-dest` must always be an absolute path such as `/src/docs`.
|
The `container-dest` must always be an absolute path such as `/src/docs`.
|
||||||
The `host-src` can either be an absolute path or a `name` value. If you
|
The `host-src` can either be an absolute path or a `name` value. If you
|
||||||
supply an absolute path for the `host-dir`, Docker bind-mounts to the path
|
supply an absolute path for the `host-src`, Docker bind-mounts to the path
|
||||||
you specify. If you supply a `name`, Docker creates a named volume by that `name`.
|
you specify. If you supply a `name`, Docker creates a named volume by that `name`.
|
||||||
|
|
||||||
A `name` value must start with an alphanumeric character,
|
A `name` value must start with an alphanumeric character,
|
||||||
|
|||||||
@@ -1,8 +1,11 @@
|
|||||||
package context
|
package context
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"gotest.tools/v3/assert"
|
||||||
"gotest.tools/v3/golden"
|
"gotest.tools/v3/golden"
|
||||||
"gotest.tools/v3/icmd"
|
"gotest.tools/v3/icmd"
|
||||||
)
|
)
|
||||||
@@ -19,3 +22,73 @@ func TestContextList(t *testing.T) {
|
|||||||
})
|
})
|
||||||
golden.Assert(t, result.Stdout(), "context-ls.golden")
|
golden.Assert(t, result.Stdout(), "context-ls.golden")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestContextImportNoTLS(t *testing.T) {
|
||||||
|
d, _ := ioutil.TempDir("", "")
|
||||||
|
defer func() {
|
||||||
|
os.RemoveAll(d)
|
||||||
|
}()
|
||||||
|
cmd := icmd.Command("docker", "context", "import", "remote", "./testdata/test-dockerconfig.tar")
|
||||||
|
cmd.Env = append(cmd.Env,
|
||||||
|
"DOCKER_CONFIG="+d,
|
||||||
|
)
|
||||||
|
icmd.RunCmd(cmd).Assert(t, icmd.Success)
|
||||||
|
|
||||||
|
cmd = icmd.Command("docker", "context", "ls")
|
||||||
|
cmd.Env = append(cmd.Env,
|
||||||
|
"DOCKER_CONFIG="+d,
|
||||||
|
"KUBECONFIG=./testdata/test-kubeconfig", // Allows reuse of context-ls.golden
|
||||||
|
)
|
||||||
|
result := icmd.RunCmd(cmd).Assert(t, icmd.Success)
|
||||||
|
golden.Assert(t, result.Stdout(), "context-ls.golden")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestContextImportTLS(t *testing.T) {
|
||||||
|
d, _ := ioutil.TempDir("", "")
|
||||||
|
defer func() {
|
||||||
|
os.RemoveAll(d)
|
||||||
|
}()
|
||||||
|
cmd := icmd.Command("docker", "context", "import", "test", "./testdata/test-dockerconfig-tls.tar")
|
||||||
|
cmd.Env = append(cmd.Env,
|
||||||
|
"DOCKER_CONFIG="+d,
|
||||||
|
)
|
||||||
|
icmd.RunCmd(cmd).Assert(t, icmd.Success)
|
||||||
|
|
||||||
|
cmd = icmd.Command("docker", "context", "ls")
|
||||||
|
cmd.Env = append(cmd.Env,
|
||||||
|
"DOCKER_CONFIG="+d,
|
||||||
|
)
|
||||||
|
result := icmd.RunCmd(cmd).Assert(t, icmd.Success)
|
||||||
|
golden.Assert(t, result.Stdout(), "context-ls-tls.golden")
|
||||||
|
|
||||||
|
b, err := ioutil.ReadFile(d + "/contexts/tls/9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08/kubernetes/key.pem")
|
||||||
|
assert.NilError(t, err)
|
||||||
|
assert.Equal(t, string(b), `-----BEGIN RSA PRIVATE KEY-----
|
||||||
|
MIIEpAIBAAKCAQEArQk77K5sgrQYY6HiQ1y7AC+67HrRB36oEvR+Fq60RsFcc3cZ
|
||||||
|
xAvMkRSBPjQyskjdYY7kfykGHhfJxGKopb3cDJx3eDBxjgAniwnnOMmHVWbf8Eik
|
||||||
|
o0sNxkgzQPGq83nL3QvVxm3xgqe4nlTdR/Swoq6Pv0oaVYvPPMnaZIF89SJ/wlNT
|
||||||
|
myCs6Uq00dICi20II+M2Nw9b+EVEK4ENl+SlrsK7iuoBIh/H0ZghxOthO9J/HeBb
|
||||||
|
hmM4wcs1OonhPDYKHEaChYA7/Q3/8OBp3bAdlQJ1ziyP3ROAKHL2NwwkGZ8o8HP8
|
||||||
|
u0ex/NAb8w5J5WNePqYQd/sqfisfNpA5VIKcEQIDAQABAoIBABLo4W2aGi2mdMve
|
||||||
|
kxV9esoobSsOuO0ywDdiFK1x5i2dT/cmWuB70Z1BOmaL2cZ2BAt3TC1BVHPRcbFO
|
||||||
|
ftOuDfAq4Tt3P9Ge3rNpH6WrEGka1voxVhyqRRUYKtG8F0yIUOkVNAV9WllG7vwO
|
||||||
|
ligY63y7yuXCuWID51/jR0SYiglXz6G4gcJKFXtugXXiLUIg08GVWkwOsrACC+hR
|
||||||
|
mhcHly1926VhN5+ozjNU/GZ1LaTuK6erBZakH5bqlN97s5rrk0ZRwk/JtnkoRRdI
|
||||||
|
cq0918Za2vqGDHZ3MqLttL52YfDXPIEJPwlFdvC/+sXK2NhUB/xY4yuliU3sY0sf
|
||||||
|
XsIvIWECgYEAwD8AnZI0hnGv8hc6zJppHFRwhrtLZ+09SJwPv5Y4wxuuk5dzNkpf
|
||||||
|
xCNo5hjSVYA1MMmWG8p/sEXo2IyCT8sWDNCn9kieTXihxRxbj88Y2qA5O4N46Zy4
|
||||||
|
kPngjkP5PPDMkwaQQgUr9LvlWS7P6OJkH18ZN8s3QhMaKcHu9FFT44UCgYEA5mte
|
||||||
|
mMSDf9hUK3IK+yrGX62qc2H+ecXN3Zf3nehyiz+dX4ZXhBwBkwJ/mHvuAZPfoFUN
|
||||||
|
Xg6cdyWFJg9ynm45JXnDjmYPGmFLn0mP3Mje/+SbbW2fdFWHJW/maqj4uUqqgQd+
|
||||||
|
pGNzKXq34MzDrpsqIJ7AHu3LYVMOoLAVqC7LXh0CgYEAnLF9ZfFqQH7fgvouIeBl
|
||||||
|
dgLZKOf2AUJcJheVunnN0DF67K+P55tdTTfzY0CuB6SVNivI3uQBiYKh1AdKm5ET
|
||||||
|
auSTUmlEJi8B4/BGLQQG5QOdQoXZgsgLo5cX0b1To7k9dUTvRfCDMFoKCNPgAJiu
|
||||||
|
NOfFXTWU15VMSObaRmcXciUCgYEA5e1cXwsxwUAodZX+eTXs8ArHHQ47Nl55GFeN
|
||||||
|
wufybRuUuX7AE9cyhvUmSA3aqX5a144noaTo40fwftNJZ+jLY6cGyjDzfzp5kMCC
|
||||||
|
KynSxPzlUCPkytyR2Hy6K9LjJ1rnm4vUBswqXcjUdiE+Xxz8w8JGKlbV7Q9JeHVd
|
||||||
|
lw7i5s0CgYAn9T9ySI3xCbrUa/XV/ZY2hopUdH5CDPeTd2eH+L+lctkD9nlzLrpj
|
||||||
|
qij+jaEUweymNx0uttgv02J3DYcIIvVq3RNAwORy5Mp9KasHmjbW2xq+HAq5yFOO
|
||||||
|
1ma82F5zeUl+bKqjMRCY8IVZ349VxRZtb2RVVEKyVswb7HmKp6gGbA==
|
||||||
|
-----END RSA PRIVATE KEY-----
|
||||||
|
`)
|
||||||
|
}
|
||||||
|
|||||||
@@ -0,0 +1,3 @@
|
|||||||
|
NAME DESCRIPTION DOCKER ENDPOINT KUBERNETES ENDPOINT ORCHESTRATOR
|
||||||
|
default * Current DOCKER_HOST based configuration unix:///var/run/docker.sock swarm
|
||||||
|
remote my remote cluster ssh://someserver https://someserver (default) kubernetes
|
||||||
+3
@@ -0,0 +1,3 @@
|
|||||||
|
NAME DESCRIPTION DOCKER ENDPOINT KUBERNETES ENDPOINT ORCHESTRATOR
|
||||||
|
default * Current DOCKER_HOST based configuration unix:///var/run/docker.sock swarm
|
||||||
|
test unix:///var/run/docker.sock https://kubernetes.docker.internal:6443 (default) swarm
|
||||||
BIN
Binary file not shown.
BIN
Binary file not shown.
+7
-7
@@ -504,13 +504,13 @@ and foreground Docker containers.
|
|||||||
**--network**=*type*
|
**--network**=*type*
|
||||||
Set the Network mode for the container. Supported values are:
|
Set the Network mode for the container. Supported values are:
|
||||||
|
|
||||||
| Value | Description |
|
| Value | Description |
|
||||||
|:----------------------------|:-----------------------------------------------------------------------------------------|
|
|:-----------------------------|:-----------------------------------------------------------------------------------------|
|
||||||
| **none** | No networking in the container. |
|
| **none** | No networking in the container. |
|
||||||
| **bridge** | Connect the container to the default Docker bridge via veth interfaces. |
|
| **bridge** | Connect the container to the default Docker bridge via veth interfaces. |
|
||||||
| **host** | Use the host's network stack inside the container. |
|
| **host** | Use the host's network stack inside the container. |
|
||||||
| **container:**_name_|_id_ | Use the network stack of another container, specified via its _name_ or _id_. |
|
| **container:**_name_\|_id_ | Use the network stack of another container, specified via its _name_ or _id_. |
|
||||||
| _network-name_|_network-id_ | Connects the container to a user created network (using `docker network create` command) |
|
| _network-name_\|_network-id_ | Connects the container to a user created network (using `docker network create` command) |
|
||||||
|
|
||||||
Default is **bridge**.
|
Default is **bridge**.
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -18,5 +18,5 @@ for FILE in *.md; do
|
|||||||
continue
|
continue
|
||||||
fi
|
fi
|
||||||
mkdir -p "./man${num}"
|
mkdir -p "./man${num}"
|
||||||
md2man -in "$FILE" -out "./man${num}/${name}"
|
go-md2man -in "$FILE" -out "./man${num}/${name}"
|
||||||
done
|
done
|
||||||
|
|||||||
@@ -1,8 +1,10 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env sh
|
||||||
set -eu
|
set -eu
|
||||||
|
|
||||||
|
TARGET=${TARGET:-"build"}
|
||||||
|
|
||||||
PLATFORM=${PLATFORM:-}
|
PLATFORM=${PLATFORM:-}
|
||||||
VERSION=${VERSION:-"unknown-version"}
|
VERSION=${VERSION:-$(git describe --match 'v[0-9]*' --dirty='.m' --always --tags | sed 's/^v//' 2>/dev/null || echo "unknown-version" )}
|
||||||
GITCOMMIT=${GITCOMMIT:-$(git rev-parse --short HEAD 2> /dev/null || true)}
|
GITCOMMIT=${GITCOMMIT:-$(git rev-parse --short HEAD 2> /dev/null || true)}
|
||||||
BUILDTIME=${BUILDTIME:-$(date -u +"%Y-%m-%dT%H:%M:%SZ")}
|
BUILDTIME=${BUILDTIME:-$(date -u +"%Y-%m-%dT%H:%M:%SZ")}
|
||||||
|
|
||||||
@@ -20,15 +22,15 @@ export LDFLAGS="\
|
|||||||
${LDFLAGS:-} \
|
${LDFLAGS:-} \
|
||||||
"
|
"
|
||||||
|
|
||||||
GOOS="${GOOS:-$(go env GOHOSTOS)}"
|
GOOS="$(go env GOOS)"
|
||||||
GOARCH="${GOARCH:-$(go env GOHOSTARCH)}"
|
GOARCH="$(go env GOARCH)"
|
||||||
if [ "${GOARCH}" = "arm" ]; then
|
if [ "${GOARCH}" = "arm" ]; then
|
||||||
GOARM="${GOARM:-$(go env GOHOSTARM)}"
|
GOARM="$(go env GOARM)"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
TARGET="build/docker-$GOOS-$GOARCH"
|
TARGET="$TARGET/docker-${GOOS}-${GOARCH}"
|
||||||
if [ "${GOARCH}" = "arm" ] && [ -n "${GOARM}" ]; then
|
if [ "${GOARCH}" = "arm" ] && [ -n "${GOARM}" ]; then
|
||||||
TARGET="${TARGET}-v${GOARM}"
|
TARGET="${TARGET}-v${GOARM}"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ "${GOOS}" = "windows" ]; then
|
if [ "${GOOS}" = "windows" ]; then
|
||||||
|
|||||||
+73
-7
@@ -1,14 +1,80 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env sh
|
||||||
#
|
#
|
||||||
# Build a static binary for the host OS/ARCH
|
# Build a static binary for the host OS/ARCH
|
||||||
#
|
#
|
||||||
|
|
||||||
set -eu -o pipefail
|
set -eu
|
||||||
|
|
||||||
source ./scripts/build/.variables
|
: "${CGO_ENABLED=}"
|
||||||
|
: "${GO_LINKMODE=static}"
|
||||||
|
: "${GO_BUILDMODE=}"
|
||||||
|
: "${GO_BUILDTAGS=}"
|
||||||
|
: "${GO_STRIP=}"
|
||||||
|
|
||||||
echo "Building statically linked $TARGET"
|
. ./scripts/build/.variables
|
||||||
export CGO_ENABLED=0
|
|
||||||
go build -o "${TARGET}" --ldflags "${LDFLAGS}" "${SOURCE}"
|
|
||||||
|
|
||||||
ln -sf "$(basename "${TARGET}")" build/docker
|
if [ -z "$CGO_ENABLED" ]; then
|
||||||
|
case "$(go env GOOS)" in
|
||||||
|
linux)
|
||||||
|
case "$(go env GOARCH)" in
|
||||||
|
amd64|arm64|arm|s390x)
|
||||||
|
CGO_ENABLED=1
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
CGO_ENABLED=0
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
;;
|
||||||
|
darwin|windows)
|
||||||
|
CGO_ENABLED=1
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
CGO_ENABLED=0
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
fi
|
||||||
|
export CGO_ENABLED
|
||||||
|
if [ "$CGO_ENABLED" = "1" ] && [ "$(go env GOOS)" != "windows" ]; then
|
||||||
|
case "$(go env GOARCH)" in
|
||||||
|
mips*|ppc64)
|
||||||
|
# pie build mode is not supported on mips architectures
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
GO_BUILDMODE="-buildmode=pie"
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
GO_BUILDTAGS="$GO_BUILDTAGS pkcs11"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$CGO_ENABLED" = "1" ] && [ "$GO_LINKMODE" = "static" ] && [ "$(go env GOOS)" = "linux" ]; then
|
||||||
|
LDFLAGS="$LDFLAGS -extldflags -static"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -n "$GO_STRIP" ]; then
|
||||||
|
LDFLAGS="$LDFLAGS -s -w"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$(go env GOOS)" = "windows" ]; then
|
||||||
|
# Generate a Windows file version of the form major,minor,patch,build
|
||||||
|
VERSION_QUAD=$(printf "%s" "$VERSION" | sed -re 's/^([0-9.]*).*$/\1/' | tr . , | sed -re 's/^[0-9]+$/\0,0/' | sed -re 's/^[0-9]+,[0-9]+$/\0,0/' | sed -re 's/^[0-9]+,[0-9]+,[0-9]+$/\0,0/')
|
||||||
|
|
||||||
|
set --
|
||||||
|
[ -n "$VERSION" ] && set -- "$@" -D "DOCKER_VERSION=\"$VERSION\""
|
||||||
|
[ -n "$VERSION_QUAD" ] && set -- "$@" -D "DOCKER_VERSION_QUAD=$VERSION_QUAD"
|
||||||
|
[ -n "$GITCOMMIT" ] && set -- "$@" -D "DOCKER_COMMIT=\"$GITCOMMIT\""
|
||||||
|
|
||||||
|
windres=$($(go env CC) --print-prog-name=windres)
|
||||||
|
|
||||||
|
target="$(dirname "$0")/../../cli/winresources/rsrc_$(go env GOARCH).syso"
|
||||||
|
mkdir -p "$(dirname "${target}")"
|
||||||
|
"$windres" -i "$(dirname "$0")/../winresources/docker.rc" -o "$target" "$@"
|
||||||
|
echo "package winresources" > "$(dirname "${target}")/stub_windows.go"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Building $GO_LINKMODE $(basename "${TARGET}")"
|
||||||
|
|
||||||
|
export GO111MODULE=auto
|
||||||
|
|
||||||
|
go build -o "${TARGET}" -tags "${GO_BUILDTAGS}" --ldflags "${LDFLAGS}" ${GO_BUILDMODE} "${SOURCE}"
|
||||||
|
|
||||||
|
ln -sf "$(basename "${TARGET}")" "$(dirname "${TARGET}")/docker"
|
||||||
|
|||||||
@@ -1,33 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
#
|
|
||||||
# Build a binary for all supported platforms
|
|
||||||
#
|
|
||||||
|
|
||||||
set -eu -o pipefail
|
|
||||||
|
|
||||||
BUILDDIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
|
|
||||||
export SHELL=bash
|
|
||||||
|
|
||||||
jobs=(
|
|
||||||
"$BUILDDIR/windows" \
|
|
||||||
"$BUILDDIR/osx" \
|
|
||||||
"GOOS=linux GOARCH=amd64 $BUILDDIR/binary" \
|
|
||||||
"GOOS=linux GOARCH=arm $BUILDDIR/binary" \
|
|
||||||
"GOOS=linux GOARCH=ppc64le $BUILDDIR/binary" \
|
|
||||||
"GOOS=linux GOARCH=s390x $BUILDDIR/binary" \
|
|
||||||
)
|
|
||||||
|
|
||||||
# Outside of circleCI run all at once. On circleCI run two at a time because
|
|
||||||
# each container has access to two cores.
|
|
||||||
group=${CROSS_GROUP-"all"}
|
|
||||||
|
|
||||||
if [ "$group" = "all" ]; then
|
|
||||||
|
|
||||||
echo "Building binaries for all platforms"
|
|
||||||
parallel ::: "${jobs[@]}"
|
|
||||||
exit 0
|
|
||||||
|
|
||||||
fi
|
|
||||||
|
|
||||||
declare -i start="$group*2"
|
|
||||||
parallel ::: "${jobs[@]:$start:2}"
|
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
#
|
|
||||||
# Build a dynamically linked binary for the host OS/ARCH
|
|
||||||
#
|
|
||||||
|
|
||||||
set -eu -o pipefail
|
|
||||||
|
|
||||||
source ./scripts/build/.variables
|
|
||||||
|
|
||||||
echo "Building dynamically linked $TARGET"
|
|
||||||
export CGO_ENABLED=1
|
|
||||||
case "$(go env GOARCH)" in
|
|
||||||
mips*|ppc64)
|
|
||||||
# pie build mode is not supported on mips architectures
|
|
||||||
GO_BUILDMODE=""
|
|
||||||
;;
|
|
||||||
*)
|
|
||||||
GO_BUILDMODE="-buildmode=pie"
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
|
|
||||||
go build -o "${TARGET}" -tags pkcs11 --ldflags "${LDFLAGS}" ${GO_BUILDMODE} "${SOURCE}"
|
|
||||||
|
|
||||||
ln -sf "$(basename "${TARGET}")" build/docker
|
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
#
|
|
||||||
# Build an osx binary from linux
|
|
||||||
#
|
|
||||||
|
|
||||||
set -eu -o pipefail
|
|
||||||
|
|
||||||
source ./scripts/build/.variables
|
|
||||||
|
|
||||||
export CGO_ENABLED=1
|
|
||||||
export GOOS=darwin
|
|
||||||
export GOARCH=amd64
|
|
||||||
export CC=o64-clang
|
|
||||||
export CXX=o64-clang++
|
|
||||||
export LDFLAGS="$LDFLAGS -linkmode external -s"
|
|
||||||
export LDFLAGS_STATIC_DOCKER='-extld='${CC}
|
|
||||||
|
|
||||||
# Override TARGET
|
|
||||||
TARGET="build/docker-$GOOS-$GOARCH"
|
|
||||||
|
|
||||||
echo "Building $TARGET"
|
|
||||||
go build -o "${TARGET}" -tags pkcs11 --ldflags "${LDFLAGS}" "${SOURCE}"
|
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
#
|
|
||||||
# Build a windows binary from linux
|
|
||||||
#
|
|
||||||
|
|
||||||
set -eu -o pipefail
|
|
||||||
|
|
||||||
source ./scripts/build/.variables
|
|
||||||
|
|
||||||
export CC=x86_64-w64-mingw32-gcc
|
|
||||||
export CGO_ENABLED=1
|
|
||||||
export GOOS=windows
|
|
||||||
export GOARCH=amd64
|
|
||||||
|
|
||||||
# Override TARGET
|
|
||||||
TARGET="build/docker-$GOOS-$GOARCH.exe"
|
|
||||||
|
|
||||||
echo "Generating windows resources"
|
|
||||||
go generate ./cli/winresources
|
|
||||||
|
|
||||||
echo "Building $TARGET"
|
|
||||||
# TODO: -tags pkcs11
|
|
||||||
go build -o "${TARGET}" --ldflags "${LDFLAGS}" "${SOURCE}"
|
|
||||||
@@ -4,9 +4,9 @@ set -eu -o pipefail
|
|||||||
|
|
||||||
mkdir -p ./man/man1
|
mkdir -p ./man/man1
|
||||||
|
|
||||||
if ! command -v md2man &> /dev/null; then
|
if ! command -v go-md2man &> /dev/null; then
|
||||||
# yay, go install creates a binary named "v2" ¯\_(ツ)_/¯
|
# yay, go install creates a binary named "v2" ¯\_(ツ)_/¯
|
||||||
go build -o "/go/bin/md2man" ./vendor/github.com/cpuguy83/go-md2man/v2
|
go build -o "/go/bin/go-md2man" ./vendor/github.com/cpuguy83/go-md2man/v2
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Generate man pages from cobra commands
|
# Generate man pages from cobra commands
|
||||||
|
|||||||
@@ -1,45 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
#
|
|
||||||
# Compile the Windows resources into the sources
|
|
||||||
#
|
|
||||||
|
|
||||||
set -eu -o pipefail
|
|
||||||
|
|
||||||
SCRIPTDIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
|
|
||||||
# shellcheck source=/go/src/github.com/docker/cli/scripts/build/.variables
|
|
||||||
source "$SCRIPTDIR"/../build/.variables
|
|
||||||
|
|
||||||
RESOURCES=$SCRIPTDIR/../winresources
|
|
||||||
|
|
||||||
TEMPDIR=$(mktemp -d)
|
|
||||||
trap 'rm -rf $TEMPDIR' EXIT
|
|
||||||
|
|
||||||
if [ "$(go env GOHOSTOS)" = "windows" ]; then
|
|
||||||
WINDRES=windres
|
|
||||||
else
|
|
||||||
# Cross compiling
|
|
||||||
WINDRES=x86_64-w64-mingw32-windres
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Generate a Windows file version of the form major,minor,patch,build (with any part optional)
|
|
||||||
VERSION_QUAD=$(printf "%s" "$VERSION" | sed -re 's/^([0-9.]*).*$/\1/' | tr . ,)
|
|
||||||
|
|
||||||
# Pass version and commit information into the resource compiler
|
|
||||||
defs=
|
|
||||||
[ -n "$VERSION" ] && defs+=( "-D DOCKER_VERSION=\"$VERSION\"")
|
|
||||||
[ -n "$VERSION_QUAD" ] && defs+=( "-D DOCKER_VERSION_QUAD=$VERSION_QUAD")
|
|
||||||
[ -n "$GITCOMMIT" ] && defs+=( "-D DOCKER_COMMIT=\"$GITCOMMIT\"")
|
|
||||||
|
|
||||||
makeres() {
|
|
||||||
# shellcheck disable=SC2086
|
|
||||||
"$WINDRES" \
|
|
||||||
-i "$RESOURCES/$1" \
|
|
||||||
-o "$3" \
|
|
||||||
-F "$2" \
|
|
||||||
--use-temp-file \
|
|
||||||
-I "$TEMPDIR" \
|
|
||||||
${defs[*]}
|
|
||||||
}
|
|
||||||
|
|
||||||
makeres docker.rc pe-x86-64 rsrc_amd64.syso
|
|
||||||
makeres docker.rc pe-i386 rsrc_386.syso
|
|
||||||
+4
-5
@@ -1,5 +1,4 @@
|
|||||||
cloud.google.com/go ceeb313ad77b789a7fa5287b36a1d127b69b7093 # v0.44.3
|
cloud.google.com/go ceeb313ad77b789a7fa5287b36a1d127b69b7093 # v0.44.3
|
||||||
github.com/agl/ed25519 5312a61534124124185d41f09206b9fef1d88403
|
|
||||||
github.com/Azure/go-ansiterm d6e3b3328b783f23731bc4d058875b0371ff8109
|
github.com/Azure/go-ansiterm d6e3b3328b783f23731bc4d058875b0371ff8109
|
||||||
github.com/beorn7/perks 37c8de3658fcb183f997c4e13e8337516ab753e6 # v1.0.1
|
github.com/beorn7/perks 37c8de3658fcb183f997c4e13e8337516ab753e6 # v1.0.1
|
||||||
github.com/cespare/xxhash/v2 d7df74196a9e781ede915320c11c378c1b2f3a1f # v2.1.1
|
github.com/cespare/xxhash/v2 d7df74196a9e781ede915320c11c378c1b2f3a1f # v2.1.1
|
||||||
@@ -14,8 +13,8 @@ github.com/creack/pty 2a38352e8b4d7ab6c336eef107e4
|
|||||||
github.com/davecgh/go-spew 8991bc29aa16c548c550c7ff78260e27b9ab7c73 # v1.1.1
|
github.com/davecgh/go-spew 8991bc29aa16c548c550c7ff78260e27b9ab7c73 # v1.1.1
|
||||||
github.com/docker/compose-on-kubernetes 78e6a00beda64ac8ccb9fec787e601fe2ce0d5bb # v0.5.0-alpha1
|
github.com/docker/compose-on-kubernetes 78e6a00beda64ac8ccb9fec787e601fe2ce0d5bb # v0.5.0-alpha1
|
||||||
github.com/docker/distribution 0d3efadf0154c2b8a4e7b6621fff9809655cc580
|
github.com/docker/distribution 0d3efadf0154c2b8a4e7b6621fff9809655cc580
|
||||||
github.com/docker/docker f0014860c1b3345e1fcc7ed81c491298de2633fb # v20.10.1
|
github.com/docker/docker 46229ca1d815cfd4b50eb377ac75ad8300e13a85
|
||||||
github.com/docker/docker-credential-helpers 54f0238b6bf101fc3ad3b34114cb5520beb562f5 # v0.6.3
|
github.com/docker/docker-credential-helpers 38bea2ce277ad0c9d2a6230692b0606ca5286526
|
||||||
github.com/docker/go d30aec9fd63c35133f8f79c3412ad91a3b08be06 # Contains a customized version of canonical/json and is used by Notary. The package is periodically rebased on current Go versions.
|
github.com/docker/go d30aec9fd63c35133f8f79c3412ad91a3b08be06 # Contains a customized version of canonical/json and is used by Notary. The package is periodically rebased on current Go versions.
|
||||||
github.com/docker/go-connections 7395e3f8aa162843a74ed6d48e79627d9792ac55 # v0.4.0
|
github.com/docker/go-connections 7395e3f8aa162843a74ed6d48e79627d9792ac55 # v0.4.0
|
||||||
github.com/docker/go-events e31b211e4f1cd09aa76fe4ac244571fab96ae47f
|
github.com/docker/go-events e31b211e4f1cd09aa76fe4ac244571fab96ae47f
|
||||||
@@ -68,7 +67,7 @@ github.com/shurcooL/sanitized_anchor_name 7bfe4c7ecddb3666a94b053b422c
|
|||||||
github.com/sirupsen/logrus 6699a89a232f3db797f2e280639854bbc4b89725 # v1.7.0
|
github.com/sirupsen/logrus 6699a89a232f3db797f2e280639854bbc4b89725 # v1.7.0
|
||||||
github.com/spf13/cobra 86f8bfd7fef868a174e1b606783bd7f5c82ddf8f # v1.1.1
|
github.com/spf13/cobra 86f8bfd7fef868a174e1b606783bd7f5c82ddf8f # v1.1.1
|
||||||
github.com/spf13/pflag 2e9d26c8c37aae03e3f9d4e90b7116f5accb7cab # v1.0.5
|
github.com/spf13/pflag 2e9d26c8c37aae03e3f9d4e90b7116f5accb7cab # v1.0.5
|
||||||
github.com/theupdateframework/notary d6e1431feb32348e0650bf7551ac5cffd01d857b # v0.6.1
|
github.com/theupdateframework/notary bf96a202a09a312ae005cd312fc06ff4d2c166ce # v0.7.0-21-gbf96a202
|
||||||
github.com/tonistiigi/fsutil 0834f99b7b85462efb69b4f571a4fa3ca7da5ac9
|
github.com/tonistiigi/fsutil 0834f99b7b85462efb69b4f571a4fa3ca7da5ac9
|
||||||
github.com/tonistiigi/go-rosetta f79598599c5d34ea253b56a1d7c89bc6a96de7db
|
github.com/tonistiigi/go-rosetta f79598599c5d34ea253b56a1d7c89bc6a96de7db
|
||||||
github.com/tonistiigi/units 6950e57a87eaf136bbe44ef2ec8e75b9e3569de2
|
github.com/tonistiigi/units 6950e57a87eaf136bbe44ef2ec8e75b9e3569de2
|
||||||
@@ -80,7 +79,7 @@ golang.org/x/crypto c1f2f97bffc9c53fc40a1a28a5b4
|
|||||||
golang.org/x/net ab34263943818b32f575efc978a3d24e80b04bd7
|
golang.org/x/net ab34263943818b32f575efc978a3d24e80b04bd7
|
||||||
golang.org/x/oauth2 bf48bf16ab8d622ce64ec6ce98d2c98f916b6303
|
golang.org/x/oauth2 bf48bf16ab8d622ce64ec6ce98d2c98f916b6303
|
||||||
golang.org/x/sync cd5d95a43a6e21273425c7ae415d3df9ea832eeb
|
golang.org/x/sync cd5d95a43a6e21273425c7ae415d3df9ea832eeb
|
||||||
golang.org/x/sys eeed37f84f13f52d35e095e8023ba65671ff86a1
|
golang.org/x/sys b64e53b001e413bd5067f36d4e439eded3827374
|
||||||
golang.org/x/term f5c789dd3221ff39d752ac54467d762de7cfbec6
|
golang.org/x/term f5c789dd3221ff39d752ac54467d762de7cfbec6
|
||||||
golang.org/x/text 23ae387dee1f90d29a23c0e87ee0b46038fbed0e # v0.3.3
|
golang.org/x/text 23ae387dee1f90d29a23c0e87ee0b46038fbed0e # v0.3.3
|
||||||
golang.org/x/time 555d28b269f0569763d25dbe1a237ae74c6bcc82
|
golang.org/x/time 555d28b269f0569763d25dbe1a237ae74c6bcc82
|
||||||
|
|||||||
-27
@@ -1,27 +0,0 @@
|
|||||||
Copyright (c) 2012 The Go Authors. All rights reserved.
|
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without
|
|
||||||
modification, are permitted provided that the following conditions are
|
|
||||||
met:
|
|
||||||
|
|
||||||
* Redistributions of source code must retain the above copyright
|
|
||||||
notice, this list of conditions and the following disclaimer.
|
|
||||||
* Redistributions in binary form must reproduce the above
|
|
||||||
copyright notice, this list of conditions and the following disclaimer
|
|
||||||
in the documentation and/or other materials provided with the
|
|
||||||
distribution.
|
|
||||||
* Neither the name of Google Inc. nor the names of its
|
|
||||||
contributors may be used to endorse or promote products derived from
|
|
||||||
this software without specific prior written permission.
|
|
||||||
|
|
||||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
||||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
||||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
||||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
||||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
||||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
||||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
||||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
||||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
||||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
-127
@@ -1,127 +0,0 @@
|
|||||||
// Copyright 2013 The Go Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
// Package ed25519 implements the Ed25519 signature algorithm. See
|
|
||||||
// http://ed25519.cr.yp.to/.
|
|
||||||
package ed25519
|
|
||||||
|
|
||||||
// This code is a port of the public domain, "ref10" implementation of ed25519
|
|
||||||
// from SUPERCOP.
|
|
||||||
|
|
||||||
import (
|
|
||||||
"crypto/sha512"
|
|
||||||
"crypto/subtle"
|
|
||||||
"io"
|
|
||||||
|
|
||||||
"github.com/agl/ed25519/edwards25519"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
PublicKeySize = 32
|
|
||||||
PrivateKeySize = 64
|
|
||||||
SignatureSize = 64
|
|
||||||
)
|
|
||||||
|
|
||||||
// GenerateKey generates a public/private key pair using randomness from rand.
|
|
||||||
func GenerateKey(rand io.Reader) (publicKey *[PublicKeySize]byte, privateKey *[PrivateKeySize]byte, err error) {
|
|
||||||
privateKey = new([64]byte)
|
|
||||||
publicKey = new([32]byte)
|
|
||||||
_, err = io.ReadFull(rand, privateKey[:32])
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
h := sha512.New()
|
|
||||||
h.Write(privateKey[:32])
|
|
||||||
digest := h.Sum(nil)
|
|
||||||
|
|
||||||
digest[0] &= 248
|
|
||||||
digest[31] &= 127
|
|
||||||
digest[31] |= 64
|
|
||||||
|
|
||||||
var A edwards25519.ExtendedGroupElement
|
|
||||||
var hBytes [32]byte
|
|
||||||
copy(hBytes[:], digest)
|
|
||||||
edwards25519.GeScalarMultBase(&A, &hBytes)
|
|
||||||
A.ToBytes(publicKey)
|
|
||||||
|
|
||||||
copy(privateKey[32:], publicKey[:])
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Sign signs the message with privateKey and returns a signature.
|
|
||||||
func Sign(privateKey *[PrivateKeySize]byte, message []byte) *[SignatureSize]byte {
|
|
||||||
h := sha512.New()
|
|
||||||
h.Write(privateKey[:32])
|
|
||||||
|
|
||||||
var digest1, messageDigest, hramDigest [64]byte
|
|
||||||
var expandedSecretKey [32]byte
|
|
||||||
h.Sum(digest1[:0])
|
|
||||||
copy(expandedSecretKey[:], digest1[:])
|
|
||||||
expandedSecretKey[0] &= 248
|
|
||||||
expandedSecretKey[31] &= 63
|
|
||||||
expandedSecretKey[31] |= 64
|
|
||||||
|
|
||||||
h.Reset()
|
|
||||||
h.Write(digest1[32:])
|
|
||||||
h.Write(message)
|
|
||||||
h.Sum(messageDigest[:0])
|
|
||||||
|
|
||||||
var messageDigestReduced [32]byte
|
|
||||||
edwards25519.ScReduce(&messageDigestReduced, &messageDigest)
|
|
||||||
var R edwards25519.ExtendedGroupElement
|
|
||||||
edwards25519.GeScalarMultBase(&R, &messageDigestReduced)
|
|
||||||
|
|
||||||
var encodedR [32]byte
|
|
||||||
R.ToBytes(&encodedR)
|
|
||||||
|
|
||||||
h.Reset()
|
|
||||||
h.Write(encodedR[:])
|
|
||||||
h.Write(privateKey[32:])
|
|
||||||
h.Write(message)
|
|
||||||
h.Sum(hramDigest[:0])
|
|
||||||
var hramDigestReduced [32]byte
|
|
||||||
edwards25519.ScReduce(&hramDigestReduced, &hramDigest)
|
|
||||||
|
|
||||||
var s [32]byte
|
|
||||||
edwards25519.ScMulAdd(&s, &hramDigestReduced, &expandedSecretKey, &messageDigestReduced)
|
|
||||||
|
|
||||||
signature := new([64]byte)
|
|
||||||
copy(signature[:], encodedR[:])
|
|
||||||
copy(signature[32:], s[:])
|
|
||||||
return signature
|
|
||||||
}
|
|
||||||
|
|
||||||
// Verify returns true iff sig is a valid signature of message by publicKey.
|
|
||||||
func Verify(publicKey *[PublicKeySize]byte, message []byte, sig *[SignatureSize]byte) bool {
|
|
||||||
if sig[63]&224 != 0 {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
var A edwards25519.ExtendedGroupElement
|
|
||||||
if !A.FromBytes(publicKey) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
edwards25519.FeNeg(&A.X, &A.X)
|
|
||||||
edwards25519.FeNeg(&A.T, &A.T)
|
|
||||||
|
|
||||||
h := sha512.New()
|
|
||||||
h.Write(sig[:32])
|
|
||||||
h.Write(publicKey[:])
|
|
||||||
h.Write(message)
|
|
||||||
var digest [64]byte
|
|
||||||
h.Sum(digest[:0])
|
|
||||||
|
|
||||||
var hReduced [32]byte
|
|
||||||
edwards25519.ScReduce(&hReduced, &digest)
|
|
||||||
|
|
||||||
var R edwards25519.ProjectiveGroupElement
|
|
||||||
var b [32]byte
|
|
||||||
copy(b[:], sig[32:])
|
|
||||||
edwards25519.GeDoubleScalarMultVartime(&R, &hReduced, &A, &b)
|
|
||||||
|
|
||||||
var checkR [32]byte
|
|
||||||
R.ToBytes(&checkR)
|
|
||||||
return subtle.ConstantTimeCompare(sig[:32], checkR[:]) == 1
|
|
||||||
}
|
|
||||||
-1411
File diff suppressed because it is too large
Load Diff
-1773
File diff suppressed because it is too large
Load Diff
+2
-1
@@ -4,7 +4,8 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
|
||||||
|
exec "golang.org/x/sys/execabs"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Program is an interface to execute external programs.
|
// Program is an interface to execute external programs.
|
||||||
|
|||||||
+8
@@ -0,0 +1,8 @@
|
|||||||
|
module github.com/docker/docker-credential-helpers
|
||||||
|
|
||||||
|
go 1.13
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/danieljoos/wincred v1.1.0
|
||||||
|
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4
|
||||||
|
)
|
||||||
Generated
Vendored
+13
@@ -21,6 +21,13 @@ import (
|
|||||||
// when the credentials are not in the keychain.
|
// when the credentials are not in the keychain.
|
||||||
const errCredentialsNotFound = "The specified item could not be found in the keychain."
|
const errCredentialsNotFound = "The specified item could not be found in the keychain."
|
||||||
|
|
||||||
|
// errCredentialsNotFound is the specific error message returned by OS X
|
||||||
|
// when environment does not allow showing dialog to unlock keychain.
|
||||||
|
const errInteractionNotAllowed = "User interaction is not allowed."
|
||||||
|
|
||||||
|
// ErrInteractionNotAllowed is returned if keychain password prompt can not be shown.
|
||||||
|
var ErrInteractionNotAllowed = errors.New(`keychain cannot be accessed because the current session does not allow user interaction. The keychain may be locked; unlock it by running "security -v unlock-keychain ~/Library/Keychains/login.keychain-db" and try again`)
|
||||||
|
|
||||||
// Osxkeychain handles secrets using the OS X Keychain as store.
|
// Osxkeychain handles secrets using the OS X Keychain as store.
|
||||||
type Osxkeychain struct{}
|
type Osxkeychain struct{}
|
||||||
|
|
||||||
@@ -89,6 +96,9 @@ func (h Osxkeychain) Get(serverURL string) (string, string, error) {
|
|||||||
if goMsg == errCredentialsNotFound {
|
if goMsg == errCredentialsNotFound {
|
||||||
return "", "", credentials.NewErrCredentialsNotFound()
|
return "", "", credentials.NewErrCredentialsNotFound()
|
||||||
}
|
}
|
||||||
|
if goMsg == errInteractionNotAllowed {
|
||||||
|
return "", "", ErrInteractionNotAllowed
|
||||||
|
}
|
||||||
|
|
||||||
return "", "", errors.New(goMsg)
|
return "", "", errors.New(goMsg)
|
||||||
}
|
}
|
||||||
@@ -117,6 +127,9 @@ func (h Osxkeychain) List() (map[string]string, error) {
|
|||||||
if goMsg == errCredentialsNotFound {
|
if goMsg == errCredentialsNotFound {
|
||||||
return make(map[string]string), nil
|
return make(map[string]string), nil
|
||||||
}
|
}
|
||||||
|
if goMsg == errInteractionNotAllowed {
|
||||||
|
return nil, ErrInteractionNotAllowed
|
||||||
|
}
|
||||||
|
|
||||||
return nil, errors.New(goMsg)
|
return nil, errors.New(goMsg)
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -5,12 +5,12 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/moby/sys/symlink"
|
"github.com/moby/sys/symlink"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
|
exec "golang.org/x/sys/execabs"
|
||||||
)
|
)
|
||||||
|
|
||||||
type gitRepo struct {
|
type gitRepo struct {
|
||||||
|
|||||||
+1
-1
@@ -11,7 +11,6 @@ import (
|
|||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"runtime"
|
"runtime"
|
||||||
"strconv"
|
"strconv"
|
||||||
@@ -25,6 +24,7 @@ import (
|
|||||||
"github.com/docker/docker/pkg/pools"
|
"github.com/docker/docker/pkg/pools"
|
||||||
"github.com/docker/docker/pkg/system"
|
"github.com/docker/docker/pkg/system"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
|
exec "golang.org/x/sys/execabs"
|
||||||
)
|
)
|
||||||
|
|
||||||
type (
|
type (
|
||||||
|
|||||||
+8
-3
@@ -35,13 +35,13 @@ const (
|
|||||||
|
|
||||||
// MkdirAllAndChown creates a directory (include any along the path) and then modifies
|
// MkdirAllAndChown creates a directory (include any along the path) and then modifies
|
||||||
// ownership to the requested uid/gid. If the directory already exists, this
|
// ownership to the requested uid/gid. If the directory already exists, this
|
||||||
// function will still change ownership to the requested uid/gid pair.
|
// function will still change ownership and permissions.
|
||||||
func MkdirAllAndChown(path string, mode os.FileMode, owner Identity) error {
|
func MkdirAllAndChown(path string, mode os.FileMode, owner Identity) error {
|
||||||
return mkdirAs(path, mode, owner, true, true)
|
return mkdirAs(path, mode, owner, true, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
// MkdirAndChown creates a directory and then modifies ownership to the requested uid/gid.
|
// MkdirAndChown creates a directory and then modifies ownership to the requested uid/gid.
|
||||||
// If the directory already exists, this function still changes ownership.
|
// If the directory already exists, this function still changes ownership and permissions.
|
||||||
// Note that unlike os.Mkdir(), this function does not return IsExist error
|
// Note that unlike os.Mkdir(), this function does not return IsExist error
|
||||||
// in case path already exists.
|
// in case path already exists.
|
||||||
func MkdirAndChown(path string, mode os.FileMode, owner Identity) error {
|
func MkdirAndChown(path string, mode os.FileMode, owner Identity) error {
|
||||||
@@ -50,7 +50,7 @@ func MkdirAndChown(path string, mode os.FileMode, owner Identity) error {
|
|||||||
|
|
||||||
// MkdirAllAndChownNew creates a directory (include any along the path) and then modifies
|
// MkdirAllAndChownNew creates a directory (include any along the path) and then modifies
|
||||||
// ownership ONLY of newly created directories to the requested uid/gid. If the
|
// ownership ONLY of newly created directories to the requested uid/gid. If the
|
||||||
// directories along the path exist, no change of ownership will be performed
|
// directories along the path exist, no change of ownership or permissions will be performed
|
||||||
func MkdirAllAndChownNew(path string, mode os.FileMode, owner Identity) error {
|
func MkdirAllAndChownNew(path string, mode os.FileMode, owner Identity) error {
|
||||||
return mkdirAs(path, mode, owner, true, false)
|
return mkdirAs(path, mode, owner, true, false)
|
||||||
}
|
}
|
||||||
@@ -234,3 +234,8 @@ func parseSubidFile(path, username string) (ranges, error) {
|
|||||||
|
|
||||||
return rangeList, s.Err()
|
return rangeList, s.Err()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CurrentIdentity returns the identity of the current process
|
||||||
|
func CurrentIdentity() Identity {
|
||||||
|
return Identity{UID: os.Getuid(), GID: os.Getegid()}
|
||||||
|
}
|
||||||
|
|||||||
+10
-4
@@ -40,7 +40,7 @@ func mkdirAs(path string, mode os.FileMode, owner Identity, mkAll, chownExisting
|
|||||||
}
|
}
|
||||||
|
|
||||||
// short-circuit--we were called with an existing directory and chown was requested
|
// short-circuit--we were called with an existing directory and chown was requested
|
||||||
return lazyChown(path, owner.UID, owner.GID, stat)
|
return setPermissions(path, mode, owner.UID, owner.GID, stat)
|
||||||
}
|
}
|
||||||
|
|
||||||
if os.IsNotExist(err) {
|
if os.IsNotExist(err) {
|
||||||
@@ -71,7 +71,7 @@ func mkdirAs(path string, mode os.FileMode, owner Identity, mkAll, chownExisting
|
|||||||
// even if it existed, we will chown the requested path + any subpaths that
|
// even if it existed, we will chown the requested path + any subpaths that
|
||||||
// didn't exist when we called MkdirAll
|
// didn't exist when we called MkdirAll
|
||||||
for _, pathComponent := range paths {
|
for _, pathComponent := range paths {
|
||||||
if err := lazyChown(pathComponent, owner.UID, owner.GID, nil); err != nil {
|
if err := setPermissions(pathComponent, mode, owner.UID, owner.GID, nil); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -213,10 +213,11 @@ func callGetent(database, key string) (io.Reader, error) {
|
|||||||
return bytes.NewReader(out), nil
|
return bytes.NewReader(out), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// lazyChown performs a chown only if the uid/gid don't match what's requested
|
// setPermissions performs a chown/chmod only if the uid/gid don't match what's requested
|
||||||
// Normally a Chown is a no-op if uid/gid match, but in some cases this can still cause an error, e.g. if the
|
// Normally a Chown is a no-op if uid/gid match, but in some cases this can still cause an error, e.g. if the
|
||||||
// dir is on an NFS share, so don't call chown unless we absolutely must.
|
// dir is on an NFS share, so don't call chown unless we absolutely must.
|
||||||
func lazyChown(p string, uid, gid int, stat *system.StatT) error {
|
// Likewise for setting permissions.
|
||||||
|
func setPermissions(p string, mode os.FileMode, uid, gid int, stat *system.StatT) error {
|
||||||
if stat == nil {
|
if stat == nil {
|
||||||
var err error
|
var err error
|
||||||
stat, err = system.Stat(p)
|
stat, err = system.Stat(p)
|
||||||
@@ -224,6 +225,11 @@ func lazyChown(p string, uid, gid int, stat *system.StatT) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if os.FileMode(stat.Mode()).Perm() != mode.Perm() {
|
||||||
|
if err := os.Chmod(p, mode.Perm()); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
if stat.UID() == uint32(uid) && stat.GID() == uint32(gid) {
|
if stat.UID() == uint32(uid) && stat.GID() == uint32(gid) {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
+5
-5
@@ -20,11 +20,11 @@ github.com/creack/pty 2a38352e8b4d7ab6c336eef107e4
|
|||||||
github.com/sirupsen/logrus 6699a89a232f3db797f2e280639854bbc4b89725 # v1.7.0
|
github.com/sirupsen/logrus 6699a89a232f3db797f2e280639854bbc4b89725 # v1.7.0
|
||||||
github.com/tchap/go-patricia a7f0089c6f496e8e70402f61733606daa326cac5 # v2.3.0
|
github.com/tchap/go-patricia a7f0089c6f496e8e70402f61733606daa326cac5 # v2.3.0
|
||||||
golang.org/x/net ab34263943818b32f575efc978a3d24e80b04bd7
|
golang.org/x/net ab34263943818b32f575efc978a3d24e80b04bd7
|
||||||
golang.org/x/sys eeed37f84f13f52d35e095e8023ba65671ff86a1
|
golang.org/x/sys b64e53b001e413bd5067f36d4e439eded3827374
|
||||||
github.com/docker/go-units 519db1ee28dcc9fd2474ae59fca29a810482bfb1 # v0.4.0
|
github.com/docker/go-units 519db1ee28dcc9fd2474ae59fca29a810482bfb1 # v0.4.0
|
||||||
github.com/docker/go-connections 7395e3f8aa162843a74ed6d48e79627d9792ac55 # v0.4.0
|
github.com/docker/go-connections 7395e3f8aa162843a74ed6d48e79627d9792ac55 # v0.4.0
|
||||||
golang.org/x/text 23ae387dee1f90d29a23c0e87ee0b46038fbed0e # v0.3.3
|
golang.org/x/text 23ae387dee1f90d29a23c0e87ee0b46038fbed0e # v0.3.3
|
||||||
gotest.tools/v3 bb0d8a963040ea5048dcef1a14d8f8b58a33d4b3 # v3.0.2
|
gotest.tools/v3 568bc57cc5c19a2ef85e5749870b49a4cc2ab54d # v3.0.3
|
||||||
github.com/google/go-cmp 3af367b6b30c263d47e8895973edcca9a49cf029 # v0.2.0
|
github.com/google/go-cmp 3af367b6b30c263d47e8895973edcca9a49cf029 # v0.2.0
|
||||||
github.com/syndtr/gocapability 42c35b4376354fd554efc7ad35e0b7f94e3a0ffb
|
github.com/syndtr/gocapability 42c35b4376354fd554efc7ad35e0b7f94e3a0ffb
|
||||||
|
|
||||||
@@ -33,7 +33,7 @@ github.com/imdario/mergo 1afb36080aec31e0d1528973ebe6
|
|||||||
golang.org/x/sync cd5d95a43a6e21273425c7ae415d3df9ea832eeb
|
golang.org/x/sync cd5d95a43a6e21273425c7ae415d3df9ea832eeb
|
||||||
|
|
||||||
# buildkit
|
# buildkit
|
||||||
github.com/moby/buildkit 8142d66b5ebde79846b869fba30d9d30633e74aa # v0.8.1
|
github.com/moby/buildkit 68bb095353c65bc3993fd534c26cf77fe05e61b1 # v0.8 branch
|
||||||
github.com/tonistiigi/fsutil 0834f99b7b85462efb69b4f571a4fa3ca7da5ac9
|
github.com/tonistiigi/fsutil 0834f99b7b85462efb69b4f571a4fa3ca7da5ac9
|
||||||
github.com/tonistiigi/units 6950e57a87eaf136bbe44ef2ec8e75b9e3569de2
|
github.com/tonistiigi/units 6950e57a87eaf136bbe44ef2ec8e75b9e3569de2
|
||||||
github.com/grpc-ecosystem/grpc-opentracing 8e809c8a86450a29b90dcc9efbf062d0fe6d9746
|
github.com/grpc-ecosystem/grpc-opentracing 8e809c8a86450a29b90dcc9efbf062d0fe6d9746
|
||||||
@@ -47,7 +47,7 @@ github.com/grpc-ecosystem/go-grpc-middleware 3c51f7f332123e8be5a157c0802a
|
|||||||
# libnetwork
|
# libnetwork
|
||||||
|
|
||||||
# When updating, also update LIBNETWORK_COMMIT in hack/dockerfile/install/proxy.installer accordingly
|
# When updating, also update LIBNETWORK_COMMIT in hack/dockerfile/install/proxy.installer accordingly
|
||||||
github.com/docker/libnetwork 5c6a95bfb20c61571a00f913c6b91959ede84e8d
|
github.com/docker/libnetwork fa125a3512ee0f6187721c88582bf8c4378bd4d7
|
||||||
github.com/docker/go-events e31b211e4f1cd09aa76fe4ac244571fab96ae47f
|
github.com/docker/go-events e31b211e4f1cd09aa76fe4ac244571fab96ae47f
|
||||||
github.com/armon/go-radix e39d623f12e8e41c7b5529e9a9dd67a1e2261f80
|
github.com/armon/go-radix e39d623f12e8e41c7b5529e9a9dd67a1e2261f80
|
||||||
github.com/armon/go-metrics eb0af217e5e9747e41dd5303755356b62d28e3ec
|
github.com/armon/go-metrics eb0af217e5e9747e41dd5303755356b62d28e3ec
|
||||||
@@ -176,7 +176,7 @@ github.com/morikuni/aec 39771216ff4c63d11f5e604076f9
|
|||||||
# metrics
|
# metrics
|
||||||
github.com/docker/go-metrics b619b3592b65de4f087d9f16863a7e6ff905973c # v0.0.1
|
github.com/docker/go-metrics b619b3592b65de4f087d9f16863a7e6ff905973c # v0.0.1
|
||||||
|
|
||||||
github.com/opencontainers/selinux 25504e34a9826d481f6e2903963ecaa881749124 # v1.6.0
|
github.com/opencontainers/selinux 2f45b3796d18f1ab4c9fc0c888a98d0a0fd6e429 # v1.8.0
|
||||||
github.com/willf/bitset 559910e8471e48d76d9e5a1ba15842dee77ad45d # v1.1.11
|
github.com/willf/bitset 559910e8471e48d76d9e5a1ba15842dee77ad45d # v1.1.11
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
+27
-13
@@ -21,7 +21,7 @@ for more information.
|
|||||||
|
|
||||||
Notary aims to make the internet more secure by making it easy for people to
|
Notary aims to make the internet more secure by making it easy for people to
|
||||||
publish and verify content. We often rely on TLS to secure our communications
|
publish and verify content. We often rely on TLS to secure our communications
|
||||||
with a web server which is inherently flawed, as any compromise of the server
|
with a web server, which is inherently flawed, as any compromise of the server
|
||||||
enables malicious content to be substituted for the legitimate content.
|
enables malicious content to be substituted for the legitimate content.
|
||||||
|
|
||||||
With Notary, publishers can sign their content offline using keys kept highly
|
With Notary, publishers can sign their content offline using keys kept highly
|
||||||
@@ -46,11 +46,16 @@ Notary is based on [The Update Framework](https://www.theupdateframework.com/),
|
|||||||
|
|
||||||
## Security
|
## Security
|
||||||
|
|
||||||
|
Any security vulnerabilities can be reported to security@docker.com.
|
||||||
|
|
||||||
See Notary's [service architecture docs](docs/service_architecture.md#threat-model) for more information about our threat model, which details the varying survivability and severities for key compromise as well as mitigations.
|
See Notary's [service architecture docs](docs/service_architecture.md#threat-model) for more information about our threat model, which details the varying survivability and severities for key compromise as well as mitigations.
|
||||||
|
|
||||||
Notary's last security audit was on July 31, 2015 by NCC ([results](docs/resources/ncc_docker_notary_audit_2015_07_31.pdf)).
|
### Security Audits
|
||||||
|
|
||||||
Any security vulnerabilities can be reported to security@docker.com.
|
Notary has had two public security audits:
|
||||||
|
|
||||||
|
* [August 7, 2018 by Cure53](docs/resources/cure53_tuf_notary_audit_2018_08_07.pdf) covering TUF and Notary
|
||||||
|
* [July 31, 2015 by NCC](docs/resources/ncc_docker_notary_audit_2015_07_31.pdf) covering Notary
|
||||||
|
|
||||||
# Getting started with the Notary CLI
|
# Getting started with the Notary CLI
|
||||||
|
|
||||||
@@ -65,7 +70,7 @@ For more advanced usage, see the
|
|||||||
|
|
||||||
To use the CLI against a local Notary server rather than against Docker Hub:
|
To use the CLI against a local Notary server rather than against Docker Hub:
|
||||||
|
|
||||||
1. Ensure that you have [docker and docker-compose](http://docs.docker.com/compose/install/) installed.
|
1. Ensure that you have [docker and docker-compose](https://docs.docker.com/compose/install/) installed.
|
||||||
1. `git clone https://github.com/theupdateframework/notary.git` and from the cloned repository path,
|
1. `git clone https://github.com/theupdateframework/notary.git` and from the cloned repository path,
|
||||||
start up a local Notary server and signer and copy the config file and testing certs to your
|
start up a local Notary server and signer and copy the config file and testing certs to your
|
||||||
local Notary config directory:
|
local Notary config directory:
|
||||||
@@ -88,6 +93,20 @@ URL is specified already in the configuration, file you copied.
|
|||||||
You can also leave off the `-d ~/.docker/trust` argument if you do not care
|
You can also leave off the `-d ~/.docker/trust` argument if you do not care
|
||||||
to use `notary` with Docker images.
|
to use `notary` with Docker images.
|
||||||
|
|
||||||
|
## Upgrading dependencies
|
||||||
|
|
||||||
|
To prevent mistakes in vendoring the go modules a buildscript has been added to properly vendor the modules using the correct version of Go to mitigate differences in CI and development environment.
|
||||||
|
|
||||||
|
Following procedure should be executed to upgrade a dependency. Preferably keep dependency upgrades in a separate commit from your code changes.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
go get -u github.com/spf13/viper
|
||||||
|
buildscripts/circle-validate-vendor.sh
|
||||||
|
git add .
|
||||||
|
git commit -m "Upgraded github.com/spf13/viper"
|
||||||
|
```
|
||||||
|
|
||||||
|
The `buildscripts/circle-validate-vendor.sh` runs `go mod tidy` and `go mod vendor` using the given version of Go to prevent differences if you are for example running on a different version of Go.
|
||||||
|
|
||||||
## Building Notary
|
## Building Notary
|
||||||
|
|
||||||
@@ -97,25 +116,20 @@ branch and contains features for the next release.
|
|||||||
|
|
||||||
Prerequisites:
|
Prerequisites:
|
||||||
|
|
||||||
- Go >= 1.7.1
|
* Go >= 1.12
|
||||||
- Fedora: `dnf install golang`
|
|
||||||
- libtool development headers installed
|
|
||||||
- Ubuntu: `apt-get install libltdl-dev`
|
|
||||||
- CentOS/RedHat: `yum install libtool-ltdl-devel`
|
|
||||||
- Fedora: `dnf install libtool-ltdl-devel`
|
|
||||||
- Mac OS ([Homebrew](http://brew.sh/)): `brew install libtool`
|
|
||||||
|
|
||||||
Set [```GOPATH```](https://golang.org/doc/code.html#GOPATH). Then, run:
|
Set [```GOPATH```](https://golang.org/doc/code.html#GOPATH). Then, run:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
$ export GO111MODULE=on
|
||||||
$ go get github.com/theupdateframework/notary
|
$ go get github.com/theupdateframework/notary
|
||||||
# build with pcks11 support by default to support yubikey
|
# build with pkcs11 support by default to support yubikey
|
||||||
$ go install -tags pkcs11 github.com/theupdateframework/notary/cmd/notary
|
$ go install -tags pkcs11 github.com/theupdateframework/notary/cmd/notary
|
||||||
$ notary
|
$ notary
|
||||||
```
|
```
|
||||||
|
|
||||||
To build the server and signer, run `docker-compose build`.
|
To build the server and signer, run `docker-compose build`.
|
||||||
|
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
[](https://app.fossa.io/projects/git%2Bgithub.com%2Ftheupdateframework%2Fnotary?ref=badge_large)
|
[](https://app.fossa.io/projects/git%2Bgithub.com%2Ftheupdateframework%2Fnotary?ref=badge_large)
|
||||||
|
|||||||
+9
-3
@@ -35,7 +35,10 @@ func getFileNames(dirName string) ([]os.FileInfo, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return fileInfos, err
|
return fileInfos, err
|
||||||
}
|
}
|
||||||
defer dir.Close()
|
defer func() {
|
||||||
|
_ = dir.Close()
|
||||||
|
}()
|
||||||
|
|
||||||
dirListing, err = dir.Readdir(0)
|
dirListing, err = dir.Readdir(0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fileInfos, err
|
return fileInfos, err
|
||||||
@@ -89,7 +92,7 @@ func (cl FileChangelist) Add(c Change) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
filename := fmt.Sprintf("%020d_%s.change", time.Now().UnixNano(), uuid.Generate())
|
filename := fmt.Sprintf("%020d_%s.change", time.Now().UnixNano(), uuid.Generate())
|
||||||
return ioutil.WriteFile(filepath.Join(cl.dir, filename), cJSON, 0644)
|
return ioutil.WriteFile(filepath.Join(cl.dir, filename), cJSON, 0600)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove deletes the changes found at the given indices
|
// Remove deletes the changes found at the given indices
|
||||||
@@ -120,7 +123,10 @@ func (cl FileChangelist) Clear(archive string) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
defer dir.Close()
|
defer func() {
|
||||||
|
_ = dir.Close()
|
||||||
|
}()
|
||||||
|
|
||||||
files, err := dir.Readdir(0)
|
files, err := dir.Readdir(0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|||||||
+1
-1
@@ -20,7 +20,7 @@ type Changelist interface {
|
|||||||
// Remove deletes the changes corresponding with the indices given
|
// Remove deletes the changes corresponding with the indices given
|
||||||
Remove(idxs []int) error
|
Remove(idxs []int) error
|
||||||
|
|
||||||
// Close syncronizes any pending writes to the underlying
|
// Close synchronizes any pending writes to the underlying
|
||||||
// storage and closes the file/connection
|
// storage and closes the file/connection
|
||||||
Close() error
|
Close() error
|
||||||
|
|
||||||
|
|||||||
+68
-360
@@ -7,10 +7,8 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"regexp"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
canonicaljson "github.com/docker/go/canonical/json"
|
canonicaljson "github.com/docker/go/canonical/json"
|
||||||
@@ -39,7 +37,6 @@ func init() {
|
|||||||
|
|
||||||
// repository stores all the information needed to operate on a notary repository.
|
// repository stores all the information needed to operate on a notary repository.
|
||||||
type repository struct {
|
type repository struct {
|
||||||
baseDir string
|
|
||||||
gun data.GUN
|
gun data.GUN
|
||||||
baseURL string
|
baseURL string
|
||||||
changelist changelist.Changelist
|
changelist changelist.Changelist
|
||||||
@@ -56,7 +53,8 @@ type repository struct {
|
|||||||
// NewFileCachedRepository is a wrapper for NewRepository that initializes
|
// NewFileCachedRepository is a wrapper for NewRepository that initializes
|
||||||
// a file cache from the provided repository, local config information and a crypto service.
|
// a file cache from the provided repository, local config information and a crypto service.
|
||||||
// It also retrieves the remote store associated to the base directory under where all the
|
// It also retrieves the remote store associated to the base directory under where all the
|
||||||
// trust files will be stored and the specified GUN.
|
// trust files will be stored (This is normally defaults to "~/.notary" or "~/.docker/trust"
|
||||||
|
// when enabling Docker content trust) and the specified GUN.
|
||||||
//
|
//
|
||||||
// In case of a nil RoundTripper, a default offline store is used instead.
|
// In case of a nil RoundTripper, a default offline store is used instead.
|
||||||
func NewFileCachedRepository(baseDir string, gun data.GUN, baseURL string, rt http.RoundTripper,
|
func NewFileCachedRepository(baseDir string, gun data.GUN, baseURL string, rt http.RoundTripper,
|
||||||
@@ -90,16 +88,13 @@ func NewFileCachedRepository(baseDir string, gun data.GUN, baseURL string, rt ht
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return NewRepository(baseDir, gun, baseURL, remoteStore, cache, trustPinning, cryptoService, cl)
|
return NewRepository(gun, baseURL, remoteStore, cache, trustPinning, cryptoService, cl)
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewRepository is the base method that returns a new notary repository.
|
// NewRepository is the base method that returns a new notary repository.
|
||||||
// It takes the base directory under where all the trust files will be stored
|
|
||||||
// (This is normally defaults to "~/.notary" or "~/.docker/trust" when enabling
|
|
||||||
// docker content trust).
|
|
||||||
// It expects an initialized cache. In case of a nil remote store, a default
|
// It expects an initialized cache. In case of a nil remote store, a default
|
||||||
// offline store is used.
|
// offline store is used.
|
||||||
func NewRepository(baseDir string, gun data.GUN, baseURL string, remoteStore store.RemoteStore, cache store.MetadataStore,
|
func NewRepository(gun data.GUN, baseURL string, remoteStore store.RemoteStore, cache store.MetadataStore,
|
||||||
trustPinning trustpinning.TrustPinConfig, cryptoService signed.CryptoService, cl changelist.Changelist) (Repository, error) {
|
trustPinning trustpinning.TrustPinConfig, cryptoService signed.CryptoService, cl changelist.Changelist) (Repository, error) {
|
||||||
|
|
||||||
// Repo's remote store is either a valid remote store or an OfflineStore
|
// Repo's remote store is either a valid remote store or an OfflineStore
|
||||||
@@ -114,7 +109,6 @@ func NewRepository(baseDir string, gun data.GUN, baseURL string, remoteStore sto
|
|||||||
nRepo := &repository{
|
nRepo := &repository{
|
||||||
gun: gun,
|
gun: gun,
|
||||||
baseURL: baseURL,
|
baseURL: baseURL,
|
||||||
baseDir: baseDir,
|
|
||||||
changelist: cl,
|
changelist: cl,
|
||||||
cache: cache,
|
cache: cache,
|
||||||
remoteStore: remoteStore,
|
remoteStore: remoteStore,
|
||||||
@@ -131,20 +125,62 @@ func (r *repository) GetGUN() data.GUN {
|
|||||||
return r.gun
|
return r.gun
|
||||||
}
|
}
|
||||||
|
|
||||||
// Target represents a simplified version of the data TUF operates on, so external
|
func (r *repository) updateTUF(forWrite bool) error {
|
||||||
// applications don't have to depend on TUF data types.
|
repo, invalid, err := LoadTUFRepo(TUFLoadOptions{
|
||||||
type Target struct {
|
GUN: r.gun,
|
||||||
Name string // the name of the target
|
TrustPinning: r.trustPinning,
|
||||||
Hashes data.Hashes // the hash of the target
|
CryptoService: r.cryptoService,
|
||||||
Length int64 // the size in bytes of the target
|
Cache: r.cache,
|
||||||
Custom *canonicaljson.RawMessage // the custom data provided to describe the file at TARGETPATH
|
RemoteStore: r.remoteStore,
|
||||||
|
AlwaysCheckInitialized: forWrite,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
r.tufRepo = repo
|
||||||
|
r.invalid = invalid
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// TargetWithRole represents a Target that exists in a particular role - this is
|
// ListTargets calls update first before listing targets
|
||||||
// produced by ListTargets and GetTargetByName
|
func (r *repository) ListTargets(roles ...data.RoleName) ([]*TargetWithRole, error) {
|
||||||
type TargetWithRole struct {
|
if err := r.updateTUF(false); err != nil {
|
||||||
Target
|
return nil, err
|
||||||
Role data.RoleName
|
}
|
||||||
|
return NewReadOnly(r.tufRepo).ListTargets(roles...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetTargetByName calls update first before getting target by name
|
||||||
|
func (r *repository) GetTargetByName(name string, roles ...data.RoleName) (*TargetWithRole, error) {
|
||||||
|
if err := r.updateTUF(false); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return NewReadOnly(r.tufRepo).GetTargetByName(name, roles...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetAllTargetMetadataByName calls update first before getting targets by name
|
||||||
|
func (r *repository) GetAllTargetMetadataByName(name string) ([]TargetSignedStruct, error) {
|
||||||
|
if err := r.updateTUF(false); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return NewReadOnly(r.tufRepo).GetAllTargetMetadataByName(name)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListRoles calls update first before getting roles
|
||||||
|
func (r *repository) ListRoles() ([]RoleWithSignatures, error) {
|
||||||
|
if err := r.updateTUF(false); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return NewReadOnly(r.tufRepo).ListRoles()
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetDelegationRoles calls update first before getting all delegation roles
|
||||||
|
func (r *repository) GetDelegationRoles() ([]data.Role, error) {
|
||||||
|
if err := r.updateTUF(false); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return NewReadOnly(r.tufRepo).GetDelegationRoles()
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewTarget is a helper method that returns a Target
|
// NewTarget is a helper method that returns a Target
|
||||||
@@ -493,167 +529,6 @@ func (r *repository) RemoveTarget(targetName string, roles ...data.RoleName) err
|
|||||||
return addChange(r.changelist, template, roles...)
|
return addChange(r.changelist, template, roles...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ListTargets lists all targets for the current repository. The list of
|
|
||||||
// roles should be passed in order from highest to lowest priority.
|
|
||||||
//
|
|
||||||
// IMPORTANT: if you pass a set of roles such as [ "targets/a", "targets/x"
|
|
||||||
// "targets/a/b" ], even though "targets/a/b" is part of the "targets/a" subtree
|
|
||||||
// its entries will be strictly shadowed by those in other parts of the "targets/a"
|
|
||||||
// subtree and also the "targets/x" subtree, as we will defer parsing it until
|
|
||||||
// we explicitly reach it in our iteration of the provided list of roles.
|
|
||||||
func (r *repository) ListTargets(roles ...data.RoleName) ([]*TargetWithRole, error) {
|
|
||||||
if err := r.Update(false); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(roles) == 0 {
|
|
||||||
roles = []data.RoleName{data.CanonicalTargetsRole}
|
|
||||||
}
|
|
||||||
targets := make(map[string]*TargetWithRole)
|
|
||||||
for _, role := range roles {
|
|
||||||
// Define an array of roles to skip for this walk (see IMPORTANT comment above)
|
|
||||||
skipRoles := utils.RoleNameSliceRemove(roles, role)
|
|
||||||
|
|
||||||
// Define a visitor function to populate the targets map in priority order
|
|
||||||
listVisitorFunc := func(tgt *data.SignedTargets, validRole data.DelegationRole) interface{} {
|
|
||||||
// We found targets so we should try to add them to our targets map
|
|
||||||
for targetName, targetMeta := range tgt.Signed.Targets {
|
|
||||||
// Follow the priority by not overriding previously set targets
|
|
||||||
// and check that this path is valid with this role
|
|
||||||
if _, ok := targets[targetName]; ok || !validRole.CheckPaths(targetName) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
targets[targetName] = &TargetWithRole{
|
|
||||||
Target: Target{
|
|
||||||
Name: targetName,
|
|
||||||
Hashes: targetMeta.Hashes,
|
|
||||||
Length: targetMeta.Length,
|
|
||||||
Custom: targetMeta.Custom,
|
|
||||||
},
|
|
||||||
Role: validRole.Name,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
r.tufRepo.WalkTargets("", role, listVisitorFunc, skipRoles...)
|
|
||||||
}
|
|
||||||
|
|
||||||
var targetList []*TargetWithRole
|
|
||||||
for _, v := range targets {
|
|
||||||
targetList = append(targetList, v)
|
|
||||||
}
|
|
||||||
|
|
||||||
return targetList, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetTargetByName returns a target by the given name. If no roles are passed
|
|
||||||
// it uses the targets role and does a search of the entire delegation
|
|
||||||
// graph, finding the first entry in a breadth first search of the delegations.
|
|
||||||
// If roles are passed, they should be passed in descending priority and
|
|
||||||
// the target entry found in the subtree of the highest priority role
|
|
||||||
// will be returned.
|
|
||||||
// See the IMPORTANT section on ListTargets above. Those roles also apply here.
|
|
||||||
func (r *repository) GetTargetByName(name string, roles ...data.RoleName) (*TargetWithRole, error) {
|
|
||||||
if err := r.Update(false); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(roles) == 0 {
|
|
||||||
roles = append(roles, data.CanonicalTargetsRole)
|
|
||||||
}
|
|
||||||
var resultMeta data.FileMeta
|
|
||||||
var resultRoleName data.RoleName
|
|
||||||
var foundTarget bool
|
|
||||||
for _, role := range roles {
|
|
||||||
// Define an array of roles to skip for this walk (see IMPORTANT comment above)
|
|
||||||
skipRoles := utils.RoleNameSliceRemove(roles, role)
|
|
||||||
|
|
||||||
// Define a visitor function to find the specified target
|
|
||||||
getTargetVisitorFunc := func(tgt *data.SignedTargets, validRole data.DelegationRole) interface{} {
|
|
||||||
if tgt == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
// We found the target and validated path compatibility in our walk,
|
|
||||||
// so we should stop our walk and set the resultMeta and resultRoleName variables
|
|
||||||
if resultMeta, foundTarget = tgt.Signed.Targets[name]; foundTarget {
|
|
||||||
resultRoleName = validRole.Name
|
|
||||||
return tuf.StopWalk{}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
// Check that we didn't error, and that we assigned to our target
|
|
||||||
if err := r.tufRepo.WalkTargets(name, role, getTargetVisitorFunc, skipRoles...); err == nil && foundTarget {
|
|
||||||
return &TargetWithRole{Target: Target{Name: name, Hashes: resultMeta.Hashes, Length: resultMeta.Length, Custom: resultMeta.Custom}, Role: resultRoleName}, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil, ErrNoSuchTarget(name)
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// TargetSignedStruct is a struct that contains a Target, the role it was found in, and the list of signatures for that role
|
|
||||||
type TargetSignedStruct struct {
|
|
||||||
Role data.DelegationRole
|
|
||||||
Target Target
|
|
||||||
Signatures []data.Signature
|
|
||||||
}
|
|
||||||
|
|
||||||
//ErrNoSuchTarget is returned when no valid trust data is found.
|
|
||||||
type ErrNoSuchTarget string
|
|
||||||
|
|
||||||
func (f ErrNoSuchTarget) Error() string {
|
|
||||||
return fmt.Sprintf("No valid trust data for %s", string(f))
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetAllTargetMetadataByName searches the entire delegation role tree to find the specified target by name for all
|
|
||||||
// roles, and returns a list of TargetSignedStructs for each time it finds the specified target.
|
|
||||||
// If given an empty string for a target name, it will return back all targets signed into the repository in every role
|
|
||||||
func (r *repository) GetAllTargetMetadataByName(name string) ([]TargetSignedStruct, error) {
|
|
||||||
if err := r.Update(false); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
var targetInfoList []TargetSignedStruct
|
|
||||||
|
|
||||||
// Define a visitor function to find the specified target
|
|
||||||
getAllTargetInfoByNameVisitorFunc := func(tgt *data.SignedTargets, validRole data.DelegationRole) interface{} {
|
|
||||||
if tgt == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
// We found a target and validated path compatibility in our walk,
|
|
||||||
// so add it to our list if we have a match
|
|
||||||
// if we have an empty name, add all targets, else check if we have it
|
|
||||||
var targetMetaToAdd data.Files
|
|
||||||
if name == "" {
|
|
||||||
targetMetaToAdd = tgt.Signed.Targets
|
|
||||||
} else {
|
|
||||||
if meta, ok := tgt.Signed.Targets[name]; ok {
|
|
||||||
targetMetaToAdd = data.Files{name: meta}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for targetName, resultMeta := range targetMetaToAdd {
|
|
||||||
targetInfo := TargetSignedStruct{
|
|
||||||
Role: validRole,
|
|
||||||
Target: Target{Name: targetName, Hashes: resultMeta.Hashes, Length: resultMeta.Length, Custom: resultMeta.Custom},
|
|
||||||
Signatures: tgt.Signatures,
|
|
||||||
}
|
|
||||||
targetInfoList = append(targetInfoList, targetInfo)
|
|
||||||
}
|
|
||||||
// continue walking to all child roles
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check that we didn't error, and that we found the target at least once
|
|
||||||
if err := r.tufRepo.WalkTargets(name, "", getAllTargetInfoByNameVisitorFunc); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if len(targetInfoList) == 0 {
|
|
||||||
return nil, ErrNoSuchTarget(name)
|
|
||||||
}
|
|
||||||
return targetInfoList, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetChangelist returns the list of the repository's unpublished changes
|
// GetChangelist returns the list of the repository's unpublished changes
|
||||||
func (r *repository) GetChangelist() (changelist.Changelist, error) {
|
func (r *repository) GetChangelist() (changelist.Changelist, error) {
|
||||||
return r.changelist, nil
|
return r.changelist, nil
|
||||||
@@ -671,51 +546,6 @@ func (r *repository) getRemoteStore() store.RemoteStore {
|
|||||||
return r.remoteStore
|
return r.remoteStore
|
||||||
}
|
}
|
||||||
|
|
||||||
// RoleWithSignatures is a Role with its associated signatures
|
|
||||||
type RoleWithSignatures struct {
|
|
||||||
Signatures []data.Signature
|
|
||||||
data.Role
|
|
||||||
}
|
|
||||||
|
|
||||||
// ListRoles returns a list of RoleWithSignatures objects for this repo
|
|
||||||
// This represents the latest metadata for each role in this repo
|
|
||||||
func (r *repository) ListRoles() ([]RoleWithSignatures, error) {
|
|
||||||
// Update to latest repo state
|
|
||||||
if err := r.Update(false); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get all role info from our updated keysDB, can be empty
|
|
||||||
roles := r.tufRepo.GetAllLoadedRoles()
|
|
||||||
|
|
||||||
var roleWithSigs []RoleWithSignatures
|
|
||||||
|
|
||||||
// Populate RoleWithSignatures with Role from keysDB and signatures from TUF metadata
|
|
||||||
for _, role := range roles {
|
|
||||||
roleWithSig := RoleWithSignatures{Role: *role, Signatures: nil}
|
|
||||||
switch role.Name {
|
|
||||||
case data.CanonicalRootRole:
|
|
||||||
roleWithSig.Signatures = r.tufRepo.Root.Signatures
|
|
||||||
case data.CanonicalTargetsRole:
|
|
||||||
roleWithSig.Signatures = r.tufRepo.Targets[data.CanonicalTargetsRole].Signatures
|
|
||||||
case data.CanonicalSnapshotRole:
|
|
||||||
roleWithSig.Signatures = r.tufRepo.Snapshot.Signatures
|
|
||||||
case data.CanonicalTimestampRole:
|
|
||||||
roleWithSig.Signatures = r.tufRepo.Timestamp.Signatures
|
|
||||||
default:
|
|
||||||
if !data.IsDelegation(role.Name) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if _, ok := r.tufRepo.Targets[role.Name]; ok {
|
|
||||||
// We'll only find a signature if we've published any targets with this delegation
|
|
||||||
roleWithSig.Signatures = r.tufRepo.Targets[role.Name].Signatures
|
|
||||||
}
|
|
||||||
}
|
|
||||||
roleWithSigs = append(roleWithSigs, roleWithSig)
|
|
||||||
}
|
|
||||||
return roleWithSigs, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Publish pushes the local changes in signed material to the remote notary-server
|
// Publish pushes the local changes in signed material to the remote notary-server
|
||||||
// Conceptually it performs an operation similar to a `git rebase`
|
// Conceptually it performs an operation similar to a `git rebase`
|
||||||
func (r *repository) Publish() error {
|
func (r *repository) Publish() error {
|
||||||
@@ -736,7 +566,7 @@ func (r *repository) Publish() error {
|
|||||||
func (r *repository) publish(cl changelist.Changelist) error {
|
func (r *repository) publish(cl changelist.Changelist) error {
|
||||||
var initialPublish bool
|
var initialPublish bool
|
||||||
// update first before publishing
|
// update first before publishing
|
||||||
if err := r.Update(true); err != nil {
|
if err := r.updateTUF(true); err != nil {
|
||||||
// If the remote is not aware of the repo, then this is being published
|
// If the remote is not aware of the repo, then this is being published
|
||||||
// for the first time. Try to initialize the repository before publishing.
|
// for the first time. Try to initialize the repository before publishing.
|
||||||
if _, ok := err.(ErrRepositoryNotExist); ok {
|
if _, ok := err.(ErrRepositoryNotExist); ok {
|
||||||
@@ -863,7 +693,14 @@ func (r *repository) oldKeysForLegacyClientSupport(legacyVersions int, initialPu
|
|||||||
}
|
}
|
||||||
oldKeys := make(map[string]data.PublicKey)
|
oldKeys := make(map[string]data.PublicKey)
|
||||||
|
|
||||||
c, err := r.bootstrapClient(true)
|
c, err := bootstrapClient(TUFLoadOptions{
|
||||||
|
GUN: r.gun,
|
||||||
|
TrustPinning: r.trustPinning,
|
||||||
|
CryptoService: r.cryptoService,
|
||||||
|
Cache: r.cache,
|
||||||
|
RemoteStore: r.remoteStore,
|
||||||
|
AlwaysCheckInitialized: true,
|
||||||
|
})
|
||||||
// require a server connection to fetch old roots
|
// require a server connection to fetch old roots
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -1003,135 +840,6 @@ func (r *repository) saveMetadata(ignoreSnapshot bool) error {
|
|||||||
return r.cache.Set(data.CanonicalSnapshotRole.String(), snapshotJSON)
|
return r.cache.Set(data.CanonicalSnapshotRole.String(), snapshotJSON)
|
||||||
}
|
}
|
||||||
|
|
||||||
// returns a properly constructed ErrRepositoryNotExist error based on this
|
|
||||||
// repo's information
|
|
||||||
func (r *repository) errRepositoryNotExist() error {
|
|
||||||
host := r.baseURL
|
|
||||||
parsed, err := url.Parse(r.baseURL)
|
|
||||||
if err == nil {
|
|
||||||
host = parsed.Host // try to exclude the scheme and any paths
|
|
||||||
}
|
|
||||||
return ErrRepositoryNotExist{remote: host, gun: r.gun}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update bootstraps a trust anchor (root.json) before updating all the
|
|
||||||
// metadata from the repo.
|
|
||||||
func (r *repository) Update(forWrite bool) error {
|
|
||||||
c, err := r.bootstrapClient(forWrite)
|
|
||||||
if err != nil {
|
|
||||||
if _, ok := err.(store.ErrMetaNotFound); ok {
|
|
||||||
return r.errRepositoryNotExist()
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
repo, invalid, err := c.Update()
|
|
||||||
if err != nil {
|
|
||||||
// notFound.Resource may include a version or checksum so when the role is root,
|
|
||||||
// it will be root, <version>.root or root.<checksum>.
|
|
||||||
notFound, ok := err.(store.ErrMetaNotFound)
|
|
||||||
isRoot, _ := regexp.MatchString(`\.?`+data.CanonicalRootRole.String()+`\.?`, notFound.Resource)
|
|
||||||
if ok && isRoot {
|
|
||||||
return r.errRepositoryNotExist()
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
// we can be assured if we are at this stage that the repo we built is good
|
|
||||||
// no need to test the following function call for an error as it will always be fine should the repo be good- it is!
|
|
||||||
r.tufRepo = repo
|
|
||||||
r.invalid = invalid
|
|
||||||
warnRolesNearExpiry(repo)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// bootstrapClient attempts to bootstrap a root.json to be used as the trust
|
|
||||||
// anchor for a repository. The checkInitialized argument indicates whether
|
|
||||||
// we should always attempt to contact the server to determine if the repository
|
|
||||||
// is initialized or not. If set to true, we will always attempt to download
|
|
||||||
// and return an error if the remote repository errors.
|
|
||||||
//
|
|
||||||
// Populates a tuf.RepoBuilder with this root metadata. If the root metadata
|
|
||||||
// downloaded is a newer version than what is on disk, then intermediate
|
|
||||||
// versions will be downloaded and verified in order to rotate trusted keys
|
|
||||||
// properly. Newer root metadata must always be signed with the previous
|
|
||||||
// threshold and keys.
|
|
||||||
//
|
|
||||||
// Fails if the remote server is reachable and does not know the repo
|
|
||||||
// (i.e. before the first r.Publish()), in which case the error is
|
|
||||||
// store.ErrMetaNotFound, or if the root metadata (from whichever source is used)
|
|
||||||
// is not trusted.
|
|
||||||
//
|
|
||||||
// Returns a TUFClient for the remote server, which may not be actually
|
|
||||||
// operational (if the URL is invalid but a root.json is cached).
|
|
||||||
func (r *repository) bootstrapClient(checkInitialized bool) (*tufClient, error) {
|
|
||||||
minVersion := 1
|
|
||||||
// the old root on disk should not be validated against any trust pinning configuration
|
|
||||||
// because if we have an old root, it itself is the thing that pins trust
|
|
||||||
oldBuilder := tuf.NewRepoBuilder(r.gun, r.GetCryptoService(), trustpinning.TrustPinConfig{})
|
|
||||||
|
|
||||||
// by default, we want to use the trust pinning configuration on any new root that we download
|
|
||||||
newBuilder := tuf.NewRepoBuilder(r.gun, r.GetCryptoService(), r.trustPinning)
|
|
||||||
|
|
||||||
// Try to read root from cache first. We will trust this root until we detect a problem
|
|
||||||
// during update which will cause us to download a new root and perform a rotation.
|
|
||||||
// If we have an old root, and it's valid, then we overwrite the newBuilder to be one
|
|
||||||
// preloaded with the old root or one which uses the old root for trust bootstrapping.
|
|
||||||
if rootJSON, err := r.cache.GetSized(data.CanonicalRootRole.String(), store.NoSizeLimit); err == nil {
|
|
||||||
// if we can't load the cached root, fail hard because that is how we pin trust
|
|
||||||
if err := oldBuilder.Load(data.CanonicalRootRole, rootJSON, minVersion, true); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// again, the root on disk is the source of trust pinning, so use an empty trust
|
|
||||||
// pinning configuration
|
|
||||||
newBuilder = tuf.NewRepoBuilder(r.gun, r.GetCryptoService(), trustpinning.TrustPinConfig{})
|
|
||||||
|
|
||||||
if err := newBuilder.Load(data.CanonicalRootRole, rootJSON, minVersion, false); err != nil {
|
|
||||||
// Ok, the old root is expired - we want to download a new one. But we want to use the
|
|
||||||
// old root to verify the new root, so bootstrap a new builder with the old builder
|
|
||||||
// but use the trustpinning to validate the new root
|
|
||||||
minVersion = oldBuilder.GetLoadedVersion(data.CanonicalRootRole)
|
|
||||||
newBuilder = oldBuilder.BootstrapNewBuilderWithNewTrustpin(r.trustPinning)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
remote := r.getRemoteStore()
|
|
||||||
|
|
||||||
if !newBuilder.IsLoaded(data.CanonicalRootRole) || checkInitialized {
|
|
||||||
// remoteErr was nil and we were not able to load a root from cache or
|
|
||||||
// are specifically checking for initialization of the repo.
|
|
||||||
|
|
||||||
// if remote store successfully set up, try and get root from remote
|
|
||||||
// We don't have any local data to determine the size of root, so try the maximum (though it is restricted at 100MB)
|
|
||||||
tmpJSON, err := remote.GetSized(data.CanonicalRootRole.String(), store.NoSizeLimit)
|
|
||||||
if err != nil {
|
|
||||||
// we didn't have a root in cache and were unable to load one from
|
|
||||||
// the server. Nothing we can do but error.
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if !newBuilder.IsLoaded(data.CanonicalRootRole) {
|
|
||||||
// we always want to use the downloaded root if we couldn't load from cache
|
|
||||||
if err := newBuilder.Load(data.CanonicalRootRole, tmpJSON, minVersion, false); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
err = r.cache.Set(data.CanonicalRootRole.String(), tmpJSON)
|
|
||||||
if err != nil {
|
|
||||||
// if we can't write cache we should still continue, just log error
|
|
||||||
logrus.Errorf("could not save root to cache: %s", err.Error())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// We can only get here if remoteErr != nil (hence we don't download any new root),
|
|
||||||
// and there was no root on disk
|
|
||||||
if !newBuilder.IsLoaded(data.CanonicalRootRole) {
|
|
||||||
return nil, ErrRepoNotInitialized{}
|
|
||||||
}
|
|
||||||
|
|
||||||
return newTufClient(oldBuilder, newBuilder, remote, r.cache), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// RotateKey removes all existing keys associated with the role. If no keys are
|
// RotateKey removes all existing keys associated with the role. If no keys are
|
||||||
// specified in keyList, then this creates and adds one new key or delegates
|
// specified in keyList, then this creates and adds one new key or delegates
|
||||||
// managing the key to the server. If key(s) are specified by keyList, then they are
|
// managing the key to the server. If key(s) are specified by keyList, then they are
|
||||||
@@ -1273,7 +981,7 @@ func DeleteTrustData(baseDir string, gun data.GUN, URL string, rt http.RoundTrip
|
|||||||
if deleteRemote {
|
if deleteRemote {
|
||||||
remote, err := getRemoteStore(URL, gun, rt)
|
remote, err := getRemoteStore(URL, gun, rt)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logrus.Error("unable to instantiate a remote store: %v", err)
|
logrus.Errorf("unable to instantiate a remote store: %v", err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := remote.RemoveAll(); err != nil {
|
if err := remote.RemoveAll(); err != nil {
|
||||||
|
|||||||
+1
-37
@@ -7,7 +7,6 @@ import (
|
|||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
"github.com/theupdateframework/notary"
|
"github.com/theupdateframework/notary"
|
||||||
"github.com/theupdateframework/notary/client/changelist"
|
"github.com/theupdateframework/notary/client/changelist"
|
||||||
store "github.com/theupdateframework/notary/storage"
|
|
||||||
"github.com/theupdateframework/notary/tuf/data"
|
"github.com/theupdateframework/notary/tuf/data"
|
||||||
"github.com/theupdateframework/notary/tuf/utils"
|
"github.com/theupdateframework/notary/tuf/utils"
|
||||||
)
|
)
|
||||||
@@ -77,7 +76,7 @@ func (r *repository) AddDelegationPaths(name data.RoleName, paths []string) erro
|
|||||||
}
|
}
|
||||||
|
|
||||||
// RemoveDelegationKeysAndPaths creates changelist entries to remove provided delegation key IDs and paths.
|
// RemoveDelegationKeysAndPaths creates changelist entries to remove provided delegation key IDs and paths.
|
||||||
// This method composes RemoveDelegationPaths and RemoveDelegationKeys (each creates one changelist if called).
|
// This method composes RemoveDelegationPaths and RemoveDelegationKeys (each creates one changelist entry if called).
|
||||||
func (r *repository) RemoveDelegationKeysAndPaths(name data.RoleName, keyIDs, paths []string) error {
|
func (r *repository) RemoveDelegationKeysAndPaths(name data.RoleName, keyIDs, paths []string) error {
|
||||||
if len(paths) > 0 {
|
if len(paths) > 0 {
|
||||||
err := r.RemoveDelegationPaths(name, paths)
|
err := r.RemoveDelegationPaths(name, paths)
|
||||||
@@ -201,41 +200,6 @@ func newDeleteDelegationChange(name data.RoleName, content []byte) *changelist.T
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetDelegationRoles returns the keys and roles of the repository's delegations
|
|
||||||
// Also converts key IDs to canonical key IDs to keep consistent with signing prompts
|
|
||||||
func (r *repository) GetDelegationRoles() ([]data.Role, error) {
|
|
||||||
// Update state of the repo to latest
|
|
||||||
if err := r.Update(false); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// All top level delegations (ex: targets/level1) are stored exclusively in targets.json
|
|
||||||
_, ok := r.tufRepo.Targets[data.CanonicalTargetsRole]
|
|
||||||
if !ok {
|
|
||||||
return nil, store.ErrMetaNotFound{Resource: data.CanonicalTargetsRole.String()}
|
|
||||||
}
|
|
||||||
|
|
||||||
// make a copy for traversing nested delegations
|
|
||||||
allDelegations := []data.Role{}
|
|
||||||
|
|
||||||
// Define a visitor function to populate the delegations list and translate their key IDs to canonical IDs
|
|
||||||
delegationCanonicalListVisitor := func(tgt *data.SignedTargets, validRole data.DelegationRole) interface{} {
|
|
||||||
// For the return list, update with a copy that includes canonicalKeyIDs
|
|
||||||
// These aren't validated by the validRole
|
|
||||||
canonicalDelegations, err := translateDelegationsToCanonicalIDs(tgt.Signed.Delegations)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
allDelegations = append(allDelegations, canonicalDelegations...)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
err := r.tufRepo.WalkTargets("", "", delegationCanonicalListVisitor)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return allDelegations, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func translateDelegationsToCanonicalIDs(delegationInfo data.Delegations) ([]data.Role, error) {
|
func translateDelegationsToCanonicalIDs(delegationInfo data.Delegations) ([]data.Role, error) {
|
||||||
canonicalDelegations := make([]data.Role, len(delegationInfo.Roles))
|
canonicalDelegations := make([]data.Role, len(delegationInfo.Roles))
|
||||||
// Do a copy by value to ensure local delegation metadata is untouched
|
// Do a copy by value to ensure local delegation metadata is untouched
|
||||||
|
|||||||
+121
-18
@@ -6,42 +6,145 @@ import (
|
|||||||
"github.com/theupdateframework/notary/tuf/signed"
|
"github.com/theupdateframework/notary/tuf/signed"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Repository represents the set of options that must be supported over a TUF repo.
|
// ReadOnly represents the set of options that must be supported over a TUF repo for
|
||||||
type Repository interface {
|
// reading
|
||||||
// General management operations
|
type ReadOnly interface {
|
||||||
Initialize(rootKeyIDs []string, serverManagedRoles ...data.RoleName) error
|
// ListTargets lists all targets for the current repository. The list of
|
||||||
InitializeWithCertificate(rootKeyIDs []string, rootCerts []data.PublicKey, serverManagedRoles ...data.RoleName) error
|
// roles should be passed in order from highest to lowest priority.
|
||||||
Publish() error
|
//
|
||||||
|
// IMPORTANT: if you pass a set of roles such as [ "targets/a", "targets/x"
|
||||||
// Target Operations
|
// "targets/a/b" ], even though "targets/a/b" is part of the "targets/a" subtree
|
||||||
AddTarget(target *Target, roles ...data.RoleName) error
|
// its entries will be strictly shadowed by those in other parts of the "targets/a"
|
||||||
RemoveTarget(targetName string, roles ...data.RoleName) error
|
// subtree and also the "targets/x" subtree, as we will defer parsing it until
|
||||||
|
// we explicitly reach it in our iteration of the provided list of roles.
|
||||||
ListTargets(roles ...data.RoleName) ([]*TargetWithRole, error)
|
ListTargets(roles ...data.RoleName) ([]*TargetWithRole, error)
|
||||||
|
|
||||||
|
// GetTargetByName returns a target by the given name. If no roles are passed
|
||||||
|
// it uses the targets role and does a search of the entire delegation
|
||||||
|
// graph, finding the first entry in a breadth first search of the delegations.
|
||||||
|
// If roles are passed, they should be passed in descending priority and
|
||||||
|
// the target entry found in the subtree of the highest priority role
|
||||||
|
// will be returned.
|
||||||
|
// See the IMPORTANT section on ListTargets above. Those roles also apply here.
|
||||||
GetTargetByName(name string, roles ...data.RoleName) (*TargetWithRole, error)
|
GetTargetByName(name string, roles ...data.RoleName) (*TargetWithRole, error)
|
||||||
|
|
||||||
|
// GetAllTargetMetadataByName searches the entire delegation role tree to find
|
||||||
|
// the specified target by name for all roles, and returns a list of
|
||||||
|
// TargetSignedStructs for each time it finds the specified target.
|
||||||
|
// If given an empty string for a target name, it will return back all targets
|
||||||
|
// signed into the repository in every role
|
||||||
GetAllTargetMetadataByName(name string) ([]TargetSignedStruct, error)
|
GetAllTargetMetadataByName(name string) ([]TargetSignedStruct, error)
|
||||||
|
|
||||||
// Changelist operations
|
// ListRoles returns a list of RoleWithSignatures objects for this repo
|
||||||
|
// This represents the latest metadata for each role in this repo
|
||||||
|
ListRoles() ([]RoleWithSignatures, error)
|
||||||
|
|
||||||
|
// GetDelegationRoles returns the keys and roles of the repository's delegations
|
||||||
|
// Also converts key IDs to canonical key IDs to keep consistent with signing prompts
|
||||||
|
GetDelegationRoles() ([]data.Role, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Repository represents the set of options that must be supported over a TUF repo
|
||||||
|
// for both reading and writing.
|
||||||
|
type Repository interface {
|
||||||
|
ReadOnly
|
||||||
|
|
||||||
|
// ------------------- Publishing operations -------------------
|
||||||
|
|
||||||
|
// GetGUN returns the GUN associated with the repository
|
||||||
|
GetGUN() data.GUN
|
||||||
|
|
||||||
|
// SetLegacyVersion sets the number of versions back to fetch roots to sign with
|
||||||
|
SetLegacyVersions(int)
|
||||||
|
|
||||||
|
// ----- General management operations -----
|
||||||
|
|
||||||
|
// Initialize creates a new repository by using rootKey as the root Key for the
|
||||||
|
// TUF repository. The remote store/server must be reachable (and is asked to
|
||||||
|
// generate a timestamp key and possibly other serverManagedRoles), but the
|
||||||
|
// created repository result is only stored on local cache, not published to
|
||||||
|
// the remote store. To do that, use r.Publish() eventually.
|
||||||
|
Initialize(rootKeyIDs []string, serverManagedRoles ...data.RoleName) error
|
||||||
|
|
||||||
|
// InitializeWithCertificate initializes the repository with root keys and their
|
||||||
|
// corresponding certificates
|
||||||
|
InitializeWithCertificate(rootKeyIDs []string, rootCerts []data.PublicKey, serverManagedRoles ...data.RoleName) error
|
||||||
|
|
||||||
|
// Publish pushes the local changes in signed material to the remote notary-server
|
||||||
|
// Conceptually it performs an operation similar to a `git rebase`
|
||||||
|
Publish() error
|
||||||
|
|
||||||
|
// ----- Target Operations -----
|
||||||
|
|
||||||
|
// AddTarget creates new changelist entries to add a target to the given roles
|
||||||
|
// in the repository when the changelist gets applied at publish time.
|
||||||
|
// If roles are unspecified, the default role is "targets"
|
||||||
|
AddTarget(target *Target, roles ...data.RoleName) error
|
||||||
|
|
||||||
|
// RemoveTarget creates new changelist entries to remove a target from the given
|
||||||
|
// roles in the repository when the changelist gets applied at publish time.
|
||||||
|
// If roles are unspecified, the default role is "target".
|
||||||
|
RemoveTarget(targetName string, roles ...data.RoleName) error
|
||||||
|
|
||||||
|
// ----- Changelist operations -----
|
||||||
|
|
||||||
|
// GetChangelist returns the list of the repository's unpublished changes
|
||||||
GetChangelist() (changelist.Changelist, error)
|
GetChangelist() (changelist.Changelist, error)
|
||||||
|
|
||||||
// Role operations
|
// ----- Role operations -----
|
||||||
ListRoles() ([]RoleWithSignatures, error)
|
|
||||||
GetDelegationRoles() ([]data.Role, error)
|
// AddDelegation creates changelist entries to add provided delegation public keys and paths.
|
||||||
|
// This method composes AddDelegationRoleAndKeys and AddDelegationPaths (each creates one changelist if called).
|
||||||
AddDelegation(name data.RoleName, delegationKeys []data.PublicKey, paths []string) error
|
AddDelegation(name data.RoleName, delegationKeys []data.PublicKey, paths []string) error
|
||||||
|
|
||||||
|
// AddDelegationRoleAndKeys creates a changelist entry to add provided delegation public keys.
|
||||||
|
// This method is the simplest way to create a new delegation, because the delegation must have at least
|
||||||
|
// one key upon creation to be valid since we will reject the changelist while validating the threshold.
|
||||||
AddDelegationRoleAndKeys(name data.RoleName, delegationKeys []data.PublicKey) error
|
AddDelegationRoleAndKeys(name data.RoleName, delegationKeys []data.PublicKey) error
|
||||||
|
|
||||||
|
// AddDelegationPaths creates a changelist entry to add provided paths to an existing delegation.
|
||||||
|
// This method cannot create a new delegation itself because the role must meet the key threshold upon
|
||||||
|
// creation.
|
||||||
AddDelegationPaths(name data.RoleName, paths []string) error
|
AddDelegationPaths(name data.RoleName, paths []string) error
|
||||||
|
|
||||||
|
// RemoveDelegationKeysAndPaths creates changelist entries to remove provided delegation key IDs and
|
||||||
|
// paths. This method composes RemoveDelegationPaths and RemoveDelegationKeys (each creates one
|
||||||
|
// changelist entry if called).
|
||||||
RemoveDelegationKeysAndPaths(name data.RoleName, keyIDs, paths []string) error
|
RemoveDelegationKeysAndPaths(name data.RoleName, keyIDs, paths []string) error
|
||||||
|
|
||||||
|
// RemoveDelegationRole creates a changelist to remove all paths and keys from a role, and delete the
|
||||||
|
// role in its entirety.
|
||||||
RemoveDelegationRole(name data.RoleName) error
|
RemoveDelegationRole(name data.RoleName) error
|
||||||
|
|
||||||
|
// RemoveDelegationPaths creates a changelist entry to remove provided paths from an existing delegation.
|
||||||
RemoveDelegationPaths(name data.RoleName, paths []string) error
|
RemoveDelegationPaths(name data.RoleName, paths []string) error
|
||||||
|
|
||||||
|
// RemoveDelegationKeys creates a changelist entry to remove provided keys from an existing delegation.
|
||||||
|
// When this changelist is applied, if the specified keys are the only keys left in the role,
|
||||||
|
// the role itself will be deleted in its entirety.
|
||||||
|
// It can also delete a key from all delegations under a parent using a name
|
||||||
|
// with a wildcard at the end.
|
||||||
RemoveDelegationKeys(name data.RoleName, keyIDs []string) error
|
RemoveDelegationKeys(name data.RoleName, keyIDs []string) error
|
||||||
|
|
||||||
|
// ClearDelegationPaths creates a changelist entry to remove all paths from an existing delegation.
|
||||||
ClearDelegationPaths(name data.RoleName) error
|
ClearDelegationPaths(name data.RoleName) error
|
||||||
|
|
||||||
// Witness and other re-signing operations
|
// ----- Witness and other re-signing operations -----
|
||||||
|
|
||||||
|
// Witness creates change objects to witness (i.e. re-sign) the given
|
||||||
|
// roles on the next publish. One change is created per role
|
||||||
Witness(roles ...data.RoleName) ([]data.RoleName, error)
|
Witness(roles ...data.RoleName) ([]data.RoleName, error)
|
||||||
|
|
||||||
// Key Operations
|
// ----- Key Operations -----
|
||||||
|
|
||||||
|
// RotateKey removes all existing keys associated with the role. If no keys are
|
||||||
|
// specified in keyList, then this creates and adds one new key or delegates
|
||||||
|
// managing the key to the server. If key(s) are specified by keyList, then they are
|
||||||
|
// used for signing the role.
|
||||||
|
// These changes are staged in a changelist until publish is called.
|
||||||
RotateKey(role data.RoleName, serverManagesKey bool, keyList []string) error
|
RotateKey(role data.RoleName, serverManagesKey bool, keyList []string) error
|
||||||
|
|
||||||
|
// GetCryptoService is the getter for the repository's CryptoService, which is used
|
||||||
|
// to sign all updates.
|
||||||
GetCryptoService() signed.CryptoService
|
GetCryptoService() signed.CryptoService
|
||||||
SetLegacyVersions(int)
|
|
||||||
GetGUN() data.GUN
|
|
||||||
}
|
}
|
||||||
|
|||||||
+257
@@ -0,0 +1,257 @@
|
|||||||
|
package client
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
canonicaljson "github.com/docker/go/canonical/json"
|
||||||
|
store "github.com/theupdateframework/notary/storage"
|
||||||
|
"github.com/theupdateframework/notary/tuf"
|
||||||
|
"github.com/theupdateframework/notary/tuf/data"
|
||||||
|
"github.com/theupdateframework/notary/tuf/utils"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Target represents a simplified version of the data TUF operates on, so external
|
||||||
|
// applications don't have to depend on TUF data types.
|
||||||
|
type Target struct {
|
||||||
|
Name string // the name of the target
|
||||||
|
Hashes data.Hashes // the hash of the target
|
||||||
|
Length int64 // the size in bytes of the target
|
||||||
|
Custom *canonicaljson.RawMessage // the custom data provided to describe the file at TARGETPATH
|
||||||
|
}
|
||||||
|
|
||||||
|
// TargetWithRole represents a Target that exists in a particular role - this is
|
||||||
|
// produced by ListTargets and GetTargetByName
|
||||||
|
type TargetWithRole struct {
|
||||||
|
Target
|
||||||
|
Role data.RoleName
|
||||||
|
}
|
||||||
|
|
||||||
|
// TargetSignedStruct is a struct that contains a Target, the role it was found in, and the list of signatures for that role
|
||||||
|
type TargetSignedStruct struct {
|
||||||
|
Role data.DelegationRole
|
||||||
|
Target Target
|
||||||
|
Signatures []data.Signature
|
||||||
|
}
|
||||||
|
|
||||||
|
//ErrNoSuchTarget is returned when no valid trust data is found.
|
||||||
|
type ErrNoSuchTarget string
|
||||||
|
|
||||||
|
func (f ErrNoSuchTarget) Error() string {
|
||||||
|
return fmt.Sprintf("No valid trust data for %s", string(f))
|
||||||
|
}
|
||||||
|
|
||||||
|
// RoleWithSignatures is a Role with its associated signatures
|
||||||
|
type RoleWithSignatures struct {
|
||||||
|
Signatures []data.Signature
|
||||||
|
data.Role
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewReadOnly is the base method that returns a new notary repository for reading.
|
||||||
|
// It expects an initialized cache. In case of a nil remote store, a default
|
||||||
|
// offline store is used.
|
||||||
|
func NewReadOnly(repo *tuf.Repo) ReadOnly {
|
||||||
|
return &reader{tufRepo: repo}
|
||||||
|
}
|
||||||
|
|
||||||
|
type reader struct {
|
||||||
|
tufRepo *tuf.Repo
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListTargets lists all targets for the current repository. The list of
|
||||||
|
// roles should be passed in order from highest to lowest priority.
|
||||||
|
//
|
||||||
|
// IMPORTANT: if you pass a set of roles such as [ "targets/a", "targets/x"
|
||||||
|
// "targets/a/b" ], even though "targets/a/b" is part of the "targets/a" subtree
|
||||||
|
// its entries will be strictly shadowed by those in other parts of the "targets/a"
|
||||||
|
// subtree and also the "targets/x" subtree, as we will defer parsing it until
|
||||||
|
// we explicitly reach it in our iteration of the provided list of roles.
|
||||||
|
func (r *reader) ListTargets(roles ...data.RoleName) ([]*TargetWithRole, error) {
|
||||||
|
if len(roles) == 0 {
|
||||||
|
roles = []data.RoleName{data.CanonicalTargetsRole}
|
||||||
|
}
|
||||||
|
targets := make(map[string]*TargetWithRole)
|
||||||
|
for _, role := range roles {
|
||||||
|
// Define an array of roles to skip for this walk (see IMPORTANT comment above)
|
||||||
|
skipRoles := utils.RoleNameSliceRemove(roles, role)
|
||||||
|
|
||||||
|
// Define a visitor function to populate the targets map in priority order
|
||||||
|
listVisitorFunc := func(tgt *data.SignedTargets, validRole data.DelegationRole) interface{} {
|
||||||
|
// We found targets so we should try to add them to our targets map
|
||||||
|
for targetName, targetMeta := range tgt.Signed.Targets {
|
||||||
|
// Follow the priority by not overriding previously set targets
|
||||||
|
// and check that this path is valid with this role
|
||||||
|
if _, ok := targets[targetName]; ok || !validRole.CheckPaths(targetName) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
targets[targetName] = &TargetWithRole{
|
||||||
|
Target: Target{
|
||||||
|
Name: targetName,
|
||||||
|
Hashes: targetMeta.Hashes,
|
||||||
|
Length: targetMeta.Length,
|
||||||
|
Custom: targetMeta.Custom,
|
||||||
|
},
|
||||||
|
Role: validRole.Name,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
r.tufRepo.WalkTargets("", role, listVisitorFunc, skipRoles...)
|
||||||
|
}
|
||||||
|
|
||||||
|
var targetList []*TargetWithRole
|
||||||
|
for _, v := range targets {
|
||||||
|
targetList = append(targetList, v)
|
||||||
|
}
|
||||||
|
|
||||||
|
return targetList, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetTargetByName returns a target by the given name. If no roles are passed
|
||||||
|
// it uses the targets role and does a search of the entire delegation
|
||||||
|
// graph, finding the first entry in a breadth first search of the delegations.
|
||||||
|
// If roles are passed, they should be passed in descending priority and
|
||||||
|
// the target entry found in the subtree of the highest priority role
|
||||||
|
// will be returned.
|
||||||
|
// See the IMPORTANT section on ListTargets above. Those roles also apply here.
|
||||||
|
func (r *reader) GetTargetByName(name string, roles ...data.RoleName) (*TargetWithRole, error) {
|
||||||
|
if len(roles) == 0 {
|
||||||
|
roles = append(roles, data.CanonicalTargetsRole)
|
||||||
|
}
|
||||||
|
var resultMeta data.FileMeta
|
||||||
|
var resultRoleName data.RoleName
|
||||||
|
var foundTarget bool
|
||||||
|
for _, role := range roles {
|
||||||
|
// Define an array of roles to skip for this walk (see IMPORTANT comment above)
|
||||||
|
skipRoles := utils.RoleNameSliceRemove(roles, role)
|
||||||
|
|
||||||
|
// Define a visitor function to find the specified target
|
||||||
|
getTargetVisitorFunc := func(tgt *data.SignedTargets, validRole data.DelegationRole) interface{} {
|
||||||
|
if tgt == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
// We found the target and validated path compatibility in our walk,
|
||||||
|
// so we should stop our walk and set the resultMeta and resultRoleName variables
|
||||||
|
if resultMeta, foundTarget = tgt.Signed.Targets[name]; foundTarget {
|
||||||
|
resultRoleName = validRole.Name
|
||||||
|
return tuf.StopWalk{}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
// Check that we didn't error, and that we assigned to our target
|
||||||
|
if err := r.tufRepo.WalkTargets(name, role, getTargetVisitorFunc, skipRoles...); err == nil && foundTarget {
|
||||||
|
return &TargetWithRole{Target: Target{Name: name, Hashes: resultMeta.Hashes, Length: resultMeta.Length, Custom: resultMeta.Custom}, Role: resultRoleName}, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil, ErrNoSuchTarget(name)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetAllTargetMetadataByName searches the entire delegation role tree to find the specified target by name for all
|
||||||
|
// roles, and returns a list of TargetSignedStructs for each time it finds the specified target.
|
||||||
|
// If given an empty string for a target name, it will return back all targets signed into the repository in every role
|
||||||
|
func (r *reader) GetAllTargetMetadataByName(name string) ([]TargetSignedStruct, error) {
|
||||||
|
var targetInfoList []TargetSignedStruct
|
||||||
|
|
||||||
|
// Define a visitor function to find the specified target
|
||||||
|
getAllTargetInfoByNameVisitorFunc := func(tgt *data.SignedTargets, validRole data.DelegationRole) interface{} {
|
||||||
|
if tgt == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
// We found a target and validated path compatibility in our walk,
|
||||||
|
// so add it to our list if we have a match
|
||||||
|
// if we have an empty name, add all targets, else check if we have it
|
||||||
|
var targetMetaToAdd data.Files
|
||||||
|
if name == "" {
|
||||||
|
targetMetaToAdd = tgt.Signed.Targets
|
||||||
|
} else {
|
||||||
|
if meta, ok := tgt.Signed.Targets[name]; ok {
|
||||||
|
targetMetaToAdd = data.Files{name: meta}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for targetName, resultMeta := range targetMetaToAdd {
|
||||||
|
targetInfo := TargetSignedStruct{
|
||||||
|
Role: validRole,
|
||||||
|
Target: Target{Name: targetName, Hashes: resultMeta.Hashes, Length: resultMeta.Length, Custom: resultMeta.Custom},
|
||||||
|
Signatures: tgt.Signatures,
|
||||||
|
}
|
||||||
|
targetInfoList = append(targetInfoList, targetInfo)
|
||||||
|
}
|
||||||
|
// continue walking to all child roles
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check that we didn't error, and that we found the target at least once
|
||||||
|
if err := r.tufRepo.WalkTargets(name, "", getAllTargetInfoByNameVisitorFunc); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if len(targetInfoList) == 0 {
|
||||||
|
return nil, ErrNoSuchTarget(name)
|
||||||
|
}
|
||||||
|
return targetInfoList, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListRoles returns a list of RoleWithSignatures objects for this repo
|
||||||
|
// This represents the latest metadata for each role in this repo
|
||||||
|
func (r *reader) ListRoles() ([]RoleWithSignatures, error) {
|
||||||
|
// Get all role info from our updated keysDB, can be empty
|
||||||
|
roles := r.tufRepo.GetAllLoadedRoles()
|
||||||
|
|
||||||
|
var roleWithSigs []RoleWithSignatures
|
||||||
|
|
||||||
|
// Populate RoleWithSignatures with Role from keysDB and signatures from TUF metadata
|
||||||
|
for _, role := range roles {
|
||||||
|
roleWithSig := RoleWithSignatures{Role: *role, Signatures: nil}
|
||||||
|
switch role.Name {
|
||||||
|
case data.CanonicalRootRole:
|
||||||
|
roleWithSig.Signatures = r.tufRepo.Root.Signatures
|
||||||
|
case data.CanonicalTargetsRole:
|
||||||
|
roleWithSig.Signatures = r.tufRepo.Targets[data.CanonicalTargetsRole].Signatures
|
||||||
|
case data.CanonicalSnapshotRole:
|
||||||
|
roleWithSig.Signatures = r.tufRepo.Snapshot.Signatures
|
||||||
|
case data.CanonicalTimestampRole:
|
||||||
|
roleWithSig.Signatures = r.tufRepo.Timestamp.Signatures
|
||||||
|
default:
|
||||||
|
if !data.IsDelegation(role.Name) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if _, ok := r.tufRepo.Targets[role.Name]; ok {
|
||||||
|
// We'll only find a signature if we've published any targets with this delegation
|
||||||
|
roleWithSig.Signatures = r.tufRepo.Targets[role.Name].Signatures
|
||||||
|
}
|
||||||
|
}
|
||||||
|
roleWithSigs = append(roleWithSigs, roleWithSig)
|
||||||
|
}
|
||||||
|
return roleWithSigs, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetDelegationRoles returns the keys and roles of the repository's delegations
|
||||||
|
// Also converts key IDs to canonical key IDs to keep consistent with signing prompts
|
||||||
|
func (r *reader) GetDelegationRoles() ([]data.Role, error) {
|
||||||
|
// All top level delegations (ex: targets/level1) are stored exclusively in targets.json
|
||||||
|
_, ok := r.tufRepo.Targets[data.CanonicalTargetsRole]
|
||||||
|
if !ok {
|
||||||
|
return nil, store.ErrMetaNotFound{Resource: data.CanonicalTargetsRole.String()}
|
||||||
|
}
|
||||||
|
|
||||||
|
// make a copy for traversing nested delegations
|
||||||
|
allDelegations := []data.Role{}
|
||||||
|
|
||||||
|
// Define a visitor function to populate the delegations list and translate their key IDs to canonical IDs
|
||||||
|
delegationCanonicalListVisitor := func(tgt *data.SignedTargets, validRole data.DelegationRole) interface{} {
|
||||||
|
// For the return list, update with a copy that includes canonicalKeyIDs
|
||||||
|
// These aren't validated by the validRole
|
||||||
|
canonicalDelegations, err := translateDelegationsToCanonicalIDs(tgt.Signed.Delegations)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
allDelegations = append(allDelegations, canonicalDelegations...)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
err := r.tufRepo.WalkTargets("", "", delegationCanonicalListVisitor)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return allDelegations, nil
|
||||||
|
}
|
||||||
+149
-11
@@ -3,9 +3,11 @@ package client
|
|||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"regexp"
|
||||||
|
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
"github.com/theupdateframework/notary"
|
"github.com/theupdateframework/notary"
|
||||||
|
"github.com/theupdateframework/notary/cryptoservice"
|
||||||
store "github.com/theupdateframework/notary/storage"
|
store "github.com/theupdateframework/notary/storage"
|
||||||
"github.com/theupdateframework/notary/trustpinning"
|
"github.com/theupdateframework/notary/trustpinning"
|
||||||
"github.com/theupdateframework/notary/tuf"
|
"github.com/theupdateframework/notary/tuf"
|
||||||
@@ -21,16 +23,6 @@ type tufClient struct {
|
|||||||
newBuilder tuf.RepoBuilder
|
newBuilder tuf.RepoBuilder
|
||||||
}
|
}
|
||||||
|
|
||||||
// newTufClient initialized a tufClient with the given repo, remote source of content, and cache
|
|
||||||
func newTufClient(oldBuilder, newBuilder tuf.RepoBuilder, remote store.RemoteStore, cache store.MetadataStore) *tufClient {
|
|
||||||
return &tufClient{
|
|
||||||
oldBuilder: oldBuilder,
|
|
||||||
newBuilder: newBuilder,
|
|
||||||
remote: remote,
|
|
||||||
cache: cache,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update performs an update to the TUF repo as defined by the TUF spec
|
// Update performs an update to the TUF repo as defined by the TUF spec
|
||||||
func (c *tufClient) Update() (*tuf.Repo, *tuf.Repo, error) {
|
func (c *tufClient) Update() (*tuf.Repo, *tuf.Repo, error) {
|
||||||
// 1. Get timestamp
|
// 1. Get timestamp
|
||||||
@@ -139,7 +131,7 @@ func (c *tufClient) updateRoot() error {
|
|||||||
|
|
||||||
// Write newest to cache
|
// Write newest to cache
|
||||||
if err := c.cache.Set(data.CanonicalRootRole.String(), raw); err != nil {
|
if err := c.cache.Set(data.CanonicalRootRole.String(), raw); err != nil {
|
||||||
logrus.Debugf("unable to write %s to cache: %d.%s", newestVersion, data.CanonicalRootRole, err)
|
logrus.Debugf("unable to write %d.%s to cache: %s", newestVersion, data.CanonicalRootRole, err)
|
||||||
}
|
}
|
||||||
logrus.Debugf("finished updating root files")
|
logrus.Debugf("finished updating root files")
|
||||||
return nil
|
return nil
|
||||||
@@ -323,3 +315,149 @@ func (c *tufClient) tryLoadRemote(consistentInfo tuf.ConsistentInfo, old []byte)
|
|||||||
}
|
}
|
||||||
return raw, nil
|
return raw, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TUFLoadOptions are provided to LoadTUFRepo, which loads a TUF repo from cache,
|
||||||
|
// from a remote store, or both
|
||||||
|
type TUFLoadOptions struct {
|
||||||
|
GUN data.GUN
|
||||||
|
TrustPinning trustpinning.TrustPinConfig
|
||||||
|
CryptoService signed.CryptoService
|
||||||
|
Cache store.MetadataStore
|
||||||
|
RemoteStore store.RemoteStore
|
||||||
|
AlwaysCheckInitialized bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// bootstrapClient attempts to bootstrap a root.json to be used as the trust
|
||||||
|
// anchor for a repository. The checkInitialized argument indicates whether
|
||||||
|
// we should always attempt to contact the server to determine if the repository
|
||||||
|
// is initialized or not. If set to true, we will always attempt to download
|
||||||
|
// and return an error if the remote repository errors.
|
||||||
|
//
|
||||||
|
// Populates a tuf.RepoBuilder with this root metadata. If the root metadata
|
||||||
|
// downloaded is a newer version than what is on disk, then intermediate
|
||||||
|
// versions will be downloaded and verified in order to rotate trusted keys
|
||||||
|
// properly. Newer root metadata must always be signed with the previous
|
||||||
|
// threshold and keys.
|
||||||
|
//
|
||||||
|
// Fails if the remote server is reachable and does not know the repo
|
||||||
|
// (i.e. before any metadata has been published), in which case the error is
|
||||||
|
// store.ErrMetaNotFound, or if the root metadata (from whichever source is used)
|
||||||
|
// is not trusted.
|
||||||
|
//
|
||||||
|
// Returns a TUFClient for the remote server, which may not be actually
|
||||||
|
// operational (if the URL is invalid but a root.json is cached).
|
||||||
|
func bootstrapClient(l TUFLoadOptions) (*tufClient, error) {
|
||||||
|
minVersion := 1
|
||||||
|
// the old root on disk should not be validated against any trust pinning configuration
|
||||||
|
// because if we have an old root, it itself is the thing that pins trust
|
||||||
|
oldBuilder := tuf.NewRepoBuilder(l.GUN, l.CryptoService, trustpinning.TrustPinConfig{})
|
||||||
|
|
||||||
|
// by default, we want to use the trust pinning configuration on any new root that we download
|
||||||
|
newBuilder := tuf.NewRepoBuilder(l.GUN, l.CryptoService, l.TrustPinning)
|
||||||
|
|
||||||
|
// Try to read root from cache first. We will trust this root until we detect a problem
|
||||||
|
// during update which will cause us to download a new root and perform a rotation.
|
||||||
|
// If we have an old root, and it's valid, then we overwrite the newBuilder to be one
|
||||||
|
// preloaded with the old root or one which uses the old root for trust bootstrapping.
|
||||||
|
if rootJSON, err := l.Cache.GetSized(data.CanonicalRootRole.String(), store.NoSizeLimit); err == nil {
|
||||||
|
// if we can't load the cached root, fail hard because that is how we pin trust
|
||||||
|
if err := oldBuilder.Load(data.CanonicalRootRole, rootJSON, minVersion, true); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// again, the root on disk is the source of trust pinning, so use an empty trust
|
||||||
|
// pinning configuration
|
||||||
|
newBuilder = tuf.NewRepoBuilder(l.GUN, l.CryptoService, trustpinning.TrustPinConfig{})
|
||||||
|
|
||||||
|
if err := newBuilder.Load(data.CanonicalRootRole, rootJSON, minVersion, false); err != nil {
|
||||||
|
// Ok, the old root is expired - we want to download a new one. But we want to use the
|
||||||
|
// old root to verify the new root, so bootstrap a new builder with the old builder
|
||||||
|
// but use the trustpinning to validate the new root
|
||||||
|
minVersion = oldBuilder.GetLoadedVersion(data.CanonicalRootRole)
|
||||||
|
newBuilder = oldBuilder.BootstrapNewBuilderWithNewTrustpin(l.TrustPinning)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !newBuilder.IsLoaded(data.CanonicalRootRole) || l.AlwaysCheckInitialized {
|
||||||
|
// remoteErr was nil and we were not able to load a root from cache or
|
||||||
|
// are specifically checking for initialization of the repo.
|
||||||
|
|
||||||
|
// if remote store successfully set up, try and get root from remote
|
||||||
|
// We don't have any local data to determine the size of root, so try the maximum (though it is restricted at 100MB)
|
||||||
|
tmpJSON, err := l.RemoteStore.GetSized(data.CanonicalRootRole.String(), store.NoSizeLimit)
|
||||||
|
if err != nil {
|
||||||
|
// we didn't have a root in cache and were unable to load one from
|
||||||
|
// the server. Nothing we can do but error.
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if !newBuilder.IsLoaded(data.CanonicalRootRole) {
|
||||||
|
// we always want to use the downloaded root if we couldn't load from cache
|
||||||
|
if err := newBuilder.Load(data.CanonicalRootRole, tmpJSON, minVersion, false); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = l.Cache.Set(data.CanonicalRootRole.String(), tmpJSON)
|
||||||
|
if err != nil {
|
||||||
|
// if we can't write cache we should still continue, just log error
|
||||||
|
logrus.Errorf("could not save root to cache: %s", err.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// We can only get here if remoteErr != nil (hence we don't download any new root),
|
||||||
|
// and there was no root on disk
|
||||||
|
if !newBuilder.IsLoaded(data.CanonicalRootRole) {
|
||||||
|
return nil, ErrRepoNotInitialized{}
|
||||||
|
}
|
||||||
|
|
||||||
|
return &tufClient{
|
||||||
|
oldBuilder: oldBuilder,
|
||||||
|
newBuilder: newBuilder,
|
||||||
|
remote: l.RemoteStore,
|
||||||
|
cache: l.Cache,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// LoadTUFRepo bootstraps a trust anchor (root.json) from cache (if provided) before updating
|
||||||
|
// all the metadata for the repo from the remote (if provided). It loads a TUF repo from cache,
|
||||||
|
// from a remote store, or both.
|
||||||
|
func LoadTUFRepo(options TUFLoadOptions) (*tuf.Repo, *tuf.Repo, error) {
|
||||||
|
// set some sane defaults, so nothing has to be provided necessarily
|
||||||
|
if options.RemoteStore == nil {
|
||||||
|
options.RemoteStore = store.OfflineStore{}
|
||||||
|
}
|
||||||
|
if options.Cache == nil {
|
||||||
|
options.Cache = store.NewMemoryStore(nil)
|
||||||
|
}
|
||||||
|
if options.CryptoService == nil {
|
||||||
|
options.CryptoService = cryptoservice.EmptyService
|
||||||
|
}
|
||||||
|
|
||||||
|
c, err := bootstrapClient(options)
|
||||||
|
if err != nil {
|
||||||
|
if _, ok := err.(store.ErrMetaNotFound); ok {
|
||||||
|
return nil, nil, ErrRepositoryNotExist{
|
||||||
|
remote: options.RemoteStore.Location(),
|
||||||
|
gun: options.GUN,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
repo, invalid, err := c.Update()
|
||||||
|
if err != nil {
|
||||||
|
// notFound.Resource may include a version or checksum so when the role is root,
|
||||||
|
// it will be root, <version>.root or root.<checksum>.
|
||||||
|
notFound, ok := err.(store.ErrMetaNotFound)
|
||||||
|
isRoot, _ := regexp.MatchString(`\.?`+data.CanonicalRootRole.String()+`\.?`, notFound.Resource)
|
||||||
|
if ok && isRoot {
|
||||||
|
return nil, nil, ErrRepositoryNotExist{
|
||||||
|
remote: options.RemoteStore.Location(),
|
||||||
|
gun: options.GUN,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
warnRolesNearExpiry(repo)
|
||||||
|
return repo, invalid, nil
|
||||||
|
}
|
||||||
|
|||||||
+3
@@ -21,6 +21,9 @@ var (
|
|||||||
// ErrRootKeyNotEncrypted is returned if a root key being imported is
|
// ErrRootKeyNotEncrypted is returned if a root key being imported is
|
||||||
// unencrypted
|
// unencrypted
|
||||||
ErrRootKeyNotEncrypted = errors.New("only encrypted root keys may be imported")
|
ErrRootKeyNotEncrypted = errors.New("only encrypted root keys may be imported")
|
||||||
|
|
||||||
|
// EmptyService is an empty crypto service
|
||||||
|
EmptyService = NewCryptoService()
|
||||||
)
|
)
|
||||||
|
|
||||||
// CryptoService implements Sign and Create, holding a specific GUN and keystore to
|
// CryptoService implements Sign and Create, holding a specific GUN and keystore to
|
||||||
|
|||||||
+1
-1
@@ -3,7 +3,7 @@ package notary
|
|||||||
import (
|
import (
|
||||||
"crypto"
|
"crypto"
|
||||||
// Need to import md5 so can test availability.
|
// Need to import md5 so can test availability.
|
||||||
_ "crypto/md5"
|
_ "crypto/md5" // #nosec
|
||||||
)
|
)
|
||||||
|
|
||||||
// FIPSEnabled returns true if running in FIPS mode.
|
// FIPSEnabled returns true if running in FIPS mode.
|
||||||
|
|||||||
+58
@@ -0,0 +1,58 @@
|
|||||||
|
module github.com/theupdateframework/notary
|
||||||
|
|
||||||
|
go 1.12
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/BurntSushi/toml v0.3.1 // indirect
|
||||||
|
github.com/Shopify/logrus-bugsnag v0.0.0-20170309145241-6dbc35f2c30d
|
||||||
|
github.com/beorn7/perks v1.0.1 // indirect
|
||||||
|
github.com/bitly/go-simplejson v0.5.0 // indirect
|
||||||
|
github.com/bugsnag/bugsnag-go v1.0.5
|
||||||
|
github.com/bugsnag/osext v0.0.0-20130617224835-0dd3f918b21b // indirect
|
||||||
|
github.com/bugsnag/panicwrap v0.0.0-20151223152923-e2c28503fcd0 // indirect
|
||||||
|
github.com/cloudflare/cfssl v0.0.0-20180223231731-4e2dcbde5004 // 1.3.1
|
||||||
|
github.com/denisenkom/go-mssqldb v0.0.0-20191128021309-1d7a30a10f73 // indirect
|
||||||
|
github.com/docker/distribution v2.7.1+incompatible
|
||||||
|
github.com/docker/go v1.5.1-1.0.20160303222718-d30aec9fd63c
|
||||||
|
github.com/docker/go-connections v0.4.0
|
||||||
|
github.com/docker/go-metrics v0.0.0-20180209012529-399ea8c73916 // indirect
|
||||||
|
github.com/docker/libtrust v0.0.0-20160708172513-aabc10ec26b7 // indirect
|
||||||
|
github.com/dvsekhvalnov/jose2go v0.0.0-20170216131308-f21a8cedbbae
|
||||||
|
github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5 // indirect
|
||||||
|
github.com/go-sql-driver/mysql v1.5.0
|
||||||
|
github.com/gogo/protobuf v1.0.0 // indirect
|
||||||
|
github.com/golang/protobuf v1.3.4
|
||||||
|
github.com/google/certificate-transparency-go v1.0.10-0.20180222191210-5ab67e519c93 // indirect
|
||||||
|
github.com/gorilla/mux v1.7.0
|
||||||
|
github.com/inconshreveable/mousetrap v1.0.0 // indirect
|
||||||
|
github.com/jinzhu/gorm v0.0.0-20170222002820-5409931a1bb8
|
||||||
|
github.com/jinzhu/inflection v0.0.0-20170102125226-1c35d901db3d // indirect
|
||||||
|
github.com/jinzhu/now v1.1.1 // indirect
|
||||||
|
github.com/juju/loggo v0.0.0-20190526231331-6e530bcce5d8 // indirect
|
||||||
|
github.com/lib/pq v1.9.0
|
||||||
|
github.com/magiconair/properties v1.5.3 // indirect
|
||||||
|
github.com/mattn/go-sqlite3 v1.6.0
|
||||||
|
github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect
|
||||||
|
github.com/miekg/pkcs11 v1.0.3
|
||||||
|
github.com/mitchellh/mapstructure v1.0.0 // indirect
|
||||||
|
github.com/opencontainers/go-digest v1.0.0 // indirect
|
||||||
|
github.com/opencontainers/image-spec v1.0.1 // indirect
|
||||||
|
github.com/pkg/errors v0.9.1 // indirect
|
||||||
|
github.com/prometheus/client_golang v0.9.0-pre1.0.20180209125602-c332b6f63c06
|
||||||
|
github.com/prometheus/client_model v0.0.0-20171117100541-99fa1f4be8e5 // indirect
|
||||||
|
github.com/prometheus/common v0.0.0-20180110214958-89604d197083 // indirect
|
||||||
|
github.com/prometheus/procfs v0.0.0-20180125133057-cb4147076ac7 // indirect
|
||||||
|
github.com/sirupsen/logrus v1.8.1
|
||||||
|
github.com/spf13/cast v0.0.0-20150508191742-4d07383ffe94 // indirect
|
||||||
|
github.com/spf13/cobra v0.0.1
|
||||||
|
github.com/spf13/jwalterweatherman v0.0.0-20141219030609-3d60171a6431 // indirect
|
||||||
|
github.com/spf13/pflag v1.0.0 // indirect
|
||||||
|
github.com/spf13/viper v0.0.0-20150530192845-be5ff3e4840c
|
||||||
|
github.com/stretchr/testify v1.5.1
|
||||||
|
golang.org/x/crypto v0.0.0-20201117144127-c1f2f97bffc9
|
||||||
|
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3
|
||||||
|
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e
|
||||||
|
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221
|
||||||
|
google.golang.org/grpc v1.0.5
|
||||||
|
gopkg.in/rethinkdb/rethinkdb-go.v6 v6.2.1
|
||||||
|
)
|
||||||
+4
-4
@@ -12,7 +12,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/theupdateframework/notary"
|
"github.com/theupdateframework/notary"
|
||||||
"golang.org/x/crypto/ssh/terminal"
|
"golang.org/x/term"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@@ -49,7 +49,7 @@ var (
|
|||||||
// Upon successful passphrase retrievals, the passphrase will be cached such that
|
// Upon successful passphrase retrievals, the passphrase will be cached such that
|
||||||
// subsequent prompts will produce the same passphrase.
|
// subsequent prompts will produce the same passphrase.
|
||||||
func PromptRetriever() notary.PassRetriever {
|
func PromptRetriever() notary.PassRetriever {
|
||||||
if !terminal.IsTerminal(int(os.Stdin.Fd())) {
|
if !term.IsTerminal(int(os.Stdin.Fd())) {
|
||||||
return func(string, string, bool, int) (string, bool, error) {
|
return func(string, string, bool, int) (string, bool, error) {
|
||||||
return "", false, ErrNoInput
|
return "", false, ErrNoInput
|
||||||
}
|
}
|
||||||
@@ -200,8 +200,8 @@ func GetPassphrase(in *bufio.Reader) ([]byte, error) {
|
|||||||
err error
|
err error
|
||||||
)
|
)
|
||||||
|
|
||||||
if terminal.IsTerminal(int(os.Stdin.Fd())) {
|
if term.IsTerminal(int(os.Stdin.Fd())) {
|
||||||
passphrase, err = terminal.ReadPassword(int(os.Stdin.Fd()))
|
passphrase, err = term.ReadPassword(int(os.Stdin.Fd()))
|
||||||
} else {
|
} else {
|
||||||
passphrase, err = in.ReadBytes('\n')
|
passphrase, err = in.ReadBytes('\n')
|
||||||
}
|
}
|
||||||
|
|||||||
+4
-2
@@ -137,14 +137,16 @@ func (f *FilesystemStore) GetSized(name string, size int64) ([]byte, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
file, err := os.OpenFile(p, os.O_RDONLY, notary.PrivNoExecPerms)
|
file, err := os.Open(p)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if os.IsNotExist(err) {
|
if os.IsNotExist(err) {
|
||||||
err = ErrMetaNotFound{Resource: name}
|
err = ErrMetaNotFound{Resource: name}
|
||||||
}
|
}
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
defer file.Close()
|
defer func() {
|
||||||
|
_ = file.Close()
|
||||||
|
}()
|
||||||
|
|
||||||
if size == NoSizeLimit {
|
if size == NoSizeLimit {
|
||||||
size = notary.MaxDownloadSize
|
size = notary.MaxDownloadSize
|
||||||
|
|||||||
+13
-1
@@ -111,6 +111,18 @@ type HTTPStore struct {
|
|||||||
roundTrip http.RoundTripper
|
roundTrip http.RoundTripper
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NewNotaryServerStore returns a new HTTPStore against a URL which should represent a notary
|
||||||
|
// server
|
||||||
|
func NewNotaryServerStore(serverURL string, gun data.GUN, roundTrip http.RoundTripper) (RemoteStore, error) {
|
||||||
|
return NewHTTPStore(
|
||||||
|
serverURL+"/v2/"+gun.String()+"/_trust/tuf/",
|
||||||
|
"",
|
||||||
|
"json",
|
||||||
|
"key",
|
||||||
|
roundTrip,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
// NewHTTPStore initializes a new store against a URL and a number of configuration options.
|
// NewHTTPStore initializes a new store against a URL and a number of configuration options.
|
||||||
//
|
//
|
||||||
// In case of a nil `roundTrip`, a default offline store is used instead.
|
// In case of a nil `roundTrip`, a default offline store is used instead.
|
||||||
@@ -363,5 +375,5 @@ func (s HTTPStore) RotateKey(role data.RoleName) ([]byte, error) {
|
|||||||
|
|
||||||
// Location returns a human readable name for the storage location
|
// Location returns a human readable name for the storage location
|
||||||
func (s HTTPStore) Location() string {
|
func (s HTTPStore) Location() string {
|
||||||
return s.baseURL.String()
|
return s.baseURL.Host
|
||||||
}
|
}
|
||||||
|
|||||||
+1
@@ -15,6 +15,7 @@ type MetadataStore interface {
|
|||||||
SetMulti(map[string][]byte) error
|
SetMulti(map[string][]byte) error
|
||||||
RemoveAll() error
|
RemoveAll() error
|
||||||
Remove(name string) error
|
Remove(name string) error
|
||||||
|
Location() string
|
||||||
}
|
}
|
||||||
|
|
||||||
// PublicKeyStore must be implemented by a key service
|
// PublicKeyStore must be implemented by a key service
|
||||||
|
|||||||
Generated
Vendored
+2
@@ -6,4 +6,6 @@ var possiblePkcs11Libs = []string{
|
|||||||
"/usr/local/lib/libykcs11.dylib",
|
"/usr/local/lib/libykcs11.dylib",
|
||||||
"/usr/local/docker/lib/libykcs11.dylib",
|
"/usr/local/docker/lib/libykcs11.dylib",
|
||||||
"/usr/local/docker-experimental/lib/libykcs11.dylib",
|
"/usr/local/docker-experimental/lib/libykcs11.dylib",
|
||||||
|
// default location on arm64
|
||||||
|
"/opt/homebrew/lib/libykcs11.dylib",
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -359,7 +359,7 @@ func (rb *repoBuilder) GenerateSnapshot(prev *data.SignedSnapshot) ([]byte, int,
|
|||||||
|
|
||||||
// loadedNotChecksummed should currently contain the root awaiting checksumming,
|
// loadedNotChecksummed should currently contain the root awaiting checksumming,
|
||||||
// since it has to have been loaded. Since the snapshot was generated using
|
// since it has to have been loaded. Since the snapshot was generated using
|
||||||
// the root and targets data (there may not be any) that that have been loaded,
|
// the root and targets data (there may not be any) that have been loaded,
|
||||||
// remove all of them from rb.loadedNotChecksummed
|
// remove all of them from rb.loadedNotChecksummed
|
||||||
for tgtName := range rb.repo.Targets {
|
for tgtName := range rb.repo.Targets {
|
||||||
delete(rb.loadedNotChecksummed, data.RoleName(tgtName))
|
delete(rb.loadedNotChecksummed, data.RoleName(tgtName))
|
||||||
|
|||||||
+5
-4
@@ -12,9 +12,9 @@ import (
|
|||||||
"io"
|
"io"
|
||||||
"math/big"
|
"math/big"
|
||||||
|
|
||||||
"github.com/agl/ed25519"
|
|
||||||
"github.com/docker/go/canonical/json"
|
"github.com/docker/go/canonical/json"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
|
"golang.org/x/crypto/ed25519"
|
||||||
)
|
)
|
||||||
|
|
||||||
// PublicKey is the necessary interface for public keys
|
// PublicKey is the necessary interface for public keys
|
||||||
@@ -484,9 +484,10 @@ func (k RSAPrivateKey) Sign(rand io.Reader, msg []byte, opts crypto.SignerOpts)
|
|||||||
|
|
||||||
// Sign creates an ed25519 signature
|
// Sign creates an ed25519 signature
|
||||||
func (k ED25519PrivateKey) Sign(rand io.Reader, msg []byte, opts crypto.SignerOpts) (signature []byte, err error) {
|
func (k ED25519PrivateKey) Sign(rand io.Reader, msg []byte, opts crypto.SignerOpts) (signature []byte, err error) {
|
||||||
priv := [ed25519.PrivateKeySize]byte{}
|
priv := make([]byte, ed25519.PrivateKeySize)
|
||||||
copy(priv[:], k.private[ed25519.PublicKeySize:])
|
// The ed25519 key is serialized as public key then private key, so just use private key here.
|
||||||
return ed25519.Sign(&priv, msg)[:], nil
|
copy(priv, k.private[ed25519.PublicKeySize:])
|
||||||
|
return ed25519.Sign(ed25519.PrivateKey(priv), msg)[:], nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sign on an UnknownPrivateKey raises an error because the client does not
|
// Sign on an UnknownPrivateKey raises an error because the client does not
|
||||||
|
|||||||
+1
-1
@@ -55,7 +55,7 @@ func (e ErrInvalidRole) Error() string {
|
|||||||
|
|
||||||
// ValidRole only determines the name is semantically
|
// ValidRole only determines the name is semantically
|
||||||
// correct. For target delegated roles, it does NOT check
|
// correct. For target delegated roles, it does NOT check
|
||||||
// the the appropriate parent roles exist.
|
// the appropriate parent roles exist.
|
||||||
func ValidRole(name RoleName) bool {
|
func ValidRole(name RoleName) bool {
|
||||||
if IsDelegation(name) {
|
if IsDelegation(name) {
|
||||||
return true
|
return true
|
||||||
|
|||||||
+1
-1
@@ -59,7 +59,7 @@ func IsValidSnapshotStructure(s Snapshot) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewSnapshot initilizes a SignedSnapshot with a given top level root
|
// NewSnapshot initializes a SignedSnapshot with a given top level root
|
||||||
// and targets objects
|
// and targets objects
|
||||||
func NewSnapshot(root *Signed, targets *Signed) (*SignedSnapshot, error) {
|
func NewSnapshot(root *Signed, targets *Signed) (*SignedSnapshot, error) {
|
||||||
logrus.Debug("generating new snapshot...")
|
logrus.Debug("generating new snapshot...")
|
||||||
|
|||||||
+1
-1
@@ -54,7 +54,7 @@ func isValidTargetsStructure(t Targets, roleName RoleName) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewTargets intiializes a new empty SignedTargets object
|
// NewTargets initializes a new empty SignedTargets object
|
||||||
func NewTargets() *SignedTargets {
|
func NewTargets() *SignedTargets {
|
||||||
return &SignedTargets{
|
return &SignedTargets{
|
||||||
Signatures: make([]Signature, 0),
|
Signatures: make([]Signature, 0),
|
||||||
|
|||||||
+1
-1
@@ -186,7 +186,7 @@ type FileMeta struct {
|
|||||||
|
|
||||||
// Equals returns true if the other FileMeta object is equivalent to this one
|
// Equals returns true if the other FileMeta object is equivalent to this one
|
||||||
func (f FileMeta) Equals(o FileMeta) bool {
|
func (f FileMeta) Equals(o FileMeta) bool {
|
||||||
if o.Length != f.Length || len(f.Hashes) != len(f.Hashes) {
|
if o.Length != f.Length || len(o.Hashes) != len(f.Hashes) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
if f.Custom == nil && o.Custom != nil || f.Custom != nil && o.Custom == nil {
|
if f.Custom == nil && o.Custom != nil || f.Custom != nil && o.Custom == nil {
|
||||||
|
|||||||
+1
-1
@@ -39,7 +39,7 @@ type CryptoService interface {
|
|||||||
KeyService
|
KeyService
|
||||||
}
|
}
|
||||||
|
|
||||||
// Verifier defines an interface for verfying signatures. An implementer
|
// Verifier defines an interface for verifying signatures. An implementer
|
||||||
// of this interface should verify signatures for one and only one
|
// of this interface should verify signatures for one and only one
|
||||||
// signing scheme.
|
// signing scheme.
|
||||||
type Verifier interface {
|
type Verifier interface {
|
||||||
|
|||||||
+2
-1
@@ -87,7 +87,8 @@ func Sign(service CryptoService, s *data.Signed, signingKeys []data.PublicKey,
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, sig := range s.Signatures {
|
for i := range s.Signatures {
|
||||||
|
sig := s.Signatures[i]
|
||||||
if _, ok := signingKeyIDs[sig.KeyID]; ok {
|
if _, ok := signingKeyIDs[sig.KeyID]; ok {
|
||||||
// key is in the set of key IDs for which a signature has been created
|
// key is in the set of key IDs for which a signature has been created
|
||||||
continue
|
continue
|
||||||
|
|||||||
+6
-6
@@ -10,9 +10,9 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"math/big"
|
"math/big"
|
||||||
|
|
||||||
"github.com/agl/ed25519"
|
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
"github.com/theupdateframework/notary/tuf/data"
|
"github.com/theupdateframework/notary/tuf/data"
|
||||||
|
"golang.org/x/crypto/ed25519"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@@ -39,26 +39,26 @@ func (v Ed25519Verifier) Verify(key data.PublicKey, sig []byte, msg []byte) erro
|
|||||||
if key.Algorithm() != data.ED25519Key {
|
if key.Algorithm() != data.ED25519Key {
|
||||||
return ErrInvalidKeyType{}
|
return ErrInvalidKeyType{}
|
||||||
}
|
}
|
||||||
var sigBytes [ed25519.SignatureSize]byte
|
sigBytes := make([]byte, ed25519.SignatureSize)
|
||||||
if len(sig) != ed25519.SignatureSize {
|
if len(sig) != ed25519.SignatureSize {
|
||||||
logrus.Debugf("signature length is incorrect, must be %d, was %d.", ed25519.SignatureSize, len(sig))
|
logrus.Debugf("signature length is incorrect, must be %d, was %d.", ed25519.SignatureSize, len(sig))
|
||||||
return ErrInvalid
|
return ErrInvalid
|
||||||
}
|
}
|
||||||
copy(sigBytes[:], sig)
|
copy(sigBytes, sig)
|
||||||
|
|
||||||
var keyBytes [ed25519.PublicKeySize]byte
|
keyBytes := make([]byte, ed25519.PublicKeySize)
|
||||||
pub := key.Public()
|
pub := key.Public()
|
||||||
if len(pub) != ed25519.PublicKeySize {
|
if len(pub) != ed25519.PublicKeySize {
|
||||||
logrus.Errorf("public key is incorrect size, must be %d, was %d.", ed25519.PublicKeySize, len(pub))
|
logrus.Errorf("public key is incorrect size, must be %d, was %d.", ed25519.PublicKeySize, len(pub))
|
||||||
return ErrInvalidKeyLength{msg: fmt.Sprintf("ed25519 public key must be %d bytes.", ed25519.PublicKeySize)}
|
return ErrInvalidKeyLength{msg: fmt.Sprintf("ed25519 public key must be %d bytes.", ed25519.PublicKeySize)}
|
||||||
}
|
}
|
||||||
n := copy(keyBytes[:], key.Public())
|
n := copy(keyBytes, key.Public())
|
||||||
if n < ed25519.PublicKeySize {
|
if n < ed25519.PublicKeySize {
|
||||||
logrus.Errorf("failed to copy the key, must have %d bytes, copied %d bytes.", ed25519.PublicKeySize, n)
|
logrus.Errorf("failed to copy the key, must have %d bytes, copied %d bytes.", ed25519.PublicKeySize, n)
|
||||||
return ErrInvalid
|
return ErrInvalid
|
||||||
}
|
}
|
||||||
|
|
||||||
if !ed25519.Verify(&keyBytes, msg, &sigBytes) {
|
if !ed25519.Verify(ed25519.PublicKey(keyBytes), msg, sigBytes) {
|
||||||
logrus.Debugf("failed ed25519 verification")
|
logrus.Debugf("failed ed25519 verification")
|
||||||
return ErrInvalid
|
return ErrInvalid
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -39,7 +39,7 @@ import (
|
|||||||
"crypto/elliptic"
|
"crypto/elliptic"
|
||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
"crypto/rsa"
|
"crypto/rsa"
|
||||||
"crypto/sha1"
|
"crypto/sha1" // #nosec
|
||||||
"crypto/x509"
|
"crypto/x509"
|
||||||
"crypto/x509/pkix"
|
"crypto/x509/pkix"
|
||||||
"encoding/asn1"
|
"encoding/asn1"
|
||||||
|
|||||||
+1
-1
@@ -30,7 +30,7 @@ func RoleNameSliceContains(ss []data.RoleName, s data.RoleName) bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// RoleNameSliceRemove removes the the given RoleName from the slice, returning a new slice
|
// RoleNameSliceRemove removes the given RoleName from the slice, returning a new slice
|
||||||
func RoleNameSliceRemove(ss []data.RoleName, s data.RoleName) []data.RoleName {
|
func RoleNameSliceRemove(ss []data.RoleName, s data.RoleName) []data.RoleName {
|
||||||
res := []data.RoleName{}
|
res := []data.RoleName{}
|
||||||
for _, v := range ss {
|
for _, v := range ss {
|
||||||
|
|||||||
+1
-1
@@ -16,10 +16,10 @@ import (
|
|||||||
"math/big"
|
"math/big"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/agl/ed25519"
|
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
"github.com/theupdateframework/notary"
|
"github.com/theupdateframework/notary"
|
||||||
"github.com/theupdateframework/notary/tuf/data"
|
"github.com/theupdateframework/notary/tuf/data"
|
||||||
|
"golang.org/x/crypto/ed25519"
|
||||||
)
|
)
|
||||||
|
|
||||||
// CanonicalKeyID returns the ID of the public bytes version of a TUF key.
|
// CanonicalKeyID returns the ID of the public bytes version of a TUF key.
|
||||||
|
|||||||
-59
@@ -1,59 +0,0 @@
|
|||||||
github.com/Shopify/logrus-bugsnag 6dbc35f2c30d1e37549f9673dd07912452ab28a5
|
|
||||||
github.com/sirupsen/logrus f006c2ac4710855cf0f916dd6b77acf6b048dc6e # v1.0.3
|
|
||||||
github.com/agl/ed25519 278e1ec8e8a6e017cd07577924d6766039146ced
|
|
||||||
github.com/bugsnag/bugsnag-go 13fd6b8acda029830ef9904df6b63be0a83369d0
|
|
||||||
github.com/bugsnag/panicwrap e2c28503fcd0675329da73bf48b33404db873782
|
|
||||||
github.com/bugsnag/osext 0dd3f918b21bec95ace9dc86c7e70266cfc5c702
|
|
||||||
github.com/docker/distribution edc3ab29cdff8694dd6feb85cfeb4b5f1b38ed9c
|
|
||||||
github.com/opencontainers/go-digest a6d0ee40d4207ea02364bd3b9e8e77b9159ba1eb
|
|
||||||
github.com/docker/go-connections 7395e3f8aa162843a74ed6d48e79627d9792ac55
|
|
||||||
github.com/docker/go d30aec9fd63c35133f8f79c3412ad91a3b08be06
|
|
||||||
github.com/dvsekhvalnov/jose2go f21a8cedbbae609f623613ec8f81125c243212e6 # v1.3
|
|
||||||
github.com/go-sql-driver/mysql a0583e0143b1624142adab07e0e97fe106d99561 # v1.3
|
|
||||||
github.com/gorilla/mux 53c1911da2b537f792e7cafcb446b05ffe33b996 # v1.6.1
|
|
||||||
github.com/jinzhu/gorm 5409931a1bb87e484d68d649af9367c207713ea2
|
|
||||||
github.com/jinzhu/inflection 1c35d901db3da928c72a72d8458480cc9ade058f
|
|
||||||
github.com/lib/pq 0dad96c0b94f8dee039aa40467f767467392a0af
|
|
||||||
github.com/mattn/go-sqlite3 6c771bb9887719704b210e87e934f08be014bdb1 # v1.6.0
|
|
||||||
github.com/miekg/pkcs11 5f6e0d0dad6f472df908c8e968a98ef00c9224bb
|
|
||||||
github.com/prometheus/client_golang 449ccefff16c8e2b7229f6be1921ba22f62461fe
|
|
||||||
github.com/prometheus/client_model fa8ad6fec33561be4280a8f0514318c79d7f6cb6 # model-0.0.2-12-gfa8ad6f
|
|
||||||
github.com/prometheus/procfs b1afdc266f54247f5dc725544f5d351a8661f502
|
|
||||||
github.com/prometheus/common 4fdc91a58c9d3696b982e8a680f4997403132d44
|
|
||||||
github.com/golang/protobuf c3cefd437628a0b7d31b34fe44b3a7a540e98527
|
|
||||||
github.com/spf13/cobra 7b2c5ac9fc04fc5efafb60700713d4fa609b777b # v0.0.1
|
|
||||||
github.com/spf13/viper be5ff3e4840cf692388bde7a057595a474ef379e
|
|
||||||
golang.org/x/crypto 76eec36fa14229c4b25bb894c2d0e591527af429
|
|
||||||
golang.org/x/net 6a513affb38dc9788b449d59ffed099b8de18fa0
|
|
||||||
golang.org/x/sys 314a259e304ff91bd6985da2a7149bbf91237993
|
|
||||||
google.golang.org/grpc 708a7f9f3283aa2d4f6132d287d78683babe55c8 # v1.0.5
|
|
||||||
github.com/pkg/errors 839d9e913e063e28dfd0e6c7b7512793e0a48be9
|
|
||||||
|
|
||||||
github.com/spf13/pflag e57e3eeb33f795204c1ca35f56c44f83227c6e66 # v1.0.0
|
|
||||||
github.com/spf13/cast 4d07383ffe94b5e5a6fa3af9211374a4507a0184
|
|
||||||
gopkg.in/yaml.v2 5420a8b6744d3b0345ab293f6fcba19c978f1183 # v2.2.1
|
|
||||||
gopkg.in/fatih/pool.v2 cba550ebf9bce999a02e963296d4bc7a486cb715
|
|
||||||
github.com/gorilla/context 14f550f51af52180c2eefed15e5fd18d63c0a64a # unused
|
|
||||||
github.com/spf13/jwalterweatherman 3d60171a64319ef63c78bd45bd60e6eab1e75f8b
|
|
||||||
github.com/mitchellh/mapstructure 2caf8efc93669b6c43e0441cdc6aed17546c96f3
|
|
||||||
github.com/magiconair/properties 624009598839a9432bd97bb75552389422357723 # v1.5.3
|
|
||||||
github.com/kr/text 6807e777504f54ad073ecef66747de158294b639
|
|
||||||
github.com/kr/pretty bc9499caa0f45ee5edb2f0209fbd61fbf3d9018f # go.weekly.2011-12-22-18-gbc9499c
|
|
||||||
github.com/hailocab/go-hostpool e80d13ce29ede4452c43dea11e79b9bc8a15b478
|
|
||||||
github.com/docker/libtrust aabc10ec26b754e797f9028f4589c5b7bd90dc20
|
|
||||||
github.com/beorn7/perks b965b613227fddccbfffe13eae360ed3fa822f8d
|
|
||||||
github.com/BurntSushi/toml a368813c5e648fee92e5f6c30e3944ff9d5e8895
|
|
||||||
|
|
||||||
github.com/matttproud/golang_protobuf_extensions d0c3fe89de86839aecf2e0579c40ba3bb336a453
|
|
||||||
github.com/inconshreveable/mousetrap 76626ae9c91c4f2a10f34cad8ce83ea42c93bb75
|
|
||||||
|
|
||||||
gopkg.in/dancannon/gorethink.v3 e324d6ad938205da6c1e8a0179dc97a5b1a92185 https://github.com/docker/gorethink # v3.0.0-logrus
|
|
||||||
# dependencies of gorethink.v3
|
|
||||||
gopkg.in/gorethink/gorethink.v2 ac5be4ae8538d44ae8843b97fc9f90860cb48a85 https://github.com/docker/gorethink # v2.2.2-logrus
|
|
||||||
github.com/cenk/backoff 32cd0c5b3aef12c76ed64aaf678f6c79736be7dc # v1.0.0
|
|
||||||
|
|
||||||
# Testing requirements
|
|
||||||
github.com/stretchr/testify 089c7181b8c728499929ff09b62d3fdd8df8adff
|
|
||||||
github.com/cloudflare/cfssl 4e2dcbde500472449917533851bf4bae9bdff562 # v1.3.1
|
|
||||||
github.com/google/certificate-transparency-go 5ab67e519c93568ac3ee50fd6772a5bcf8aa460d
|
|
||||||
github.com/gogo/protobuf 1adfc126b41513cc696b209667c8656ea7aac67c # v1.0.0
|
|
||||||
+2
@@ -1,5 +1,7 @@
|
|||||||
# sys
|
# sys
|
||||||
|
|
||||||
|
[](https://pkg.go.dev/golang.org/x/sys)
|
||||||
|
|
||||||
This repository holds supplemental Go packages for low-level interactions with
|
This repository holds supplemental Go packages for low-level interactions with
|
||||||
the operating system.
|
the operating system.
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -2,7 +2,7 @@
|
|||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by a BSD-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
// +build !gccgo
|
// +build gc
|
||||||
|
|
||||||
#include "textflag.h"
|
#include "textflag.h"
|
||||||
|
|
||||||
|
|||||||
+18
-22
@@ -39,34 +39,34 @@ func initOptions() {
|
|||||||
|
|
||||||
func archInit() {
|
func archInit() {
|
||||||
switch runtime.GOOS {
|
switch runtime.GOOS {
|
||||||
case "android", "darwin", "ios", "netbsd", "openbsd":
|
case "freebsd":
|
||||||
// Android and iOS don't seem to allow reading these registers.
|
readARM64Registers()
|
||||||
//
|
case "linux", "netbsd":
|
||||||
// NetBSD:
|
doinit()
|
||||||
// ID_AA64ISAR0_EL1 is a privileged register and cannot be read from EL0.
|
default:
|
||||||
// It can be read via sysctl(3). Example for future implementers:
|
// Most platforms don't seem to allow reading these registers.
|
||||||
// https://nxr.netbsd.org/xref/src/usr.sbin/cpuctl/arch/aarch64.c
|
|
||||||
//
|
//
|
||||||
// OpenBSD:
|
// OpenBSD:
|
||||||
// See https://golang.org/issue/31746
|
// See https://golang.org/issue/31746
|
||||||
//
|
setMinimalFeatures()
|
||||||
// Fake the minimal features expected by
|
|
||||||
// TestARM64minimalFeatures.
|
|
||||||
ARM64.HasASIMD = true
|
|
||||||
ARM64.HasFP = true
|
|
||||||
case "linux":
|
|
||||||
doinit()
|
|
||||||
default:
|
|
||||||
readARM64Registers()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// setMinimalFeatures fakes the minimal ARM64 features expected by
|
||||||
|
// TestARM64minimalFeatures.
|
||||||
|
func setMinimalFeatures() {
|
||||||
|
ARM64.HasASIMD = true
|
||||||
|
ARM64.HasFP = true
|
||||||
|
}
|
||||||
|
|
||||||
func readARM64Registers() {
|
func readARM64Registers() {
|
||||||
Initialized = true
|
Initialized = true
|
||||||
|
|
||||||
// ID_AA64ISAR0_EL1
|
parseARM64SystemRegisters(getisar0(), getisar1(), getpfr0())
|
||||||
isar0 := getisar0()
|
}
|
||||||
|
|
||||||
|
func parseARM64SystemRegisters(isar0, isar1, pfr0 uint64) {
|
||||||
|
// ID_AA64ISAR0_EL1
|
||||||
switch extractBits(isar0, 4, 7) {
|
switch extractBits(isar0, 4, 7) {
|
||||||
case 1:
|
case 1:
|
||||||
ARM64.HasAES = true
|
ARM64.HasAES = true
|
||||||
@@ -124,8 +124,6 @@ func readARM64Registers() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ID_AA64ISAR1_EL1
|
// ID_AA64ISAR1_EL1
|
||||||
isar1 := getisar1()
|
|
||||||
|
|
||||||
switch extractBits(isar1, 0, 3) {
|
switch extractBits(isar1, 0, 3) {
|
||||||
case 1:
|
case 1:
|
||||||
ARM64.HasDCPOP = true
|
ARM64.HasDCPOP = true
|
||||||
@@ -147,8 +145,6 @@ func readARM64Registers() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ID_AA64PFR0_EL1
|
// ID_AA64PFR0_EL1
|
||||||
pfr0 := getpfr0()
|
|
||||||
|
|
||||||
switch extractBits(pfr0, 16, 19) {
|
switch extractBits(pfr0, 16, 19) {
|
||||||
case 0:
|
case 0:
|
||||||
ARM64.HasFP = true
|
ARM64.HasFP = true
|
||||||
|
|||||||
+1
-1
@@ -2,7 +2,7 @@
|
|||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by a BSD-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
// +build !gccgo
|
// +build gc
|
||||||
|
|
||||||
#include "textflag.h"
|
#include "textflag.h"
|
||||||
|
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user