Skip to content

Commit

Permalink
check suspitious file extensions and add suspitious filenames (#150)
Browse files Browse the repository at this point in the history
* check files with file extension and add accept-key option

* add accept-file-extension option

* update README
  • Loading branch information
tomoyamachi authored Sep 10, 2021
1 parent b68ff92 commit bba0267
Show file tree
Hide file tree
Showing 15 changed files with 222 additions and 52 deletions.
25 changes: 24 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -627,7 +627,14 @@ The `--ignore, -i` option can ignore specified checkpoints.
$ dockle -i CIS-DI-0001 -i DKL-DI-0006 [IMAGE_NAME]
```
Or, use `.dockleignore` file.
Or, use `DOCKLE_IGNORS`:
```
export DOCKLE_IGNORES=CIS-DI-0001,DKL-DI-0006
dockle [IMAGE_NAME]
```
Or, use `.dockleignore` file:
```bash
$ cat .dockleignore
Expand All @@ -637,6 +644,22 @@ CIS-DI-0001
DKL-DI-0006
```
### Accept suspitious `environment variables` / `files` / `file extensions`
```bash
# --accept-key value, --ak value You can add acceptable keywords.
dockle -ak GPG_KEY -ak KEYCLOAK_VERSION [IMAGE_NAME]
or DOCKLE_ACCEPT_KEYS=GPG_KEY,KEYCLOAK_VERSION dockle [IMAGE_NAME]
# --accept-file value, --af value You can add acceptable file names.
dockle -af id_rsa -af id_dsa [IMAGE_NAME]
or DOCKLE_ACCEPT_FILES=id_rsa,id_dsa dockle [IMAGE_NAME]
# --accept-file-extension value, --ae value You can add acceptable file extensions.
dockle -ae pem -ae log [IMAGE_NAME]
or DOCKLE_ACCEPT_FILE_EXTENSIONS=pem,log dockle [IMAGE_NAME]
```
## Continuous Integration (CI)
You can scan your built image with `Dockle` in Travis CI/CircleCI.
Expand Down
31 changes: 21 additions & 10 deletions cmd/dockle/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,13 +43,24 @@ OPTIONS:
Usage: "input file path instead of image name",
},
cli.StringSliceFlag{
Name: "ignore, i",
Usage: "checkpoints to ignore. You can use .dockleignore too.",
Name: "ignore, i",
EnvVar: "DOCKLE_IGNORES",
Usage: "checkpoints to ignore. You can use .dockleignore too.",
},
cli.StringSliceFlag{
Name: "accept-key, a",
EnvVar: "ACCEPT_KEY",
Usage: "For CIS-DI-0010. You can add acceptable keywords. e.g) -a GPG_KEY -a KEYCLOAK",
Name: "accept-key, ak",
EnvVar: "DOCKLE_ACCEPT_KEYS",
Usage: "For CIS-DI-0010. You can add acceptable keywords. e.g) -ak GPG_KEY -ak KEYCLOAK",
},
cli.StringSliceFlag{
Name: "accept-file, af",
EnvVar: "DOCKLE_ACCEPT_FILES",
Usage: "For CIS-DI-0010. You can add acceptable file names. e.g) -af id_rsa -af config.json",
},
cli.StringSliceFlag{
Name: "accept-file-extension, ae",
EnvVar: "DOCKLE_ACCEPT_FILE_EXTENSIONS",
Usage: "For CIS-DI-0010. You can add acceptable file extensions. e.g) -ae pem -ae log",
},
cli.StringFlag{
Name: "format, f",
Expand Down Expand Up @@ -79,17 +90,17 @@ OPTIONS:
Usage: "suppress log output",
},
cli.BoolFlag{
Name: "no-color",
Name: "no-color",
EnvVar: "NO_COLOR",
Usage: "suppress log output",
Usage: "suppress log output",
},

// Registry flag
cli.DurationFlag{
Name: "timeout, t",
Value: time.Second * 90,
Name: "timeout, t",
Value: time.Second * 90,
EnvVar: "DOCKLE_TIMEOUT",
Usage: "docker timeout. e.g) 5s, 5m...",
Usage: "docker timeout. e.g) 5s, 5m...",
},
cli.StringFlag{
Name: "authurl",
Expand Down
8 changes: 8 additions & 0 deletions pkg/assessor/assessor.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ var assessors []Assessor
type Assessor interface {
Assess(deckodertypes.FileMap) ([]*types.Assessment, error)
RequiredFiles() []string
RequiredExtensions() []string
RequiredPermissions() []os.FileMode
}

Expand Down Expand Up @@ -63,6 +64,13 @@ func LoadRequiredFiles() (filenames []string) {
return filenames
}

func LoadRequiredExtensions() (extensions []string) {
for _, assessor := range assessors {
extensions = append(extensions, assessor.RequiredExtensions()...)
}
return extensions
}

func LoadRequiredPermissions() (permissions []os.FileMode) {
for _, assessor := range assessors {
permissions = append(permissions, assessor.RequiredPermissions()...)
Expand Down
4 changes: 4 additions & 0 deletions pkg/assessor/cache/cache.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,10 @@ func (a CacheAssessor) RequiredFiles() []string {
return append(reqFiles, reqDirs...)
}

func (a CacheAssessor) RequiredExtensions() []string {
return []string{}
}

func (a CacheAssessor) RequiredPermissions() []os.FileMode {
return []os.FileMode{}
}
6 changes: 5 additions & 1 deletion pkg/assessor/contentTrust/contentTrust.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ var HostEnvironmentFileName = "ENVIRONMENT variable on HOST OS"

type ContentTrustAssessor struct{}

func (a ContentTrustAssessor) Assess(fileMap deckodertypes.FileMap) ([]*types.Assessment, error) {
func (a ContentTrustAssessor) Assess(_ deckodertypes.FileMap) ([]*types.Assessment, error) {
log.Logger.Debug("Scan start : DOCKER_CONTENT_TRUST")

if os.Getenv("DOCKER_CONTENT_TRUST") != "1" {
Expand All @@ -32,6 +32,10 @@ func (a ContentTrustAssessor) RequiredFiles() []string {
return []string{}
}

func (a ContentTrustAssessor) RequiredExtensions() []string {
return []string{}
}

func (a ContentTrustAssessor) RequiredPermissions() []os.FileMode {
return []os.FileMode{}
}
91 changes: 78 additions & 13 deletions pkg/assessor/credential/credential.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"fmt"
"os"
"path/filepath"
"unicode/utf8"

deckodertypes "github.com/goodwithtech/deckoder/types"

Expand All @@ -17,28 +18,92 @@ type CredentialAssessor struct{}
func (a CredentialAssessor) Assess(fileMap deckodertypes.FileMap) ([]*types.Assessment, error) {
log.Logger.Debug("Start scan : credential files")
assesses := []*types.Assessment{}
reqFiles := a.RequiredFiles()
fmap := makeMaps(a.RequiredFiles())
fexts := makeMaps(a.RequiredExtensions())
for filename := range fileMap {
basename := filepath.Base(filename)
// check exist target files
for _, reqFilename := range reqFiles {
if reqFilename == basename {
assesses = append(
assesses,
&types.Assessment{
Code: types.AvoidCredential,
Filename: filename,
Desc: fmt.Sprintf("Suspicious filename found : %s ", filename),
})
break
}
if _, ok := fmap[basename]; ok {
assesses = append(
assesses,
&types.Assessment{
Code: types.AvoidCredential,
Filename: filename,
Desc: fmt.Sprintf("Suspicious filename found : %s (You can suppress it with \"-af %s\")", filename, basename),
})
} else if _, ok := fexts[filepath.Ext(basename)]; ok {
assesses = append(
assesses,
&types.Assessment{
Code: types.AvoidCredential,
Filename: filename,
Desc: fmt.Sprintf("Suspicious file extension found : %s (You can suppress it with \"-ae %s\")", filename, trimFirstRune(filepath.Ext(basename))),
})
}
}
return assesses, nil
}

func trimFirstRune(s string) string {
_, i := utf8.DecodeRuneInString(s)
return s[i:]
}

func makeMaps(keys []string) map[string]struct{} {
maps := make(map[string]struct{})
for i := 0; i < len(keys); i++ {
maps[keys[i]] = struct{}{}
}
return maps
}

func (a CredentialAssessor) RequiredFiles() []string {
return []string{"credentials.json", "credential.json", "credentials", "credential"}
return []string{
"credentials.json",
"credential.json",
"config.json",
"credentials",
"credential",
"password.txt",
"id_rsa",
"id_dsa",
"id_ecdsa",
"id_ed25519",
"secret_token.rb",
"carrierwave.rb",
"omniauth.rb",
"settings.py",
"database.yml",
"credentials.xml",
}
}

func (a CredentialAssessor) RequiredExtensions() []string {
// reference: https://github.com/eth0izzle/shhgit/blob/master/config.yaml
return []string{
".key",
".secret",
".pem",
".p12",
".pkcs12",
".pfx",
".asc",
".ovpn",
".private_key",
".cscfg",
".rdp",
".mdf",
".sdf",
".bek",
".tpm",
".fve",
".jks",
".psafe3",
".agilekeychain",
".keychain",
".pcap",
".gnucache",
}
}

func (a CredentialAssessor) RequiredPermissions() []os.FileMode {
Expand Down
4 changes: 4 additions & 0 deletions pkg/assessor/group/group.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,10 @@ func (a GroupAssessor) RequiredFiles() []string {
return []string{"etc/group"}
}

func (a GroupAssessor) RequiredExtensions() []string {
return []string{}
}

func (a GroupAssessor) RequiredPermissions() []os.FileMode {
return []os.FileMode{}
}
6 changes: 5 additions & 1 deletion pkg/assessor/hosts/hosts.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import (

type HostsAssessor struct{}

func (a HostsAssessor) Assess(fileMap deckodertypes.FileMap) ([]*types.Assessment, error) {
func (a HostsAssessor) Assess(_ deckodertypes.FileMap) ([]*types.Assessment, error) {
log.Logger.Debug("Start scan : /etc/hosts")

assesses := []*types.Assessment{}
Expand All @@ -23,6 +23,10 @@ func (a HostsAssessor) RequiredFiles() []string {
return []string{"etc/hosts"}
}

func (a HostsAssessor) RequiredExtensions() []string {
return []string{}
}

func (a HostsAssessor) RequiredPermissions() []os.FileMode {
return []os.FileMode{}
}
4 changes: 4 additions & 0 deletions pkg/assessor/manifest/manifest.go
Original file line number Diff line number Diff line change
Expand Up @@ -270,6 +270,10 @@ func (a ManifestAssessor) RequiredFiles() []string {
return []string{}
}

func (a ManifestAssessor) RequiredExtensions() []string {
return []string{}
}

func (a ManifestAssessor) RequiredPermissions() []os.FileMode {
return []os.FileMode{}
}
Expand Down
4 changes: 4 additions & 0 deletions pkg/assessor/passwd/passwd.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,10 @@ func (a PasswdAssessor) RequiredFiles() []string {
return []string{"etc/shadow", "etc/master.passwd"}
}

func (a PasswdAssessor) RequiredExtensions() []string {
return []string{}
}

func (a PasswdAssessor) RequiredPermissions() []os.FileMode {
return []os.FileMode{}
}
4 changes: 4 additions & 0 deletions pkg/assessor/privilege/suid.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,10 @@ func (a PrivilegeAssessor) RequiredFiles() []string {
return []string{}
}

func (a PrivilegeAssessor) RequiredExtensions() []string {
return []string{}
}

//const GidMode os.FileMode = 4000
func (a PrivilegeAssessor) RequiredPermissions() []os.FileMode {
return []os.FileMode{os.ModeSetgid, os.ModeSetuid}
Expand Down
4 changes: 4 additions & 0 deletions pkg/assessor/user/user.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,10 @@ func (a UserAssessor) RequiredFiles() []string {
return []string{"etc/passwd"}
}

func (a UserAssessor) RequiredExtensions() []string {
return []string{}
}

func (a UserAssessor) RequiredPermissions() []os.FileMode {
return []os.FileMode{}
}
3 changes: 2 additions & 1 deletion pkg/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,8 @@ func Run(c *cli.Context) (err error) {
}
}
manifest.AddAcceptanceKeys(c.StringSlice("accept-key"))

scanner.AddAcceptanceFiles(c.StringSlice("accept-file"))
scanner.AddAcceptanceExtensions(c.StringSlice("accept-file-extension"))
log.Logger.Debug("Start assessments...")
assessments, err := scanner.ScanImage(ctx, imageName, filePath, dockerOption)
if err != nil {
Expand Down
Loading

0 comments on commit bba0267

Please sign in to comment.