fix(tiller): validate names before performing operations

There were several places where an invalid name could be interpreted
into a wild-card by Kubernetes. That allows Bad Things.

This fix requires names to match the Kubernetes pattern for naming.

Closes #1594
pull/1598/head
Matt Butcher 8 years ago
parent 04f203d2d0
commit 02a1cf382c
No known key found for this signature in database
GPG Key ID: DCD5F5E5EF32C345

@ -70,6 +70,17 @@ var (
// ListDefaultLimit is the default limit for number of items returned in a list. // ListDefaultLimit is the default limit for number of items returned in a list.
var ListDefaultLimit int64 = 512 var ListDefaultLimit int64 = 512
// ValidName is a regular expression for names.
//
// According to the Kubernetes help text, the regular expression it uses is:
//
// (([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])?
//
// We modified that. First, we added start and end delimiters. Second, we changed
// the final ? to + to require that the pattern match at least once. This modification
// prevents an empty string from matching.
var ValidName = regexp.MustCompile("^(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])+$")
// ReleaseServer implements the server-side gRPC endpoint for the HAPI services. // ReleaseServer implements the server-side gRPC endpoint for the HAPI services.
type ReleaseServer struct { type ReleaseServer struct {
env *environment.Environment env *environment.Environment
@ -211,7 +222,7 @@ func (s *ReleaseServer) GetReleaseStatus(c ctx.Context, req *services.GetRelease
return nil, errIncompatibleVersion return nil, errIncompatibleVersion
} }
if req.Name == "" { if !ValidName.MatchString(req.Name) {
return nil, errMissingRelease return nil, errMissingRelease
} }
@ -267,9 +278,10 @@ func (s *ReleaseServer) GetReleaseContent(c ctx.Context, req *services.GetReleas
return nil, errIncompatibleVersion return nil, errIncompatibleVersion
} }
if req.Name == "" { if !ValidName.MatchString(req.Name) {
return nil, errMissingRelease return nil, errMissingRelease
} }
if req.Version <= 0 { if req.Version <= 0 {
rel, err := s.env.Releases.Deployed(req.Name) rel, err := s.env.Releases.Deployed(req.Name)
return &services.GetReleaseContentResponse{Release: rel}, err return &services.GetReleaseContentResponse{Release: rel}, err
@ -355,7 +367,7 @@ func (s *ReleaseServer) reuseValues(req *services.UpdateReleaseRequest, current
// prepareUpdate builds an updated release for an update operation. // prepareUpdate builds an updated release for an update operation.
func (s *ReleaseServer) prepareUpdate(req *services.UpdateReleaseRequest) (*release.Release, *release.Release, error) { func (s *ReleaseServer) prepareUpdate(req *services.UpdateReleaseRequest) (*release.Release, *release.Release, error) {
if req.Name == "" { if !ValidName.MatchString(req.Name) {
return nil, nil, errMissingRelease return nil, nil, errMissingRelease
} }
@ -486,7 +498,7 @@ func (s *ReleaseServer) performKubeUpdate(currentRelease, targetRelease *release
// the previous release's configuration // the previous release's configuration
func (s *ReleaseServer) prepareRollback(req *services.RollbackReleaseRequest) (*release.Release, *release.Release, error) { func (s *ReleaseServer) prepareRollback(req *services.RollbackReleaseRequest) (*release.Release, *release.Release, error) {
switch { switch {
case req.Name == "": case !ValidName.MatchString(req.Name):
return nil, nil, errMissingRelease return nil, nil, errMissingRelease
case req.Version < 0: case req.Version < 0:
return nil, nil, errInvalidRevision return nil, nil, errInvalidRevision
@ -905,7 +917,7 @@ func (s *ReleaseServer) UninstallRelease(c ctx.Context, req *services.UninstallR
return nil, errIncompatibleVersion return nil, errIncompatibleVersion
} }
if req.Name == "" { if !ValidName.MatchString(req.Name) {
log.Printf("uninstall: Release not found: %s", req.Name) log.Printf("uninstall: Release not found: %s", req.Name)
return nil, errMissingRelease return nil, errMissingRelease
} }

@ -144,6 +144,28 @@ func upgradeReleaseVersion(rel *release.Release) *release.Release {
} }
} }
func TestValidName(t *testing.T) {
for name, valid := range map[string]bool{
"nina pinta santa-maria": false,
"nina-pinta-santa-maria": true,
"-nina": false,
"pinta-": false,
"santa-maria": true,
"niña": false,
"...": false,
"pinta...": false,
"santa...maria": true,
"": false,
" ": false,
".nina.": false,
"nina.pinta": true,
} {
if valid != ValidName.MatchString(name) {
t.Errorf("Expected %q to be %t", name, valid)
}
}
}
func TestGetVersionSet(t *testing.T) { func TestGetVersionSet(t *testing.T) {
rs := rsFixture() rs := rsFixture()
vs, err := rs.getVersionSet() vs, err := rs.getVersionSet()

Loading…
Cancel
Save