Wednesday, April 17, 2024

Bitnami Helm Charts are Now More Secure Than Ever

Bitnami-packaged open source software is loved by developers for its ease of use, which enables developers to directly pull a Bitnami package and seamlessly start using it with little effort. The fact that Bitnami-packaged open source software accounts for over 3 billion pulls per year on DockerHub is a testament to its popularity among developers. But, apart from the ease of use, we also aim to make our software inherently more secure and reliable by updating packaging practices per industry standards. That’s why, over the past few weeks, our team has worked on improving the security of Bitnami-packaged Helm charts.

As a starting point for these improvements, we decided to leverage the risk analysis, security compliance, and misconfiguration scanning capabilities of Armo’s Kubescape (an open source Kubernetes security platform). The best practices codified as controls by Armo helped us identify a list of preventative, detective, or corrective measures, which we then implemented. Some of the major improvements we have completed are listed below.

Enabling containers to function without group root

Until now, Bitnami charts used user 1001 and group root (following Openshift standards). However, Kubernetes platforms increasingly encourage the use of more secure configurations for applications, which does not allow for the use of the group root. With the new changes, Bitnami containers and Helm charts will no longer need the group root to function, making 1001 the new default runAsGroup. This setting can be reverted by setting runAsGroup back to 0. 

Enabling usage of immutable filesystems

Using immutable filesystems is a mandatory requirement of security checklists such as NSA or MITRE. This configuration helps enforce an immutable infrastructure strategy; the container only needs to write on the mounted volume that persists the state. An immutable root filesystem is also capable of preventing malicious binaries from writing to the host system. To enable use of immutable filesystems, we changed all writable paths of containers to emptyDir volumes. In some cases, we needed to add extra init containers that copy folders like conf, plugins, or logs. This is necessary because mounted volumes replace the contents of the original folders. It’s important to note that this may cause issues with customization scripts, such as  initScripts or the use of custom command and arg. Using older versions of Bitnami containers may cause problems because they might lack the necessary bash changes to allow readOnlyRootFilesystem. This setting can be reverted by setting readOnlyRootFilesystem back to false.

Ensuring no pod is without a resource request or limit

Having pods without any resource requests or limits is increasingly discouraged as these containers may deplete node resources. Currently, our charts warn users if the resources object is not set. Additionally, we have the resourcesPreset value to assist users when testing different resource configurations, but this is not recommended for production. With the new changes, we set the resourcesPreset value to the minimum size that operates in our internal testing. This size is not the recommended size by the Bitnami team, but it is the minimum size for basic testing of the solution. It is critical that users set resource values according to your requirements and use case. 

It’s important to note that users already setting the resources value will not be affected by this change. Users who are not setting resources may experience a performance drop in their workloads, as they would be configured to a minimal value for basic testing. In these cases, we strongly recommend users set the resources value before any upgrade. This setting can be reverted by setting resourcesPreset back to none.

Enabling functioning in OpenShift restricted-v2 SCC

Before our changes, Bitnami charts did not function in Openshift restricted-v2 Security Context Constraints(SCC) because they set user and group to 1001. To address this, we enabled the automatic adaptation of the containerSecurityContext and podSecurityContext sections when running in Openshift installations. If the detected platform is Openshift, the values runAsUser, runAsGroup, and fsGroup will be automatically removed, allowing the Openshift platform to select the appropriate user and group IDs.

Users deploying in platforms other than Openshift are not affected by this change. Openshift users who set their runAsUser, runAsGroup, and fsGroup values will find that these are removed during an upgrade. This setting can be reverted by setting global.compatibility.openshift.adaptSecurityContext to false.

For detailed information on all improvements check out this GitHub issue.

Using the Tanzu OSS Health Assessment to understand the impact of these changes

Earlier this month, we launched the Tanzu OSS Health Assessment—a free-to-use tool that provides a comprehensive overview of your open source software dependencies and the potential risks you may face. 

To understand how the new improvements in Bitnami packages can impact your security posture, let’s look at the OSS health assessment report generated before and after the security improvements were implemented.

Let’s look at the OSS health assessment report of a repository with the Helm charts—Nginx, ClickHouse, Grafana and PostgreSQL—before the security improvements were made.

As you can see, the percentage of Misconfigured resources were 54.55%.

Now let’s look at the health assessment report of the same Helm charts after the implementation of our improvement measures.

From the snaps, you can see that misconfigured resources have dropped from 54.55% to 17.39%, implying a significantly better security posture, thanks to the aforementioned improvements.

You can also see that the security vulnerabilities plummeted from 684 to just 4. This drop is due to the fact that the Bitnami Helm charts are sourced from Tanzu Application Catalog with Photon OS as the base image, and not the standard Debian OS (which is the base OS for the community edition of Bitnami packages).

