Skip to content

Commit c966fca

Browse files
committed
Set config file mode to 0600 in packages
This PR changes the main config file (<beatname>.yml) from 0644 to 0600. It adds a test that checks all RPM, deb, tar.gz, and zip files to ensure that the config file has the correct file mode.
1 parent 2a4f5be commit c966fca

File tree

4 files changed

+295
-0
lines changed

4 files changed

+295
-0
lines changed

CHANGELOG.asciidoc

+1
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ https://github.com/elastic/beats/compare/v5.1.1...master[Check the HEAD diff]
6161
- The limit for the number of fields is increased via the mapping template. {pull}3275[3275]
6262
- Updated to Go 1.7.4. {pull}3277[3277]
6363
- Added a NOTICE file containing the notices and licenses of the dependencies. {pull}3334[3334].
64+
- RPM/deb packages will now install the config file with 0600 permissions. {pull}3382[3382]
6465

6566
*Metricbeat*
6667

Makefile

+2
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,8 @@ package: update beats-dashboards
9696
mkdir -p build/upload/
9797
$(foreach var,$(BEATS),cp -r $(var)/build/upload/ build/upload/$(var) || exit 1;)
9898
cp -r build/dashboards-upload build/upload/dashboards
99+
# Run tests on the generated packages.
100+
go test ./dev-tools/package_test.go -files "${shell pwd}/build/upload/*/*"
99101

100102
# Upload nightly builds to S3
101103
.PHONY: upload-nightlies-s3

dev-tools/package_test.go

