Code Monkey home page Code Monkey logo

oras-credentials-go's Introduction

Credential Management for oras-go

Build Status codecov Go Report Card Go Reference

banner

oras-credentials-go is a credential management library designed for oras-go. It supports reading, saving, and removing credentials from Docker configuration files and external credential stores that follow the Docker credential helper protocol.

Important

This repository has been archived and is no longer maintained.

The APIs previously located in this library have been moved to oras-go. As a result, these APIs are now deprecated and users should use the packages in oras-go instead.

Versioning

The oras-credentials-go library follows Semantic Versioning, where breaking changes are reserved for MAJOR releases, and MINOR and PATCH releases must be 100% backwards compatible.

Docs

Code of Conduct

This project has adopted the CNCF Code of Conduct.

oras-credentials-go's People

Contributors

dependabot[bot] avatar shizhmsft avatar terryhowe avatar uanid avatar wangxiaoxuan273 avatar wwwsylvia avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar

oras-credentials-go's Issues

Support logout

Design discussion: #18

Proposed API:

package credentials

func Logout(ctx context.Context, store Store, registryName string) error {
	panic("not implemented") // TODO: Implement
}

References:

Create examples

Examples are needed for:

  • NewNativeStore
  • NewFileStore
  • NewStore
  • NewStoreFromDocker
  • NewStoreWithFallbacks
  • Login
  • Logout
  • Credential

Support login

Design discussion: #18

Proposed API:

package credentials

func Login(ctx context.Context, store Store, registry remote.Registry, cred auth.Credential) error {
	panic("not implemented") // TODO: Implement
}

References:

`storeWithFallbacks` should leverage the native store of the fallback store for saving credentials

Currently, storeWithFallbacks only saves credentials in the primary store.

// Put saves credentials into the StoreWithFallbacks. It puts
// the credentials into the primary store.
func (sf *storeWithFallbacks) Put(ctx context.Context, serverAddress string, cred auth.Credential) error {
return sf.stores[0].Put(ctx, serverAddress, cred)
}

However, this pattern does not work well for the case where:

  • The primary store does not have native store available
  • The primary store disables putting credentials in plaintext
  • The fallback store has a native store available

In such case, ideally storeWithFallbacks should save the credentials in the native store of the fallback store.

Support `Credential()` method

The method should return a Credential() function that can be used by auth.Client of oras-go v2.

Proposed API:

package credentials

func Credential(store Store) func(context.Context, string) (auth.Credential, error) {
}

Design discussion: #18

Enforce branch policies on this repository

