Skip to content

Commit 7eb1575

Browse files
authored
Merge pull request #566 from jetstack/disable-config-server-field
VC-35630: User can now omit the `server` field in config when using the Venafi Connection mode
2 parents 6eb33e4 + d03c705 commit 7eb1575

File tree

8 files changed

+522
-228
lines changed

8 files changed

+522
-228
lines changed

deploy/charts/venafi-kubernetes-agent/README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,13 +26,13 @@ If you do not have one, you can sign up for a free trial now at:
2626
| authentication.venafiConnection.namespace | string | `"venafi"` | The namespace of a VenafiConnection resource which contains the configuration for authenticating to Venafi. |
2727
| command | list | `[]` | Specify the command to run overriding default binary. |
2828
| config | object | `{"clientId":"","clusterDescription":"","clusterName":"","configmap":{"key":null,"name":null},"ignoredSecretTypes":["kubernetes.io/service-account-token","kubernetes.io/dockercfg","kubernetes.io/dockerconfigjson","kubernetes.io/basic-auth","kubernetes.io/ssh-auth","bootstrap.kubernetes.io/token","helm.sh/release.v1"],"period":"0h1m0s","server":"https://api.venafi.cloud/"}` | Configuration section for the Venafi Kubernetes Agent itself |
29-
| config.clientId | string | `""` | The client-id returned from the Venafi Control Plane |
29+
| config.clientId | string | `""` | The client-id to be used for authenticating with the Venafi Control Plane. Only useful when using a Key Pair Service Account in the Venafi Control Plane. You can obtain the cliend ID by creating a Key Pair Service Account in the Venafi Control Plane. |
3030
| config.clusterDescription | string | `""` | Description for the cluster resource if it needs to be created in Venafi Control Plane |
3131
| config.clusterName | string | `""` | Name for the cluster resource if it needs to be created in Venafi Control Plane |
3232
| config.configmap | object | `{"key":null,"name":null}` | Specify ConfigMap details to load config from an existing resource. This should be blank by default unless you have you own config. |
3333
| config.ignoredSecretTypes | list | `["kubernetes.io/service-account-token","kubernetes.io/dockercfg","kubernetes.io/dockerconfigjson","kubernetes.io/basic-auth","kubernetes.io/ssh-auth","bootstrap.kubernetes.io/token","helm.sh/release.v1"]` | Reduce the memory usage of the agent and reduce the load on the Kubernetes API server by omitting various common Secret types when listing Secrets. These Secret types will be added to a "type!=<type>" field selector in the agent config. * https://docs.venafi.cloud/vaas/k8s-components/t-cfg-tlspk-agent/#configuration * https://kubernetes.io/docs/concepts/configuration/secret/#secret-types * https://kubernetes.io/docs/concepts/overview/working-with-objects/field-selectors/#list-of-supported-fields |
3434
| config.period | string | `"0h1m0s"` | Send data back to the platform every minute unless changed |
35-
| config.server | string | `"https://api.venafi.cloud/"` | Overrides the server if using a proxy in your environment For the EU variant use: https://api.venafi.eu/ |
35+
| config.server | string | `"https://api.venafi.cloud/"` | API URL of the Venafi Control Plane API. For EU tenants, set this value to https://api.venafi.eu/. If you are using the VenafiConnection authentication method, you must set the API URL using the field `spec.vcp.url` on the VenafiConnection resource instead. |
3636
| crds.forceRemoveValidationAnnotations | bool | `false` | The 'x-kubernetes-validations' annotation is not supported in Kubernetes 1.22 and below. This annotation is used by CEL, which is a feature introduced in Kubernetes 1.25 that improves how validation is performed. This option allows to force the 'x-kubernetes-validations' annotation to be excluded, even on Kubernetes 1.25+ clusters. |
3737
| crds.venafiConnection | object | `{"include":false}` | Optionally include the VenafiConnection CRDs |
3838
| crds.venafiConnection.include | bool | `false` | When set to false, the rendered output does not contain the VenafiConnection CRDs and RBAC. This is useful for when the Venafi Connection resources are already installed separately. |

deploy/charts/venafi-kubernetes-agent/values.yaml

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -191,10 +191,15 @@ authentication:
191191

