Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: introduce credentials.NewMemoryStoreFromDockerConfig #850

Merged
merged 1 commit into from
Jan 15, 2025

Conversation

programmer04
Copy link
Contributor

@programmer04 programmer04 commented Nov 27, 2024

What this PR does / why we need it:

Currently, struct Config that represents a docker configuration file can be populated only with the function

Load(configPath string) (*Config, error)

that expects to have a file available on the disk and read it. Sometimes, this is a redundant step and a security risk when file content is available in memory. Here is a real-world example

Hence, to overcome the aforementioned problems, this PR introduces a new constructor - NewMemoryStoreFromDockerConfig that can populate memoryStore from standard config in the form of []byte.

The proposal from @Wwwsylvia and @shizhMSFT described in the #850 (comment)

@Wwwsylvia
Copy link
Member

Hi @programmer04, thank you for the PR!

From the original issue Kong/gateway-operator#521, it looks like your scenario involves reading plaintext credentials from regcred without needing to update or delete them. Is that correct?

If so, rather than modifying the original credentials file store implementation, we could introduce something like ReadOnlyFileStore, which allows creating a store from a reader and does not support write operations on the credentials.

To get unblocked, you can consider implementing the ReadOnlyFileStore type in your code base. Since the plan is to support read operations only on the auth entry, the implementation would be much more straightforward than the current implementation in oras-go (no need to worry about concurrent writes, accidental overwrites, etc.).

If we feel like this functionality is necessary to be in oras-go, we can open an issue to track the feature request. What do you think?

@programmer04
Copy link
Contributor Author

Hey @Wwwsylvia! Thanks for the response

Yes, you're right. The problem I am trying to resolve is populating an oras credential store robustly directly from in-memory (io.Reader) a plain text representation of standard JSON Docker credentials. It can be read-only, it's sufficient, but it should be as good as FileStore in handling all of the cases, see the below.

Here in FileStore operation Get is implemented like that

// Get retrieves credentials from the store for the given server address.
func (fs *FileStore) Get(_ context.Context, serverAddress string) (auth.Credential, error) {
return fs.config.GetCredential(serverAddress)
}

where
// GetAuthConfig returns an auth.Credential for serverAddress.
func (cfg *Config) GetCredential(serverAddress string) (auth.Credential, error) {
cfg.rwLock.RLock()
defer cfg.rwLock.RUnlock()
authCfgBytes, ok := cfg.authsCache[serverAddress]
if !ok {
// NOTE: the auth key for the server address may have been stored with
// a http/https prefix in legacy config files, e.g. "registry.example.com"
// can be stored as "https://registry.example.com/".
var matched bool
for addr, auth := range cfg.authsCache {
if toHostname(addr) == serverAddress {
matched = true
authCfgBytes = auth
break
}
}
if !matched {
return auth.EmptyCredential, nil
}
}
var authCfg AuthConfig
if err := json.Unmarshal(authCfgBytes, &authCfg); err != nil {
return auth.EmptyCredential, fmt.Errorf("failed to unmarshal auth field: %w: %v", ErrInvalidConfigFormat, err)
}
return authCfg.Credential()
}

contains logic to make it work for all cases.

There is memoryStore, but I don't see a way how easily implement generating such a store from standard JSON Docker credentials than handles all of edge cases, etc. like FileStore with its underlying Config struct that is coupled with actual file by a path field.

The problem is that there is a path field in Config (this structure maps JSON Docker creds). It's hard to break that coupling, this is why I proposed a workaround in PR to avoid breaking anything. Maybe you have an idea of how to tackle it differently?

@Wwwsylvia
Copy link
Member

Wwwsylvia commented Dec 18, 2024

The problem is that there is a path field in Config (this structure maps JSON Docker creds). It's hard to break that coupling, this is why I proposed a workaround in PR to avoid breaking anything. Maybe you have an idea of how to tackle it differently?

The file store and the internal Config struct was designed for reading/writing Docker config files. If read-only is sufficient, we can implement a new store like ReadOnlyFileStore that loads the config from a reader. To support such store, we also need to implement a new Config like ReadOnlyConfig. This way we won't add complexity to the existing implementation and can gain better performance in the read-only scenario.

It would look somewhat like:

type ReadOnlyConfig struct {
	Auths map[string]AuthConfig
}

func LoadFromReader(reader io.Reader) (*ReadOnlyConfig, error) {
	// TODO: read from the reader and decode the content into Auths
	return &ReadOnlyConfig{}, nil
}

func (c *ReadOnlyConfig) GetCredential(serverAddress string) (auth.Credential, error) {
	// TODO: get the credential from Auths with best effort
	return auth.Credential{}, nil
}
type ReadOnlyFileStore struct {
	*config.ReadOnlyConfig
}

func NewReadOnlyFileStoreFromReader(reader io.Reader) (*ReadOnlyFileStore, error) {
	cfg, err := config.LoadFromReader(reader)
	if err != nil {
		return nil, err
	}
	return &ReadOnlyFileStore{ReadOnlyConfig: cfg}, nil
}

func (fs *ReadOnlyFileStore) Get(serverAddress string) (auth.Credential, error) {
	return fs.ReadOnlyConfig.GetCredential(serverAddress)
}

