Use Secrets with Oracle Cloud Native Environment
Introduction
Many applications have information, such as passwords, that only authorized users should access. This situation is where Kubernetes Secrets can help. They provide a way to securely manage sensitive data such as passwords, SSH, and API keys. Using secrets allows the separation of confidential data from the application's code, which reduces the risk of sensitive data being exposed or modified during the workflow of your Pods.
Before you start, it helps to know some basic details about Kubernetes Secrets:
- They must be present before being used by a Pod.
- Defined using a KEY and VALUE pair.
- Limited to a maximum size of 1MB to prevent them from using all of the kube-apiserver and kubelet memory.
- Define them as immutable to protect them from accidental or unwanted modification.
Important: Kubernetes Secrets are not encrypted by default because they are only Base64-encoded and are stored unencrypted in etcd. So anyone with access to etcd can view or alter a Secret. Using Kubernetes Secrets can be made safe by using any of these methods:
- Enable Encryption at Rest for Secrets.
- Configure least-privilege access by using RBAC authorization .
- Restrict access to secrets to specific containers.
- Using an external secrets management service.
This tutorial covers the basics of Kubernetes Secrets and demonstrates a simple use case.
For more information about Oracle Cloud Native Environment 2, please refer to the current Release Documentation site.
Objectives
In this tutorial, you'll learn to:
- Create a Secret
- View a Secret
- Decode a Secret
- Use a Secret to define an environment variable in a deployment
Prerequisites
- Installation of Oracle Cloud Native Environment (Oracle CNE)
- A single control node and one worker node
Configure Oracle Cloud Native Environment
Note: If running in your own tenancy, read the linux-virt-labs
GitHub project README.md and complete the prerequisites before deploying the lab environment.
Open a terminal on the Luna Desktop.
Clone the
linux-virt-labs
GitHub project.git clone https://github.com/oracle-devrel/linux-virt-labs.git
Change into the working directory.
cd linux-virt-labs/ocne2
Install the required collections.
ansible-galaxy collection install -r requirements.yml
Deploy the lab environment.
ansible-playbook create_instance.yml -e localhost_python_interpreter="/usr/bin/python3.6" -e install_ocne_rpm=true -e create_ocne_cluster=true -e "ocne_cluster_node_options='-n 1 -w 1'"
The free lab environment requires the extra variable
local_python_interpreter
, which setsansible_python_interpreter
for plays running on localhost. This variable is needed because the environment installs the RPM package for the Oracle Cloud Infrastructure SDK for Python, located under the python3.6 modules.The default deployment shape uses the AMD CPU and Oracle Linux 8. To use an Intel CPU or Oracle Linux 9, add
-e instance_shape="VM.Standard3.Flex"
or-e os_version="9"
to the deployment command.Important: Wait for the playbook to run successfully and reach the pause task. At this stage of the playbook, the installation of the Oracle CNE is complete, and the instances are ready. Take note of the previous play, which prints the public and private IP addresses of the nodes it deploys and any other deployment information needed while running the lab.
Access the Kubernetes Cluster
Open a terminal and connect via SSH to the ocne instance.
ssh oracle@<ip_address_of_instance>
Wait for the cluster to stabilize and all pods to report in a running state.
watch kubectl get pods -A
Once all the pods show a STATUS of Running, type
ctrl-c
to exit thewatch
command.Confirm how many nodes are present.
kubectl get nodes
Creating Secrets
There are three ways to create Kubernetes Secrets, which are:
- Using the kubectl command-line tool to manage secrets directly from the command line.
- Using the kubectl command-line tool with a resource configuration file.
- Using the Kustomize object management tool with a
kustomization.yaml
file.
Using Kubectl Directly from the Command Line
Kubectl creates secrets in one of two ways:
- Passing them directly via the command line.
- Storing them as credentials in files.
Create a secret using literal values passed directly from the command line.
kubectl create secret generic my-literal-secret --from-literal=username=my-user --from-literal=password=my-password
Note: Use single quotes
''
to escape special characters such as$
,\
,$
,&
,=
and!
included in the string values. Otherwise, the command shell will interpret them.Example Output:
[oracle@ocne ~]$ kubectl create secret generic my-literal-secret --from-literal=username=my-user --from-literal=password=my-password secret/my-literal-secret created
Using Kubectl with Stored Credential Files
The file's contents become the VALUE, and the filename becomes the KEY.
Create the credential files.
echo -n 'admin' > ./username.txt echo -n 'S!B\*d$zDsb=' > ./password.txt
Create a Secret using saved files.
kubectl create secret generic my-file-secret \ --from-file=username=./username.txt \ --from-file=password=./password.txt
Note: The default behavior uses the filename as the KEY value to show how to override the default behavior and declare the KEY name-value directly from the command line using the
--from-file=[key]=[path to file]
method.Extra Information:
Alternatively, you can create a secret from multiple files stored in a sub-directory.
Create a subdirectory.
mkdir secrets
Create the credential files.
echo -n 'user1' > ./secrets/username.txt echo -n 'my-super-secret-password' > ./secrets/password.txt
Create a Secret using credential files in a subdirectory.
kubectl create secret generic my-secret --from-file=./secrets/
Note: This method uses each filename as the KEY value and the file's contents as the VALUE.
Using Kubectl to Apply a YAML Configuration File
Create the Secrets YAML definition file.
cat << EOF | tee db-credentials.yaml > /dev/null apiVersion: v1 kind: Secret metadata: name: db-credentials # immutable: true type: Opaque data: username: "bXktdXNlcg==" password: "bXktcGFzc3dvcmQ=" EOF
Note: The Secret values in the YAML file must be BASE64 encoded.
Apply the Secret.
kubectl apply -f db-credentials.yaml
Using Kustomize with a kustomization.yaml
File
Creating Secrets with Kustomize requires a kustomization.yaml
file. That file should define a secretGenerator
using one of the following methods:
- Files storing the secret data value(s) and the filenames become the KEY values.
- The unencrypted literal version of the data value in a file.
- Environment variable (
.env
) files.
All the above do not require the secret values to be Base64 encoded when using Kustomize. The name of the YAML file used by Kustomize must be either kustomization.yaml
or kustomization.yml
.
This example shows how to use Literals to create a Secret.
Create the secretGenerator file.
cat << EOF | tee kustomization.yaml > /dev/null secretGenerator: - name: database-credentials literals: - username=admin - password=password EOF
Generate the secret with Kustomize.
kubectl -n default apply -k .
Example Output:
[oracle@ocne ~]$ kubectl -n default apply -k . secret/database-credentials-fd8288cb7g created
Managing Kubernetes Secrets
You can store Kubernetes Secrets in different namespaces. So you need to use the -n
option to retrieve Secrets from a specific namespace, or use --all-namespaces
or -A
to retrieve Secrets from all namespaces. The kubectl
command uses the default
namespace if you don't specify a namespace. The same behavior applies when creating a Kubernetes Secret.
List Existing Kubernetes Secrets
List the newly created Secrets.
kubectl get secrets
Example Output:
NAME TYPE DATA AGE database-credentials-fd8288cb7g Opaque 2 34s db-credentials Opaque 2 2m16s my-file-secret Opaque 2 2m40s my-literal-secret Opaque 2 2m51s
Get more details about the newly created Secrets.
kubectl describe secrets
Example Output:
[oracle@ocne ~]$ kubectl describe secrets Name: database-credentials-fd8288cb7g Namespace: default Labels: <none> Annotations: <none> Type: Opaque Data ==== password: 8 bytes username: 5 bytes Name: db-credentials Namespace: default ... ... Name: my-literal-secret Namespace: default Labels: <none> Annotations: <none> Type: Opaque Data ==== username: 7 bytes password: 11 bytes
Notice that the
kubectl get
andkubectl describe
commands do not display the contents of asecret
to prevent accidental exposure or inclusion in a log file.View information for one of the Secrets.
kubectl -n default describe secret db-credentials
Example Output:
[oracle@ocne ~]$ kubectl -n default describe secret db-credentials Name: db-credentials Namespace: default Labels: <none> Annotations: <none> Type: Opaque Data ==== password: 11 bytes username: 7 bytes
Decode a Kubernetes Secret
View the stored data for one of the Secrets.
kubectl -n default get secret db-credentials -o jsonpath='{.data}'
Example Output:
[oracle@ocne ~]$ kubectl -n default get secret db-credentials -o jsonpath='{.data}' {"password":"bXktcGFzc3dvcmQ=","username":"bXktdXNlcg=="}[oracle@ocne ~]$
The output displays the encoded Key:Value pairs for the secret data. The value data is base64-encoded.
Decode the encoded Key:Value data.
echo <BASE64-VALUE-FOR-PASSWORD> | base64 --decode
Example Output:
[oracle@ocne ~]$ echo bXktc3VwZXItc2VjcmV0LXBhc3Nvd3Jk | base64 --decode my-password[oracle@ocne ~]$
WARNING: These steps could result in a record of the secret data in your shell history. Avoid this by combining both steps, as shown in the next step.
A more secure method to decode encoded Key:Value data.
kubectl -n default get secret db-credentials -o jsonpath='{.data.password}' | base64 --decode
Example Output:
[oracle@ocne ~]$ kubectl -n default get secret db-credentials -o jsonpath='{.data.password}' | base64 --decode my-password[oracle@ocne ~]$
Edit a Secret
Like many Kubernetes objects, you can edit a Kubernetes Secret. The only exception is when a secret is declared immutable .
Edit a Secret.
kubectl edit secrets my-literal-secret
The default editor opens (
vi
by default) to allow you to update the Base64 encoded secret values in thedata:
field.Example Output:
# Please edit the object below. Lines beginning with a '#' will be ignored, # and an empty file will abort the edit. If an error occurs while saving this file will be # reopened with the relevant failures. # apiVersion: v1 data: password: bXktcGFzc3dvcmQ= username: bXktdXNlcg== kind: Secret metadata: creationTimestamp: "2025-05-09T10:56:14Z" name: my-literal-secret namespace: default resourceVersion: "1689" uid: 394dfda3-025a-417d-bbfe-c4851a6b6cff type: Opaque ~ ~ ~ ~ "/tmp/kubectl-edit-1267071353.yaml" 16L, 480C
Exit the editor without saving by pressing the
Esc
key, followed by the:q!
keys.Example Output:
[oracle@ocne ~]$ kubectl edit secrets my-literal-secret Edit cancelled, no changes made.
Delete a Secret
Yo u can remove a secret using the kubectl -n <NAMESPACE> delete
command.
Delete the Secrets.
kubectl -n default delete secret my-file-secret my-literal-secret
Example Output:
[oracle@ocne ~]$ kubectl -n default delete secret my-file-secret my-literal-secret secret "my-file-secret" deleted secret "my-literal-secret" deleted
Note: You can delete multiple secrets by space-delimiting them.
Use Secrets with a Deployment
Next, you will create a deployment using the Kubernetes Secret data you created.
List all Secrets in the default namespace.
kubectl get secrets -n default
Example Output:
[oracle@ocne ~]$ kubectl get secrets -n default NAME TYPE DATA AGE database-credentials-fd8288cb7g Opaque 2 10m db-credentials Opaque 2 8m
Create a deployment YAML file.
cat << EOF | tee echo-deployment.yaml > /dev/null apiVersion: apps/v1 kind: Deployment metadata: name: echo-deployment spec: replicas: 1 selector: matchLabels: app: echo template: metadata: labels: app: echo spec: containers: - name: echo image: ghcr.io/oracle/oraclelinux:9 command: ["/bin/bash", "-c"] args: ["echo 'Username: $USER' 'Password: $PASSWORD'; sleep infinity"] env: - name: USER valueFrom: secretKeyRef: name: db-credentials key: username - name: PASSWORD valueFrom: secretKeyRef: name: db-credentials key: password restartPolicy: Always EOF
Create the deployment
kubectl -n default apply -f echo-deployment.yaml
Confirm it deploys.
kubectl get deployments
You may need to re-query a few times while it deploys.
Example Output:
[oracle@ocne ~]$ kubectl get deployments NAME READY UP-TO-DATE AVAILABLE AGE echo-deployment 1/1 1 1 4m
Get the name of the deployed Pod.
kubectl get pods
Example Output:
[oracle@ocne ~]$ kubectl get pods NAME READY STATUS RESTARTS AGE echo-deployment-59bff74847-9nnkq 1/1 Running 0 6m
Note: Your Pod's name will be different.
Verify the deployed Pod used the secret.
kubectl -n default describe pod <POD-NAME>
Where:
<POD-NAME>
- The Pod name in your deployment.
Example Output:
[oracle@ocne ~]$ kubectl -n default describe pod echo-deployment-59bff74847-9nnkq Name: echo-deployment-59bff74847-9nnkq Namespace: default Priority: 0 Service Account: default Node: ocne-worker-1/192.168.122.77 Start Time: Mon, 12 May 2025 13:42:25 +0000 ... ... Ready: True Restart Count: 0 Environment: USER: <set to the key 'username' in secret 'db-credentials'> Optional: false PASSWORD: <set to the key 'password' in secret 'db-credentials'> Optional: false Mounts: /var/run/secrets/kubernetes.io/serviceaccount from kube-api-access-5fp4d (ro) Conditions: Type Status PodReadyToStartContainers True Initialized True ... .. QoS Class: BestEffort Node-Selectors: <none> Tolerations: node.kubernetes.io/not-ready:NoExecute op=Exists for 300s node.kubernetes.io/unreachable:NoExecute op=Exists for 300s Events: <none> [oracle@ocne ~]$
Note: Check the
Environment:
section confirms the$USER
and$PASSWORD
variables are present.Confirm the environment variables are present in the deployed Pod.
kubectl exec -it <POD-NAME> -- printenv USER PASSWORD
Where:
<POD-NAME>
- The Pod name in your deployment.
Example Output:
[oracle@ocne ~]$ kubectl exec -it echo-deployment-59bff74847-9nnkq -- printenv USER PASSWORD my-user my-password
The output confirms you have successfully used Kubernetes Secrets as an environment variable in the deployed pod.
Next steps
This tutorial demonstrated how to create and use Kubernetes Secrets to restrict unauthorized access to sensitive information. However, this is only the start. Check out the Oracle Linux Training Station for additional tutorials and content.