192192
# -- Configuration section for the Venafi Kubernetes Agent itself
193193
config:
194-
# -- Overrides the server if using a proxy in your environment
195-
# For the EU variant use: https://api.venafi.eu/
194+
# -- API URL of the Venafi Control Plane API. For EU tenants, set this value to
195+
# https://api.venafi.eu/. If you are using the VenafiConnection authentication
196+
# method, you must set the API URL using the field `spec.vcp.url` on the
197+
# VenafiConnection resource instead.
196198
server: "https://api.venafi.cloud/"
197-
# -- The client-id returned from the Venafi Control Plane
199+
# -- The client-id to be used for authenticating with the Venafi Control
200+
# Plane. Only useful when using a Key Pair Service Account in the Venafi
201+
# Control Plane. You can obtain the cliend ID by creating a Key Pair Service
202+
# Account in the Venafi Control Plane.
198203
clientId: ""
199204
# -- Send data back to the platform every minute unless changed
200205
period: "0h1m0s"

pkg/agent/config.go

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import (
1919
"github.com/pkg/errors"
2020
"github.com/spf13/cobra"
2121
"gopkg.in/yaml.v3"
22+
"k8s.io/client-go/rest"
2223
)
2324

2425
const (
@@ -266,13 +267,22 @@ func getConfiguration(log *log.Logger, cfg Config, flags AgentCmdFlags) (Config,
266267
flags.VenafiCloudMode = true
267268
}
268269

269-
baseURL := cfg.Server
270-
if baseURL == "" {
271-
log.Printf("Using deprecated Endpoint configuration. User Server instead.")
272-
baseURL = fmt.Sprintf("%s://%s", cfg.Endpoint.Protocol, cfg.Endpoint.Host)
273-
_, err := url.Parse(baseURL)
274-
if err != nil {
275-
return Config{}, nil, fmt.Errorf("failed to parse server URL: %w", err)
270+
// In VenafiConnection mode, we don't need the server field. For the other
271+
// modes, we do need to validate the server field.
272+
var baseURL string
273+
if flags.VenConnName != "" {
274+
if cfg.Server != "" {
275+
log.Printf("ignoring the server field specified in the config file. In Venafi Connection mode, this field is not needed. Use the VenafiConnection's spec.vcp.url field instead.")
276+
}
277+
} else {
278+
baseURL = cfg.Server
279+
if baseURL == "" {
280+
log.Printf("Using deprecated Endpoint configuration. User Server instead.")
281+
baseURL = fmt.Sprintf("%s://%s", cfg.Endpoint.Protocol, cfg.Endpoint.Host)
282+
_, err := url.Parse(baseURL)
283+
if err != nil {
284+
return Config{}, nil, fmt.Errorf("failed to parse server URL: %w", err)
285+
}
276286
}
277287
}
278288

@@ -354,7 +364,8 @@ func getConfiguration(log *log.Logger, cfg Config, flags AgentCmdFlags) (Config,
354364
log.Printf(`ignoring venafi-cloud.uploader_id. In Venafi Connection mode, this field is not needed.`)
355365
}
356366

357-
restCfg, err := kubeconfig.LoadRESTConfig("")
367+
var restCfg *rest.Config
368+
restCfg, err = kubeconfig.LoadRESTConfig("")
358369
if err != nil {
359370
return Config{}, nil, fmt.Errorf("failed to load kubeconfig: %w", err)
360371
}

pkg/agent/config_test.go

Lines changed: 143 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,24 @@ package agent
22

33
import (
44
"bytes"
5+
"context"
6+
"crypto/x509"
57
"fmt"
68
"io"
79
"log"
10+
"net/http"
811
"os"
912
"strings"
1013
"testing"
1114
"time"
1215

1316
"github.com/jetstack/preflight/pkg/client"
17+
"github.com/jetstack/preflight/pkg/testutil"
1418
"github.com/kylelemons/godebug/diff"
19+
"github.com/spf13/cobra"
1520
"github.com/stretchr/testify/assert"
21+
"github.com/stretchr/testify/require"
22+
"gopkg.in/yaml.v3"
1623
)
1724

1825
func TestGetConfiguration(t *testing.T) {
@@ -171,7 +178,7 @@ func TestGetConfiguration(t *testing.T) {
171178
assert.Equal(t, Config{}, got)
172179
})
173180

174-
t.Run("warning about venafi-cloud.uploader_id and venafi-cloud.upload_path being skipped", func(t *testing.T) {
181+
t.Run("warning about server, venafi-cloud.uploader_id, and venafi-cloud.upload_path being skipped", func(t *testing.T) {
175182
log, out := withLogs(t)
176183
cfg := fillRequired(Config{VenafiCloud: &VenafiCloudConfig{
177184
UploaderID: "test-agent",
@@ -184,7 +191,112 @@ func TestGetConfiguration(t *testing.T) {
184191
assert.Equal(t, cfg, got)
185192
assert.Contains(t, out.String(), "ignoring venafi-cloud.uploader_id")
186193
assert.Contains(t, out.String(), "ignoring venafi-cloud.upload_path")
194+
assert.Contains(t, out.String(), "ignoring the server field")
187195
})
196+
197+
t.Run("server field can be left empty in venconn mode", func(t *testing.T) {
198+
_, _, err := getConfiguration(discardLogs(t),
199+
withConfig(testutil.Undent(`
200+
server: ""
201+
period: 1h`,
202+
)),
203+
withCmdLineFlags("--venafi-connection", "venafi-components", "--install-namespace", "venafi"))
204+
assert.NoError(t, err)
205+
})
206+
})
207+
}
208+
209+
// Slower test cases due to envtest. That's why they are separated from the
210+
// other tests.
211+
func Test_getConfiguration_urlWhenVenafiConnection(t *testing.T) {
212+
t.Run("the server field is ignored when VenafiConnection is used", func(t *testing.T) {
213+
_, restCfg, kcl := testutil.WithEnvtest(t)
214+
os.Setenv("KUBECONFIG", testutil.WithKubeconfig(t, restCfg))
215+
srv, fakeCrt, setVenafiCloudAssert := testutil.FakeVenafiCloud(t)
216+
for _, obj := range testutil.Parse(
217+
testutil.VenConnRBAC + testutil.Undent(fmt.Sprintf(`
218+
---
219+
apiVersion: jetstack.io/v1alpha1
220+
kind: VenafiConnection
221+
metadata:
222+
name: venafi-components
223+
namespace: venafi
224+
spec:
225+
vcp:
226+
url: "%s"
227+
accessToken:
228+
- secret:
229+
name: accesstoken
230+
fields: [accesstoken]
231+
---
232+
apiVersion: v1
233+
kind: Secret
234+
metadata:
235+
name: accesstoken
236+
namespace: venafi
237+
stringData:
238+
accesstoken: VALID_ACCESS_TOKEN
239+
---
240+
apiVersion: rbac.authorization.k8s.io/v1
241+
kind: Role
242+
metadata:
243+
name: venafi-connection-accesstoken-reader
244+
namespace: venafi
245+
rules:
246+
- apiGroups: [""]
247+
resources: ["secrets"]
248+
verbs: ["get"]
249+
resourceNames: ["accesstoken"]
250+
---
251+
apiVersion: rbac.authorization.k8s.io/v1
252+
kind: RoleBinding
253+
metadata:
254+
name: venafi-connection-accesstoken-reader
255+
namespace: venafi
256+
roleRef:
257+
apiGroup: rbac.authorization.k8s.io
258+
kind: Role
259+
name: venafi-connection-accesstoken-reader
260+
subjects:
261+
- kind: ServiceAccount
262+
name: venafi-connection
263+
namespace: venafi`, srv.URL))) {
264+
require.NoError(t, kcl.Create(context.Background(), obj))
265+
}
266+
267+
// The URL received by the fake Venafi Cloud server should be the one
268+
// coming from the VenafiConnection, not the one from the config.
269+
setVenafiCloudAssert(func(t testing.TB, r *http.Request) {
270+
assert.Equal(t, srv.URL, "https://"+r.Host)
271+
})
272+
273+
cfg, err := ParseConfig([]byte(testutil.Undent(`
274+
server: "http://should-be-ignored"
275+
period: 1h
276+
`)), true)
277+
assert.NoError(t, err)
278+
279+
_, cl, err := getConfiguration(discardLogs(t),
280+
cfg,
281+
withCmdLineFlags("--venafi-connection", "venafi-components", "--install-namespace", "venafi"),
282+
)
283+
assert.NoError(t, err)
284+
285+
// `Start(ctx)` needs to be stopped before the apiserver is stopped.
286+
// https://github.com/jetstack/venafi-connection-lib/pull/158#issuecomment-1949002322
287+
ctx, cancel := context.WithCancel(context.Background())
288+
t.Cleanup(cancel)
289+
go func() {
290+
require.NoError(t, cl.(*client.VenConnClient).Start(ctx))
291+
}()
292+
certPool := x509.NewCertPool()
293+
certPool.AddCert(fakeCrt)
294+
tr := http.DefaultTransport.(*http.Transport).Clone()
295+
tr.TLSClientConfig.RootCAs = certPool
296+
cl.(*client.VenConnClient).Client.Transport = tr
297+
298+
err = cl.PostDataReadingsWithOptions(nil, client.Options{ClusterName: "test cluster name"})
299+
assert.NoError(t, err)
188300
})
189301
}
190302

@@ -457,15 +569,43 @@ func withFile(t testing.TB, content string) string {
457569
return f.Name()
458570
}
459571

460-
func withLogs(t testing.TB) (*log.Logger, *bytes.Buffer) {
572+
func withLogs(_ testing.TB) (*log.Logger, *bytes.Buffer) {
461573
b := bytes.Buffer{}
462574
return log.New(&b, "", 0), &b
463575
}
464576

465-
func discardLogs(t testing.TB) *log.Logger {
577+
func discardLogs(_ testing.TB) *log.Logger {
466578
return log.New(io.Discard, "", 0)
467579
}
468580

581+
// ParseConfig does some validation but we don't want this extra validation, so
582+
// this is just using yaml.Unmarshal.
583+
func withConfig(s string) Config {
584+
var cfg Config
585+
586+
err := yaml.Unmarshal([]byte(s), &cfg)
587+
if err != nil {
588+
panic(err)
589+
}
590+
return cfg
591+
}
592+
593+
func withCmdLineFlags(flags ...string) AgentCmdFlags {
594+
parsed := withoutCmdLineFlags()
595+
agentCmd := &cobra.Command{}
596+
InitAgentCmdFlags(agentCmd, &parsed)
597+
err := agentCmd.ParseFlags(flags)
598+
if err != nil {
599+
panic(err)
600+
}
601+
602+
return parsed
603+
}
604+
605+
func withoutCmdLineFlags() AgentCmdFlags {
606+
return AgentCmdFlags{}
607+
}
608+
469609
const fakeKubeconfig = `
470610
apiVersion: v1
471611
clusters:

pkg/client/client_venafi_cloud.go

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -288,9 +288,6 @@ func (c *VenafiCloudClient) updateAccessToken() error {
288288
values.Set("assertion", jwtToken)
289289

290290
tokenURL := fullURL(c.baseURL, accessTokenEndpoint)
291-
if err != nil {
292-
return err
293-
}
294291

295292
encoded := values.Encode()
296293
request, err := http.NewRequest(http.MethodPost, tokenURL, strings.NewReader(encoded))

pkg/client/client_venconn.go

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -29,10 +29,15 @@ import (
2929
type VenConnClient struct {
3030
agentMetadata *api.AgentMetadata
3131
connHandler venafi_client.ConnectionHandler
32-
installNS string // Namespace in which the agent is running in.
33-
venConnName string // Name of the VenafiConnection resource to use.
34-
venConnNS string // Namespace of the VenafiConnection resource to use.
35-
client *http.Client // Used to make HTTP requests to Venafi Cloud.
32+
installNS string // Namespace in which the agent is running in.
33+
venConnName string // Name of the VenafiConnection resource to use.
34+
venConnNS string // Namespace of the VenafiConnection resource to use.
35+
36+
// Used to make HTTP requests to Venafi Cloud. This field is public for
37+
// testing purposes so that we can configure trusted CAs; there should be a
38+
// way to do that without messing with the client directly (e.g., a flag to
39+
// pass a custom CA?), but it's not there yet.
40+
Client *http.Client
3641
}
3742

3843
// NewVenConnClient lets you make requests to the Venafi Cloud backend using the
@@ -111,7 +116,7 @@ func NewVenConnClient(restcfg *rest.Config, agentMetadata *api.AgentMetadata, in
111116
installNS: installNS,
112117
venConnName: venConnName,
113118
venConnNS: venConnNS,
114-
client: vcpClient,
119+
Client: vcpClient,
115120
}, nil
116121
}
117122

@@ -180,7 +185,7 @@ func (c *VenConnClient) PostDataReadingsWithOptions(readings []*api.DataReading,
180185
}
181186
req.URL.RawQuery = q.Encode()
182187

183-
res, err := c.client.Do(req)
188+
res, err := c.Client.Do(req)
184189
if err != nil {
185190
return err
186191
}

0 commit comments

Comments
 (0)