func (fs *ReadOnlyFileStore) Put(serverAddress string, cred auth.Credential) error {
	return errors.New("cannot put credentials in read-only file store")
}

func (fs *ReadOnlyFileStore) Delete(serverAddress string) error {
	return errors.New("cannot delete credentials in read-only file store")
}

@programmer04 programmer04 changed the title feat: allow loading config from io.Reader feat: introduce ReadOnlyFileStore Dec 18, 2024
@programmer04 programmer04 force-pushed the cfg-from-reader branch 4 times, most recently from 5ceabd4 to 30e4961 Compare December 18, 2024 15:01
@programmer04
Copy link
Contributor Author

programmer04 commented Dec 18, 2024

Thanks for the detailed guidance @Wwwsylvia I've updated PR (code, title, description) according to the solution proposed by you in #850 (comment) I even tested it with my code Kong/gateway-operator#940. Please take a look

registry/remote/credentials/internal/config/config.go Outdated Show resolved Hide resolved
registry/remote/credentials/file_store.go Outdated Show resolved Hide resolved
registry/remote/credentials/file_store.go Outdated Show resolved Hide resolved
@programmer04 programmer04 force-pushed the cfg-from-reader branch 2 times, most recently from de2c43a to 0ad13e6 Compare December 20, 2024 12:31
@programmer04 programmer04 force-pushed the cfg-from-reader branch 2 times, most recently from b70d35b to 83d5c64 Compare January 9, 2025 10:51
Copy link

codecov bot commented Jan 9, 2025

Codecov Report

Attention: Patch coverage is 84.21053% with 3 lines in your changes missing coverage. Please review.

Project coverage is 76.05%. Comparing base (1a9e250) to head (0469ba1).
Report is 1 commits behind head on main.

Files with missing lines Patch % Lines
registry/remote/credentials/memory_store.go 82.35% 2 Missing and 1 partial ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #850      +/-   ##
==========================================
+ Coverage   75.98%   76.05%   +0.06%     
==========================================
  Files          63       63              
  Lines        6025     6042      +17     
==========================================
+ Hits         4578     4595      +17     
  Misses       1066     1066              
  Partials      381      381              

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

registry/remote/credentials/internal/config/helpers.go Outdated Show resolved Hide resolved
registry/remote/credentials/internal/config/helpers.go Outdated Show resolved Hide resolved
registry/remote/credentials/file_store_test.go Outdated Show resolved Hide resolved
registry/remote/credentials/readonly_file_store.go Outdated Show resolved Hide resolved
@programmer04 programmer04 force-pushed the cfg-from-reader branch 4 times, most recently from e7582ea to f8e6d98 Compare January 9, 2025 17:10
@programmer04 programmer04 requested a review from Wwwsylvia January 9, 2025 17:29
Copy link
Member

@Wwwsylvia Wwwsylvia left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@Wwwsylvia Wwwsylvia changed the title feat: introduce ReadOnlyFileStore feat: introduce credentials.ReadOnlyFileStore Jan 13, 2025
registry/remote/credentials/registry.go Outdated Show resolved Hide resolved
registry/remote/credentials/readonly_file_store.go Outdated Show resolved Hide resolved
@programmer04 programmer04 changed the title feat: introduce credentials.ReadOnlyFileStore feat: introduce credentials.NewMemoryStoreFromConfig Jan 13, 2025
registry/remote/credentials/memory_store.go Outdated Show resolved Hide resolved
registry/remote/credentials/memory_store.go Outdated Show resolved Hide resolved
registry/remote/credentials/memory_store.go Outdated Show resolved Hide resolved
Copy link
Member

@Wwwsylvia Wwwsylvia left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@programmer04
Copy link
Contributor Author

Thanks, @Wwwsylvia for the great collaboration and review. It needs approval from @shizhMSFT too to merge it since he requested changes. And we're done

@shizhMSFT shizhMSFT changed the title feat: introduce credentials.NewMemoryStoreFromConfig feat: introduce credentials.NewMemoryStoreFromDockerConfig Jan 15, 2025
Copy link
Contributor

@shizhMSFT shizhMSFT left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@programmer04
Copy link
Contributor Author

Thanks @shizhMSFT for the great collaboration and review. It seems I can't merge it, someone with write permissions has to approve CI run for it and click the merge button

Copy link
Member

@Wwwsylvia Wwwsylvia left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@Wwwsylvia Wwwsylvia merged commit a15da41 into oras-project:main Jan 15, 2025
9 checks passed
@programmer04 programmer04 deleted the cfg-from-reader branch January 15, 2025 10:35
@Wwwsylvia
Copy link
Member

@programmer04 Merged. 🎉Thanks a lot for your efforts and the contribution!!

@programmer04
Copy link
Contributor Author

Thanks @Wwwsylvia! The last question is when do you release a new version? The last one is almost a year old, and looking at the main branch, it shows that many nice improvements have been made since then. In my opinion, it's high time to make a new release. WDYT?

@Wwwsylvia
Copy link
Member

@programmer04 I agree, I think we are pretty close to a new release. We will discuss it and hopefully we can make a release next week.

@Wwwsylvia
Copy link
Member

Hi @programmer04 , just a heads up - We still have to finish all documentation work in the v2.6.0 milestone. We have set the target date of the release to the end of February.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants