Skip to content
This repository has been archived by the owner on Mar 9, 2022. It is now read-only.

Commit

Permalink
Merge pull request #88 from Random-Liu/add-image-authentication
Browse files Browse the repository at this point in the history
Add pull image authentication.
  • Loading branch information
Random-Liu authored Jun 22, 2017
2 parents 4b53d84 + d5674be commit 6de96a5
Show file tree
Hide file tree
Showing 2 changed files with 86 additions and 9 deletions.
44 changes: 35 additions & 9 deletions pkg/server/image_pull.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,11 @@ package server

import (
gocontext "context"
"encoding/base64"
"encoding/json"
"fmt"
"net/http"
"strings"
"sync"
"time"

Expand Down Expand Up @@ -77,7 +79,6 @@ import (
// contents are missing but snapshots are ready, is the image still "READY"?

// PullImage pulls an image with authentication config.
// TODO(mikebrow): harden api (including figuring out at what layer we should be blocking on duplicate requests.)
func (c *criContainerdService) PullImage(ctx context.Context, r *runtime.PullImageRequest) (retRes *runtime.PullImageResponse, retErr error) {
glog.V(2).Infof("PullImage %q with auth config %+v", r.GetImage().GetImage(), r.GetAuth())
defer func() {
Expand All @@ -88,10 +89,8 @@ func (c *criContainerdService) PullImage(ctx context.Context, r *runtime.PullIma
}()
image := r.GetImage().GetImage()

// TODO(random-liu): [P1] Schema 1 image is not supported in containerd now, we need to support
// it for backward compatiblity.
// TODO(mikebrow): add truncIndex for image id
imageID, repoTag, repoDigest, err := c.pullImage(ctx, image)
imageID, repoTag, repoDigest, err := c.pullImage(ctx, image, r.GetAuth())
if err != nil {
return nil, fmt.Errorf("failed to pull image %q: %v", image, err)
}
Expand Down Expand Up @@ -173,8 +172,37 @@ func (r *resourceSet) all() map[string]struct{} {
return resources
}

// ParseAuth parses AuthConfig and returns username and password/secret required by containerd.
func ParseAuth(auth *runtime.AuthConfig) (string, string, error) {
if auth == nil {
return "", "", nil
}
if auth.Username != "" {
return auth.Username, auth.Password, nil
}
if auth.IdentityToken != "" {
return "", auth.IdentityToken, nil
}
if auth.Auth != "" {
decLen := base64.StdEncoding.DecodedLen(len(auth.Auth))
decoded := make([]byte, decLen)
_, err := base64.StdEncoding.Decode(decoded, []byte(auth.Auth))
if err != nil {
return "", "", err
}
fields := strings.SplitN(string(decoded), ":", 2)
if len(fields) != 2 {
return "", "", fmt.Errorf("invalid decoded auth: %q", decoded)
}
user, passwd := fields[0], fields[1]
return user, strings.Trim(passwd, "\x00"), nil
}
// TODO(random-liu): Support RegistryToken.
return "", "", fmt.Errorf("invalid auth config")
}

// pullImage pulls image and returns image id (config digest), repoTag and repoDigest.
func (c *criContainerdService) pullImage(ctx context.Context, rawRef string) (
func (c *criContainerdService) pullImage(ctx context.Context, rawRef string, auth *runtime.AuthConfig) (
// TODO(random-liu): Replace with client.Pull.
string, string, string, error) {
namedRef, err := normalizeImageRef(rawRef)
Expand All @@ -189,10 +217,8 @@ func (c *criContainerdService) pullImage(ctx context.Context, rawRef string) (

// Resolve the image reference to get descriptor and fetcher.
resolver := docker.NewResolver(docker.ResolverOptions{
// TODO(random-liu): Add authentication by setting credentials.
// TODO(random-liu): Handle https.
PlainHTTP: true,
Client: http.DefaultClient,
Credentials: func(string) (string, string, error) { return ParseAuth(auth) },
Client: http.DefaultClient,
})
_, desc, err := resolver.Resolve(ctx, ref)
if err != nil {
Expand Down
51 changes: 51 additions & 0 deletions pkg/server/image_pull_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,13 @@ limitations under the License.
package server

import (
"encoding/base64"
"fmt"
"sync"
"testing"

"github.com/stretchr/testify/assert"
"k8s.io/kubernetes/pkg/kubelet/apis/cri/v1alpha1/runtime"

"github.com/kubernetes-incubator/cri-containerd/pkg/metadata"
)
Expand Down Expand Up @@ -93,3 +95,52 @@ func TestResources(t *testing.T) {
assert.True(t, ok)
}
}

func TestParseAuth(t *testing.T) {
testUser := "username"
testPasswd := "password"
testAuthLen := base64.StdEncoding.EncodedLen(len(testUser + ":" + testPasswd))
testAuth := make([]byte, testAuthLen)
base64.StdEncoding.Encode(testAuth, []byte(testUser+":"+testPasswd))
invalidAuth := make([]byte, testAuthLen)
base64.StdEncoding.Encode(invalidAuth, []byte(testUser+"@"+testPasswd))
for desc, test := range map[string]struct {
auth *runtime.AuthConfig
expectedUser string
expectedSecret string
expectErr bool
}{
"should not return error if auth config is nil": {},
"should return error if no supported auth is provided": {
auth: &runtime.AuthConfig{},
expectErr: true,
},
"should support identity token": {
auth: &runtime.AuthConfig{IdentityToken: "abcd"},
expectedSecret: "abcd",
},
"should support username and password": {
auth: &runtime.AuthConfig{
Username: testUser,
Password: testPasswd,
},
expectedUser: testUser,
expectedSecret: testPasswd,
},
"should support auth": {
auth: &runtime.AuthConfig{Auth: string(testAuth)},
expectedUser: testUser,
expectedSecret: testPasswd,
},
"should return error for invalid auth": {
auth: &runtime.AuthConfig{Auth: string(invalidAuth)},
expectErr: true,
},
} {
t.Logf("TestCase %q", desc)
u, s, err := ParseAuth(test.auth)
assert.Equal(t, test.expectErr, err != nil)
assert.Equal(t, test.expectedUser, u)
assert.Equal(t, test.expectedSecret, s)
}
}

0 comments on commit 6de96a5

Please sign in to comment.