High severity7.8NVD Advisory· Published Dec 18, 2017· Updated May 13, 2026
CVE-2017-15104
CVE-2017-15104
Description
An access flaw was found in Heketi 5, where the heketi.json configuration file was world readable. An attacker having local access to the Heketi server could read plain-text passwords from the heketi.json file.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
github.com/heketi/heketiGo | < 5.0.1 | 5.0.1 |
Affected products
1Patches
1787bae461b23api: input validation
7 files changed · +171 −4
apps/glusterfs/app_device.go+13 −0 modified@@ -28,6 +28,13 @@ func (a *App) DeviceAdd(w http.ResponseWriter, r *http.Request) { return } + err = msg.Validate() + if err != nil { + http.Error(w, "validation failed: "+err.Error(), http.StatusBadRequest) + logger.LogError("validation failed: " + err.Error()) + return + } + // Check the message has devices if msg.Name == "" { http.Error(w, "no devices added", http.StatusBadRequest) @@ -323,6 +330,12 @@ func (a *App) DeviceSetState(w http.ResponseWriter, r *http.Request) { http.Error(w, "request unable to be parsed", 422) return } + err = msg.Validate() + if err != nil { + http.Error(w, "validation failed: "+err.Error(), http.StatusBadRequest) + logger.LogError("validation failed: " + err.Error()) + return + } // Check for valid id, return immediately if not valid err = a.db.View(func(tx *bolt.Tx) error {
apps/glusterfs/app_node.go+13 −0 modified@@ -28,6 +28,13 @@ func (a *App) NodeAdd(w http.ResponseWriter, r *http.Request) { return } + err = msg.Validate() + if err != nil { + http.Error(w, "validation failed: "+err.Error(), http.StatusBadRequest) + logger.LogError("validation failed: " + err.Error()) + return + } + // Check information in JSON request if len(msg.Hostnames.Manage) == 0 { http.Error(w, "Manage hostname missing", http.StatusBadRequest) @@ -330,6 +337,12 @@ func (a *App) NodeSetState(w http.ResponseWriter, r *http.Request) { http.Error(w, "request unable to be parsed", 422) return } + err = msg.Validate() + if err != nil { + http.Error(w, "validation failed: "+err.Error(), http.StatusBadRequest) + logger.LogError("validation failed: " + err.Error()) + return + } // Check state is supported err = a.db.View(func(tx *bolt.Tx) error {
apps/glusterfs/app_volume.go+12 −0 modified@@ -34,6 +34,12 @@ func (a *App) VolumeCreate(w http.ResponseWriter, r *http.Request) { http.Error(w, "request unable to be parsed", 422) return } + err = msg.Validate() + if err != nil { + http.Error(w, "validation failed: "+err.Error(), http.StatusBadRequest) + logger.LogError("validation failed: " + err.Error()) + return + } switch { case msg.Gid < 0: @@ -287,6 +293,12 @@ func (a *App) VolumeExpand(w http.ResponseWriter, r *http.Request) { return } logger.Debug("Msg: %v", msg) + err = msg.Validate() + if err != nil { + http.Error(w, "validation failed: "+err.Error(), http.StatusBadRequest) + logger.LogError("validation failed: " + err.Error()) + return + } if msg.Size < 1 { http.Error(w, "Invalid volume size", http.StatusBadRequest)
executors/sshexec/device.go+2 −2 modified@@ -33,7 +33,7 @@ func (s *SshExecutor) DeviceSetup(host, device, vgid string) (d *executors.Devic // Setup commands commands := []string{ - fmt.Sprintf("pvcreate --metadatasize=128M --dataalignment=256K %v", device), + fmt.Sprintf("pvcreate --metadatasize=128M --dataalignment=256K '%v'", device), fmt.Sprintf("vgcreate %v %v", s.vgName(vgid), device), } @@ -65,7 +65,7 @@ func (s *SshExecutor) DeviceTeardown(host, device, vgid string) error { // Setup commands commands := []string{ fmt.Sprintf("vgremove %v", s.vgName(vgid)), - fmt.Sprintf("pvremove %v", device), + fmt.Sprintf("pvremove '%v'", device), } // Execute command
glide.lock+8 −2 modified@@ -1,6 +1,8 @@ -hash: 59834cf573ea7a48f90686852f6c02428d2ffee8ecfcfeafac4ca2e52638f00a -updated: 2017-05-22T14:00:49.364236742-04:00 +hash: 5fc263bfacc703e0a599166ccae968891dd53d7b4e42a3d8356b3194e45833bb +updated: 2017-12-18T17:52:34.69084539+05:30 imports: +- name: github.com/asaskevich/govalidator + version: 852d82c746b23d9b357b210ea470d99f4e023b72 - name: github.com/auth0/go-jwt-middleware version: f3f7de3b9e394e3af3b88e1b9457f6f71d1ae0ac - name: github.com/Azure/go-ansiterm @@ -53,6 +55,10 @@ imports: version: 6aced65f8501fe1217321abf0749d354824ba2ff - name: github.com/go-openapi/swag version: 1d0bd113de87027671077d3c71eb3ac5d7dbba72 +- name: github.com/go-ozzo/ozzo-validation + version: 85dcd8368eba387e65a03488b003e233994e87e9 + subpackages: + - is - name: github.com/gogo/protobuf version: e18d7aa8f8c624c915db340349aad4c49b10d173 subpackages:
glide.yaml+2 −0 modified@@ -22,3 +22,5 @@ import: - ssh/agent - package: k8s.io/client-go version: v3.0.0-beta.0 +- package: github.com/go-ozzo/ozzo-validation + version: v3.3
pkg/glusterfs/api/types.go+121 −0 modified@@ -18,9 +18,36 @@ package api import ( "fmt" + "regexp" "sort" + + "github.com/go-ozzo/ozzo-validation" + "github.com/go-ozzo/ozzo-validation/is" +) + +var ( + // Restricting the deviceName to much smaller subset of Unix Path + // as unix path takes almost everything except NULL + deviceNameRe = regexp.MustCompile("^/[a-zA-Z0-9_./-]+$") + + // Volume name constraints decided by looking at + // "cli_validate_volname" function in cli-cmd-parser.c of gluster code + volumeNameRe = regexp.MustCompile("^[a-zA-Z0-9_-]+$") + + blockVolNameRe = regexp.MustCompile("^[a-zA-Z0-9_-]+$") ) +// ValidateUUID is written this way because heketi UUID does not +// conform to neither UUID v4 nor v5. +func ValidateUUID(value interface{}) error { + s, _ := value.(string) + err := validation.Validate(s, validation.RuneLength(32, 32), is.Hexadecimal) + if err != nil { + return fmt.Errorf("not a valid UUID") + } + return nil +} + // State type EntryState string @@ -31,6 +58,15 @@ const ( EntryStateFailed EntryState = "failed" ) +func ValidateEntryState(value interface{}) error { + s, _ := value.(string) + err := validation.Validate(s, validation.Required, validation.In(EntryStateOnline, EntryStateOffline, EntryStateFailed)) + if err != nil { + return fmt.Errorf("state requested is not valid") + } + return nil +} + type DurabilityType string const ( @@ -39,11 +75,26 @@ const ( DurabilityEC DurabilityType = "disperse" ) +func ValidateDurabilityType(value interface{}) error { + s, _ := value.(string) + err := validation.Validate(s, validation.Required, validation.In(DurabilityReplicate, DurabilityDistributeOnly, DurabilityEC)) + if err != nil { + return fmt.Errorf("durability type requested is not valid") + } + return nil +} + // Common type StateRequest struct { State EntryState `json:"state"` } +func (statereq StateRequest) Validate() error { + return validation.ValidateStruct(&statereq, + validation.Field(&statereq.State, validation.Required, validation.By(ValidateEntryState)), + ) +} + // Storage values in KB type StorageSize struct { Total uint64 `json:"total"` @@ -56,6 +107,35 @@ type HostAddresses struct { Storage sort.StringSlice `json:"storage"` } +func ValidateManagementHostname(value interface{}) error { + s, _ := value.(sort.StringSlice) + for _, fqdn := range s { + err := validation.Validate(fqdn, validation.Required, is.Host) + if err != nil { + return fmt.Errorf("Manage hostname should be valid hostname") + } + } + return nil +} + +func ValidateStorageHostname(value interface{}) error { + s, _ := value.(sort.StringSlice) + for _, ip := range s { + err := validation.Validate(ip, validation.Required, is.Host) + if err != nil { + return fmt.Errorf("Storage hostname should be valid IP") + } + } + return nil +} + +func (hostadd HostAddresses) Validate() error { + return validation.ValidateStruct(&hostadd, + validation.Field(&hostadd.Manage, validation.Required, validation.By(ValidateManagementHostname)), + validation.Field(&hostadd.Storage, validation.Required, validation.By(ValidateStorageHostname)), + ) +} + // Brick type BrickInfo struct { Id string `json:"id"` @@ -73,11 +153,24 @@ type Device struct { Name string `json:"name"` } +func (dev Device) Validate() error { + return validation.ValidateStruct(&dev, + validation.Field(&dev.Name, validation.Required, validation.Match(deviceNameRe)), + ) +} + type DeviceAddRequest struct { Device NodeId string `json:"node"` } +func (devAddReq DeviceAddRequest) Validate() error { + return validation.ValidateStruct(&devAddReq, + validation.Field(&devAddReq.Device, validation.Required), + validation.Field(&devAddReq.NodeId, validation.Required, validation.By(ValidateUUID)), + ) +} + type DeviceInfo struct { Device Storage StorageSize `json:"storage"` @@ -97,6 +190,14 @@ type NodeAddRequest struct { ClusterId string `json:"cluster"` } +func (req NodeAddRequest) Validate() error { + return validation.ValidateStruct(&req, + validation.Field(&req.Zone, validation.Required, validation.Min(1)), + validation.Field(&req.Hostnames, validation.Required), + validation.Field(&req.ClusterId, validation.Required, validation.By(ValidateUUID)), + ) +} + type NodeInfo struct { NodeAddRequest Id string `json:"id"` @@ -160,6 +261,20 @@ type VolumeCreateRequest struct { } `json:"snapshot"` } +func (volCreateRequest VolumeCreateRequest) Validate() error { + return validation.ValidateStruct(&volCreateRequest, + validation.Field(&volCreateRequest.Size, validation.Required, validation.Min(1)), + validation.Field(&volCreateRequest.Clusters, validation.By(ValidateUUID)), + validation.Field(&volCreateRequest.Name, validation.Match(volumeNameRe)), + validation.Field(&volCreateRequest.Durability, validation.Skip), + validation.Field(&volCreateRequest.Gid, validation.Skip), + validation.Field(&volCreateRequest.GlusterVolumeOptions, validation.Skip), + // This is possibly a bug in validation lib, ignore next two lines for now + // validation.Field(&volCreateRequest.Snapshot.Enable, validation.In(true, false)), + // validation.Field(&volCreateRequest.Snapshot.Factor, validation.Min(1.0)), + ) +} + type VolumeInfo struct { VolumeCreateRequest Id string `json:"id"` @@ -186,6 +301,12 @@ type VolumeExpandRequest struct { Size int `json:"expand_size"` } +func (volExpandReq VolumeExpandRequest) Validate() error { + return validation.ValidateStruct(&volExpandReq, + validation.Field(&volExpandReq.Size, validation.Required, validation.Min(1)), + ) +} + // Constructors func NewVolumeInfoResponse() *VolumeInfoResponse {
Vulnerability mechanics
Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
7- access.redhat.com/errata/RHSA-2017:3481nvdThird Party AdvisoryWEB
- access.redhat.com/security/cve/CVE-2017-15104nvdThird Party AdvisoryWEB
- bugzilla.redhat.com/show_bug.cginvdIssue TrackingThird Party AdvisoryWEB
- github.com/advisories/GHSA-q9vw-wr57-xjv3ghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2017-15104ghsaADVISORY
- github.com/heketi/heketi/commit/787bae461b23003a4daa4d1d639016a754cf6b00ghsaWEB
- github.com/heketi/heketi/releases/tag/v5.0.1nvdRelease NotesWEB
News mentions
0No linked articles in our index yet.