Git Product home page Git Product logo

chartmuseum's Introduction

ChartMuseum

GitHub Actions status Go Report Card GoDoc


ChartMuseum is an open-source Helm Chart Repository server written in Go (Golang), with support for cloud storage backends, including Google Cloud Storage, Amazon S3, Microsoft Azure Blob Storage, Alibaba Cloud OSS Storage, Openstack Object Storage, Oracle Cloud Infrastructure Object Storage, Baidu Cloud BOS Storage, Tencent Cloud Object Storage, DigitalOcean Spaces, Minio, and etcd.

Works as a valid Helm Chart Repository, and also provides an API for uploading charts.

Powered by some great Go technology:

API

Helm Chart Repository

  • GET /index.yaml - retrieved when you run helm repo add chartmuseum http://localhost:8080/
  • GET /charts/mychart-0.1.0.tgz - retrieved when you run helm install chartmuseum/mychart
  • GET /charts/mychart-0.1.0.tgz.prov - retrieved when you run helm install with the --verify flag

Chart Manipulation

  • POST /api/charts - upload a new chart version
  • POST /api/prov - upload a new provenance file
  • DELETE /api/charts/<name>/<version> - delete a chart version (and corresponding provenance file)
  • GET /api/charts - list all charts
  • GET /api/charts/<name> - list all versions of a chart
  • GET /api/charts/<name>/<version> - describe a chart version
  • GET /api/charts/<name>/<version>/templates - get chart template
  • GET /api/charts/<name>/<version>/values - get chart values
  • HEAD /api/charts/<name> - check if chart exists (any versions)
  • HEAD /api/charts/<name>/<version> - check if chart version exists

Server Info

  • GET / - HTML welcome page
  • GET /info - returns current ChartMuseum version
  • GET /health - returns 200 OK

Uploading a Chart Package

Follow "How to Run" section below to get ChartMuseum up and running at http://localhost:8080

First create mychart-0.1.0.tgz using the Helm CLI:

cd mychart/
helm package .

Upload mychart-0.1.0.tgz:

curl --data-binary "@mychart-0.1.0.tgz" http://localhost:8080/api/charts

If you've signed your package and generated a provenance file, upload it with:

curl --data-binary "@mychart-0.1.0.tgz.prov" http://localhost:8080/api/prov

Both files can also be uploaded at once (or one at a time) on the /api/charts route using the multipart/form-data format:

curl -F "[email protected]" -F "[email protected]" http://localhost:8080/api/charts

You can also use the helm-push plugin:

helm cm-push mychart/ chartmuseum

Installing Charts into Kubernetes

Add the URL to your ChartMuseum installation to the local repository list:

helm repo add chartmuseum http://localhost:8080

Search for charts:

helm search repo chartmuseum/

Install chart:

helm install chartmuseum/mychart --generate-name

How to Run

CLI

Installation

You can use the installer script:

curl https://raw.githubusercontent.com/helm/chartmuseum/main/scripts/get-chartmuseum | bash

or download manually from the releases page, which also contains all package checksums and signatures.

Determine your version with chartmuseum --version.

Configuration

Show all CLI options with chartmuseum --help. Common configurations can be seen below.

All command-line options can be specified as environment variables, which are defined by the command-line option, capitalized, with all -'s replaced with _'s.

For example, the env var STORAGE_AMAZON_BUCKET can be used in place of --storage-amazon-bucket.

Using a configuration file

Use chartmuseum --config config.yaml to read configuration from a file.

When using file-based configuration, the corresponding option name can be looked up in pkg/config/vars.go. It would be the key of configVars entry corresponding to the command line option / environment variable. For example, --storage corresponds to storage.backend in the configuration file.

Here's a complete example of a config.yaml:

debug: true
port: 8080
storage.backend: local
storage.local.rootdir: <storage_path>
bearerauth: 1
authrealm: <authorization server url>
authservice: <authorization server service name>
authcertpath: <path to authorization server public pem file>
authactionssearchpath: <optional: JMESPath to find allowed actions in a jwt token>
depth: 2

Using with Amazon S3 or Compatible services like Minio or DigitalOcean.

Make sure your environment is properly setup to access my-s3-bucket

For Amazon S3, endpoint is automatically inferred.

chartmuseum --debug --port=8080 \
  --storage="amazon" \
  --storage-amazon-bucket="my-s3-bucket" \
  --storage-amazon-prefix="" \
  --storage-amazon-region="us-east-1"

For S3 compatible services like Minio, set the credentials using environment variables and pass the endpoint.

export AWS_ACCESS_KEY_ID=""
export AWS_SECRET_ACCESS_KEY=""
chartmuseum --debug --port=8080 \
  --storage="amazon" \
  --storage-amazon-bucket="my-s3-bucket" \
  --storage-amazon-prefix="" \
  --storage-amazon-region="us-east-1" \
  --storage-amazon-endpoint="my-s3-compatible-service-endpoint"

You need at least the following permissions inside your IAM Policy

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "AllowListObjects",
      "Effect": "Allow",
      "Action": [
        "s3:ListBucket"
      ],
      "Resource": "arn:aws:s3:::my-s3-bucket"
    },
    {
      "Sid": "AllowObjectsCRUD",
      "Effect": "Allow",
      "Action": [
        "s3:DeleteObject",
        "s3:GetObject",
        "s3:PutObject"
      ],
      "Resource": "arn:aws:s3:::my-s3-bucket/*"
    }
  ]
}

In order to work with AWS service accounts you may need to set AWS_SDK_LOAD_CONFIG=1 in your environment. For more context, please see here.

If you are using S3-Compatible storage, provider of S3 storage has disabled path-style and force virtual hosted-style, you can use specify storage-amazon-force-path-style options as following example:

export AWS_ACCESS_KEY_ID=""
export AWS_SECRET_ACCESS_KEY=""
chartmuseum --debug --port=8080 \
  --storage="amazon" \
  --storage-amazon-bucket="my-s3-bucket" \
  --storage-amazon-prefix="" \
  --storage-amazon-region="us-east-1" \
  --storage-amazon-endpoint="my-s3-compatible-service-endpoint"
  --storage-amazon-force-path-style=false

For DigitalOcean, set the credentials using environment variable and pass the endpoint. Note below, that the region us-east-1 needs to be set, since that is how the DigitalOcean cli implementation functions. The actual region of your spaces location is defined by the endpoint. Below we are using Frankfurt as an example.

