Add subresource imagestreams/layers for allowing push/pull access to the
registry.
Update registry authentication code to use imagestreams/layers.
Remove use of user's token for registry<->OpenShift interactions except
for when auto-provisioning an image stream. Otherwise, use the
registry's credentials when interacting with OpenShift. The user's
access is checked via a SubjectAccessReview check during authorization
of the request.
| ... | ... |
@@ -135,7 +135,7 @@ func GetBootstrapClusterRoles() []authorizationapi.ClusterRole {
|
| 135 | 135 |
Rules: []authorizationapi.PolicyRule{
|
| 136 | 136 |
{
|
| 137 | 137 |
Verbs: util.NewStringSet("get"),
|
| 138 |
- Resources: util.NewStringSet("imagestreams"),
|
|
| 138 |
+ Resources: util.NewStringSet("imagestreams/layers"),
|
|
| 139 | 139 |
}, |
| 140 | 140 |
}, |
| 141 | 141 |
}, |
| ... | ... |
@@ -146,7 +146,7 @@ func GetBootstrapClusterRoles() []authorizationapi.ClusterRole {
|
| 146 | 146 |
Rules: []authorizationapi.PolicyRule{
|
| 147 | 147 |
{
|
| 148 | 148 |
Verbs: util.NewStringSet("get", "update"),
|
| 149 |
- Resources: util.NewStringSet("imagestreams"),
|
|
| 149 |
+ Resources: util.NewStringSet("imagestreams/layers"),
|
|
| 150 | 150 |
}, |
| 151 | 151 |
}, |
| 152 | 152 |
}, |
| ... | ... |
@@ -221,13 +221,12 @@ func GetBootstrapClusterRoles() []authorizationapi.ClusterRole {
|
| 221 | 221 |
Resources: util.NewStringSet("imagestreamimages", "imagestreamtags", "imagestreams"),
|
| 222 | 222 |
}, |
| 223 | 223 |
{
|
| 224 |
- // TODO: remove "create" once we re-enable user authentication in the registry |
|
| 225 |
- Verbs: util.NewStringSet("create", "update"),
|
|
| 224 |
+ Verbs: util.NewStringSet("update"),
|
|
| 226 | 225 |
Resources: util.NewStringSet("imagestreams"),
|
| 227 | 226 |
}, |
| 228 | 227 |
{
|
| 229 | 228 |
Verbs: util.NewStringSet("create"),
|
| 230 |
- Resources: util.NewStringSet("imagerepositorymappings", "imagestreammappings"),
|
|
| 229 |
+ Resources: util.NewStringSet("imagestreammappings"),
|
|
| 231 | 230 |
}, |
| 232 | 231 |
}, |
| 233 | 232 |
}, |
| ... | ... |
@@ -90,6 +90,7 @@ func (ac *AccessController) Authorized(ctx context.Context, accessRecords ...reg |
| 90 | 90 |
return nil, err |
| 91 | 91 |
} |
| 92 | 92 |
|
| 93 |
+ // TODO try to find a better way to handle this |
|
| 93 | 94 |
if req.URL.Path == "/healthz" {
|
| 94 | 95 |
return ctx, nil |
| 95 | 96 |
} |
| ... | ... |
@@ -109,6 +110,7 @@ func (ac *AccessController) Authorized(ctx context.Context, accessRecords ...reg |
| 109 | 109 |
challenge.err = ErrTokenInvalid |
| 110 | 110 |
return nil, challenge |
| 111 | 111 |
} |
| 112 |
+ |
|
| 112 | 113 |
osAuthParts := strings.SplitN(string(payload), ":", 2) |
| 113 | 114 |
if len(osAuthParts) != 2 {
|
| 114 | 115 |
challenge.err = ErrOpenShiftTokenRequired |
| ... | ... |
@@ -131,7 +133,7 @@ func (ac *AccessController) Authorized(ctx context.Context, accessRecords ...reg |
| 131 | 131 |
} |
| 132 | 132 |
|
| 133 | 133 |
for _, access := range accessRecords {
|
| 134 |
- log.Debugf("%s:%s:%s", access.Resource.Type, access.Resource.Name, access.Action)
|
|
| 134 |
+ log.Debugf("OpenShift auth: checking for access to %s:%s:%s", access.Resource.Type, access.Resource.Name, access.Action)
|
|
| 135 | 135 |
|
| 136 | 136 |
switch access.Resource.Type {
|
| 137 | 137 |
case "repository": |
| ... | ... |
@@ -166,7 +168,7 @@ func (ac *AccessController) Authorized(ctx context.Context, accessRecords ...reg |
| 166 | 166 |
return nil, challenge |
| 167 | 167 |
} |
| 168 | 168 |
|
| 169 |
- return WithUserClient(ctx, client), nil |
|
| 169 |
+ return ctx, nil |
|
| 170 | 170 |
default: |
| 171 | 171 |
challenge.err = fmt.Errorf("Unknown action: %s", access.Action)
|
| 172 | 172 |
return nil, challenge |
| ... | ... |
@@ -188,7 +190,7 @@ func verifyOpenShiftUser(client *client.Client) error {
|
| 188 | 188 |
func verifyImageStreamAccess(namespace, imageRepo, verb string, client *client.Client) error {
|
| 189 | 189 |
sar := authorizationapi.SubjectAccessReview{
|
| 190 | 190 |
Verb: verb, |
| 191 |
- Resource: "imageStreams", |
|
| 191 |
+ Resource: "imagestreams/layers", |
|
| 192 | 192 |
ResourceName: imageRepo, |
| 193 | 193 |
} |
| 194 | 194 |
response, err := client.SubjectAccessReviews(namespace).Create(&sar) |
| ... | ... |
@@ -100,13 +100,14 @@ func (r *repository) ExistsByTag(ctx context.Context, tag string) (bool, error) |
| 100 | 100 |
|
| 101 | 101 |
// Get retrieves the manifest with digest `dgst`. |
| 102 | 102 |
func (r *repository) Get(ctx context.Context, dgst digest.Digest) (*manifest.SignedManifest, error) {
|
| 103 |
- _, err := r.getImageStreamImage(ctx, dgst) |
|
| 104 |
- if err != nil {
|
|
| 103 |
+ if _, err := r.getImageStreamImage(ctx, dgst); err != nil {
|
|
| 104 |
+ log.Errorf("Error retrieving ImageStreamImage %s/%s@%s: %v", r.namespace, r.name, dgst.String(), err)
|
|
| 105 | 105 |
return nil, err |
| 106 | 106 |
} |
| 107 |
- // TODO: we already fetched it above |
|
| 107 |
+ |
|
| 108 | 108 |
image, err := r.getImage(dgst) |
| 109 | 109 |
if err != nil {
|
| 110 |
+ log.Errorf("Error retrieving image %s: %v", dgst.String(), err)
|
|
| 110 | 111 |
return nil, err |
| 111 | 112 |
} |
| 112 | 113 |
|
| ... | ... |
@@ -233,16 +234,10 @@ func (r *repository) Delete(ctx context.Context, dgst digest.Digest) error {
|
| 233 | 233 |
|
| 234 | 234 |
// getImageStream retrieves the ImageStream for r. |
| 235 | 235 |
func (r *repository) getImageStream(ctx context.Context) (*imageapi.ImageStream, error) {
|
| 236 |
- client, ok := UserClientFrom(ctx) |
|
| 237 |
- if !ok {
|
|
| 238 |
- return nil, fmt.Errorf("error retrieving ImageStream: OpenShift user client unavailable")
|
|
| 239 |
- } |
|
| 240 |
- return client.ImageStreams(r.namespace).Get(r.name) |
|
| 236 |
+ return r.registryClient.ImageStreams(r.namespace).Get(r.name) |
|
| 241 | 237 |
} |
| 242 | 238 |
|
| 243 |
-// getImage retrieves the Image with digest `dgst`. This uses the registry's |
|
| 244 |
-// credentials and should ONLY be called after verifying the user has access |
|
| 245 |
-// to the image stream the imgae belongs to. |
|
| 239 |
+// getImage retrieves the Image with digest `dgst`. |
|
| 246 | 240 |
func (r *repository) getImage(dgst digest.Digest) (*imageapi.Image, error) {
|
| 247 | 241 |
return r.registryClient.Images().Get(dgst.String()) |
| 248 | 242 |
} |
| ... | ... |
@@ -250,21 +245,13 @@ func (r *repository) getImage(dgst digest.Digest) (*imageapi.Image, error) {
|
| 250 | 250 |
// getImageStreamTag retrieves the Image with tag `tag` for the ImageStream |
| 251 | 251 |
// associated with r. |
| 252 | 252 |
func (r *repository) getImageStreamTag(ctx context.Context, tag string) (*imageapi.ImageStreamTag, error) {
|
| 253 |
- client, ok := UserClientFrom(ctx) |
|
| 254 |
- if !ok {
|
|
| 255 |
- return nil, fmt.Errorf("error retrieving ImageStreamTag: OpenShift user client unavailable")
|
|
| 256 |
- } |
|
| 257 |
- return client.ImageStreamTags(r.namespace).Get(r.name, tag) |
|
| 253 |
+ return r.registryClient.ImageStreamTags(r.namespace).Get(r.name, tag) |
|
| 258 | 254 |
} |
| 259 | 255 |
|
| 260 | 256 |
// getImageStreamImage retrieves the Image with digest `dgst` for the ImageStream |
| 261 |
-// associated with r. This ensures the user has access to the image. |
|
| 257 |
+// associated with r. This ensures the image belongs to the image stream. |
|
| 262 | 258 |
func (r *repository) getImageStreamImage(ctx context.Context, dgst digest.Digest) (*imageapi.ImageStreamImage, error) {
|
| 263 |
- client, ok := UserClientFrom(ctx) |
|
| 264 |
- if !ok {
|
|
| 265 |
- return nil, fmt.Errorf("error retrieving ImageStreamImage: OpenShift user client unavailable")
|
|
| 266 |
- } |
|
| 267 |
- return client.ImageStreamImages(r.namespace).Get(r.name, dgst.String()) |
|
| 259 |
+ return r.registryClient.ImageStreamImages(r.namespace).Get(r.name, dgst.String()) |
|
| 268 | 260 |
} |
| 269 | 261 |
|
| 270 | 262 |
// manifestFromImage converts an Image to a SignedManifest. |