+289
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,289 @@
1+
package dev_tools
2+
3+
// This file contains tests that can be run on the generated packages.
4+
// To run these tests use `go test package_test.go`.
5+
6+
import (
7+
"archive/tar"
8+
"archive/zip"
9+
"bytes"
10+
"compress/gzip"
11+
"flag"
12+
"io"
13+
"os"
14+
"path/filepath"
15+
"regexp"
16+
"strings"
17+
"testing"
18+
19+
"github.com/blakesmith/ar"
20+
"github.com/cavaliercoder/go-rpm"
21+
)
22+
23+
const (
24+
expectedConfigMode = os.FileMode(0600)
25+
expectedConfigUID = 0
26+
expectedConfigGID = 0
27+
)
28+
29+
var (
30+
configFilePattern = regexp.MustCompile(`.*beat\.yml`)
31+
)
32+
33+
var (
34+
files = flag.String("files", "../build/upload/*/*", "filepath glob containing package files")
35+
)
36+
37+
func TestRPM(t *testing.T) {
38+
rpms := getFiles(t, regexp.MustCompile(`\.rpm$`))
39+
for _, rpm := range rpms {
40+
checkRPM(t, rpm)
41+
}
42+
}
43+
44+
func TestDeb(t *testing.T) {
45+
debs := getFiles(t, regexp.MustCompile(`\.deb$`))
46+
buf := new(bytes.Buffer)
47+
for _, deb := range debs {
48+
checkDeb(t, deb, buf)
49+
}
50+
}
51+
52+
func TestTar(t *testing.T) {
53+
tars := getFiles(t, regexp.MustCompile(`\.tar\.gz$`))
54+
for _, tar := range tars {
55+
checkTar(t, tar)
56+
}
57+
}
58+
59+
func TestZip(t *testing.T) {
60+
zips := getFiles(t, regexp.MustCompile(`^\w+beat-\S+.zip$`))
61+
for _, zip := range zips {
62+
checkZip(t, zip)
63+
}
64+
}
65+
66+
// Sub-tests
67+
68+
func checkRPM(t *testing.T, file string) {
69+
p, err := readRPM(file)
70+
if err != nil {
71+
t.Error(err)
72+
return
73+
}
74+
75+
checkConfigPermissions(t, p)
76+
}
77+
78+
func checkDeb(t *testing.T, file string, buf *bytes.Buffer) {
79+
p, err := readDeb(file, buf)
80+
if err != nil {
81+
t.Error(err)
82+
return
83+
}
84+
85+
checkConfigPermissions(t, p)
86+
checkConfigOwner(t, p)
87+
}
88+
89+
func checkTar(t *testing.T, file string) {
90+
p, err := readTar(file)
91+
if err != nil {
92+
t.Error(err)
93+
return
94+
}
95+
96+
checkConfigPermissions(t, p)
97+
checkConfigOwner(t, p)
98+
}
99+
100+
func checkZip(t *testing.T, file string) {
101+
p, err := readZip(file)
102+
if err != nil {
103+
t.Error(err)
104+
return
105+
}
106+
107+
checkConfigPermissions(t, p)
108+
}
109+
110+
// Verify that the main configuration file is installed with a 0600 file mode.
111+
func checkConfigPermissions(t *testing.T, p *packageFile) {
112+
t.Run(p.Name+" config file permissions", func(t *testing.T) {
113+
for _, entry := range p.Contents {
114+
if configFilePattern.MatchString(entry.File) {
115+
mode := entry.Mode.Perm()
116+
if expectedConfigMode != mode {
117+
t.Errorf("file %v has wrong permissions: expected=%v actual=%v",
118+
entry.Mode, expectedConfigMode, mode)
119+
}
120+
return
121+
}
122+
}
123+
t.Errorf("no config file found matching %v", configFilePattern)
124+
})
125+
}
126+
127+
func checkConfigOwner(t *testing.T, p *packageFile) {
128+
t.Run(p.Name+" config file owner", func(t *testing.T) {
129+
for _, entry := range p.Contents {
130+
if configFilePattern.MatchString(entry.File) {
131+
if expectedConfigUID != entry.UID {
132+
t.Errorf("file %v should be owned by user %v, owner=%v", entry.File, expectedConfigGID, entry.UID)
133+
}
134+
if expectedConfigGID != entry.GID {
135+
t.Errorf("file %v should be owned by group %v, group=%v", entry.File, expectedConfigGID, entry.GID)
136+
}
137+
return
138+
}
139+
}
140+
t.Errorf("no config file found matching %v", configFilePattern)
141+
})
142+
}
143+
144+
// Helpers
145+
146+
type packageFile struct {
147+
Name string
148+
Contents map[string]packageEntry
149+
}
150+
151+
type packageEntry struct {
152+
File string
153+
UID int
154+
GID int
155+
Mode os.FileMode
156+
}
157+
158+
func getFiles(t *testing.T, pattern *regexp.Regexp) []string {
159+
matches, err := filepath.Glob(*files)
160+
if err != nil {
161+
t.Fatal(err)
162+
}
163+
164+
files := matches[:0]
165+
for _, f := range matches {
166+
if pattern.MatchString(filepath.Base(f)) {
167+
files = append(files, f)
168+
}
169+
}
170+
171+
return files
172+
}
173+
174+
func readRPM(rpmFile string) (*packageFile, error) {
175+
p, err := rpm.OpenPackageFile(rpmFile)
176+
if err != nil {
177+
return nil, err
178+
}
179+
180+
contents := p.Files()
181+
pf := &packageFile{Name: filepath.Base(rpmFile), Contents: map[string]packageEntry{}}
182+
183+
for _, file := range contents {
184+
pf.Contents[file.Name()] = packageEntry{
185+
File: file.Name(),
186+
Mode: file.Mode(),
187+
}
188+
}
189+
190+
return pf, nil
191+
}
192+
193+
// readDeb reads the data.tar.gz file from the .deb.
194+
func readDeb(debFile string, dataBuffer *bytes.Buffer) (*packageFile, error) {
195+
file, err := os.Open(debFile)
196+
if err != nil {
197+
return nil, err
198+
}
199+
defer file.Close()
200+
201+
arReader := ar.NewReader(file)
202+
for {
203+
header, err := arReader.Next()
204+
if err != nil {
205+
if err == io.EOF {
206+
break
207+
}
208+
return nil, err
209+
}
210+
211+
if strings.HasPrefix(header.Name, "data.tar.gz") {
212+
dataBuffer.Reset()
213+
_, err := io.Copy(dataBuffer, arReader)
214+
if err != nil {
215+
return nil, err
216+
}
217+
218+
gz, err := gzip.NewReader(dataBuffer)
219+
if err != nil {
220+
return nil, err
221+
}
222+
defer gz.Close()
223+
224+
return readTarContents(filepath.Base(debFile), gz)
225+
}
226+
}
227+
228+
return nil, io.EOF
229+
}
230+
231+
func readTar(tarFile string) (*packageFile, error) {
232+
file, err := os.Open(tarFile)
233+
if err != nil {
234+
return nil, err
235+
}
236+
defer file.Close()
237+
238+
var fileReader io.ReadCloser = file
239+
if strings.HasSuffix(tarFile, ".gz") {
240+
if fileReader, err = gzip.NewReader(file); err != nil {
241+
return nil, err
242+
}
243+
defer fileReader.Close()
244+
}
245+
246+
return readTarContents(filepath.Base(tarFile), fileReader)
247+
}
248+
249+
func readTarContents(tarName string, data io.Reader) (*packageFile, error) {
250+
tarReader := tar.NewReader(data)
251+
252+
p := &packageFile{Name: tarName, Contents: map[string]packageEntry{}}
253+
for {
254+
header, err := tarReader.Next()
255+
if err != nil {
256+
if err == io.EOF {
257+
break
258+
}
259+
return nil, err
260+
}
261+
262+
p.Contents[header.Name] = packageEntry{
263+
File: header.Name,
264+
UID: header.Uid,
265+
GID: header.Gid,
266+
Mode: os.FileMode(header.Mode),
267+
}
268+
}
269+
270+
return p, nil
271+
}
272+
273+
func readZip(zipFile string) (*packageFile, error) {
274+
r, err := zip.OpenReader(zipFile)
275+
if err != nil {
276+
return nil, err
277+
}
278+
defer r.Close()
279+
280+
p := &packageFile{Name: filepath.Base(zipFile), Contents: map[string]packageEntry{}}
281+
for _, f := range r.File {
282+
p.Contents[f.Name] = packageEntry{
283+
File: f.Name,
284+
Mode: f.Mode(),
285+
}
286+
}
287+
288+
return p, nil
289+
}