export AWS_ACCESS_KEY_ID="spaces_access_key"
export AWS_SECRET_ACCESS_KEY="spaces_secret_key"
  chartmuseum --debug --port=8080 \
  --storage="amazon" \
  --storage-amazon-bucket="my_spaces_name" \
  --storage-amazon-prefix="my_spaces_name_subfolder" \
  --storage-amazon-region="us-east-1" \
  --storage-amazon-endpoint="https://fra1.digitaloceanspaces.com"

The access_key and secret_key can be generated from the DigitalOcean console, under the section API/Spaces_access_keys.

Note: on certain S3-based storage backends, the LastModified field on objects is truncated to the nearest second. For more info, please see issue #152.

In order to mitigate this, you may use use the --storage-timestamp-tolerance option. For example, to round to the nearest second, you could use --storage-timestamp-tolerance=1s. For acceptable values to use for this field, please see here.

Using with Google Cloud Storage

Make sure your environment is properly setup to access my-gcs-bucket.

One way to do so is to set the GOOGLE_APPLICATION_CREDENTIALS var in your environment, pointing to the JSON file containing your service account key:

export GOOGLE_APPLICATION_CREDENTIALS="/home/user/Downloads/[FILE_NAME].json"

More info on Google Cloud authentication can be found here.

chartmuseum --debug --port=8080 \
  --storage="google" \
  --storage-google-bucket="my-gcs-bucket" \
  --storage-google-prefix=""

Using with Microsoft Azure Blob Storage

Make sure your environment is properly setup to access mycontainer.

To do so, you must set the following env vars:

  • AZURE_STORAGE_ACCOUNT
  • AZURE_STORAGE_ACCESS_KEY
chartmuseum --debug --port=8080 \
  --storage="microsoft" \
  --storage-microsoft-container="mycontainer" \
  --storage-microsoft-prefix=""

Using with Alibaba Cloud OSS Storage

Make sure your environment is properly setup to access my-oss-bucket.

To do so, you must set the following env vars:

  • ALIBABA_CLOUD_ACCESS_KEY_ID
  • ALIBABA_CLOUD_ACCESS_KEY_SECRET
chartmuseum --debug --port=8080 \
  --storage="alibaba" \
  --storage-alibaba-bucket="my-oss-bucket" \
  --storage-alibaba-prefix="" \
  --storage-alibaba-endpoint="oss-cn-beijing.aliyuncs.com"

Using with Openstack Object Storage

Make sure your environment is properly setup to access mycontainer.

To do so, you must set the following env vars (depending on your openstack version):

  • OS_AUTH_URL
  • either OS_PROJECT_NAME or OS_TENANT_NAME or OS_PROJECT_ID or OS_TENANT_ID
  • either OS_DOMAIN_NAME or OS_DOMAIN_ID
  • either OS_USERNAME or OS_USERID
  • OS_PASSWORD
chartmuseum --debug --port=8080 \
  --storage="openstack" \
  --storage-openstack-container="mycontainer" \
  --storage-openstack-prefix="" \
  --storage-openstack-region="myregion"

For Swift V1 Auth you must set the following env vars:

  • ST_AUTH
  • ST_USER
  • ST_KEY
chartmuseum --debug --port=8080 \
  --storage="openstack" \
  --storage-openstack-auth="v1" \
  --storage-openstack-container="mycontainer" \
  --storage-openstack-prefix=""

Using with Oracle Cloud Infrastructure Object Storage

Make sure your environment is properly setup to access my-ocs-bucket.

More info on Oracle Cloud Infrastructure authentication can be found here.

chartmuseum --debug --port=8080 \
  --storage="oracle" \
  --storage-oracle-bucket="my-ocs-bucket" \
  --storage-oracle-prefix="" \
  --storage-oracle-compartmentid="ocid1.compartment.oc1..1234"

Using with Baidu Cloud BOS Storage

Make sure your environment is properly setup to access my-bos-bucket.

To do so, you must set the following env vars:

  • BAIDU_CLOUD_ACCESS_KEY_ID
  • BAIDU_CLOUD_ACCESS_KEY_SECRET
chartmuseum --debug --port=8080 \
  --storage="baidu" \
  --storage-baidu-bucket="my-bos-bucket" \
  --storage-baidu-prefix="" \
  --storage-baidu-endpoint="bj.bcebos.com"

Using with Tencent Cloud COS Storage

Make sure your environment is properly setup to access my-cos-bucket.

To do so, you must set the following env vars:

  • TENCENT_CLOUD_COS_SECRET_ID
  • TENCENT_CLOUD_COS_SECRET_KEY
chartmuseum --debug --port=8080 \
  --storage="tencent" \
  --storage-tencent-bucket="my-cos-bucket" \
  --storage-tencent-prefix="" \
  --storage-tencent-endpoint="cos.ap-beijing.myqcloud.com"

Using with etcd

To use etcd as backend you need the CA certificate and the signed key pair. See here

chartmuseum --debug --port=8080 \
  --storage="etcd" \
  --storage-etcd-cafile="/path/to/ca.crt" \
  --storage-etcd-certfile="/path/to/server.crt" \
  --storage-etcd-keyfile="/path/to/server.key" \
  --storage-etcd-prefix="" \
  --storage-etcd-endpoint="http://localhost:2379"

Using with local filesystem storage

