github.com/buildtool/build-tools@v0.2.29-0.20240322150259-6a1d0a553c23/pkg/docker/auth.go (about)

     1  // MIT License
     2  //
     3  // Copyright (c) 2018 buildtool
     4  //
     5  // Permission is hereby granted, free of charge, to any person obtaining a copy
     6  // of this software and associated documentation files (the "Software"), to deal
     7  // in the Software without restriction, including without limitation the rights
     8  // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     9  // copies of the Software, and to permit persons to whom the Software is
    10  // furnished to do so, subject to the following conditions:
    11  //
    12  // The above copyright notice and this permission notice shall be included in all
    13  // copies or substantial portions of the Software.
    14  //
    15  // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    16  // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    17  // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    18  // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    19  // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    20  // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
    21  // SOFTWARE.
    22  
    23  package docker
    24  
    25  import (
    26  	"context"
    27  	"crypto/ed25519"
    28  	"crypto/hmac"
    29  	"crypto/sha256"
    30  	"fmt"
    31  	"net/http"
    32  	"time"
    33  
    34  	authutil "github.com/containerd/containerd/remotes/docker/auth"
    35  
    36  	"github.com/docker/docker/api/types/registry"
    37  	"github.com/moby/buildkit/session"
    38  	"github.com/moby/buildkit/session/auth"
    39  	"golang.org/x/crypto/nacl/sign"
    40  	"google.golang.org/grpc"
    41  )
    42  
    43  type authenticator struct {
    44  	authConfig   registry.AuthConfig
    45  	registryHost string
    46  }
    47  
    48  func NewAuthenticator(registryHost string, authConfig registry.AuthConfig) Authenticator {
    49  	return &authenticator{
    50  		authConfig:   authConfig,
    51  		registryHost: registryHost,
    52  	}
    53  }
    54  
    55  func (a *authenticator) Register(server *grpc.Server) {
    56  	auth.RegisterAuthServer(server, a)
    57  }
    58  
    59  func (a authenticator) Credentials(ctx context.Context, req *auth.CredentialsRequest) (*auth.CredentialsResponse, error) {
    60  	if req.Host != a.registryHost {
    61  		return &auth.CredentialsResponse{}, nil
    62  	}
    63  	return &auth.CredentialsResponse{Username: a.authConfig.Username, Secret: a.authConfig.Password}, nil
    64  }
    65  
    66  func (a authenticator) FetchToken(ctx context.Context, req *auth.FetchTokenRequest) (*auth.FetchTokenResponse, error) {
    67  	to := authutil.TokenOptions{
    68  		Realm:    req.Realm,
    69  		Service:  req.Service,
    70  		Scopes:   req.Scopes,
    71  		Username: "",
    72  		Secret:   "",
    73  	}
    74  	// do request anonymously
    75  	resp, err := authutil.FetchToken(ctx, http.DefaultClient, nil, to)
    76  	if err != nil {
    77  		// try with auth
    78  		to.Username = a.authConfig.Username
    79  		to.Secret = a.authConfig.Password
    80  		resp, err = authutil.FetchToken(ctx, http.DefaultClient, nil, to)
    81  		if err != nil {
    82  			return nil, fmt.Errorf("failed to fetch anonymous and authenticated token, %w", err)
    83  		}
    84  	}
    85  	return toTokenResponse(resp.Token, resp.IssuedAt, resp.ExpiresIn), nil
    86  }
    87  
    88  func (ap *authenticator) GetTokenAuthority(ctx context.Context, req *auth.GetTokenAuthorityRequest) (*auth.GetTokenAuthorityResponse, error) {
    89  	key := ap.getAuthorityKey(req.Host, req.Salt)
    90  
    91  	return &auth.GetTokenAuthorityResponse{PublicKey: key[32:]}, nil
    92  }
    93  
    94  func (ap *authenticator) VerifyTokenAuthority(ctx context.Context, req *auth.VerifyTokenAuthorityRequest) (*auth.VerifyTokenAuthorityResponse, error) {
    95  	key := ap.getAuthorityKey(req.Host, req.Salt)
    96  
    97  	priv := new([64]byte)
    98  	copy((*priv)[:], key)
    99  
   100  	return &auth.VerifyTokenAuthorityResponse{Signed: sign.Sign(nil, req.Payload, priv)}, nil
   101  }
   102  
   103  var _ Authenticator = &authenticator{}
   104  
   105  type Authenticator interface {
   106  	auth.AuthServer
   107  	session.Attachable
   108  }
   109  
   110  func toTokenResponse(token string, issuedAt time.Time, expires int) *auth.FetchTokenResponse {
   111  	resp := &auth.FetchTokenResponse{
   112  		Token:     token,
   113  		ExpiresIn: int64(expires),
   114  	}
   115  	if !issuedAt.IsZero() {
   116  		resp.IssuedAt = issuedAt.Unix()
   117  	}
   118  	return resp
   119  }
   120  
   121  func (ap *authenticator) getAuthorityKey(host string, salt []byte) ed25519.PrivateKey {
   122  	mac := hmac.New(sha256.New, salt)
   123  	sum := mac.Sum(nil)
   124  
   125  	return ed25519.NewKeyFromSeed(sum[:ed25519.SeedSize])
   126  }