dev-tools/packer/xgo-scripts/before_build.sh

+3
Original file line numberDiff line numberDiff line change
@@ -62,14 +62,17 @@ cp $BEAT_NAME.template-es2x.json $PREFIX/$BEAT_NAME.template-es2x.json
6262

6363
# linux
6464
cp $BEAT_NAME.yml $PREFIX/$BEAT_NAME-linux.yml
65+
chmod 0600 $PREFIX/$BEAT_NAME-linux.yml
6566
cp $BEAT_NAME.full.yml $PREFIX/$BEAT_NAME-linux.full.yml
6667

6768
# darwin
6869
cp $BEAT_NAME.yml $PREFIX/$BEAT_NAME-darwin.yml
70+
chmod 0600 $PREFIX/$BEAT_NAME-darwin.yml
6971
cp $BEAT_NAME.full.yml $PREFIX/$BEAT_NAME-darwin.full.yml
7072

7173
# win
7274
cp $BEAT_NAME.yml $PREFIX/$BEAT_NAME-win.yml
75+
chmod 0600 $PREFIX/$BEAT_NAME-win.yml
7376
cp $BEAT_NAME.full.yml $PREFIX/$BEAT_NAME-win.full.yml
7477

7578
# Contains beat specific adjustments. As it is platform specific knowledge, it should be in packer not the beats itself

0 commit comments

Comments
 (0)