Make sure you have read-write access to ./chartstorage (will create if doesn't exist on first upload)

chartmuseum --debug --port=8080 \
  --storage="local" \
  --storage-local-rootdir="./chartstorage"

Basic Auth

If both of the following options are provided, basic http authentication will protect all routes:

  • --basic-auth-user=<user> - username for basic http authentication
  • --basic-auth-pass=<pass> - password for basic http authentication

You may want basic auth to only be applied to operations that can change Charts, i.e. PUT, POST and DELETE. So to avoid basic auth on GET operations use

  • --auth-anonymous-get - allow anonymous GET operations

Bearer/Token Auth

If all of the following options are provided, bearer auth will protect all routes:

  • --bearer-auth - enables bearer auth
  • --auth-realm=<realm> - authorization server url
  • --auth-service=<service> - authorization server service name
  • --auth-cert-path=<path> - path to authorization server public pem file
  • --auth-actions-search-path=<JMESPath> - (optional) JMESPath to find allowed actions in a jwt token

Using options above, ChartMuseum is configured with a public key, and will accept RS256 JWT tokens signed by the associated private key, passed in the Authorization header. You can use the chartmuseum/auth Go library to generate valid JWT tokens.

JWT Token without a custom JMESPath to find actions

In order to gain access to a specific resource, the JWT token must contain an access section in the claims. This section indicates which resources the user is able to access. Here is an example token payload:

{
  "exp": 1543995770,
  "iat": 1543995470,
  "access": [
    {
      "type": "artifact-repository",
      "name": "org1/repo1",
      "actions": [
        "pull"
      ]
    }
  ]
}

The type is always "artifact-repository", the name is the namespace/tenant (just use the string "repo" if using single-tenant server), and actions is an array of actions the user can perform ("pull" and/or "push).

If your JWT token structure is different, you can configure a JMESPath string. So you can define the way to find the allowed actions yourself. For the type and the the name you can use following placeholder

  • name: $NAMESPACE
  • type: $ACCESS_ENTRY_TYPE

E.g.: If you want to represent the default configuration, the JMESPath looks like: access[?name=='$NAMESPACE' && type=='$ACCESS_ENTRY_TYPE'].actions[].

For more information about how this works, please see chartmuseum/auth-server-example.

HTTPS

If both of the following options are provided, the server will listen and serve HTTPS:

  • --tls-cert=<crt> - path to tls certificate chain file
  • --tls-key=<key> - path to tls key file
HTTPS with Client Certificate Authentication

If the above HTTPS values are provided in addition to below, the server will listen and serve HTTPS and authenticate client requests against the CA certificate:

  • --tls-ca-cert=<cacert> - path to tls certificate file

Just generating index.yaml

You can specify the --gen-index option if you only wish to use ChartMuseum to generate your index.yaml file. Note that this will only work with --depth=0.

The contents of index.yaml will be printed to stdout and the program will exit. This is useful if you are satisfied with your current Helm CI/CD process and/or don't want to monitor another webservice.

Other CLI options

  • --log-json - output structured logs as json
  • --log-health - log incoming /health requests
  • --log-latency-integer - log latency as an integer (nanoseconds) instead of a string
  • --disable-api - disable all routes prefixed with /api
  • --disable-delete - explicitly disable the delete chart route
  • --disable-statefiles - disable use of index-cache.yaml
  • --allow-overwrite - allow chart versions to be re-uploaded without ?force querystring
  • --disable-force-overwrite - do not allow chart versions to be re-uploaded, even with ?force querystring
  • --chart-url=<url> - absolute url for .tgzs in index.yaml
  • --storage-amazon-endpoint=<endpoint> - alternative s3 endpoint
  • --storage-amazon-sse=<algorithm> - s3 server side encryption algorithm
  • --storage-openstack-cacert=<path> - path to a custom ca certificates bundle for openstack
  • --chart-post-form-field-name=<field> - form field which will be queried for the chart file content
  • --prov-post-form-field-name=<field> - form field which will be queried for the provenance file content
  • --index-limit=<number> - limit the number of parallel indexers
  • --context-path=<path> - base context path (new root for application routes)
  • --depth=<number> - levels of nested repos for multitenancy
  • --cors-alloworigin=<value> - value to set in the Access-Control-Allow-Origin HTTP header
  • --read-timeout=<number> - socket read timeout for http server
  • --write-timeout=<number> - socker write timeout for http server

Docker Image

Available via GitHub Container Registry (GHCR).

Example usage (local storage):

docker run --rm -it \
  -p 8080:8080 \
  -e DEBUG=1 \
  -e STORAGE=local \
  -e STORAGE_LOCAL_ROOTDIR=/charts \
  -v $(pwd)/charts:/charts \
  ghcr.io/helm/chartmuseum:v0.16.1

Example usage (S3):

docker run --rm -it \
  -p 8080:8080 \
  -e DEBUG=1 \
  -e STORAGE="amazon" \
  -e STORAGE_AMAZON_BUCKET="my-s3-bucket" \
  -e STORAGE_AMAZON_PREFIX="" \
  -e STORAGE_AMAZON_REGION="us-east-1" \
  -v ~/.aws:/home/chartmuseum/.aws:ro \
  ghcr.io/helm/chartmuseum:v0.16.1

Helm Chart

There is a Helm chart for ChartMuseum itself.

You can also view it on Artifact Hub.

To install:

helm repo add chartmuseum https://chartmuseum.github.io/charts
helm install chartmuseum/chartmuseum

If interested in making changes, please submit a PR to chartmuseum/charts. Before doing any work, please check for any currently open pull requests. Thanks!

Multitenancy

Multitenancy is supported with the --depth flag.

To begin, start with a directory structure such as

charts
├── org1
│   ├── repoa
│   │   └── nginx-ingress-0.9.3.tgz
├── org2
│   ├── repob
│   │   └── chartmuseum-0.4.0.tgz

This represents a storage layout appropriate for --depth=2. The organization level can be eliminated by using --depth=1. The default depth is 0 (singletenant server).

Start the server with --depth=2, pointing to the charts/ directory:

chartmuseum --debug --depth=2 --storage="local" --storage-local-rootdir=./charts

This example will provide two separate Helm Chart Repositories at the following locations:

  • http://localhost:8080/org1/repoa
  • http://localhost:8080/org2/repob

This should work with all supported storage backends.

To use the chart manipulation routes, simply place the name of the repo directly after "/api" in the route:

curl -F "[email protected]" http://localhost:8080/api/org1/repoa/charts

You may also experiment with the --depth-dynamic flag, which should allow for dynamic depth levels (i.e. all of /api/charts, /api/myrepo/charts, /api/org1/repoa/charts).

Pagination

For large chart repositories, you may wish to paginate the results from the GET /api/charts route.

To do so, add the offset and limit query params to the request. For example, to retrieve a list of 5 charts total, skipping the first 5 charts, you could use the following:

GET /api/charts?offset=5&limit=5

Cache

By default, the contents of index.yaml (per-tenant) will be stored in memory. This means that memory usage will continue to grow indefinitely as more charts are added to storage.

You may wish to offload this to an external cache store, especially for large, multitenant installations.

Cache Interval

When dealing with thousands of charts, you may experience latency with the default settings. This is because upon each request, the storage backend is scanned for changes compared to the cache.

If you are ok with index.yaml being out-of-date for a fixed period of time, you can improve performance by using the --cache-interval=<interval> option. When this setting is enabled, the charts available for each tenant are refreshed on a timer.

For example, to only check storage every 5 minutes, you can use --cache-interval=5m.

For valid values to use for this setting, please see here.

Using Redis

Example of using Redis as an external cache store:

chartmuseum --debug --port=8080 \
  --storage="local" \
  --storage-local-rootdir="./chartstorage" \
  --cache="redis" \
  --cache-redis-addr="localhost:6379" \
  --cache-redis-password="" \
  --cache-redis-db=0

Prometheus Metrics

ChartMuseum exposes its Prometheus metrics at the /metrics route on the main port. This can be enabled with the --enable-metrics command-line flag or the ENABLE_METRICS environment variable.

Note that the Kubernetes chart currently disables metrics by default (ENABLE_METRICS=false is set in the chart). The --disable-metrics command-line flag has be deprecated and will only be available in v0.14.0 and prior.

Below are the current application metrics exposed. Note that there is a per tenant (repo) label. The repo label corresponds to the depth parameter, so a depth=2 as the example above would have repo labels named org1/repoa and org2/repob.

Metric Type Labels Description
chartmuseum_charts_served_total Gauge {repo="*"} Total number of charts
chartmuseum_chart_versions_served_total Gauge {repo="*"} Total number of chart versions available

*: see above for repo label

There are other general global metrics harvested (per process, hence for all tenants). You can get the complete list by using the /metrics route.

Metric Type Labels Description
chartmuseum_request_duration_seconds Summary {quantile="0.5"}, {quantile="0.9"}, {quantile="0.99"} The HTTP request latencies in seconds
chartmuseum_request_duration_seconds_sum
chartmuseum_request_duration_seconds_count
chartmuseum_request_size_bytes Summary {quantile="0.5"}, {quantile="0.9"}, {quantile="0.99"} The HTTP request sizes in bytes
chartmuseum_request_size_bytes_sum
chartmuseum_request_size_bytes_count
chartmuseum_response_size_bytes Summary {quantile="0.5"}, {quantile="0.9"}, {quantile="0.99"} The HTTP response sizes in bytes
chartmuseum_response_size_bytes_sum
chartmuseum_response_size_bytes_count
go_goroutines Gauge Number of goroutines that currently exist

Notes on index.yaml

The repository index (index.yaml) is dynamically generated based on packages found in storage. If you store your own version of index.yaml, it will be completely ignored.

GET /index.yaml occurs when you run helm repo add chartmuseum http://localhost:8080 or helm repo update.

If you manually add/remove a .tgz package from storage, it will be immediately reflected in GET /index.yaml.

You are no longer required to maintain your own version of index.yaml using helm repo index --merge.

The --gen-index CLI option (described above) can be used to generate and print index.yaml to stdout.

Upon index regeneration, ChartMuseum will, however, save a statefile in storage called index-cache.yaml used for cache optimization. This file is only meant for internal use, but may be able to be used for migration to simple storage.

Mirroring the official Kubernetes repositories

Please see scripts/mirror-k8s-repos.sh for an example of how to download all .tgz packages from the official Kubernetes repositories (both stable and incubator).

You can then use ChartMuseum to serve up an internal mirror:

scripts/mirror-k8s-repos.sh
chartmuseum --debug --port=8080 --storage="local" --storage-local-rootdir="./mirror"

Custom Welcome Page

With the flag --web-template-path=<path>, you can specify the path to your custom welcome page.

The structure of the folder should be like this:

web/
  index.html
  xyz.html
  static/
      main.css
      main.js

ChartMuseum is using gin-gonic to serve the static files, this means that you can use go-template to render the files.

If you don't specify a custom welcome page, ChartMuseum will serve the default one.

Artifact Hub

By setting the flag --artifact-hub-repo-id <repo id>, ChartMuseum will serve a artifacthub-repo.yml file with the specified repo ID in the repositoryID field of the yaml file.

repositoryID: The ID of the Artifact Hub repository where the packages will be published to (optional, but it enables verified publisher)
Multitenancy

For multitenancy setups, you can provide a key value pair to the flag in the format: --artifact-hub-repo-id <repo>=<repo id>

chartmuseum --storage local --storage-local-rootdir /tmp/ --depth 1 --artifact-hub-repo-id org1=<repo id> --artifact-hub-repo-id org2=<repo2 id>

The artifacthub-repo.yml file will then be served at /org1/artifacthub-repo.yml and /org2/artifacthub-repo.yml

Original Logo

"Preserve your precious artifacts... in the cloud!"

Subprojects

The following subprojects are maintained by ChartMuseum:

Community

You can reach the ChartMuseum community and developers in the Kubernetes Slack #chartmuseum channel.

chartmuseum's People

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

chartmuseum's Issues

Race condition observed with high chart count

Hi,

We have stumbled upon a problem with our CM instance where suddenly only about half of our Google Storage backed charts were being served (without any error or hint in the debug logs whatsoever). Upon inspection, we have found this to be due to a race condition in server.addIndexObjectsAsync, where the function-global err variable can be set to non-nil by a first goroutine, while being reset just after by another, effectively fighting to set the error condition. The effect of this is that storage download errors can easily be hidden away by the concurrency logic. The more charts there are, the more likely this is to happen also. It's easy to simulate this problem by adding this block:

if rand.Float64() < 0.9 {
    return new(helm_repo.ChartVersion), fmt.Errorf("simulated IO error")
}

in server.getObjectChartVersion.

With this simulated error code added, the initial index creation code should very likely fail (rerun until it does if you get "lucky"), while it doesn't, which is wrong.

We have a fix on the burner for this coming soon.

Unable to start docker image

I believe that more details about how the tool woks is needed:
[1] What is the basic set of IAM permissions needed to work with this?
[1] Will the tool work with aws profiles (as per the existing example mounting ~/.aws in the docker image)
[1] How is the bucket accessed? Is the data replicated and then served?

Example that didn't work for me, but worked for calling the API:
policy attached to the assumed role:

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "s3:Get*",
                "s3:List*"
            ],
            "Resource": "arn:aws:s3:::*"
        }
    ]
}

