---
name: solution-tutorials-cloud-e2e-security
title: Apply end to end security to a cloud application
description: This tutorial walks you through key security services available in the IBM Cloud&reg; catalog and how to use them together. An application that provides file sharing will put security concepts into practice.
last-updated: 2025-10-16
---

> ## Documentation Index
> The table of contents for this documentation set is at https://cloud.ibm.com/docs/solution-tutorials?format=markdown
> The index for all IBM Cloud docs is at: https://cloud.ibm.com/docs/llms.txt
> Use these files to discover more information as needed.

# Apply end to end security to a cloud application
{: #cloud-e2e-security}
{: toc-content-type="tutorial"}
{: toc-services="containers, openshift, cloud-object-storage, atracker, Registry, secrets-manager, appid, Cloudant, key-protect, cloud-logs, cis"}
{: toc-completion-time="2h"}
{: toc-use-case="Cybersecurity"}

This tutorial may incur costs. Use the [Cost Estimator](https://cloud.ibm.com/estimator){: external} to generate a cost estimate based on your projected usage.
{: tip}


This tutorial walks you through key security services available in the IBM Cloud&reg; catalog and how to use them together. An application that provides file sharing will put security concepts into practice.
{: shortdesc}

No application architecture is complete without a clear understanding of potential security risks and how to protect against such threats. Application data is a critical resource which can not be lost, compromised or stolen. Additionally, data should be protected at rest and in transit through encryption techniques. Encrypting data at rest protects information from disclosure even when it is lost or stolen. Encrypting data in transit (e.g. over the Internet) through methods such as HTTPS, SSL, and TLS prevents eavesdropping and so called man-in-the-middle attacks.

Authenticating and authorizing users' access to specific resources is another common requirement for many applications. Different authentication schemes may need to be supported: customers and suppliers using social identities, partners from cloud-hosted directories, and employees from an organization’s identity provider.

## Objectives
{: #cloud-e2e-security-objectives}

* Encrypt content in storage buckets with your own encryption keys.
* Require users to authenticate before accessing an application.
* Monitor and audit security-related API calls and other actions across cloud services.


The tutorial features a sample application that enables groups of users to upload files to a common storage pool and to provides access to those files via shareable links. The application is written in Node.js and deployed as a container to either IBM Cloud Kubernetes Service or Red Hat OpenShift on IBM Cloud. It leverages several security-related services and features to improve the application's security posture.

This tutorial will work with a cluster running in Classic Infrastructure or VPC Infrastructure.

![Architecture](images/solution34-cloud-e2e-security/architecture-e2e-security.svg){: caption="Architecture diagram of the tutorial" caption-side="bottom"}
{: style="text-align: center;"}


1. User connects to the application.
2. If using a custom domain and a TLS certificate, the certificate is managed by and deployed from Secrets Manager.
3. App ID secures the application and redirects the user to the authentication page. Users can also sign up.
4. The application runs in a Kubernetes cluster from an image stored in the Container Registry. This image is automatically scanned for vulnerabilities.
5. Uploaded files are stored in Object Storage with accompanying metadata stored in IBM Cloudant.
6. Object storage buckets, App ID, and Secrets Manager services leverage a user-provided key to encrypt data.
7. Application management activities are logged by IBM Cloud Activity Tracker Event Routing and routed to IBM Cloud Logs for analysis.

## Before you begin
{: #cloud-e2e-security-prereqs}

This tutorial requires:
* IBM Cloud CLI,
   * IBM Cloud Kubernetes Service plugin (`kubernetes-service`),
   * Container Registry plugin (`container-registry`),
* `kubectl` to interact with Kubernetes clusters,
* `git` to clone source code repository.

You will find instructions to download and install these tools for your operating environment in the [Getting started with solution tutorials](https://cloud.ibm.com/docs/solution-tutorials?topic=solution-tutorials-tutorials&format=markdown) guide.

To avoid the installation of these tools you can use the [Cloud Shell](https://cloud.ibm.com/shell){: external} from the IBM Cloud console.
{: tip}



## Create services
{: #cloud-e2e-security-setup}
{: step}

In the next section, you are going to create the services used by the application.

### Decide where to deploy the application
{: #cloud-e2e-security-4}

The **location** and **resource group** of all resources that you create should match with the **location** and **resource group** of the Kubernetes cluster.

### Capture user and application activities
{: #cloud-e2e-security-activity-tracker}

The [IBM Cloud Activity Tracker Event Routing](https://cloud.ibm.com/docs/atracker?topic=atracker-about&format=markdown) must be configured to route auditing events to an IBM Cloud Logs target instance. Route audit events as described in [configuring an IBM Logs target](https://cloud.ibm.com/docs/atracker?topic=atracker-getting-started-target-cloud-logs&format=markdown) if not currently configured in your account.

At the end of this tutorial, you will review the events that were generated by completing the tutorial's steps.


### Create a cluster for the application
{: #cloud-e2e-security-6}

IBM Cloud Kubernetes Service and Red Hat OpenShift on IBM Cloud provide environments to deploy highly available apps in containers that run in Kubernetes clusters.

Skip this section if you have an existing Kubernetes cluster you want to reuse with this tutorial, throughout the remainder of this tutorial the cluster name is referenced as **secure-file-storage-cluster**, simply substitute with the name of your cluster.
{: tip}

A minimal cluster with one (1) zone, one (1) worker node and the smallest available size (**Flavor**) is sufficient for this tutorial. To create your IBM Cloud Kubernetes Service cluster, follow the steps for either [Creating VPC clusters](https://cloud.ibm.com/docs/containers?topic=containers-cluster-create-vpc-gen2&interface=ui&format=markdown) or [Creating classic clusters](https://cloud.ibm.com/docs/containers?topic=containers-cluster-create-classic&format=markdown). To create your Red Hat OpenShift on IBM Cloud cluster, follow the steps for either [Creating VPC clusters](https://cloud.ibm.com/docs/openshift?topic=openshift-cluster-create-vpc-gen2&interface=ui&format=markdown) or [Creating classic clusters](https://cloud.ibm.com/docs/openshift?topic=openshift-cluster-create-classic&format=markdown).

### Use your own encryption keys
{: #cloud-e2e-security-7}

Key Protect helps you provision encrypted keys for apps across IBM Cloud services. Key Protect and IBM Cloud Object Storage [work together to protect your data at rest](https://cloud.ibm.com/docs/key-protect?topic=key-protect-integrate-cos&format=markdown). In this section, you will create one root key for the storage bucket.

1. Create an instance of [Key Protect](https://cloud.ibm.com/catalog/services/kms){: external}.
   1. Select a **location**.
   2. Set the name to `secure-file-storage-kp`.
   3. Select the **resource group** where to create the service instance and click **Create**.
2. Under **Keys**, click the **Add** button to create a new root key. It will be used to encrypt the storage bucket and App ID data.
   1. Set the key type to **Root key**.
   2. Set the name to `secure-file-storage-root-enckey`.
   3. Then **Add key**.

Bring your own key (BYOK) by [importing an existing root key](https://cloud.ibm.com/docs/key-protect?topic=key-protect-import-root-keys&format=markdown#import-root-keys).
{: tip}


### Setup storage for user files
{: #cloud-e2e-security-8}

The file sharing application saves files to the Object Storage bucket. The relationship between files and users is stored as metadata in the IBM Cloudant database. In this section, you'll create and configure these services.

#### A bucket for the content
{: #cloud-e2e-security-9}

1. Create an instance of [Object Storage](https://cloud.ibm.com/objectstorage/create){: external}.
   1. Select a **Standard** plan and Set the **name** to `secure-file-storage-cos`.
   2. Use the same **resource group** as for the previous services and click **Create**.
2. Under **Service credentials**, create a *New credential*.
   1. Set the **name** to `secure-file-storage-cos-acckey`.
   2. For **Role** select **Writer**.
   3. Under **Advanced options**, check **Include HMAC Credential**. This is required to generate pre-signed URLs.
   4. Click **Add**.
   5. Make note of the credentials. You will need them in a later step.
3. Click **Endpoints** from the navigation sidebar:
   1. Set **Resiliency** to **Regional** and set the **Location** to the target location:
   2. For Classic infrastructure: Copy the **Private** service endpoint. It will be used later in the configuration of the application.
   3. For VPC infrastructure: Copy the **Direct** service endpoint. It will be used later in the configuration of the application.

Before creating the bucket, you will grant the Object Storage service instance access to the root key stored in the Key Protect service instance.

1. Go to [Manage > Access (IAM) > Authorizations](https://cloud.ibm.com/iam/authorizations){: external} in the IBM Cloud console.
2. Click the **Create** button.
3. In the **Source service** menu, select **Cloud Object Storage**.
4. Switch to **Resources based on selected attributes**, check **Source service instance** and select the Object Storage service instance previously created.
5. In the **Target service** menu, select **Key Protect**.
6. Switch to **Resources based on selected attributes**, check **Instance ID**, select the Key Protect service instance created earlier.
7. Enable the **Reader** role.
8. Click the **Authorize** button.

Finally, create the bucket.

1. Access the Object Storage service instance from the [Resource List](https://cloud.ibm.com/resources){: external} Under **Storage**.
2. Click **Create bucket** and then **Customize your bucket**.
   1. Use a unique value for **name**, such as `<your-initials>-secure-file-upload`.
   2. Set **Resiliency** to **Regional**.
   3. Set **Location** to the same location where you created the Key Protect service instance.
   4. Set **Storage class** to **Standard**
3. Under **Service integrations (optional) / Encryption**, enable **Key management**
   1. Select the Key Protect service instance created earlier by clicking on **Use existing instance**
   2. Select **secure-file-storage-root-enckey** as the key and click **Associate key**.
4. Under **Service integrations (optional) / Monitoring & Activity tracking**, enable **Activity tracking** to have audit events persisted for analysis.
   1. After clicking the checkmark the service information for the Activity Tracker instance in the region should be shown.
   2. Now, enable **Track Data events** and select **read & write** as **Data Events**.
5. Click **Create bucket**.

### A database map relationships between users and their files
{: #cloud-e2e-security-10}

The IBM Cloudant database will contain metadata for all files uploaded from the application.

1. Create an instance of [IBM Cloudant](https://cloud.ibm.com/catalog/services/cloudant){: external} service.
   1. Select **Cloudant** as the offering.
   2. Select a **Multitenant** environment and a **region** same as the previous services.
   3. Set the **name** to `secure-file-storage-cloudant`.
   4. Use the same **resource group** as for the previous services.
   5. Set **Authentication method** to **IAM**.
   6. Click **Create**.
2. Back to the **Resource List**, locate the newly created service and click on it. *Note: You will need to wait until the status changes to Active*.
   1. Under **Service credentials**, create **New credential**.
   2. Set the **name** to `secure-file-storage-cloudant-acckey`.
   3. For **Role** select **Manager**.
   4. Keep the default values for the remaining fields.
   5. Click **Add**.
3. Expand the newly created credentials and make note of the values. You will need them in a later step.
4. Under **Manage**, click on **Launch Dashboard**.
5. Click **Create Database** to create a **Non-partitioned** database named `secure-file-storage-metadata`.

### Authenticate users
{: #cloud-e2e-security-11}

With App ID, you can secure resources and add authentication to your applications. As an alternative not used in this tutorial, App ID can [integrate](https://cloud.ibm.com/docs/containers?topic=containers-comm-ingress-annotations&format=markdown#app-id-authentication) with Kubernetes Service to authenticate users accessing applications deployed in the cluster.

Before creating the App ID service, grant service access to Key Protect service. You must be the account owner or an administrator for the instance of Key Protect that you're working with. You must also have at least Viewer access for the App ID service.

1. Go to [Manage > Access IAM > Authorizations](https://cloud.ibm.com/iam/authorizations){: external} and click **Create**.
2. Select the **App ID** service as your source service.
3. Select **Key Protect** as your target service.
4. Switch to **Resources based on selected attributes**, check **Instance ID**, select the Key Protect service instance created earlier.
5. Assign the **Reader** role under Service access.
6. Click **Authorize** to confirm the delegated authorization.

Now, Create an instance of the App ID service.
1. Navigate to the [App ID](https://cloud.ibm.com/catalog/services/AppID){: external} service creation page.
   1. Use the same **location** used for the previous services.
   2. Select the **Graduated tier** as plan.
   3. Set the **Service name** to `secure-file-storage-appid`.
   4. Select a **resource group** same as the previous services.
   5. Select the authorized Key Protect service **name** and the **root key** from the respective dropdowns.
   6. Click **Create**.
2. Under **Manage Authentication**, in the **Authentication Settings** tab, add a **web redirect URL** pointing to the domain you will use for the application. The URL format is `https://secure-file-storage.<Ingress subdomain>/redirect_uri`. For example:
   * with the ingress subdomain: `mycluster-1234-d123456789.us-south.containers.appdomain.cloud`
   * the redirect URL is `https://secure-file-storage.mycluster-1234-d123456789.us-south.containers.appdomain.cloud/redirect_uri`.

   [App ID requires the web redirect URL](https://cloud.ibm.com/docs/appid?topic=appid-managing-idp&format=markdown#add-redirect-uri) to be **https** or **http**. You can view your Ingress subdomain in the cluster dashboard or with `ibmcloud ks cluster get --cluster <cluster-name>`.
   {: tip}

3. In the same tab under **Authentication Settings** under **Runtime Activity** enable capturing events in IBM Cloud Activity Tracker Event Routing.
4. Create service credentials:
   1. Under **Service credentials**, create **New credential**.
   2. Set the **name** to `secure-file-storage-appid-acckey`.
   3. For **Role** select **Manager**.
   4. Keep the default values for the remaining fields.
   5. Click **Add**.


You should customize the identity providers used as well as the login and user management experience in the App ID dashboard. This tutorial uses the defaults for simplicity. For a production environment, consider to use Multi-Factor Authentication (MFA) and advanced password rules.
{: tip}

## Deploy the app
{: #cloud-e2e-security-deploy}
{: step}

All services have been configured. In this section you will deploy the tutorial application to the cluster. All of this can be accomplished from a shell environment (terminal).

### Get the code
{: #cloud-e2e-security-13}

1. Get the application's code:
   ```sh
   git clone https://github.com/IBM-Cloud/secure-file-storage
   ```
   {: codeblock}

2. Go to the **secure-file-storage/app** directory:
   ```sh
   cd secure-file-storage/app
   ```
   {: codeblock}

### Fill in configuration settings and credentials
{: #cloud-e2e-security-15}

1. If you are not logged in, use `ibmcloud login` or `ibmcloud login --sso` to log in interactively. Target your IBM Cloud region and resource group.

   ```sh
   ibmcloud target -r <region> -g <resource_group>
   ```
   {: codeblock}

   You can find more CLI commands in the [General IBM Cloud CLI (ibmcloud) commands](https://cloud.ibm.com/docs/cli?topic=cli-ibmcloud_cli&format=markdown) topic in the documentation.

2. Set the environment variables required for generating configuration files in the next step.
   1. Start by setting the cluster name by replacing `<YOUR_CLUSTER_NAME>`:
      ```sh
      export MYCLUSTER=<YOUR_CLUSTER_NAME>
      ```
      {: pre}

   2. Set the ingress subdomain using `ibmcloud ks` commands:
      ```sh
      export INGRESS_SUBDOMAIN=$(ibmcloud ks cluster get --cluster $MYCLUSTER --output json | jq -r 'try(.ingressHostname) // .ingress.hostname')
      ```
      {: pre}

   3. Set the ingress secret using `ibmcloud ks` commands:
      ```sh
      export INGRESS_SECRET=$(ibmcloud ks cluster get --cluster $MYCLUSTER --output json | jq -r 'try(.ingressSecretName) // .ingress.secretName')
      ```
      {: pre}

   4. Set the image repository name to the pre-built image `icr.io/solution-tutorials/tutorial-cloud-e2e-security`:
      ```sh
      export IMAGE_REPOSITORY=icr.io/solution-tutorials/tutorial-cloud-e2e-security
      ```
      {: pre}

   5. Set additional environment variables by replacing the default values:
      ```sh
      export BASENAME=secure-file-storage
      ```
      {: pre}

   6. Set the namespace to use:
      ```sh
      export TARGET_NAMESPACE=default
      ```
      {: pre}

   7. Optionally set `$IMAGE_PULL_SECRET` environment variable only if you are using another Kubernetes namespace than the `default` namespace and the IBM Cloud Container Registry for the image. This requires additional Kubernetes configuration (e.g. [creating a container registry secret in the new namespace](https://cloud.ibm.com/docs/containers?topic=containers-registry&format=markdown#other)).

3. Run the below command to generate `secure-file-storage.yaml` and `secure-file-storage-ingress.yaml`. It will use the environment variables you just configured together with the template files `secure-file-storage.template.yaml` and `secure-file-storage-ingress.template.yaml`.
      ```sh
      ./generate_yaml.sh
      ```
      {: pre}

   As example, assuming the application is deployed to the *default* Kubernetes namespace:

   | Variable | Value | Description |
   | -------- | ----- | ----------- |
   | `$IMAGE_PULL_SECRET` | Do not define when using provided image| A secret to access the registry.  |
   | `$IMAGE_REPOSITORY` | *icr.io/solution-tutorials/tutorial-cloud-e2e-security* or *icr.io/namespace/image-name* | The URL-like identifier for the built image based on the registry URL, namespace and image name from the previous section. |
   | `$TARGET_NAMESPACE` | *default* |   the Kubernetes namespace where the app will be pushed. |
   | `$INGRESS_SUBDOMAIN` | *secure-file-stora-123456.us-south.containers.appdomain.cloud* | Retrieve from the cluster overview page or with `ibmcloud ks cluster get --cluster <your-cluster-name>`. |
   | `$INGRESS_SECRET` | *secure-file-stora-123456* | Retrieve with `ibmcloud ks cluster get --cluster <your-cluster-name>`. |
   | `$BASENAME` | *secure-file-storage* | The prefix used to identify resources. |
   {: caption="Environment variables used by the script" caption-side="bottom"}

4. Copy `credentials.template.env` to `credentials.env`:
   ```sh
   cp credentials.template.env credentials.env
   ```
   {: codeblock}

5. Edit `credentials.env` and fill in the blanks with these values:
   * the Object Storage service regional endpoint, the bucket name, the credentials created for the Object Storage service,
   * the credentials for **secure-file-storage-cloudant**,
   * and the credentials for App ID. The variable `appid_redirect_uris` is a comma-separated list of redirect URIs as discussed above.

   When using Cloud Shell, you can use `nano credentials.env` to edit the file.
   {: tip}


### Deploy to the cluster
{: #cloud-e2e-security-16}

1. Gain access to your cluster as described in the instructions **Connect via CLI** accessible from the **Actions...** menu in your console overview page.
   ```sh
   ibmcloud ks cluster config --cluster $MYCLUSTER --admin
   ```
   {: codeblock}

2. Check that an Ingress secret is available in your target namespace. If not, you need to create it.
   ```sh
   ibmcloud ks ingress secret ls -c $MYCLUSTER
   ```
   {: codeblock}

   If the Ingress secret has a CRN, use its name and CRN to create a secret in your target namespace:
   ```sh
   ibmcloud ks ingress secret create -c $MYCLUSTER -n $TARGET_NAMESPACE --cert-crn <crn-shown-in-the-output-above> --name <secret-name-shown-above>
   ```
   {: codeblock}

   If the Ingress secret does not have a CRN, use the following command to recreate it in the target namespace:
   ```sh
   kubectl get secret $INGRESS_SECRET --namespace=ibm-cert-store -oyaml | grep -v '^\s*namespace:\s'| kubectl apply  --namespace=$TARGET_NAMESPACE -f -
   ```
   {: codeblock}

3. Create the secret used by the application to obtain service credentials:
   ```sh
   kubectl create secret generic secure-file-storage-credentials --from-env-file=credentials.env
   ```
   {: codeblock}

4. Deploy the app.
   ```sh
   kubectl apply -f secure-file-storage.yaml
   ```
   {: codeblock}

5. Deploy the network routing (a ClusterIP service and Ingress) for your app to make it accessible from the public internet.
   ```sh
   kubectl apply -f secure-file-storage-ingress.yaml
   ```
   {: codeblock}




## Test the application
{: #cloud-e2e-security-5}
{: step}

The application can be accessed at `https://secure-file-storage.<your-cluster-ingress-subdomain>/`.

1. Go to the application's home page. You will be redirected to the App ID default login page.
2. Sign up for a new account with a valid email address.
3. Wait for the email in your inbox to verify the account.
4. Login.
5. Choose a file to upload. Click **Upload**.
6. Use the **Share** action on a file to generate a pre-signed URL that can be shared with others to access the file. The link is set to expire after 5 minutes.

Authenticated users have their own spaces to store files. While they can not see each other files, they can generate pre-signed URLs to grant temporary access to a specific file.

You can find more details about the application in the [source code repository](https://github.com/IBM-Cloud/secure-file-storage){: external}.

## Review Security Events
{: #cloud-e2e-security-18}
{: step}

Now that the application and its services have been successfully deployed, you can review the security events generated by that process. All the events are centrally available in IBM Cloud Logs instance.

1. From the [**Observability**](https://cloud.ibm.com/observe/logging) dashboard, select the **Cloud Logs** tab and locate the IBM Cloud Logs instance that is receiving the auditing events and click **Open dashboard**.
2. Review all logs sent to the service as you were provisioning and interacting with resources.

## Optional: Use a custom domain and encrypt network traffic
{: #cloud-e2e-security-19}
{: step}

By default, the application is accessible on a generic subdomain of `containers.appdomain.cloud`. However, it is also possible to use a custom domain with the deployed app. For continued support of **https**, access with encrypted network traffic, either a certificate for the desired hostname or a wildcard certificate needs to be provided. There are various combinations of services that can be used to manage DNS names and TLS certificates for integration into a Kubernetes application. This tutorial will use the following services:
- DNS subdomain, **secure-file-storage**, of your own custom DNS domain, that is managed by IBM Cloud Internet Services (CIS) service. For the purpose of simplifying the steps in this tutorial, we will use **example.com** for the name of the custom DNS domain, make sure to substitute with your custom DNS domain in all steps.
- [Let's Encrypt](https://letsencrypt.org/){: external} to generate the TLS certificates.
- IBM Cloud Secrets Manager to integrate with Let's Encrypt to generate the TLS certificate for **secure-file-storage.example.com** and securely store it.
- Kubernetes [External Secrets Operator](https://external-secrets.io/v0.7.0/){: external} to pull the secret TLS certificate directly from Secrets Manager

Instead of the following steps, you could also create a CNAME pointing to the app URI at your DNS provider, generate a TLS certificate and import its components into Secrets Manager.
{: tip}

### Provision CIS and Secrets Manager instances
{: #cloud-e2e-security-cis-instance}

- A [IBM Cloud Internet Services](https://cloud.ibm.com/catalog/services/internet-services){: external} instance is required. Use an existing instance or create one from this [catalog entry](https://cloud.ibm.com/catalog/services/internet-services){: external}.  A number of pricing plans are available, including a free trial. The provisioning process of a new CIS will explain how to configure your existing DNS registrar (perhaps not in IBM Cloud) to use the CIS-provided domain name servers. Export the custom domain in the shell window:
   ```sh
   export MYDOMAIN=example.com
   ```
   {: codeblock}

- An instance of Secrets Manager is required. Use an existing instance or create a new one described in [Creating a Secrets Manager service instance](https://cloud.ibm.com/docs/secrets-manager?topic=secrets-manager-create-instance&interface=ui&format=markdown). If creating a new instance, name it **secure-file-storage-sm**.  You can enhance the security of your secrets at rest by integrating with the Key Protect instance created earlier.

Create a DNS entry in the CIS instance using your Kubernetes cluster **Ingress subdomain** as the alias.
1. Open the CIS service instance, you can find it in the [Resource List](https://cloud.ibm.com/resources).
2. Click the **Reliability** tab on the left.
3. Click the **DNS** tab on the top.
4. Scroll down to the DNS Records section and click **Add** to create a new record:
   1. Type: **CNAME**
   2. Name: **secure-file-storage**
   3. Alias: The **Ingress subdomain** of your cluster. You can obtain the correct value in the shell by executing the following command:
      ```sh
      echo $INGRESS_SUBDOMAIN
      ```
   {: codeblock}

   4. Click **Add** to add the new record.

Connect Secrets Manager instance to Let's Encrypt.
1. A Let's Encrypt ACME account and associated **.pem** file is required. Use an existing one or [create one](https://cloud.ibm.com/docs/secrets-manager?topic=secrets-manager-prepare-order-certificates&format=markdown#create-acme-account):
   1. Install the **acme-account-creation-tool**.  [Creating a Let's Encrypt ACME account](https://cloud.ibm.com/docs/secrets-manager?topic=secrets-manager-prepare-order-certificates&format=markdown#create-acme-account) contains instructions and a link to the creation tool.
   2. Run **acme-account-creation-tool** to create an account specifically for this secure-file-storage example. Below is an example:
      ```sh
      $ ./acme-account-creation-tool-darwin-amd64 -e YOUREMAIL -o secure-file-storage.example.com -d letsencrypt-prod
      INFO[2022-12-28T13:30:00-08:00] Registering a new account with the CA
      INFO[2022-12-28T13:30:00-08:00] Account information written to file : secure-file-storage.example.com-account-info.json
      INFO[2022-12-28T13:30:00-08:00] Private key written to file : secure-file-storage.example.com-private-key.pem

      Account Info
      {
         "email": "YOUREMAIL",
         "registration_uri": "https://acme-v02.api.letsencrypt.org/acme/acct/891897087",
         "registration_body": {
            "status": "valid",
            "contact": [
               "mailto:YOUREMAIL"
            ]
         }
      }%
      $ ls
      secure-file-storage.example.com-account-info.json secure-file-storage.example.com-private-key.pem
      ```
2. Connect the Let's Encrypt ACME account to the Secrets Manager instance. See [Adding a certificate authority configuration in the UI](https://cloud.ibm.com/docs/secrets-manager?topic=secrets-manager-add-certificate-authority&interface=ui&format=markdown#add-certificate-authority-ui) for more details:
   1. Open the Secrets Manager service instance, you can find it in the [Resource List](https://cloud.ibm.com/resources){: external}.
   2. Open **Secrets engines** on the left and click **Public certificates**.
   3. Under **Certificate authorities** click **Add**.
   4. **Name**: LetsEncrypt and **Certificate authority**: Let's Encrypt.
   5. Under **Select file** click **Add file** and choose the **secure-file-storage.example.com-private-key.pem** or your existing **.pem** file from the chooser.
   6. Click **Add**.
3. Connect the CIS as a DNS provider:
   1. Under DNS providers click **Add**.
   2. **Name** cis and choose **Cloud Internet Services** from the dropdown.
   3. Click **Next**.
   4. In the **Authorization** tab choose the CIS instance.
   5. Click **Add**.
4. Order a certificate in Secrets Manager
   1. Open the Secrets Manager service and select **Secrets** on the left.
   2. Click **Add**.
   3. Click on **Public certificate** and then click on **Next**.
   4. Complete the form:
         - **Name** - type a name you can remember.
         - **Description** - enter a description of your choice.
         - Click on **Next**.
         - Under **Certificate authority** select your configured **Let's Encrypt** certificate authority engine.
         - Under **Key algorithm**, pick your preferred algorithm,
         - **Bundle certificates** - leave off
         - **Automatic certificate rotation** - leave off
         - Under **DNS provider** select your configured DNS provider instance
         - Click on **Select domains** check the **Select with wildcard** and leave the domain itself unchecked and click on **Done**.
   5. Click **Next**.
   6. Review your selections and click on **Add**.
   7. Click the three vertical dots menu for the active secret and choose **Details** and copy the **CRN** from the dialog. Export the value in the shell. It will look something like this:
      ```sh
      export PUBLIC_CERT_CRN=crn:v1:bluemix:public:secrets-manager:eu-de:a/abc123abc123abc123abc123:99999999-9999-9999-9999-999999999999:secret:aaaaaaaa-9999-9999-aaaa-123456781234
      ```
      {: codeblock}

5. This tutorial leverages service to service authorization to give the cluster access to the Secrets Manager service instance and its managed secrets.

   1. Go to the [IAM Authorizations page](https://cloud.ibm.com/iam/authorizations){: external} and click **Create** to add a new authorization.
   2. Under **Source** select **Kubernetes Service**, then click to pick **Specific resources**. Then, for **Source service instance**, choose your cluster.
   3. Under **Target** select **Secrets Manager**, then, going with **Specific resources** and **Instance ID**, select your Secrets Manager service instance.
   4. Finally, under **Roles** select **Manager** and grant the authorization by clicking **Authorize**.

6. Verify the values for MYDOMAIN and PUBLIC_CERT_CRN have been exported into the environment:
   ```sh
   echo MYDOMAIN $(printenv MYDOMAIN)
   echo PUBLIC_CERT_CRN $(printenv PUBLIC_CERT_CRN)
   ```
   {: codeblock}


7. Create an Ingress secret from the new TLS certificate.
   ```sh
   ibmcloud ks ingress secret create --name secure-file-storage-certificate --cluster $MYCLUSTER --cert-crn $PUBLIC_CERT_CRN --namespace $TARGET_NAMESPACE
   ```
   {: codeblock}

8. Run the below command to generate new copies of the configuration files. It will use all the environment variables you configured together with the template files `secure-file-storage.template.yaml` and `secure-file-storage.template-ingress.yaml`. You may want to first save the current version:
   ```sh
   cp secure-file-storage.yaml /tmp
   cp secure-file-storage-ingress.yaml /tmp
   ```
   {: pre}

   ```sh
   ./generate_yaml.sh
   ```
   {: pre}


9. Apply the configuration changes to your cluster:
   ```sh
   kubectl apply -f secure-file-storage-ingress.yaml
   ```
   {: codeblock}

10. Switch back to the browser. In the [IBM Cloud Resource List](https://cloud.ibm.com/resources) locate the previously created and configured App ID service and launch its management dashboard.
   * Click **Manage Authentication** on the left and the **Authentication Settings** tab on the top.
   * In the **Add web redirect URLs** form add `https://secure-file-storage.example.com/redirect_uri` as another URL.
11. Everything should be in place now. Test the app by accessing it at your configured custom domain `https://secure-file-storage.<your custom domain>`.


## Security: Rotate service credentials
{: #cloud-e2e-security-20}
{: step}

To maintain security, service credentials, passwords and other keys should be replaced (rotated) a regular basis. Many security policies have a requirement to change passwords and credentials every 90 days or with similar frequency. Moreover, in the case an employee leaves the team or in (suspected) security incidents, access privileges should be changed immediately.

In this tutorial, services are utilized for different purposes, from storing files and metadata over securing application access to managing container images. Rotating the service credentials typically involves
- renaming the existing service keys,
- creating a new set of credentials with the previously used name,
- replacing the access data in existing Kubernetes secrets and applying the changes,
- and, after verification, deactivating the old credentials by deleting the old service keys.

## Expand the tutorial
{: #cloud-e2e-security-21}

Security is never done. Try the below suggestions to enhance the security of your application.

* Replace IBM Key Protect by [Hyper Protect Crypto Services](https://cloud.ibm.com/docs/hs-crypto?topic=hs-crypto-get-started&format=markdown) for even greater security and control over encryption keys.


## Share resources
{: #cloud-e2e-security-22}

If you want to work with others on resources of this solution tutorial, you can share all or only some of the components. [IBM Cloud Identity and Access Management (IAM)](https://cloud.ibm.com/docs/account?topic=account-iamoverview&format=markdown) enables the authentication of users and service IDs and the access control to cloud resources. For granting access to a resource, you can assign [predefined access roles](https://cloud.ibm.com/docs/account?topic=account-userroles&format=markdown) to either a user, a service ID, or to an [access group](https://cloud.ibm.com/docs/account?topic=account-groups&format=markdown). An access group can be created to organize a set of users and service IDs into a single entity. It makes it easy for you to assign access. You can assign a single policy to the group instead of assigning the same access multiple times per individual user or service ID. Thus, you can organize groups for roles on your development project and align security and project management.

You can find information on the individual services and their available IAM access roles here:
* [Kubernetes Service](https://cloud.ibm.com/docs/containers?topic=containers-iam-platform-access-roles&format=markdown) or the same for [Red Hat OpenShift on IBM Cloud](https://cloud.ibm.com/docs/openshift?topic=openshift-iam-platform-access-roles&format=markdown).
* [Container Registry](https://cloud.ibm.com/docs/Registry?topic=Registry-iam&format=markdown#iam)
* [App ID](https://cloud.ibm.com/docs/appid?topic=appid-service-access-management&format=markdown)
* [IBM Cloudant](https://cloud.ibm.com/docs/Cloudant?topic=Cloudant-managing-access-for-cloudant&format=markdown)
* [Object Storage](https://cloud.ibm.com/docs/cloud-object-storage?topic=cloud-object-storage-iam-overview&format=markdown)
* [IBM Cloud Activity Tracker Event Routing](https://cloud.ibm.com/docs/atracker?topic=atracker-iam&format=markdown)
* [Key Protect](https://cloud.ibm.com/docs/key-protect?topic=key-protect-manage-access&format=markdown)
* [Secrets Manager](https://cloud.ibm.com/docs/secrets-manager?topic=secrets-manager-iam&format=markdown)

To get started, check out the [best practices for access management and how to define access groups](https://cloud.ibm.com/docs/account?topic=account-account_setup&format=markdown#resource-group-strategy).

## Remove resources
{: #cloud-e2e-security-23}
{: removeresources}

To remove the resource, delete the deployed container and then the provisioned services.

If you share an account with other users, always make sure to delete only your own resources.
{: tip}

1. Delete the deployed network configuration and the container:
   ```sh
   kubectl delete -f secure-file-storage-ingress.yaml
   ```
   {: codeblock}

   Thereafter, run the following command:
   ```sh
   kubectl delete -f secure-file-storage.yaml
   ```
   {: codeblock}

2. Delete the secrets for the deployment:
   ```sh
   kubectl delete secret secure-file-storage-credentials
   ```
   {: codeblock}

3. If you utilized Secrets Manager, [remove the related service to service authorization](https://cloud.ibm.com/iam/authorizations){: external}.

4. In the [IBM Cloud Resource List](https://cloud.ibm.com/resources) locate the resources that were created for this tutorial. Use the search box and **secure-file-storage** as pattern. Delete each of the services by clicking on the context menu next to each service and choosing **Delete Service**. Note that the Key Protect service can only be removed after the key has been deleted. Click on the service instance to get to the related dashboard and to delete the key.

Depending on the resource it might not be deleted immediately, but retained (by default for 7 days). You can reclaim the resource by deleting it permanently or restore it within the retention period. See this document on how to [use resource reclamation](https://cloud.ibm.com/docs/account?topic=account-resource-reclamation&format=markdown).
{: tip}

## Related content
{: #cloud-e2e-security-12}
{: related}

* [IBM Cloud Platform security](https://cloud.ibm.com/docs/overview?topic=overview-security&format=markdown#security)
* [Security in the IBM Cloud](https://www.ibm.com/products/cloud/security){: external}