To improve the security of the ORAS project we need to enforce the branch policies for this repository. I propose that we enforce the policies as follows:

  • Use the following rules for main and release/* branches:
    • Require PR before merging
      • Require 3 approvals
      • Dismiss stale PR approvals when new commits are pushed
      • Require review from Code Owners
      • Require status checks to pass before merging
      • Require conversation resolution before merging
      • Require signed commits
      • Do not allow bypass the above settings

Please add your comments and proposals for additional changes to this issue.

Feature Suggestion: Add InMemory Store and k8s CredentialProviderConfig Store

Hello all,

The currently supported Stores are OS Native, File Store, and Docker Store.

image

While using these in server environments, there have been some aspects that could be improved upon. For better adaptability in server environments, I'd like to suggest adding the following two stores:

InMemory Store

Description: This store saves data directly in memory.
Advantage: Although its implementation might be relatively straightforward, having it provided directly by the orsa-credentials-go library would be highly convenient for users.

CredentialProviderConfig Store

Description: Instead of the Docker Credentials Helper, this store utilizes the Credentials Provider CLI used by Kubernetes.
Advantage: With this Store, users can seamlessly use the Config utilized in a K8S environment.

Additional Reference: Kubelet Credential Provider

I believe the inclusion of these two stores would enhance usability across various server setups. Please consider this proposal and I appreciate any feedback. Thank you.

Credential library support for legacy auth config keys

Regarding the initiative to create a general authentication library for registries (thanks to @shizhMSFT in #413), I encountered some legacy behavior in the Docker credential helper libraries and thought it should be documented to ensure ORAS supports it in this new library.

Credential helpers may return keys either of the form, e.g., https://ghcr.io or ghcr.io, or the original Docker index server URL https://index.docker.io/v1/. See, Docker issue: docker/docker-credential-helpers#256

To handle this, libraries implementing auth and intending to choose an auth configuration must:

  1. Use a ToHostname routine to homogenize the input server name, removing the scheme and then path parts after the hostname. This function would take a name like https://ghcr.io/aaronfriel and return ghcr.io.

  2. First attempt a direct lookup in the map (authConfigs["ghcr.io"]), and early return on success. Otherwise, iterate over the map and perform the ToHostname conversion on each of the keys, returning the first key that equals the input ("ghcr.io" == ToHostname(key)).

Example: https://github.com/docker/cli/blob/v20.10.23/cli/config/credentials/file_store.go#L33-L47

The way to obtain Docker credentials is not compatible with Docker CLI

I upgraded oras from 0.16 to 1.1 and found an incompatible change.

I expect the old format credentials to work normally as well.

The analysis is as follows:

1. Logic in oras 0.16

In oras 0.16.0, we use docker cli get credentials.

https://github.com/oras-project/oras/blob/release-0.16/internal/credential/store.go#L61C1-L62

	return &Store{
		configs: configs,
	}, nil

authConf, err := c.GetCredentialsStore(registry).Get(registry)

https://github.com/docker/cli/blob/1401f9108517b33fe38fb3acd59557dcdf4d603d/cli/config/credentials/file_store.go#L39-L81

// ConvertToHostname converts a registry url which has http|https prepended
// to just an hostname.
// Copied from github.com/docker/docker/registry.ConvertToHostname to reduce dependencies.
func ConvertToHostname(url string) string {
	stripped := url
	if strings.HasPrefix(url, "http://") {
		stripped = strings.TrimPrefix(url, "http://")
	} else if strings.HasPrefix(url, "https://") {
		stripped = strings.TrimPrefix(url, "https://")
	}

	hostName, _, _ := strings.Cut(stripped, "/")
	return hostName
}

2. Logic in oras 1.1

In oras 1.1.0, we use oras-credentials-go get credentials.

https://github.com/oras-project/oras/blob/release-1.1/internal/credential/store.go#L31-L38

import (
	credentials "github.com/oras-project/oras-credentials-go"
)

// NewStore generates a store based on the passed-in config file paths.
func NewStore(configPaths ...string) (credentials.Store, error) {
	opts := credentials.StoreOptions{AllowPlaintextPut: true}
	if len(configPaths) == 0 {
		// use default docker config file path
		return credentials.NewStoreFromDocker(opts)
	}

	var stores []credentials.Store
	for _, config := range configPaths {
		store, err := credentials.NewStore(config, opts)
		if err != nil {
			return nil, err
		}
		stores = append(stores, store)
	}
	return credentials.NewStoreWithFallbacks(stores[0], stores[1:]...), nil
}

3. actual

If such content is in my credentials, incompatible changes will occur.

{
    "auths": {
        "https://xxx.dkr.ecr.us-west-2.amazonaws.com": {
            "username": "AWS",
            "password": "",
            "auth": "",
            "email": "[email protected]"
        }
    }
}

4. expected

I can only alter the credentials into the following format for the new oras to recognize.

{
    "auths": {
        "xxx.dkr.ecr.us-west-2.amazonaws.com": {
            "username": "AWS",
            "password": "",
            "auth": "",
            "email": "[email protected]"
        }
    }
}

Context Support for Native Store

The native store of oras-credentials-go currently depends on github.com/docker/docker-credential-helpers to interact with docker credential helper binaries and does not support context so that it cannot be cancelled when executing the helper binraries.

We should implement the native store to interact with the helper binaries directly according to the protocol with context support.

Contributors can leverage exec.CommandContext for context support for executing commands.

Note The dependency on golang.org/x/sys/execabs is not required since the security patch is enforced since go 1.19 (see doc).

See also: #18 (comment)

Rename go module

We should rename the go module from oras.land/oras-credentials-go to github.com/oras-project/oras-credentials-go.

Vote for archiving `oras-credentials-go`

With the release of v0.4.0, all existing APIs have been moved to oras-go and are now deprecated in oras-credentials-go.
The oras-credentials-go repository can now be archived to reduce maintenance costs.

To archive the oras-credentials-go repository, at least 2 approvals from from the 3 repository maintainers, or at least 3 approvals from the 4 org-level maintainers are required.

Repository maintainers:

Org-level maintainers:

Please respond LGTM or REJECT (with reasoning).

Refine error messages

The error message returned by Login() could be confusing

return fmt.Errorf("unable to ping the registry %s: %w", regClone.Reference.Registry, err)

Some users thought it's doing a network ping
"failed to validate the credentials" might be a better choice

Support creating store from a config file

Proposed API:

package credentials

// StoreOptions provides options for NewStore.
type StoreOptions struct {
	// AllowPlainText allows saving credentials in plain text in configuration file.
	AllowPlainText bool
}

// NewStore returns a new store from the settings in the configuration
// file.
func NewStore(configPath string, opts StoreOptions) Store {
	panic("not implemented") // TODO: Implement
}

Design discussion: #18

Better error prompt when Docker Desktop is installed

What is the version of your ORAS CLI

v0.16.0

What would you like to be added?

When Docker Desktop for Windows or macOS installed and is not currently running, running oras will encounter an error.

$ oras login $reg --username $name --password $secret
WARNING! Using --password via the CLI is insecure. Use --password-stdin.
Error: error storing credentials - err: exit status 1, out: `Post "[http://ipc/registry/credstore-updated":](http://ipc/registry/credstore-updated%22:) open \\.\pipe\dockerBackendApiServer: The system cannot find the file specified.`

It is better to have something like

$ oras login $reg --username $name --password $secret
WARNING! Using --password via the CLI is insecure. Use --password-stdin.
Error: error storing credentials: credential store is configured to `desktop` and docker desktop seems not running

It would be even better to give users an option to update the credential store. A sample output on Windows could be

$ oras login $reg --username $name --password $secret
WARNING! Using --password via the CLI is insecure. Use --password-stdin.
Error: error storing credentials: credential store is configured to `desktop` and docker desktop seems not running
Do you want to update the global credential store to `wincred`? [y/N]

Why is this needed for ORAS?

oras in general does not require docker or dockerd. oras reuses the docker config file and docker desktop sets the credential store to docker desktop when docker desktop is installed.

Here are the scenarios for the complete behavior.

  • Scenario 1: Running oras on Linux without docker installed
    • oras works and will save the credentials to ~/.docker/config.json on oras login.
  • Scenario 2: Running oras on Linux with docker installed
    • oras works and reuses the docker credentials in ~/.docker/config.json. If credential store is configured, oras also re-uses the credential store.
  • Scenario 3: Running oras on Windows without docker desktop installed
    • Same as Scenario 1
  • Scenario 4: Running oras on Windows with docker desktop installed
    • oras works but reuses the docker credentials in ~/.docker/config.json. By default, it requires docker desktop running to run oras.
    • Mitigation step: Edit ~/.docker/config.json and replace desktop with wincred for the credStore field.
  • Scenario 5: Running oras on macOS without docker desktop installed
    • Same as Scenario 1
  • Scenario 6: Running oras on macOS with docker desktop installed
    • Same as Scenario 4 but replace desktop with osxkeychain.

It would be better to let the users know what's the next steps, such as run Docker Desktop or update the config, when they encounter the weird pipe error.

Are you willing to submit PRs to contribute to this feature?

  • Yes, I am willing to implement it.

Update README

We should briefly introduce what this library does in the README. Maybe we can include the Goals and Non-goals as described in #18?

Expose the registry name mapping methods

As users may still need to do the mapping from docker.io to https://index.docker.io/v1/ in their code when using Store.Get(), Store.Put() and Store.Delete(), we can consider expose the existing mapping methods for their convenience.

We may export the following methods and may rename them if needed:

  • func mapStoreRegistryName(registry string) string {
    // The Docker CLI expects that the 'docker.io' credential
    // will be added under the key "https://index.docker.io/v1/"
    // See: https://github.com/moby/moby/blob/v24.0.0-beta.2/registry/config.go#L25-L48
    if registry == "docker.io" {
    return "https://index.docker.io/v1/"
    }
    return registry
    }
  • func mapAuthenticationRegistryName(hostname string) string {
    // It is expected that the traffic targetting "registry-1.docker.io"
    // will be redirected to "https://index.docker.io/v1/"
    // See: https://github.com/moby/moby/blob/v24.0.0-beta.2/registry/config.go#L25-L48
    if hostname == "registry-1.docker.io" {
    return "https://index.docker.io/v1/"
    }
    return hostname
    }

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.