~/.aws/config contains:

[profile default]
role_arn = arn:aws:iam::00redacted000:role/ReadCharts
source_profile = default

~/.aws/credentials contains the standard credentials set.

commad used: docker run -p 8080:8080 -v ~/.aws:/root/.aws:ro chartmuseum/chartmuseum:latest --debug --port=8080 --storage="amazon" --storage-amazon-bucket="mycompany-helmcharts" --storage-amazon-prefix="" --storage-amazon-region="eu-central-1"

error:

2017-10-05T09:45:33.255Z	DEBUG	chartmuseum/server.go:166	Acquiring storage cache lock
2017-10-05T09:45:33.255Z	DEBUG	chartmuseum/server.go:168	Storage cache lock acquired
2017-10-05T09:45:33.613Z	DEBUG	chartmuseum/server.go:170	Releasing storage cache lock
AccessDenied: Access Denied
	status code: 403, request id: CxxxxredactedxxxxBA, host id: c1EDzxxxxxxxredactedxxxxxxx7X+e0xxxxxxxxxRlMl8=

Separate web handlers from processing

For example getIndexFileRequestHandler() would delegate to something like getIndexFile(orgName, repoName, prefix), and this method would contain only the objects processing.

This will allow for better unit testing in the long run, and cleanly isolate the details of the web framework.

