-
Notifications
You must be signed in to change notification settings - Fork 4.7k
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
Allow to restrict imports to white listed registries #13313
Conversation
[test] |
6123bb5
to
1521270
Compare
pkg/cmd/server/api/v1/conversions.go
Outdated
@@ -99,6 +99,16 @@ func addDefaultingFuncs(scheme *runtime.Scheme) error { | |||
// The final value of OAuthConfig.MasterCA should never be nil | |||
obj.OAuthConfig.MasterCA = &s | |||
} | |||
|
|||
if len(obj.ImagePolicyConfig.AllowedRegistriesForImport) == 0 { | |||
obj.ImagePolicyConfig.AllowedRegistriesForImport = []string{ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@smarterclayton need to double check on the registry hostnames here...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We can't default this on - that breaks existing clusters.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You need to do this when we create a config, not in API defaulting.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fixed. also I moved the default whitelist to types.go (instead of burrying it in master_args)
pkg/cmd/server/api/types.go
Outdated
// images from. By default docker.io, Red Hat registry, gcr.io, Amazon ECS, quay.io are | ||
// allowed. If you want to import images from your private Docker Registry, you have to | ||
// add it into the list. | ||
AllowedRegistriesForImport []string |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Make this a sub struct with a single field.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Specify domain name.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@smarterclayton i this we also need wildcard (*) for registries like ECS. Does it make sense?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, supporting wildcard is fine. Be sure to document the wildcard syntax. A bit concerned that we shouldn't support crazy wildcards (filepath matching is crazy).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@smarterclayton i don't want to re-invent wildcard matching, so if you have better suggestion from the standard library I'm all for it :-) The filepath matching seems reasonable to me... I guess most users/admins will just use the '*' and '??' wildcards anyway.
pkg/cmd/server/api/v1/types.go
Outdated
// AllowedRegistriesForImport is a white list of registries that we allow to import the | ||
// images from. By default docker.io, Red Hat registry, gcr.io, Amazon ECS, quay.io are | ||
// allowed. If you want to import images from your private Docker Registry, you have to | ||
// add it into the list. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This needs to only impose this behavior when this is set. If empty, old behavior applies.
pkg/image/api/helper.go
Outdated
// RegistryHostname returns the registry hostname without port | ||
func (r DockerImageReference) RegistryHostname() string { | ||
registryHost := r.AsV2().Registry | ||
if strings.Contains(registryHost, ":") { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ports matter - gcr.io on port 443 is not the same as gcr.io on port 935
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i assume all registries we whitelist by default run on 443?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
so I'd say if port is not specified, it can be 443 or 80 (secure or insecure). If it is specified, it must match. I.e. docker.io matches docker.io:443, but docker.io does not match docker.io:8443
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@smarterclayton maybe we can add that to the config struct (insecure: true)
pkg/image/importer/importer.go
Outdated
@@ -129,6 +132,11 @@ func (i *ImageStreamImporter) importImages(ctx gocontext.Context, retriever Repo | |||
repoName := defaultRef.RepositoryName() | |||
registryURL := defaultRef.RegistryURL() | |||
|
|||
if !importer.isAllowedRegistry(defaultRef.RegistryHostname()) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If empty, you have to bypass this. Can't break old clusters.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
right, I will make it optional.
pkg/image/importer/importer.go
Outdated
@@ -129,6 +132,11 @@ func (i *ImageStreamImporter) importImages(ctx gocontext.Context, retriever Repo | |||
repoName := defaultRef.RepositoryName() | |||
registryURL := defaultRef.RegistryURL() | |||
|
|||
if !importer.isAllowedRegistry(defaultRef.RegistryHostname()) { | |||
isi.Status.Images[i].Status = invalidStatus("", field.Invalid(field.NewPath("from", "name"), from.Name, fmt.Sprintf("registry %q not allowed in master configuration", defaultRef.RegistryHostname()))) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This message needs to be really good - this is going to break people. You're going to have to make sure the CLI and UI do a good job of presenting this error.
Images may not be imported from %q - only images from %s are allowed
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would be good to demonstrate how this would be shown in oc import-image, oc describe is, web console, etc
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
with the current implementation, it will get surfaced in the events inside image stream... when I rework this in validation, I think it you will get the error when you run oc import-image
, so the IS won't be created at all.
Web console will get validation error as well, so I think that will be presented to users in nice form.
pkg/image/importer/importer.go
Outdated
// the master configuration. | ||
func (i *ImageStreamImporter) isAllowedRegistry(registry string) bool { | ||
for _, allowedRegistry := range i.allowedRegistries { | ||
if matched, err := filepath.Match(allowedRegistry, registry); matched && err == nil { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't like filepath because it is OS specific.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yeah, I will probably craft something custom and make it part of util/strings.go
pkg/image/importer/importer.go
Outdated
@@ -129,6 +132,11 @@ func (i *ImageStreamImporter) importImages(ctx gocontext.Context, retriever Repo | |||
repoName := defaultRef.RepositoryName() | |||
registryURL := defaultRef.RegistryURL() | |||
|
|||
if !importer.isAllowedRegistry(defaultRef.RegistryHostname()) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is this here? Put this in ImageStreamImport validation, pass the allowed registries to the strategy, not the importer.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
you're right... I thought we want to do this in importer, but this really is a validation... i will pass the list of allowed registries to import validator and verify it there.
8c2c69d
to
6fe3cbe
Compare
@smarterclayton all updated, PTAL pls. |
pkg/cmd/server/api/types.go
Outdated
DomainName string | ||
// Insecure indicates whether the registry is secury (https) or insecure (http) | ||
// By default (if not specified) the registry is assumed as secure. | ||
Insecure bool |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What about different port, how one will specify that?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If it's part of domain name (I assume from validation code) can you state that in the doc text for DomainName
, it isn't obvious.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yes, good idea
pkg/image/api/helper.go
Outdated
@@ -168,6 +169,17 @@ func (r DockerImageReference) RepositoryName() string { | |||
return r.Exact() | |||
} | |||
|
|||
// RegistryHostname returns the registry hostname without port | |||
func (r DockerImageReference) RegistryHostPort() (string, string) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shouldn't this method accept bool Insecure
? And return 80
if it's set to true
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the godoc is wrong, but yeah
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
actually I already have that fixed :)
case 2: | ||
allowedRegistryHost, allowedRegistryPort = parts[0], parts[1] | ||
default: | ||
// TODO: Should we panic/glog here? The format of allowed registries should be |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
glog for sure
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
glog not needed, so I will remove it from here (see Clayton comment)
pkg/cmd/server/api/types.go
Outdated
// name. The domain name might include wildcards, like '*' or '??'. | ||
type RegistryLocation struct { | ||
// DomainName specifies a domain name for the registry | ||
DomainName string |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Additionally, since we allow wildcard matches here, that should be mentioned in the doc as well.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yeah, I mention it in the comment for the registry location to make it more visible, but I can move it down.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Now what about I realized it's not an issue because oc tag
? 😄oc tag
modifies the spec of ImageStream and leaves the actually image creation on importer controller.
Nevertheless I'd like to see at least an integration test proving that the oc tag
case is handled.
default: | ||
// TODO: Should we panic/glog here? The format of allowed registries should be | ||
// validated during the server startup. | ||
return errs |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
At least log an error an continue.
pkg/image/api/helper.go
Outdated
@@ -168,6 +169,17 @@ func (r DockerImageReference) RepositoryName() string { | |||
return r.Exact() | |||
} | |||
|
|||
// RegistryHostname returns the registry hostname without port |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
godoc
pkg/image/api/helper.go
Outdated
return hostname, port | ||
} | ||
// The default registry port is https/443 | ||
return registryHost, "443" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The 443
may be wrong. Empty port should be returned if not specified.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@miminar empty port mean 80 right? that might be incorrect.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we default to https in RegistryURL() anyway, so I think this is fine.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(I added a bool option to make @soltysh happy :-)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
empty port mean 80 right? that might be incorrect.
Empty port means not specified in this case.
we default to https in RegistryURL() anyway, so I think this is fine.
ah, that's unfortunate...
isi: &api.ImageStreamImport{ObjectMeta: validMeta, Spec: repoFn("unknown.registry:5000/foo/bar")}, | ||
expected: field.ErrorList{}, | ||
allowedRegistries: &configapi.AllowedRegistries{}, | ||
}, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you add one more test case with AllowedRegistries
not empty and matching the registry?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
sure, but that case is covered when allowedRegistry is set to defaultallowedregistry and matched docker.io
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
added extra test case.
// By default all registries have set to be "secure", iow. the port for them is | ||
// defaulted to "443". | ||
// If the registry you are adding here is insecure, you can add 'Insecure: true' which | ||
// in that case it will default to port '80'. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
s/which in that case it will default to port '80'/to make it default to "80"/
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fixed. (moved to types anyway)
pkg/cmd/server/api/types.go
Outdated
type RegistryLocation struct { | ||
// DomainName specifies a domain name for the registry | ||
DomainName string | ||
// Insecure indicates whether the registry is secury (https) or insecure (http) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
typo
c6010e7
to
e5b7594
Compare
I will strongly argue against fixing the error part as part of this PR (the 2nd commit). I'd prefer having a proper solution to error handling inside the entire ImageStream. Currently we only have nice error handling presented per tag, but none when it comes to failing when |
@soltysh my fix is addressing the repository import as well.... in that case the repository is used as "tag", which sux and can be improved. |
d5257ed
to
c6fd96b
Compare
pkg/image/controller/controller.go
Outdated
@@ -163,7 +169,50 @@ func (c *ImportController) Next(stream *api.ImageStream, notifier Notifier) erro | |||
if apierrs.IsNotFound(err) && client.IsStatusErrorKind(err, "imageStream") { | |||
return ErrNotImportable | |||
} | |||
glog.V(4).Infof("Import stream %s/%s partial=%t error: %v", stream.Namespace, stream.Name, partial, err) | |||
glog.V(4).Infof("Import stream %s/%s partial=%t error: %#+v", stream.Namespace, stream.Name, partial, err) | |||
if apierrs.IsInvalid(err) && client.IsStatusErrorKind(err, "ImageStreamImport") { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@smarterclayton this uglyness is needed to properly report the import errors to users who:
- Create ImageStream that contains multiple tags (typically examples/imagestreams...)
- The import fails because the registry used in ImageStream is invalid (for tag or for repository)
- The error is then logged into glog.V(4) and burried in server log
- User end up with ImageStream that tells you the tags are being imported... forever.
(in addition, if this happen, all other tags in that IS are not imported even though they are valid... but one problem at the time)...
And since the StatusInvalid will not tell us what tag failed specifically you have to guess it from the error message (which sux big time, but I can't figure out better solution).
I would said that this code is just a band-aid for having the import error reported properly (since white-listing registries needs to do this), but we should fix the error reporting in general from the image streams in 1.6.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, please open a new issue and I will start working on it right after this merges.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Spawned #13319
c6fd96b
to
13b12ab
Compare
glog.V(5).Infof("invalid registry domain format: %s", registry.DomainName) | ||
continue | ||
} | ||
if util.IsWildcardMatch(registryHost, allowedRegistryHost) && registryPort == allowedRegistryPort { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So if the whitelist has a benevolent entry with insecure==true
and someone wants import a from the repo securely, he won't be allowed to.
Another case this fails is an import from a registry hosted on :443
with invalid certificate - user is forced to use the insecure flag to no avail because the ports won't match here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@miminar in that case you will have to add both combinations (secure and insecure) to the whitelist
[test] |
[test] |
b5f7288
to
f6da4cf
Compare
[test] one more time |
b9b9f3b
to
107e83d
Compare
@smarterclayton @stevekuznetsov i still can't figure out what is wrong on GCE here... it seems like the image tags failed to import (presumably because something prevented them to). I wonder if we have some special master-config for GCE or we use the one that the Origin generates (iow. do we have whitelist or not). Also I know that I'm annoying with this but it would really help to have master logs from GCE :-/ |
[test] |
This LGTM (since I wasn't explicit last time ;)) |
You can run a deployment yourself pretty easily, or ssh in while it is
running.
The master config is generated by the Ansible install.
On Apr 5, 2017, at 1:25 PM, Michal Fojtik <[email protected]> wrote:
@smarterclayton <https://github.com/smarterclayton> @stevekuznetsov
<https://github.com/stevekuznetsov> i still can't figure out what is wrong
on GCE here... it seems like the image tags failed to import (presumably
because something prevented them to). I wonder if we have some special
master-config for GCE or we use the one that the Origin generates (iow. do
we have whitelist or not).
Also I know that I'm annoying with this but it would *really* help to have
master logs from GCE :-/
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<#13313 (comment)>,
or mute
the thread
<https://github.com/notifications/unsubscribe-auth/ABG_p-iJd4nw36eU2NRwl40b7Sr5YaDEks5rs3oegaJpZM4MXTJq>
.
|
gce docker flake [test] |
[test] |
107e83d
to
c058b4d
Compare
@smarterclayton got it... it was actually my fault (missing nil-check)... it reflected on GCE because the master configuration we generate there for some reason does not include the whitelist (which means all images are allowed)... |
[test] (should pass this time) |
IIRC, the configuration we have there is coming from our ansible script, doesn't that needs to be updated to reflect your changes in that case? |
Evaluated for origin test up to c058b4d |
@soltysh I kind of like that it tests the "legacy" case where it is not set and the origin in that case should not enforce any whitelist and the legacy behavior is preserved... in fact it helped to discover this bug. |
@mfojtik completely agree, but that causes discrepancy between what we get when running installer manually vs what ansible does for us. Although I wonder which part of the ansible would be causing that. I though that |
continuous-integration/openshift-jenkins/test SUCCESS (https://ci.openshift.redhat.com/jenkins/job/test_pull_request_origin/601/) (Base Commit: b7a5b0f) |
[merge] |
continuous-integration/openshift-jenkins/merge SUCCESS (https://ci.openshift.redhat.com/jenkins/job/test_pull_request_origin/601/) (Image: devenv-rhel7_6119) |
Evaluated for origin merge up to c058b4d |
Ansible generates a config from a template, does not use write-config.
Only new clusters in Ansible should get these flags, old users need to opt
in (otherwise you could break working namespaces).
…On Thu, Apr 6, 2017 at 4:47 PM, OpenShift Bot ***@***.***> wrote:
Merged #13313 <#13313>.
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<#13313 (comment)>, or mute
the thread
<https://github.com/notifications/unsubscribe-auth/ABG_p6RBWorkUk6I01uHGvwwHhfQmsviks5rtPsCgaJpZM4MXTJq>
.
|
{DomainName: "gcr.io"}, | ||
{DomainName: "quay.io"}, | ||
// FIXME: Probably need to have more fine-tuned pattern defined | ||
{DomainName: "*.amazonaws.com"}, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should not have added this - this effectively invalidates everything we did to secure this. This means that anyone can go and set a registry up on amazonaws and defeat this protection.
This change will allow to restrict image importing to registries that are white listed in the master-config.yaml. By default (when not specified) the legacy behavior is retained and any registry is allowed for the image import.
However, for a new clusters when the master-config.yaml is created, we picked the known public registries and whitelisted them explicitely.
Q/A:
What impact will this have to existing users?
Unless you recreate the
master-config.yaml
, everything should work as it worked before.What impact will this have to new users?
When the master-config.yaml is written to disk, the list of registries we allow to import from will be created. In case you want to import Docker image from third-party registry, that registry needs to be added to the list (a server restart is required after that change).
The verification is done in image import validation, so:
In case of creating the ImageStream directly with tags, the importer will report errors if the untrusted
registry is used in the ImageStream status:
In case you already have images imported from third-party registries that are not whitelisted
the images will remain but the scheduled import won't work for them anymore (you will see the error above).