Weldr API: allow globing in distro image type deny list

Allow globing patterns in distro-specific image type deny list of Weldr
API configuration. Extend unit tests to verify simple globing patterns.

Update NEWS entry.

Signed-off-by: Tomas Hozza <thozza@redhat.com>
This commit is contained in:
Tomas Hozza 2021-08-02 14:15:45 +02:00 committed by Ondřej Budai
parent a9b676e43e
commit 0a71054d86
3 changed files with 56 additions and 13 deletions

View file

@ -1,9 +1,9 @@
# Weldr API: introduce the ablility to limit exposed Image Types by configuration # Weldr API: introduce the ablility to limit exposed Image Types by configuration
Extend Weldr API to accept a map of distribution-specific lists of denied Extend Weldr API to accept a map of distribution-specific lists of denied
image types, which should not be exposed via API. A special name `*` can be image types, which should not be exposed via API. It is allowed to use
used to match any Distribution or any Image Type. This functionality is needed globing patterns as Distribution and Image Type names. This functionality
to not expose image types which can't be successfully built outside is needed to not expose image types which can't be successfully built outside
of Red Hat VPN. of Red Hat VPN.
The list of denied Image Types is defined in `osbuild-composer` configuration, The list of denied Image Types is defined in `osbuild-composer` configuration,

View file

@ -24,6 +24,7 @@ import (
"time" "time"
"github.com/BurntSushi/toml" "github.com/BurntSushi/toml"
"github.com/gobwas/glob"
"github.com/google/uuid" "github.com/google/uuid"
"github.com/julienschmidt/httprouter" "github.com/julienschmidt/httprouter"
@ -389,27 +390,40 @@ func (api *API) openImageFile(composeId uuid.UUID, compose store.Compose) (io.Re
// the distro-specific ImageType Denylist provided to the API from configuration. // the distro-specific ImageType Denylist provided to the API from configuration.
// If the given ImageType is not allowed the method returns an `false`. // If the given ImageType is not allowed the method returns an `false`.
// Otherwise `true` is returned. // Otherwise `true` is returned.
func (api *API) isImageTypeAllowed(distroName, imageType string) bool { func (api *API) isImageTypeAllowed(distroName, imageType string) (bool, error) {
anyImageType := "*"
anyDistro := "*"
for deniedDistro, deniedImgTypes := range api.distrosImageTypeDenylist { for deniedDistro, deniedImgTypes := range api.distrosImageTypeDenylist {
if distroName == deniedDistro || deniedDistro == anyDistro { deniedDistroPattern, err := glob.Compile(deniedDistro)
if err != nil {
// the bool return value here does not have any real meaning
return true, err
}
if deniedDistroPattern.Match(distroName) {
for _, deniedImgType := range deniedImgTypes { for _, deniedImgType := range deniedImgTypes {
if imageType == deniedImgType || deniedImgType == anyImageType { deniedImageTypePattern, err := glob.Compile(deniedImgType)
return false if err != nil {
// the bool return value here does not have any real meaning
return true, err
}
if deniedImageTypePattern.Match(imageType) {
return false, nil
} }
} }
} }
} }
return true
return true, nil
} }
// getImageType returns the ImageType for the selected distro // getImageType returns the ImageType for the selected distro
// This is necessary because different distros support different image types, and the image // This is necessary because different distros support different image types, and the image
// type may have a different package set than other distros. // type may have a different package set than other distros.
func (api *API) getImageType(distroName, imageType string) (distro.ImageType, error) { func (api *API) getImageType(distroName, imageType string) (distro.ImageType, error) {
if !api.isImageTypeAllowed(distroName, imageType) { imgAllowed, err := api.isImageTypeAllowed(distroName, imageType)
if err != nil {
return nil, fmt.Errorf("error while checking if image type is allowed: %v", err)
}
if !imgAllowed {
return nil, fmt.Errorf("image type %q for distro %q is denied by configuration", imageType, distroName) return nil, fmt.Errorf("image type %q for distro %q is denied by configuration", imageType, distroName)
} }
@ -2522,7 +2536,16 @@ func (api *API) composeTypesHandler(writer http.ResponseWriter, request *http.Re
} }
for _, format := range arch.ListImageTypes() { for _, format := range arch.ListImageTypes() {
if !api.isImageTypeAllowed(distroName, format) { imgAllowed, err := api.isImageTypeAllowed(distroName, format)
if err != nil {
errors := responseError{
ID: "InternalError",
Msg: fmt.Sprintf("Error while checking if image type is allowed: %v", err),
}
statusResponseError(writer, http.StatusInternalServerError, errors)
return
}
if !imgAllowed {
continue continue
} }
reply.Types = append(reply.Types, composeType{format, true}) reply.Types = append(reply.Types, composeType{format, true})

View file

@ -1691,6 +1691,26 @@ func TestComposePOST_ImageTypeDenylist(t *testing.T) {
expectedComposeLocal, expectedComposeLocal,
[]string{"build_id"}, []string{"build_id"},
}, },
{
"/api/v1/compose",
fmt.Sprintf(`{"blueprint_name": "test","compose_type": "%s","branch": "master"}`, test_distro.TestImageTypeName),
map[string][]string{fmt.Sprintf("%s*", test_distro.TestDistroName): {fmt.Sprintf("%s*", test_distro.TestImageTypeName)}},
http.StatusBadRequest,
fmt.Sprintf(`{"status":false,"errors":[{"id":"ComposeError","msg":"Failed to get compose type \"%[1]s\": image type \"%[1]s\" for distro \"%[2]s\" is denied by configuration"}]}`,
test_distro.TestImageTypeName, test_distro.TestDistro2Name),
expectedComposeLocal,
[]string{"build_id"},
},
{
"/api/v1/compose",
fmt.Sprintf(`{"blueprint_name": "test","compose_type": "%s","branch": "master"}`, test_distro.TestImageType2Name),
map[string][]string{fmt.Sprintf("%s*", test_distro.TestDistroName): {fmt.Sprintf("%s*", test_distro.TestImageTypeName)}},
http.StatusBadRequest,
fmt.Sprintf(`{"status":false,"errors":[{"id":"ComposeError","msg":"Failed to get compose type \"%[1]s\": image type \"%[1]s\" for distro \"%[2]s\" is denied by configuration"}]}`,
test_distro.TestImageType2Name, test_distro.TestDistro2Name),
expectedComposeLocal,
[]string{"build_id"},
},
} }
tempdir, err := ioutil.TempDir("", "weldr-tests-") tempdir, err := ioutil.TempDir("", "weldr-tests-")