Bug: Deleting a chart having a suffix in its version via the API causes no removal from index

Hello,

We observed that deleting a chart having a suffix in its version via the API causes no removal from the index. The deletion from the file system works:

POST, GET, DELETE for chart having a version without suffix:

curl -X POST -k --data-binary "@mychart-1.0.4.tgz" localhost:8080/api/charts
{"saved":true}

curl localhost:8080/api/charts
{"mychart":[{"name":"mychart","version":"1.0.4","description":"mychart","apiVersion":"v1","urls":["charts/mychart-1.0.4.tgz"],"created":"2017-11-24T16:09:28.993494102+01:00","digest":"a4f08b5a9ccd5a958429c3ecf5fa45fdf2f271882cba7924c4fc9204645a8c7f"}]}

curl -X DELETE  localhost:8080/api/charts/mychart/1.0.4
{"deleted":true}

curl localhost:8080/api/charts
{}

POST, GET, DELETE for chart having a version with suffix:

curl -X POST -k --data-binary "@mychart-1.0.4-SNAPSHOT.tgz" localhost:8080/api/charts
{"saved":true}

curl localhost:8080/api/charts
{"mychart":[{"name":"mychart","version":"1.0.4-SNAPSHOT","description":"mychart","apiVersion":"v1","urls":["charts/mychart-1.0.4-SNAPSHOT.tgz"],"created":"2017-11-24T16:09:28.993494102+01:00","digest":"a4f08b5a9ccd5a958429c3ecf5fa45fdf2f271882cba7924c4fc9204645a8c7f"}]}

curl -X DELETE  localhost:8080/api/charts/mychart/1.0.4-SNAPSHOT
{"deleted":true}

curl localhost:8080/api/charts
{"mychart":[{"name":"mychart","version":"1.0.4-SNAPSHOT","description":"mychart","apiVersion":"v1","urls":["charts/mychart-1.0.4-SNAPSHOT.tgz"],"created":"2017-11-24T16:09:28.993494102+01:00","digest":"a4f08b5a9ccd5a958429c3ecf5fa45fdf2f271882cba7924c4fc9204645a8c7f"}]}

Best regards,
Andreas

Not supporting AWS profile and role

Hello,

Thanks for your work !

I'm trying to connect Chartmuseum to my AWS S3 bucket with a specific AWS role but I get a non authorized error.

I used this following command:

chartmuseum --debug --port=8080   --storage="amazon"   --storage-amazon-bucket=sandbox-helm-repository   --storage-amazon-prefix="" --storage-amazon-region="eu-west-1"

And I obtain this error:

AccessDenied: Access Denied
        status code: 403, request id: 2CE16226AA976744, host id: d/nSvAGPoReekLS2uEmhLpLA9kN7IWSiXrMVN+ZjRgcLrZZGqFvf6g+iUuU6xoje2si7m3nlfrA=

My file ~/.aws/credentials looks like this:

[default]    
aws_secret_access_key = my_secret_acces_key
aws_access_key_id = my_access_key_id 
[sandbox]
role_arn = arn:aws:iam::997909197264:role/OrganizationAccountAccessRole
source_profile = default

And my file ~/.aws/config looks like this:

[default]
region = eu-west-1

The command aws s3 ls --profile=sandbox works and give me the bucket with the profile Sandbox.

Do you know how I can use profile and aws role with Chartmuseum ?

Thank you !

proposal: Add X-Request-Id header