Thus, the recent security improvements in Bitnami-packaged Helm charts combined with Tanzu Application Catalog’s capability to let you use the VMware by Broadcom-maintained Linux distro—Photon OS as the base image can help you improve your security posture and minimize risks from your open source software dependencies.

Next Steps

To solve problems you may have with the Bitnami community packages—including deployment support, operational support, and bug fixes—please open an issue in the Bitnami Helm charts or containers GitHub repository. You can get the latest Bitnami-packaged software from our website, DockerHub (containers and Helm charts), Google Marketplace, Azure Marketplace, or AWS Cloud Marketplace. If you want to use Bitnami packages in production environments for mission-critical use cases, check out Tanzu Application Catalog—an enterprise version of Bitnami Application Catalog with several exclusive features that include base OS customization, app-level customization, Vulnerability Exploitability eXchange (VEX), and more. Take our OSS health assessment today and kickstart your journey toward the optimization of your open source software dependencies.

Monday, March 18, 2024

Bitnami-packaged containers and Helm charts in DockerHub are now signed by Notation

Bitnami-packaged open source software container images and Helm charts available in DockerHub are now signed by Notation, a Cloud Native Computing Foundation (CNCF) incubating project.

In December 2023, we announced that Tanzu Application Catalog, the enterprise edition of Bitnami Application Catalog, started making use of Notation as a tool for signing and verifying the open container initiative (OCI) artifacts (e.g. container images, Helm charts, and metadata bundles) that we deliver. Now, we’re happy to announce the extension of this capability to the community edition of Bitnami-packaged container images in DockerHub as well.

Read on to discover more about Notation, how you stand to benefit from this integration, and how you can verify the Notation signatures in Bitnami packages.

What is Notation?

Docker developed the Docker Content Trust (DCT), a.k.a. Notary V1, in 2015 and subsequently donated it to the CNCF. Notary V1 allows users to sign and verify container images while ensuring the integrity and authenticity of specific image tags through client-side or runtime verification. Notary v1 functions by adding the user's public key to the registry and signing the image with the key's private counterpart before uploading it. Users can then verify the image by comparing the public key against the registry command group's pulled data.

The updated and enhanced Notation seeks to improve upon the shortcomings of DCT or Notary v1. Now users can create and incorporate their own implementations of the specifications into workflows for signing and verifying multiple OCI artifacts (such as software bill of materials, scan results, and container images) using Notation. It’s intended to serve as a cross-registry, cross-industry standard for signing and validating any registry artifact or OCI image. Notation is an implementation of the Notary Project specifications and is a CNCF incubating project

Benefits of signing Bitnami images with Notation

There are several benefits of signing Bignami images with Notation, including the ability to: 

  • Ensure content integrity—By signing our container images with Notation we can guarantee the integrity of the software we deliver. The signatures generated by Notation are based on the content, creating a unique fingerprint for each version of the artifact. Any tampering with the container will result in a failed verification, alerting users to potential security threats.

  • Verify authenticity—Knowing the source of the open source software you use is critical for security and compliance. Notary Project signatures provide a way to verify the authenticity of the artifacts by confirming the identity of the signer. This ensures that your applications are built from trusted sources—Bitnami—reducing the risk of deploying compromised or malicious software.

  • Support interoperability across tools and platforms—Notation plays a vital role in standardizing the representation of signatures. This standardization enables interoperability across different tools and platforms that support the OCI image format without being tied to a specific ecosystem.

Signature verification

To locally verify the signature of a Bitnami-packaged container image, follow the steps below:

  1. Download the “rootCA.cert” file from

  2. Download and install the Notation CLI for your platform from the official releases at

  3. Add the Tanzu Application Catalog Root CA certificate:

$ ./notation cert add --type ca --store VMware notationCA.crt

  1. Import the trust policy:

$ ./notation policy import trustpolicy.json

This is an example of the trustpolicy.json file:

