New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
HELM-342: Add basic authentication support for Helm repositories #11782
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|
@@ -1,8 +1,8 @@ | ||||||||||||
# Configure Namespace-scoped Helm Chart Repositories Using Namespaced CRDs | ||||||||||||
|
||||||||||||
The cluster-scoped `[HelmChartRepository](https://github.com/openshift/api/blob/master/helm/v1beta1/0000_10-helm-chart-repository.crd.yaml)` CRD for Helm repository provides the ability for admins to add Helm repositories as custom resources. For helm chart repositories that support tls authentication the tls configuration secret has to be created in `openshift-config` namespace. | ||||||||||||
The cluster-scoped [HelmChartRepository](https://github.com/openshift/api/blob/master/helm/v1beta1/0000_10-helm-chart-repository.crd.yaml) custom resource definition (CRD) for Helm repository provides the ability for admins to add Helm repositories as custom resources (CRs). For Helm chart repositories that support TLS authentication, the TLS configuration secret has to be created in the `openshift-config` namespace. | ||||||||||||
|
||||||||||||
The namespace-scoped `[ProjectHelmChartRepository](https://github.com/openshift/api/blob/master/helm/v1beta1/0000_10-project-chart-repository.crd.yaml)` CRD allows project members with the appropriate RBAC permissions to create Helm repository resources of their choice but scoped to their namespace. Such project members can see charts from both cluster-scoped and namespace-scoped Helm repository resources (CRs). For namespace-scoped helm chart repositories that support tls authentication the tls configuration secret and certificate authority config map must be created in the namespace where the repository is getting instantiated. | ||||||||||||
The namespace-scoped `[ProjectHelmChartRepository](https://github.com/openshift/api/blob/master/helm/v1beta1/0000_10-project-chart-repository.crd.yaml)` CRD allows project members with the appropriate RBAC permissions to create Helm repository resources of their choice but scoped to their namespace. Such project members can see charts from both cluster-scoped and namespace-scoped Helm repository resources (CRs). For namespace-scoped Helm chart repositories that support TLS authentication, the TLS configuration secret and certificate authority (CA) config map must be created in the namespace where the repository is getting instantiated. The namespace-scoped Helm chart repositories also support basic authentication. A user can create a secret with a username and password in order to add authentication for their repository. | ||||||||||||
|
||||||||||||
_Note_: Administrators can limit users from creating namespace-scoped Helm repository resources. By limiting users, admins have the flexibility to control the RBAC through a namespace role instead of a cluster role. Doing so avoids unnecessary permission elevation for the user and prevents access to unauthorized services or applications. | ||||||||||||
|
||||||||||||
|
@@ -25,15 +25,16 @@ spec: | |||||||||||
# required: chart repository URL | ||||||||||||
# optional: tlsClientConfig is an optional reference to a secret by name that contains the PEM-encoded TLS client certificate and private key to present when connecting to the server. The key "tls.crt" is used to locate the client certificate. The key "tls.key" is used to locate the private key. The namespace for this secret must be same as the namespace where the project helm chart repository is getting instantiated. | ||||||||||||
# optional: ca is an optional reference to a config map by name containing the PEM-encoded CA bundle. It is used as a trust anchor to validate the TLS certificate presented by the remote server. The key "ca-bundle.crt" is used to locate the data. If empty, the default system roots are used. The namespace for this configmap must be same as the namespace where the project helm chart repository is getting instantiated. | ||||||||||||
connectionConfig: | ||||||||||||
# optional: basicAuthConfig is an optional reference to a secret by name that contains the basic authentication credentials to be present when connecting to the server. The key "username" is used to locate the username. The key "password" is used to locate the password. The namespace for this secret must be the same as the namespace where the project helm chart repository is getting instantiated. | ||||||||||||
url: HELM_CHART_REPO_URL | ||||||||||||
tlsClientConfig: HELM_CHART_TLS_CLIENT_CONFIG_SECRET_NAME | ||||||||||||
ca: HELM_CHART_CA_CONFIG_MAP_NAME | ||||||||||||
basicAuthConfig: HELM_CHART_BASIC_AUTH_SECRET_NAME | ||||||||||||
``` | ||||||||||||
|
||||||||||||
## Adding Namespace-scoped Custom Helm Chart Repositories | ||||||||||||
|
||||||||||||
To add a new namespace-scoped Helm repository, add a custom Helm Chart Repository CR, for example, `azure-sample-repo` CR to your `my-namespace` namespace: | ||||||||||||
1. To add a new namespace-scoped Helm repository, add a custom Helm Chart Repository CR, for example, `azure-sample-repo` CR to your `my-namespace` namespace: | ||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
A single step must be just as a bullet item. |
||||||||||||
|
||||||||||||
```bash | ||||||||||||
$ cat <<EOF | kubectl apply --namespace my-namespace -f - | ||||||||||||
|
@@ -46,14 +47,59 @@ spec: | |||||||||||
connectionConfig: | ||||||||||||
url: https://raw.githubusercontent.com/Azure-Samples/helm-charts/master/docs | ||||||||||||
EOF | ||||||||||||
``` | ||||||||||||
|
||||||||||||
2. Verify that the `ProjectHelmChartRepository` CR is added successfully: | ||||||||||||
|
||||||||||||
``` | ||||||||||||
$ kubectl get projecthelmchartrepositories --namespace my-namespace | ||||||||||||
NAME AGE | ||||||||||||
azure-sample-repo 1m | ||||||||||||
``` | ||||||||||||
|
||||||||||||
The addition of the namespace-scoped Helm repository does not impact the behavior of the existing cluster-scoped Helm repository. | ||||||||||||
|
||||||||||||
## Adding Namespace-scoped Custom Helm Chart Repositories Supporting Basic Authentication | ||||||||||||
|
||||||||||||
1. To create a project helm chart repository that supports basic authentication, create a secret with `username` and `password` in the same namespace where the repository is getting instantiated, for example, in the `my-namespace` namespace: | ||||||||||||
|
||||||||||||
```bash | ||||||||||||
$ cat <<EOF | kubectl apply --namespace my-namespace | ||||||||||||
-f - | ||||||||||||
apiVersion: v1 | ||||||||||||
kind: Secret | ||||||||||||
metadata: | ||||||||||||
name: basic-secret | ||||||||||||
stringData: | ||||||||||||
username: AzureDiamond | ||||||||||||
password: hunter2 | ||||||||||||
EOF | ||||||||||||
``` | ||||||||||||
|
||||||||||||
2. To add a new namespace-scoped Helm repository that supports basic authentication, add a custom Helm Chart Repository CR, for example, `azure-sample-repo` CR to your `my-namespace` namespace. Also, reference the secret created, in the `basicAuthConfig` field of the `ProjectHelmChartRepository` CR: | ||||||||||||
|
||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Suggestions:
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Add this step here. |
||||||||||||
```bash | ||||||||||||
$ cat <<EOF | kubectl apply --namespace my-namespace -f - | ||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||||
apiVersion: helm.openshift.io/v1beta1 | ||||||||||||
kind: ProjectHelmChartRepository | ||||||||||||
metadata: | ||||||||||||
name: azure-sample-repo | ||||||||||||
spec: | ||||||||||||
name: azure-sample-repo | ||||||||||||
connectionConfig: | ||||||||||||
url: https://raw.githubusercontent.com/Azure-Samples/helm-charts/master/docs | ||||||||||||
basicAuthConfig: basic-secret | ||||||||||||
EOF | ||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||||
``` | ||||||||||||
|
||||||||||||
3. Verify that the `ProjectHelmChartRepository` CR is added successfully: | ||||||||||||
|
||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||||||||||||
``` | ||||||||||||
$ kubectl get projecthelmchartrepositories --namespace my-namespace | ||||||||||||
NAME AGE | ||||||||||||
azure-sample-repo 1m | ||||||||||||
``` | ||||||||||||
|
||||||||||||
## API Endpoint `/api/helm/charts/index.yaml` | ||||||||||||
|
||||||||||||
The `/api/helm/charts/index.yaml` endpoint supports a `namespace` query parameter. For example, a GET request to `/api/helm/charts/index.yaml?namespace=my-namespace` will respond an aggregated `index.yaml` file with entities extracted from both cluster-scoped and namespace-scoped Helm repositories. |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -253,6 +253,125 @@ func TestGetChartWithTlsData(t *testing.T) { | |
} | ||
} | ||
|
||
func TestGetChartBasicAuth(t *testing.T) { | ||
tests := []struct { | ||
name string | ||
chartPath string | ||
chartName string | ||
indexEntry string | ||
repositoryNamespace string | ||
createSecret bool | ||
createNamespace bool | ||
namespace string | ||
requireError bool | ||
helmCRS []*unstructured.Unstructured | ||
}{ | ||
{ | ||
name: "mychart", | ||
chartPath: "http://localhost:8181/charts/mychart-0.1.0.tgz", | ||
chartName: "mychart", | ||
createSecret: true, | ||
createNamespace: true, | ||
namespace: "test", | ||
indexEntry: "mychart--my-repo", | ||
helmCRS: []*unstructured.Unstructured{ | ||
{ | ||
Object: map[string]interface{}{ | ||
"apiVersion": "helm.openshift.io/v1beta1", | ||
"kind": "ProjectHelmChartRepository", | ||
"metadata": map[string]interface{}{ | ||
"namespace": "test", | ||
"name": "my-repo", | ||
}, | ||
"spec": map[string]interface{}{ | ||
"connectionConfig": map[string]interface{}{ | ||
"url": "http://localhost:8181", | ||
"basicAuthConfig": map[string]interface{}{ | ||
"name": "my-repo", | ||
}, | ||
}, | ||
}, | ||
}, | ||
}, | ||
}, | ||
}, | ||
{ | ||
name: "Invalid chart url", | ||
chartPath: "http://localhost:8181/charts/mychart-0.2.0.tgz", | ||
chartName: "mychart", | ||
createSecret: true, | ||
createNamespace: true, | ||
namespace: "test", | ||
indexEntry: "mychart--my-repo", | ||
requireError: true, | ||
helmCRS: []*unstructured.Unstructured{ | ||
{ | ||
Object: map[string]interface{}{ | ||
"apiVersion": "helm.openshift.io/v1beta1", | ||
"kind": "ProjectHelmChartRepository", | ||
"metadata": map[string]interface{}{ | ||
"namespace": "test", | ||
"name": "my-repo", | ||
}, | ||
"spec": map[string]interface{}{ | ||
"connectionConfig": map[string]interface{}{ | ||
"url": "http://localhost:8181", | ||
"basicAuthConfig": map[string]interface{}{ | ||
"name": "my-repo", | ||
}, | ||
}, | ||
}, | ||
}, | ||
}, | ||
}, | ||
}, | ||
{ | ||
name: "Invalid chart url", | ||
chartPath: "../testdata/invalid.tgz", | ||
requireError: true, | ||
}, | ||
} | ||
store := storage.Init(driver.NewMemory()) | ||
actionConfig := &action.Configuration{ | ||
RESTClientGetter: FakeConfig{}, | ||
Releases: store, | ||
KubeClient: &kubefake.PrintingKubeClient{Out: ioutil.Discard}, | ||
Capabilities: chartutil.DefaultCapabilities, | ||
Log: func(format string, v ...interface{}) {}, | ||
} | ||
for _, test := range tests { | ||
t.Run(test.name, func(t *testing.T) { | ||
objs := []runtime.Object{} | ||
// create a namespace if it is not same as openshift-config | ||
if test.createNamespace { | ||
nsSpec := &v1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: test.namespace}} | ||
objs = append(objs, nsSpec) | ||
} | ||
// create a secret in required namespace | ||
if test.createSecret { | ||
data := map[string][]byte{ | ||
username: []byte("AzureDiamond"), | ||
password: []byte("hunter2"), | ||
} | ||
secretSpec := &v1.Secret{Data: data, ObjectMeta: metav1.ObjectMeta{Name: "my-repo", Namespace: test.namespace}} | ||
objs = append(objs, secretSpec) | ||
} | ||
|
||
client := K8sDynamicClientFromCRs(test.helmCRS...) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. doesnt the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm pretty sure it does, but the initial implementation was already using the dynamic client would modify the code more than initially wanted, so we kept it for the time being. |
||
clientInterface := k8sfake.NewSimpleClientset(objs...) | ||
coreClient := clientInterface.CoreV1() | ||
chart, err := GetChart(test.chartPath, actionConfig, test.namespace, client, coreClient, false, test.indexEntry) | ||
if test.requireError { | ||
require.Error(t, err) | ||
} else { | ||
require.NoError(t, err) | ||
require.NotNil(t, chart.Metadata) | ||
require.Equal(t, chart.Metadata.Name, test.chartName) | ||
} | ||
}) | ||
} | ||
} | ||
|
||
func K8sDynamicClientFromCRs(crs ...*unstructured.Unstructured) dynamic.Interface { | ||
var objs []runtime.Object | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@Kartikey-star Thanks for adding this back.