With recent changes (#19) each request is assigned a numeric "RequestId" in order to easily group log lines tied to a request.

My suggestion is to take this one step further, and send the RequestId to the client in theX-Request-Id HTTP header for further debugging, correlating client-side requests to associated log lines.

I'd also like to propose using a UUID instead of integer to prevent collisions across replicas.

This gin middleware seems to do everything we need: https://github.com/atarantini/ginrequestid

In addition, we may want to discuss changing the format of log lines, i.e. should we prefix every line with this long UUID, or make it a k/v similar to "method": "GET"?

@cjauvin @davidovich thoughts?

Customizable URL for the info page

I'm using chartmuseum in a Docker container behind a reverse proxy.

I've set the --chart-url option, so the generated URLs in the index.yaml are correct.

However, the info page (when accessing the domain with a browser) shows the client IP instead:

To add this as a local chart repository, please run the following command:
helm repo add chartmuseum https://10.11.12.13

It would be nice to have the URL in there also customizable.

Thanks!

Missing an "http auth free" health endpoint

Hi,

It would be nice to have a health endpoint at path / which is exposed without http auth even if we use http auth.

For now I can't use my chartmuseum Helm charts with http auth in my Kubernetes cluster with a GCE Ingress because healthcheck fail indefinitely. :/

2017-12-12T16:16:26.486Z        DEBUG   [322] Incoming request: /       {"reqID": "ead60a6a-defe-4450-aa35-0db06b7ba857"}
2017-12-12T16:16:26.486Z        WARN    [322] Request served    {"path": "/", "comment": "", "latency": "127.6µs", "clientIP": "10.240.0.12", "method": "GET", "statusCode": 404, "reqID": "ead60a6a-defe-4450-aa35-0db06b7ba857"}
2017-12-12T16:16:26.616Z        DEBUG   [323] Incoming request: /       {"reqID": "780a8a2e-4faf-4deb-840e-d311db2e5dc6"}
2017-12-12T16:16:26.616Z        WARN    [323] Request served    {"path": "/", "comment": "", "latency": "33.093µs", "clientIP": "10.240.0.17", "method": "GET", "statusCode": 404, "reqID": "780a8a2e-4faf-4deb-840e-d311db2e5dc6"}
2017-12-12T16:16:26.746Z        DEBUG   [324] Incoming request: /       {"reqID": "6fe8e465-30ec-41cd-8687-9b59d39b6e73"}
2017-12-12T16:16:26.746Z        WARN    [324] Request served    {"path": "/", "comment": "", "latency": "37.049µs", "clientIP": "10.240.0.4", "method": "GET", "statusCode": 404, "reqID": "6fe8e465-30ec-41cd-8687-9b59d39b6e73"}

Multitenancy

One ChartMuseum instance should be able to optionally provide multiple Helm repos

bucket is not in region error

I can't start my chartmuseum app because of the BucketRegionError: incorrect region, the bucket is not in 'us-west-2' region error

⇒  docker run --rm -it \
  -p 8080:8080 \
  -v ~/.aws:/root/.aws \
  chartmuseum/chartmuseum:v0.1.0 \
  --debug --port=8080 \
  --storage="amazon" \
  --storage-amazon-bucket="s***" \
  --storage-amazon-prefix="" \
  --storage-amazon-region="us-west-2"
2017-11-15T00:47:30.223Z        DEBUG   chartmuseum/server.go:166       Acquiring storage cache lock
2017-11-15T00:47:30.255Z        DEBUG   chartmuseum/server.go:168       Storage cache lock acquired
2017-11-15T00:47:30.841Z        DEBUG   chartmuseum/server.go:170       Releasing storage cache lock
BucketRegionError: incorrect region, the bucket is not in 'us-west-2' region
        status code: 301, request id: , host id:

If I would have done the same thing with aws s3 ls in the same container it works

⇒  docker run --rm \
>   -v ~/.aws:/root/.aws:ro \
>   --entrypoint sh \
>   chartmuseum/chartmuseum:latest \
>   -c 'apk add --update py-pip &&
quote>       pip install awscli &&
quote>       aws s3 ls s*** --region=us-west-2'
fetch http://dl-cdn.alpinelinux.org/alpine/v3.6/main/x86_64/APKINDEX.tar.gz
fetch http://dl-cdn.alpinelinux.org/alpine/v3.6/community/x86_64/APKINDEX.tar.gz
(1/12) Installing libbz2 (1.0.6-r5)
(2/12) Installing expat (2.2.0-r1)
(3/12) Installing libffi (3.2.1-r3)
(4/12) Installing gdbm (1.12-r0)
(5/12) Installing ncurses-terminfo-base (6.0_p20170930-r0)
(6/12) Installing ncurses-terminfo (6.0_p20170930-r0)
(7/12) Installing ncurses-libs (6.0_p20170930-r0)
(8/12) Installing readline (6.3.008-r5)
(9/12) Installing sqlite-libs (3.20.1-r0)
(10/12) Installing python2 (2.7.13-r1)
(11/12) Installing py-setuptools (33.1.1-r1)
(12/12) Installing py2-pip (9.0.1-r1)
Executing busybox-1.26.2-r7.trigger
OK: 62 MiB in 24 packages
Collecting awscli
  Downloading awscli-1.11.185-py2.py3-none-any.whl (1.2MB)
Collecting botocore==1.7.43 (from awscli)
  Downloading botocore-1.7.43-py2.py3-none-any.whl (3.7MB)
Collecting docutils>=0.10 (from awscli)
  Downloading docutils-0.14-py2-none-any.whl (543kB)
Collecting colorama<=0.3.7,>=0.2.5 (from awscli)
  Downloading colorama-0.3.7-py2.py3-none-any.whl
Collecting PyYAML<=3.12,>=3.10 (from awscli)
  Downloading PyYAML-3.12.tar.gz (253kB)
Collecting rsa<=3.5.0,>=3.1.2 (from awscli)
  Downloading rsa-3.4.2-py2.py3-none-any.whl (46kB)
Collecting s3transfer<0.2.0,>=0.1.9 (from awscli)
  Downloading s3transfer-0.1.11-py2.py3-none-any.whl (54kB)
Collecting python-dateutil<3.0.0,>=2.1 (from botocore==1.7.43->awscli)
  Downloading python_dateutil-2.6.1-py2.py3-none-any.whl (194kB)
Collecting jmespath<1.0.0,>=0.7.1 (from botocore==1.7.43->awscli)
  Downloading jmespath-0.9.3-py2.py3-none-any.whl
Collecting pyasn1>=0.1.3 (from rsa<=3.5.0,>=3.1.2->awscli)
  Downloading pyasn1-0.3.7-py2.py3-none-any.whl (63kB)
Collecting futures<4.0.0,>=2.2.0; python_version == "2.6" or python_version == "2.7" (from s3transfer<0.2.0,>=0.1.9->awscli)
  Downloading futures-3.1.1-py2-none-any.whl
Collecting six>=1.5 (from python-dateutil<3.0.0,>=2.1->botocore==1.7.43->awscli)
  Downloading six-1.11.0-py2.py3-none-any.whl
Installing collected packages: six, python-dateutil, jmespath, docutils, botocore, colorama, PyYAML, pyasn1, rsa, futures, s3transfer, awscli
  Running setup.py install for PyYAML: started
    Running setup.py install for PyYAML: finished with status 'done'
Successfully installed PyYAML-3.12 awscli-1.11.185 botocore-1.7.43 colorama-0.3.7 docutils-0.14 futures-3.1.1 jmespath-0.9.3 pyasn1-0.3.7 python-dateutil-2.6.1 rsa-3.4.2 s3transfer-0.1.11 six-1.11.0
                           PRE AWSLogs/

Any idea what causes this ?

TLS Client Cert Authentication

This is related to the excellent proposal in #59, but should be solvable immediately rather than as a long-term effort.

The Helm client supports TLS client certificate authentication:
helm/helm#1038 (comment)

It would be great if chartmuseum supported a --tls-ca-cert which would perform client certificate authentication of incoming requests, accepting client certs issued by the trusted CA.

Perhaps this is already supported, but I can't find it in the documentation.

Chartmuseum cannot hold a constant response time when latency to storage is high

This was discussed in the #chartmuseum slack channel.

On Google Cloud Storage, we are running chartmuseum with around 60 charts and their versions. This makes about 800 versioned charts. The index update is causing latency problems in presence of new uploads. The server cannot keep up, and response times keep getting higher and higher, caused by a threading "bubble effect". This happens when the latency to storage is very high. It can also happen if the index is big.

Here is what happens currently:

Someone posts a new chart to storage.

  1. /index.yaml detects the change
  2. chartmuseum wants to recalculate the diff
  3. another /index.yaml comes in (while 1) is not fninished),
    goto 2)

You see the picture. Every other index (while the first one is being calculated) is in wait state to redo the update, causing accumulated waits.

This issue is to track an upcoming PR which fixes the problem.

Provide a flag that allows anonymous read on GET operations when basic auth is used

It would be handy if there was a flag we could use to allow folks to GET charts anonymously when basic auth is used. This means that basic auth would only be required to PUT, PATCH and DELETE charts.

I have a PR on its way that does this but I figured it's best to raise an issue to discuss it and track any other proposed solutions.

Question for --allow-overwrite

Hi,

