Skip to content

Commit bf84e2f

Browse files
5p2O5pe25ouTlishituowagoodman
authored
Add registry certificate verification support (#1232)
* add registry certificate verification support * modify go.mod * rename registry cert options, add docs, and add test Signed-off-by: Alex Goodman <[email protected]> * update to account for changes in anchore/stereoscope#195 Signed-off-by: Alex Goodman <[email protected]> * fix cli tests Signed-off-by: Alex Goodman <[email protected]> --------- Signed-off-by: Alex Goodman <[email protected]> Co-authored-by: lishituo <[email protected]> Co-authored-by: Alex Goodman <[email protected]>
1 parent 0d5be96 commit bf84e2f

File tree

7 files changed

+125
-46
lines changed

7 files changed

+125
-46
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
/go.work
22
/go.work.sum
3+
/.grype.yaml
34

45
CHANGELOG.md
56
VERSION

README.md

Lines changed: 25 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -651,23 +651,41 @@ registry:
651651
# skip TLS verification when communicating with the registry
652652
# same as GRYPE_REGISTRY_INSECURE_SKIP_TLS_VERIFY env var
653653
insecure-skip-tls-verify: false
654+
654655
# use http instead of https when connecting to the registry
655656
# same as GRYPE_REGISTRY_INSECURE_USE_HTTP env var
656657
insecure-use-http: false
657658
659+
# filepath to a CA certificate (or directory containing *.crt, *.cert, *.pem) used to generate the client certificate
660+
# GRYPE_REGISTRY_CA_CERT env var
661+
ca-cert: ""
662+
658663
# credentials for specific registries
659664
auth:
660-
- # the URL to the registry (e.g. "docker.io", "localhost:5000", etc.)
661-
# same as GRYPE_REGISTRY_AUTH_AUTHORITY env var
662-
authority: ""
663-
# same as GRYPE_REGISTRY_AUTH_USERNAME env var
665+
# the URL to the registry (e.g. "docker.io", "localhost:5000", etc.)
666+
# GRYPE_REGISTRY_AUTH_AUTHORITY env var
667+
- authority: ""
668+
669+
# GRYPE_REGISTRY_AUTH_USERNAME env var
664670
username: ""
665-
# same as GRYPE_REGISTRY_AUTH_PASSWORD env var
671+
672+
# GRYPE_REGISTRY_AUTH_PASSWORD env var
666673
password: ""
674+
667675
# note: token and username/password are mutually exclusive
668-
# same as GRYPE_REGISTRY_AUTH_TOKEN env var
676+
# GRYPE_REGISTRY_AUTH_TOKEN env var
669677
token: ""
670-
- ... # note, more credentials can be provided via config file only
678+
679+
# filepath to the client certificate used for TLS authentication to the registry
680+
# GRYPE_REGISTRY_AUTH_TLS_CERT env var
681+
tls-cert: ""
682+
683+
# filepath to the client key used for TLS authentication to the registry
684+
# GRYPE_REGISTRY_AUTH_TLS_KEY env var
685+
tls-key: ""
686+
687+
# - ... # note, more credentials can be provided via config file only (not env vars)
688+
671689
672690
log:
673691
# use structured logging

go.mod

Lines changed: 12 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -7,19 +7,28 @@ require (
77
github.com/Masterminds/sprig/v3 v3.2.3
88
github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d
99
github.com/adrg/xdg v0.4.0
10+
github.com/anchore/bubbly v0.0.0-20230801194016-acdb4981b461
11+
github.com/anchore/clio v0.0.0-20230630162005-9535e9dc2817
12+
github.com/anchore/go-logger v0.0.0-20230531193951-db5ae83e7dbe
1013
github.com/anchore/go-testutils v0.0.0-20200925183923-d5f45b0d3c04
1114
github.com/anchore/go-version v1.2.2-0.20210903204242-51efa5b487c4
1215
github.com/anchore/packageurl-go v0.1.1-0.20230104203445-02e0a6721501
13-
github.com/anchore/stereoscope v0.0.0-20230727211946-d1f3d766295e
16+
github.com/anchore/sqlite v1.4.6-0.20220607210448-bcc6ee5c4963
17+
github.com/anchore/stereoscope v0.0.0-20230829142608-334c2222e137
18+
github.com/anchore/syft v0.88.0
1419
github.com/bmatcuk/doublestar/v2 v2.0.4
20+
github.com/charmbracelet/bubbletea v0.24.2
21+
github.com/charmbracelet/lipgloss v0.8.0
1522
github.com/docker/docker v24.0.5+incompatible
1623
github.com/dustin/go-humanize v1.0.1
1724
github.com/facebookincubator/nvdtools v0.1.5
1825
github.com/gabriel-vasile/mimetype v1.4.2
26+
github.com/gkampitakis/go-snaps v0.4.8
1927
github.com/go-test/deep v1.1.0
2028
github.com/google/go-cmp v0.5.9
2129
github.com/google/uuid v1.3.1
2230
github.com/gookit/color v1.5.4
31+
github.com/hako/durafmt v0.0.0-20210608085754-5c1018a4e16b
2332
github.com/hashicorp/go-cleanhttp v0.5.2
2433
github.com/hashicorp/go-getter v1.7.2
2534
github.com/hashicorp/go-multierror v1.1.1
@@ -29,6 +38,7 @@ require (
2938
github.com/mholt/archiver/v3 v3.5.1
3039
github.com/mitchellh/go-homedir v1.1.0
3140
github.com/mitchellh/hashstructure/v2 v2.0.2
41+
github.com/mitchellh/mapstructure v1.5.0
3242
github.com/olekukonko/tablewriter v0.0.5
3343
github.com/owenrumney/go-sarif v1.1.1
3444
github.com/pkg/profile v1.7.0
@@ -42,27 +52,14 @@ require (
4252
github.com/spf13/viper v1.16.0
4353
github.com/stretchr/testify v1.8.4
4454
github.com/wagoodman/go-partybus v0.0.0-20230516145632-8ccac152c651
55+
github.com/wagoodman/go-presenter v0.0.0-20211015174752-f9c01afc824b
4556
github.com/wagoodman/go-progress v0.0.0-20230301185719-21920a456ad5
4657
github.com/x-cray/logrus-prefixed-formatter v0.5.2
4758
golang.org/x/term v0.11.0
4859
gopkg.in/yaml.v2 v2.4.0
4960
gorm.io/gorm v1.23.10
5061
)
5162

52-
require (
53-
github.com/anchore/bubbly v0.0.0-20230801194016-acdb4981b461
54-
github.com/anchore/clio v0.0.0-20230630162005-9535e9dc2817
55-
github.com/anchore/go-logger v0.0.0-20230531193951-db5ae83e7dbe
56-
github.com/anchore/sqlite v1.4.6-0.20220607210448-bcc6ee5c4963
57-
github.com/anchore/syft v0.88.0
58-
github.com/charmbracelet/bubbletea v0.24.2
59-
github.com/charmbracelet/lipgloss v0.8.0
60-
github.com/gkampitakis/go-snaps v0.4.8
61-
github.com/hako/durafmt v0.0.0-20210608085754-5c1018a4e16b
62-
github.com/mitchellh/mapstructure v1.5.0
63-
github.com/wagoodman/go-presenter v0.0.0-20211015174752-f9c01afc824b
64-
)
65-
6663
require (
6764
cloud.google.com/go v0.110.0 // indirect
6865
cloud.google.com/go/compute v1.19.3 // indirect

go.sum

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -249,8 +249,8 @@ github.com/anchore/packageurl-go v0.1.1-0.20230104203445-02e0a6721501 h1:AV7qjwM
249249
github.com/anchore/packageurl-go v0.1.1-0.20230104203445-02e0a6721501/go.mod h1:Blo6OgJNiYF41ufcgHKkbCKF2MDOMlrqhXv/ij6ocR4=
250250
github.com/anchore/sqlite v1.4.6-0.20220607210448-bcc6ee5c4963 h1:vrf2PYH77vqVJoNR15ZuFJ63qwBMqrmGIt/7VsBhLF8=
251251
github.com/anchore/sqlite v1.4.6-0.20220607210448-bcc6ee5c4963/go.mod h1:AVRyXOUP0hTz9Cb8OlD1XnwA8t4lBPfTuwPHmEUuiLc=
252-
github.com/anchore/stereoscope v0.0.0-20230727211946-d1f3d766295e h1:S6IhYpsBCpvphlHA1tN0glSG/kjVvFzC6OJuU2qW5Pc=
253-
github.com/anchore/stereoscope v0.0.0-20230727211946-d1f3d766295e/go.mod h1:0LsgHgXO4QFnk2hsYwtqd3fR18PIZXlFLIl2qb9tu3g=
252+
github.com/anchore/stereoscope v0.0.0-20230829142608-334c2222e137 h1:ZuiAV3lYKbGPkZR42UpoKecp/9aFU38CZSjaxjXCMYc=
253+
github.com/anchore/stereoscope v0.0.0-20230829142608-334c2222e137/go.mod h1:jkylrnuv++srUa2rsqCnG0n1ShD+0k2Xa6YtoNoRjAY=
254254
github.com/anchore/syft v0.88.0 h1:QRPcXwbQnxcOIfSZ5Sd6psfVQ756VICvx/HUMsIJEBw=
255255
github.com/anchore/syft v0.88.0/go.mod h1:6GgbZflKWC7ph2Zjb5wgq0ORKhwDaHG3xcjG84FSMPo=
256256
github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8=

internal/config/registry.go

Lines changed: 25 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -16,60 +16,76 @@ type RegistryCredentials struct {
1616
Password string `yaml:"-" json:"-" mapstructure:"password"`
1717
// IMPORTANT: do not show the token in any YAML/JSON output (sensitive information)
1818
Token string `yaml:"-" json:"-" mapstructure:"token"`
19+
20+
TLSCert string `yaml:"tls-cert,omitempty" json:"tls-cert,omitempty" mapstructure:"tls-cert"`
21+
TLSKey string `yaml:"tls-key,omitempty" json:"tls-key,omitempty" mapstructure:"tls-key"`
1922
}
2023

2124
type registry struct {
2225
InsecureSkipTLSVerify bool `yaml:"insecure-skip-tls-verify" json:"insecure-skip-tls-verify" mapstructure:"insecure-skip-tls-verify"`
2326
InsecureUseHTTP bool `yaml:"insecure-use-http" json:"insecure-use-http" mapstructure:"insecure-use-http"`
2427
Auth []RegistryCredentials `yaml:"auth" json:"auth" mapstructure:"auth"`
28+
CACert string `yaml:"ca-cert" json:"ca-cert" mapstructure:"ca-cert"`
2529
}
2630

2731
func (cfg registry) loadDefaultValues(v *viper.Viper) {
2832
v.SetDefault("registry.insecure-skip-tls-verify", false)
2933
v.SetDefault("registry.insecure-use-http", false)
3034
v.SetDefault("registry.auth", []RegistryCredentials{})
35+
v.SetDefault("registry.ca-cert", "")
3136
}
3237

3338
//nolint:unparam
3439
func (cfg *registry) parseConfigValues() error {
3540
// there may be additional credentials provided by env var that should be appended to the set of credentials
36-
authority, username, password, token :=
41+
authority, username, password, token, tlsCert, tlsKey :=
3742
os.Getenv("GRYPE_REGISTRY_AUTH_AUTHORITY"),
3843
os.Getenv("GRYPE_REGISTRY_AUTH_USERNAME"),
3944
os.Getenv("GRYPE_REGISTRY_AUTH_PASSWORD"),
40-
os.Getenv("GRYPE_REGISTRY_AUTH_TOKEN")
45+
os.Getenv("GRYPE_REGISTRY_AUTH_TOKEN"),
46+
os.Getenv("GRYPE_REGISTRY_AUTH_TLS_CERT"),
47+
os.Getenv("GRYPE_REGISTRY_AUTH_TLS_KEY")
4148

42-
if hasNonEmptyCredentials(username, password, token) {
49+
if hasNonEmptyCredentials(username, password, token, tlsCert, tlsKey) {
4350
// note: we prepend the credentials such that the environment variables take precedence over on-disk configuration.
4451
cfg.Auth = append([]RegistryCredentials{
4552
{
4653
Authority: authority,
4754
Username: username,
4855
Password: password,
4956
Token: token,
57+
TLSCert: tlsCert,
58+
TLSKey: tlsKey,
5059
},
5160
}, cfg.Auth...)
5261
}
5362
return nil
5463
}
5564

56-
func hasNonEmptyCredentials(username, password, token string) bool {
57-
return password != "" && username != "" || token != ""
65+
func hasNonEmptyCredentials(username, password, token, tlsCert, tlsKey string) bool {
66+
hasUserPass := username != "" && password != ""
67+
hasToken := token != ""
68+
hasTLSMaterial := tlsCert != "" && tlsKey != ""
69+
return hasUserPass || hasToken || hasTLSMaterial
5870
}
5971

6072
func (cfg *registry) ToOptions() *image.RegistryOptions {
6173
var auth = make([]image.RegistryCredentials, len(cfg.Auth))
6274
for i, a := range cfg.Auth {
6375
auth[i] = image.RegistryCredentials{
64-
Authority: a.Authority,
65-
Username: a.Username,
66-
Password: a.Password,
67-
Token: a.Token,
76+
Authority: a.Authority,
77+
Username: a.Username,
78+
Password: a.Password,
79+
Token: a.Token,
80+
ClientCert: a.TLSCert,
81+
ClientKey: a.TLSKey,
6882
}
6983
}
84+
7085
return &image.RegistryOptions{
7186
InsecureSkipTLSVerify: cfg.InsecureSkipTLSVerify,
7287
InsecureUseHTTP: cfg.InsecureUseHTTP,
7388
Credentials: auth,
89+
CAFileOrDir: cfg.CACert,
7490
}
7591
}

internal/config/registry_test.go

Lines changed: 47 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -11,47 +11,60 @@ import (
1111

1212
func TestHasNonEmptyCredentials(t *testing.T) {
1313
tests := []struct {
14-
username, password, token string
15-
expected bool
14+
username, password, token, cert, key string
15+
expected bool
1616
}{
17+
1718
{
18-
"", "", "",
19+
"", "", "", "", "",
1920
false,
2021
},
2122
{
22-
"user", "", "",
23+
"user", "", "", "", "",
2324
false,
2425
},
2526
{
26-
"", "pass", "",
27+
"", "pass", "", "", "",
2728
false,
2829
},
2930
{
30-
"", "pass", "tok",
31+
"", "pass", "tok", "", "",
3132
true,
3233
},
3334
{
34-
"user", "", "tok",
35+
"user", "", "tok", "", "",
3536
true,
3637
},
3738
{
38-
"", "", "tok",
39+
"", "", "tok", "", "",
3940
true,
4041
},
4142
{
42-
"user", "pass", "tok",
43+
"user", "pass", "tok", "", "",
4344
true,
4445
},
4546

4647
{
47-
"user", "pass", "",
48+
"user", "pass", "", "", "",
4849
true,
4950
},
51+
{
52+
"", "", "", "cert", "key",
53+
true,
54+
},
55+
{
56+
"", "", "", "cert", "",
57+
false,
58+
},
59+
{
60+
"", "", "", "", "key",
61+
false,
62+
},
5063
}
5164

5265
for _, test := range tests {
5366
t.Run(fmt.Sprintf("%+v", test), func(t *testing.T) {
54-
assert.Equal(t, test.expected, hasNonEmptyCredentials(test.username, test.password, test.token))
67+
assert.Equal(t, test.expected, hasNonEmptyCredentials(test.username, test.password, test.token, test.cert, test.key))
5568
})
5669
}
5770
}
@@ -101,6 +114,29 @@ func Test_registry_ToOptions(t *testing.T) {
101114
Credentials: []image.RegistryCredentials{},
102115
},
103116
},
117+
{
118+
name: "provide all tls configuration",
119+
input: registry{
120+
CACert: "ca.crt",
121+
InsecureSkipTLSVerify: true,
122+
Auth: []RegistryCredentials{
123+
{
124+
TLSCert: "client.crt",
125+
TLSKey: "client.key",
126+
},
127+
},
128+
},
129+
expected: image.RegistryOptions{
130+
CAFileOrDir: "ca.crt",
131+
InsecureSkipTLSVerify: true,
132+
Credentials: []image.RegistryCredentials{
133+
{
134+
ClientCert: "client.crt",
135+
ClientKey: "client.key",
136+
},
137+
},
138+
},
139+
},
104140
}
105141

106142
for _, test := range tests {

test/cli/registry_auth_test.go

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ func TestRegistryAuth(t *testing.T) {
1818
assertions: []traitAssertion{
1919
assertInOutput("source=OciRegistry"),
2020
assertInOutput("localhost:5000/something:latest"),
21-
assertInOutput("no registry credentials configured, using the default keychain"),
21+
assertInOutput(`no registry credentials configured for "localhost:5000", using the default keychain`),
2222
},
2323
},
2424
{
@@ -57,7 +57,7 @@ func TestRegistryAuth(t *testing.T) {
5757
assertions: []traitAssertion{
5858
assertInOutput("source=OciRegistry"),
5959
assertInOutput("localhost:5000/something:latest"),
60-
assertInOutput(`no registry credentials configured, using the default keychain`),
60+
assertInOutput(`no registry credentials configured for "localhost:5000", using the default keychain`),
6161
},
6262
},
6363
{
@@ -70,6 +70,17 @@ func TestRegistryAuth(t *testing.T) {
7070
assertInOutput("insecure-use-http: true"),
7171
},
7272
},
73+
{
74+
name: "use tls configuration",
75+
args: []string{"-vvv", "registry:localhost:5000/something:latest"},
76+
env: map[string]string{
77+
"GRYPE_REGISTRY_AUTH_TLS_CERT": "place.crt",
78+
"GRYPE_REGISTRY_AUTH_TLS_KEY": "place.key",
79+
},
80+
assertions: []traitAssertion{
81+
assertInOutput("using custom TLS credentials from"),
82+
},
83+
},
7384
}
7485

7586
for _, test := range tests {

0 commit comments

Comments
 (0)