$ cat trustpolicy.json


 "version": "1.0",

 "trustPolicies": [


     "name": "Tanzu Application Catalog",

      "registryScopes": [ "*" ],

      "signatureVerification": {

       "level" : "strict"


      "trustStores": [ "ca:VMware" ],

      "trustedIdentities": [






  1. Verify the signature of a container image of the Helm chart and check the latest available version or tag from DockerHub.

$ ./notation verify

Warning: Always verify the artifact using digest(@sha256:...) rather than a tag(:6.4.3-debian-12-r20) because resolved digest may not point to the same signed artifact, as tags are mutable.

Successfully verified signature for

  1. Use the digest directly.

$ ./notation verify

Successfully verified signature for

Check out our enterprise version - Tanzu Application Catalog!

If you’re interested in learning more about the enterprise edition of Bitnami Application Catalog - Tanzu Application Catalog, check out our product webpage, solution brief, and additional resources.

Are you going to be at  KubeCon + CloudNativeCon Europe 2024? If so, you can learn how to reinforce your software supply chain security by joining our session, VEXintating your Container Images: The European Way, on March 21, 2024 (Thursday) at 15:25 - 16:00 CET.

Wednesday, March 6, 2024

Spring Boot 3.3.0-M2’s Support for Bitnami Container Images: Developer’s Guide

Author: Agustin Ventura

As you may already know, starting from version 3.1, Spring Boot has provided Docker Compose support for our projects.

What does that mean for us as developers? Simply put, it means that we can easily bootstrap our application infrastructure from an existing docker-compose.yaml or compose.yaml in our source folder root, and Spring Boot will automatically wire this infrastructure with our application at runtime. Hmmm...does that still sound a bit abstract? Let’s clarify. It means that if we have a docker-compose.yaml in our source folder, which defines a PostgreSQL database, Spring Boot will run the docker-compose.yaml when starting the app and automatically create a DataSource in our application linked to the said database. It’s as easy as that. But, it sounds too good to be true, doesn’t it? If you are wondering if there’s a caveat, we wouldn’t blame you.

It turns out there is one—the container images used must be DockerHub’s official ones. So, if you want to run, say, a Cassandra instance, you are limited to DockerHub's official Cassandra image. The same goes for MySQL, PostgreSQL, Apache Pulsar, and all the supported Service Connections. But the good news is that this limitation no longer exists.

Spring Boot 3.3.0-M2 added support for Bitnami images for a number of Service Connections. For several years now, Bitnami has been a trusted provider of hundreds of packaged applications, for the cloud as well as containerized and on your local machine. Bitnami offers you automatic packaging and verification of up-to-date versions of hundreds of open source software (OSS) applications. Now, let's take a look at a practical use case.

A practical use case

If you’re currently working on a cloud native application powered by Kubernetes and using the latest tools such as ArgoCD and Helm charts, great! Now, you want to create a local dev environment that takes advantage of this Docker Compose support, however, there’s a very small caveat: Your Helm chart in production is using a very specific version of PostgreSQL: 13.14.0 on Debian 11 r5. That’s not a big deal! You have a quick glance at bitnami/postgresql, and notice that you have exactly that very same container image at your disposal: bitnami/postgresql:13.14.0-debian-11-r5. In case you didn’t know earlier, you can avail of enterprise-grade Bitnami packages from Tanzu Application Catalog, where you can get the latest versions of hundreds of OSS built and delivered to you on an SLSA L3-compliant pipeline, along with comprehensive metadata in industry standard formats, and also the ability to custom configure them per your enterprise policies. Problem solved!

So, now, you can go ahead and work with the exact same version you have at production with guaranteed updates on Bitnami’s side and maximum compatibility with your Helm charts. It is always nice to have a choice, isn’t it?

All of this information is great, but you still might wonder what it means to you as a developer and how can you leverage Docker Compose and Bitnami image support to build awesome applications. To get answers to these questions, let's code a bit and build another Todo List REST API to showcase how we can use it (yes, another to-do list example, I know).

To-do list example

As we are going to build a Spring application, our first stop must be the great Spring Initilizr or maybe you can use the wonderful Intellij wizard.

For this example, we will choose a Gradle with Groovy project with Java and Spring Boot 3.3.0 (M2), packaging jar, and Java 21 (because we are already using the latest LTS version in production, aren't we?).

For dependencies, we are choosing:

  • Spring Web: To be able to create the REST resources for our application
  • Spring Data JDBC: In order to abstract our database operations
  • PostgreSQL Driver: To communicate with our PostgreSQL database
  • Docker Compose Support: Which is what we really want to test
  • Flyway: To initialize our database schema

Now we are ready to go! We can click Generate and download a zip file with our bootstrapped project ready to code.

Upon unzipping it and opening it with our favorite Integrated Development Environment (IDE), we can notice a few interesting things.

First, let’s look at the build.gradle:

plugins {  

  id 'java'  

  id 'org.springframework.boot' version '3.3.0-M2'  

  id 'io.spring.dependency-management' version '1.1.4'  



group = 'com.bitnami'  

version = '0.0.1-SNAPSHOT'  


java {  

  sourceCompatibility = '21'  



repositories {  


  maven { url '' }  



dependencies {  

  implementation 'org.springframework.boot:spring-boot-starter-data-jdbc'  

  implementation 'org.springframework.boot:spring-boot-starter-web'

  implementation 'org.flywaydb:flyway-core'  

  developmentOnly 'org.springframework.boot:spring-boot-docker-compose'  

  runtimeOnly 'org.postgresql:postgresql'  

  testImplementation 'org.springframework.boot:spring-boot-starter-test'  



tasks.named('test') {  



We have all of our selected dependencies, and as Spring Initilizr warned us (see above image), Docker Compose support is developmentOnly, therefore it is only available in development time and not production. This makes sense, as you will probably already have a database in production and, no matter which way you are using it to spin it up, you don't want to get a new database running every time you start your application. Just imagine having a new database running every time a Kubernetes pod starts your application, that’s definitely not the way to go. 

Next, if we have a look at the project tree, we will notice a compose.yaml sitting there.

And if we have a look at it:



    image: 'postgres:latest'  


      - 'POSTGRES_DB=mydatabase'  

      - 'POSTGRES_PASSWORD=secret'  

      - 'POSTGRES_USER=myuser'  


      - '5432'

Spring Initilizr has recognized we are using PostgreSQL and has added a sensible configuration for us, now let's change this to use our Bitnami image:



    image: 'bitnami/postgresql:13.14.0-debian-11-r15'


      - 'POSTGRES_DB=mydatabase'  

      - 'POSTGRES_PASSWORD=secret'  

      - 'POSTGRES_USER=myuser'  


      - '5432:5432'

It’s as easy as that!

Now, for the rest of the application, we can use Spring's RestController and Data JDBC support to easily create new to-do items and load them. In our controller we can create new todo items, returning a 201 CREATED response with a header containing the URI of the created item, and we can get a todo item by its id:



public class TodoItemController {  


  private final TodoItemRepository todoItemRepository;  


  public TodoItemController(TodoItemRepository todoItemRepository) {  

    this.todoItemRepository = todoItemRepository;  




  public ResponseEntity<Void> create(@RequestBody TodoItem todoItem, UriComponentsBuilder ucb) {  

    TodoItem createdItem =;  

    URI locationOfCreatedItem = ucb  




    return ResponseEntity.created(locationOfCreatedItem).build();  




  public ResponseEntity<TodoItem> get(@PathVariable UUID id) {  

    Optional<TodoItem> todoItem = todoItemRepository.findById(id);  

    return -> ResponseEntity.notFound().build());  



The repository is as easy as this:

public interface TodoItemRepository extends CrudRepository<TodoItem, UUID> {  



For the sake of completeness, here's our TodoItem:

public record TodoItem(@Id UUID id, String title, String description) {  



Sorry to disappoint you if you were expecting a fancier approach using DDD or the like, but a good old record will serve our demonstration purposes.

Finally, we need a migration script to create our table in the database, so let's create it in src/main/resource/db/migrations (which is where Flyway expects to find it) and let's call it V1_0_0__schema_creation.sql:

CREATE TABLE todo_item  


    id            UUID PRIMARY KEY DEFAULT gen_random_uuid(),  

    title VARCHAR(255) NOT NULL,  

    description VARCHAR(255) NOT NULL  


There is nothing special here, if we receive a TodoItem to insert without id, it will generate a random one.

Our whole app has six files, of which we only had to edit four. Not bad, right?

So far so good. Let’s, test it from a terminal run.

./gradlew bootRun

After a few info messages and about two seconds (I know, I know, you want under second startup time, but please consider that we are creating the schema and this is a JIT application, not a native one, we are ready to test our new todo implementation:

curl -i --location 'localhost:8080/items' \

--header 'Content-Type: application/json' \

--data '{

"title": "Groceries",

"description": "Don'\''t forget zucchini"


You'll get a 201 response and you can query your new item using the URI provided in the location header:

curl --location 'http://localhost:8080/items/5480cb38-1a0d-4498-a2de-a934e7994882'

At this point, you may be wondering, what's so special here? Isn't this just another REST CRUD tutorial using Spring Web and Spring Data JDBC? Why am I even reading this?

And, you’re right, this is no more than a REST CRUD tutorial. But, if you’ve done a few of these, you may have noticed that we skipped a mandatory and tedious step...we didn’t configure the datasource! In fact, we didn't even edit We simply added the pieces that we want to use to create our application and it’s working, no configuration needed.

Thanks to the integration of Spring Boot's Service Connection and Bitnami images, you can enjoy a zero configuration local environment with version and operating system compatibility, the latest bug fixes and distribution packages, and a standardized configuration approach. And, that means you can easily move your configuration between your environments. Isn’t that just what you wanted?