I'm not sure my question is regarding chart repo or chartmuseum, is it allow to store multiple versions of a chart? For example, I have a chart named nginx, I uploaded version 1.0.0 and 1.0.1, should I see both versions by helm search? Actually, I used --allow-overwrite parameter for chartmuseum, I can see chart 1.0.0.tgz and 1.0.1.tgz were both in the chart storage, but can only see version 1.0.1 by helm search, means only 1.0.1 in index.yaml, is this the expected behavior?

make repo subdirectroy of multitenancy optional

Right now, the current multitenancy design mandates a structure in this style: org1/repoa, org2/repoa. While I still think two levels are a good idea in a centralized hosting scenario, I also think that it might be overkill for a corporate scenario where only one chartmuseum is present and one level (the org level) would be used for different teams. It appears that the file structure of the repos could be relaxed when the org separation is enough for simple use cases.

The two levels could be useful for these corporate teams, for providing something like dev / staging / prod, but can also be too fine-grained for a team only wanting a separate namespace.

This issue is opened to discuss the merits of an optional two level separation.

Add Prometheus metrics

Hi Josh,

I have another request which stems from a requirement in the business context where we'll use ChartMuseum (but would also make sense in a more general context I believe): add Prometheus metrics, using https://github.com/prometheus/client_golang.

Things it would be nice to (optionally) measure:

  • Number of requests, by type
  • Duration of requests
  • Number of charts served
  • Time it takes to regenerate index
  • Etc.

What do you think? Of course if you agree once more, I'll be happy to propose an implementation.

Chartmuseum apparently not closing files when scanning repo

It looks as if chartmuseum may not be closing files when it scans the repo. We are looking at converting a large repo (nearly 16000 packages) to chartmuseum and are getting the following error on the initial scan:
open helmrepo/xxx.tgz: too many open files in system

I haven't dug through the code yet, but my assumption is that when it first scans the repo it open each file and doesn't close it before opening the next, or it parallelizes the scan and opens so many files that it hits the file descriptor limit. For now, I have bumped by ulimit to get around this error, but I may not be able to do that in production.

fatal error: concurrent map read and map write

When attempting to launch Chartmuseum we sometimes get the error fatal error: concurrent map read and map write.

Observed with both v0.1.0 and v0.2.0.

Running on Ubuntu 16.04 using Systemd with the below service configuration:

[Unit]
Description=Charmuseum service
After=network.target
After=systemd-user-sessions.service
After=network-online.target


[Service]
ExecStart=/opt/chartmuseum/chartmuseum --port=9001 --storage="amazon" --storage-amazon-bucket="my-bucket" --storage-amazon-prefix="" --storage-amazon-region="eu-west-1"

[Install]
WantedBy=multi-user.target

Log file:
chartmuseum.log

Ingress and tls errors while installing new chart

I try to install the latest version of chartmuseum, but getting errors about the ingress.

Helm version: v2.8.1
Host: GCP
Kubernetes: v1.9.3-gke.0

Command:
helm install --name chartmuseum -f custom.yaml incubator/chartmuseum

Custom.yaml:

ingress:
  enabled: true

  annotations:
    nginx.ingress.kubernetes.io/rewrite-target: /
    kubernetes.io/ingress.class: nginx
    kubernetes.io/tls-acme: 'true'

  hosts:
    chartmuseum.my-domain.com:
      - /charts
      - /index.yaml

  tls:
    secretName: chartmuseum-my-domain-tls
    hosts:
      - chartmuseum.my-domain.com

error:
Error: release chartmuseum failed: Ingress in version "v1beta1" cannot be handled as a Ingress: v1beta1.Ingress: Spec: v1beta1.IngressSpec: TLS: []v1beta1.IngressTLS: ReadArrayCB: expect [ or n, but found: {, parsing 621 ...}],"tls":{... at {"apiVersion":"extensions/v1beta1","kind":"Ingress","metadata":{"annotations":{"kubernetes.io/ingress.class":"nginx","kubernetes.io/tls-acme":"true","nginx.ingress.kubernetes.io/rewrite-target":"/"},"labels":{"app":"chartmuseum","chart":"chartmuseum-0.4.4","heritage":"Tiller","release":"chartmuseum"},"name":"chartmuseum-chartmuseum","namespace":"default"},"spec":{"rules":[{"host":"chartmuseum.my-domain.com","http":{"paths":[{"backend":{"serviceName":"chartmuseum-chartmuseum","servicePort":8080},"path":"/charts"},{"backend":{"serviceName":"chartmuseum-chartmuseum","servicePort":8080},"path":"/index.yaml"}]}}],"tls":{"hosts":["chartmuseum.my-domain.com"],"secretName":"chartmuseum-my-domain-tls"}}}

Separate API packages

The 3 categories under API in the readme

  • Helm Chart Repository
  • Chart Manipulation
  • Server Info

should each have their own subpackage/file

Missing hint in docu for configuration via environment variables

Due to eg Ansible has no native support for passing command parameters into the docker container I have to use a config file or environment variables (which is easily done in Ansible). After digging into the entrypoint I saw you are fine with environment variables.

A small hint in the docu would be awesome.

Azure backend reloads chart packages despite no changes

In an Azure blob storage container with a single, unmodified chart (chartmuseum-0.3.3.tgz), index.yaml is regenerated every time it is requested, despite no changes in storage.

Example output:

{"L":"DEBUG","T":"2018-02-19T11:27:25.561-0600","M":"Fetching chart list from storage"}
{"L":"DEBUG","T":"2018-02-19T11:27:25.852-0600","M":"Regenerating index.yaml"}
{"L":"DEBUG","T":"2018-02-19T11:27:25.852-0600","M":"Loading charts packages from storage (this could take awhile)","total":1}
{"L":"DEBUG","T":"2018-02-19T11:27:26.006-0600","M":"Adding chart to index","name":"chartmuseum","version":"0.3.3"}
{"L":"DEBUG","T":"2018-02-19T11:27:26.007-0600","M":"index.yaml regenerated"}
{"L":"INFO","T":"2018-02-19T11:27:26.007-0600","M":"Starting ChartMuseum","port":8080}
{"L":"DEBUG","T":"2018-02-19T11:27:28.977-0600","M":"[1] Incoming request: /index.yaml","reqID":"c6c84c97-2335-4619-9a48-049126602c7e"}
{"L":"DEBUG","T":"2018-02-19T11:27:28.977-0600","M":"[1] Fetching chart list from storage","reqID":"c6c84c97-2335-4619-9a48-049126602c7e"}
{"L":"DEBUG","T":"2018-02-19T11:27:29.068-0600","M":"[1] Regenerating index.yaml","reqID":"c6c84c97-2335-4619-9a48-049126602c7e"}
{"L":"DEBUG","T":"2018-02-19T11:27:29.188-0600","M":"[1] Updating chart in index","name":"chartmuseum","version":"0.3.3","reqID":"c6c84c97-2335-4619-9a48-049126602c7e"}
{"L":"DEBUG","T":"2018-02-19T11:27:29.188-0600","M":"[1] index.yaml regenerated","reqID":"c6c84c97-2335-4619-9a48-049126602c7e"}
{"L":"INFO","T":"2018-02-19T11:27:29.188-0600","M":"[1] Request served","path":"/index.yaml","comment":"","latency":"211.369ms","clientIP":"::1","method":"GET","statusCode":200,"reqID":"c6c84c97-2335-4619-9a48-049126602c7e"}

Multitenant server should serve multiple Helm Chart Repositories (readonly)

Continuing conversation started in #53 ...

When providing the --multitenant flag, ChartMuseum should act as multiple Helm Chart Repositories.

This issue covers implementing the following routes:

  • GET /r/<org>/<repo>/index.yaml - retrieved when you run helm repo add chartmuseum http://localhost:8080/<org>/<repo>
  • GET /r/<org>/<repo>/charts/mychart-0.1.0.tgz - retrieved when you run helm install chartmuseum/mychart
  • GET /r/<org>/<repo>/charts/mychart-0.1.0.tgz.prov - retrieved when you run helm install with the --verify flag

Contents of each index.yaml will be determined by a nested directory in the storage backend. For example, if your storage looks like

charts
├── org1
│   ├── repoa
│   │   └── nginx-ingress-0.9.3.tgz
├── org2
│   ├── repob
│   │   └── chartmuseum-0.4.0.tgz

then an index.yaml with nginx-ingress-0.9.3.tgz will be available at the route /r/org1/repoa/index.yaml, and another index.yaml with chartmuseum-0.4.0.tgz will be available at the route /r/org2/repob/index.yaml etc.

This does not include caching (for now)

Auto-generate index.yaml

It would be nice if, after uploading a new or updated chart, the index.yaml-file is automatically generated for the repository, so users don't have to generate and upload it manually.

Add possibility to post form data

Hi,

Would you be open to the idea of also supporting form data posts? With it, it would be possible to upload a chart by doing something like:

$ curl --form "chartfile=@/path/to/chart.tgz" ...

Single-tenant server should be moved to its own subpackage

Functionality in cache.go, routes.go, and handlers.go should be moved into a new subpackage so that additions of multi-tenancy features will not affect the current system.

Probably done by making Server an interface with a .Listen() method that is implemented in both the singletenant and multitenant subpackages.

cc @liamawhite

Change behaviour for root API route.

Currently,/ returns a 404. We are interested in changing this behaviour to return a redirect to /index.yaml.

During a discussion on slack with @jdolitsky. He suggested that maybe this route could return the version of chart museum that is running, but thinking through this later I thought that this is a potential security risk. Generally, you should not tell users the version of an application you are running because then they can cross reference that against potential exploits.

I am happy to do the legwork but the exact solution should probably be a result of some discussion.

Add possibility to overwrite chart in repository

Hi there,

it should be possible to overwrite existing charts served by the museum (i.e. to use PUT for creation and POST for updating a chart resource) - this would be especially useful when serving charts still under development. (similar to the possibility of uploading maven snapshots several times in the Java world)

This feature should be disabled by default and optionally enabled by providing a cmdline option, env var or something similar.

Best regards,
Andreas

Root url not using authentication when API is

It seems like the root / isn't using authentication when the /api requires it. Is this expected?

I see there is work going on in #59 and #64 and I'm guessing this is vestigial from #29 when the root was the healthcheck.

Is there a reason we leave the / unauthenticated?

More robust authentication

Currently the Helm repo standard is to use anonymous HTTP or HTTP Basic authentication. This is a proposal to support a more robust authentication scheme with Chart Museum. Hopefully it will be embraced by Helm. If it won't, it still shouldn't affect Chart Museum's compliance with the Helm repo standard since this is an additional functionality on top of the standard functionality.

This was originated from the discussion around Multi-tenancy to Chart Museum, but it's a good feature for single-tenant users also hence this issue to kick-off this discussion as well.

OAuth/JWT clarification:
OAuth2 is the common protocol for API authorization. It defines a set of scenarios (flows) that each describe how the client, server (resource server) and authorization server can work together to allow a client application to obtain an access token to some protected resources. The result of this auth dance is a token which the client can use for accessing the resources. The most common type of token is JWT, which is basically a signed JSON document with an agreed upon schema. When a user gets hold of a valid JWT (by following OAuth2, or by any other means), they will present it to the server which will validate the token, after which point the request is authenticated, and may proceed to making authorization decisions which are out of scope for now.

Chart Museum is a server that hosts charts. In OAuth2 lingo it is a resource server which rely on some other party to issue tokens. If we look at Docker Registry for reference, it seem to take similar approach of promoting OAuth2, but the open registry implementation (distribution) does not implement the entire flow (at least not as part of core), it expects the token to be issued by another component.

Taking this approach has the following benefits:

  • Doesn't require a database (as requested by early adopters) but make it optional
  • Cleanly integrate into hosting environments
  • Leverage existing OAuth/JWT frameworks

To implement this, Chart Museum needs to accept JWT token:

  • bootstrap CM using certain configuration like: trusted authorization server URL, valid signing keys, etc
  • add a middleware that will enforce auth
  • extract the token from the Authorization header
  • validate the token
  • examine allowed actions for this token (scopes)
  • allow/disallow the request (disallow should return 401 with information on the required authentication)

References:

Enable CRUD API (Chart Manipulation routes) in multitenant server

We could likely get away with using the same routes:

  • POST /api/charts - upload a new chart version
  • POST /api/prov - upload a new provenance file
  • DELETE /api/charts/<name>/<version> - delete a chart version
  • GET /api/charts - list all charts
  • GET /api/charts/<name> - list all versions of a chart
  • GET /api/charts/<name>/<version> - describe a chart version

We just need a way to pass in the name of the repo. Should we do so in the headers?

ChartMuseum-Repo: org1/repo1

or eliminate the --data-binary option and make it a form field (@davidovich)?

curl -F "repo=org1/repo1" -F "[email protected]" http://localhost:8080/api/charts

?
cc @alexei-led @itaysk

Chart already exists should not be a 500

500 indicates something went wrong at the server side.

I suggest chartmuseum should return a 403 Forbidden (as it's not overwriting) or vaguely possibly 400 Bad Request (as you should not try to overwrite a chart).

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.