TT Lab
Get started
Learn Learning paths Courses

Container Security

Environment Variables vs File Mounts — What Differs

Continue in TT Lab

This lab runs on a real VM

This box is not a Pod but a virtual machine started by KubeVirt. A Linux kernel runs separately, systemd actually manages services, and docker is not an imitation but a real Docker engine. A container started with docker run actually becomes a process, and docker exec and docker logs work as expected.

This lab used to run inside a Pod. The box had dropped all kernel privileges, so the step that starts a container was blocked, and the lab worked around that by unpacking the image archive directly. The workaround is no longer needed.

There are two things to know.

Goal

Deliver the same secret in two ways, as an environment variable and as a file mount, measure whether it is exposed in docker inspect and whether it can be rotated without a restart, and summarize the results in a comparison table. At the end, you write a simple secret detection script.

Why it matters

An environment variable is a delivery method, not a storage location. /proc/<pid>/environ can be read for as long as the process lives, and a container's environment variables are visible as they are through docker inspect to anyone who can reach the daemon. So the reassurance that "I loaded .env and deleted it, so it is fine" has no basis. More important is rotation. An environment variable is fixed at process start, so changing the value always requires a restart, while a file mount shows the new value without a restart if you overwrite the same inode. This difference decides the incident response time — assuming a credential has been made public, how many minutes it takes to revoke it and replace it and get the service back to normal is exactly that team's level of secret management.

Steps

  1. Create the /root/sec3 directory, write one line API_KEY=labhub-env-key-v1 to /root/sec3/app.env, and then change the file permission to 600.
  2. Run a sec3-env container from alpine:3.20 in the background using --env-file /root/sec3/app.env. The container's API_KEY must equal the value in the file.
  3. Save the API_KEY value read out with docker inspect sec3-env as it is to /root/sec3/leak-proof.txt.
  4. Write labhub-file-key-v1 to /root/sec3/secret.txt, and run a sec3-file container in the background with that file bind-mounted read-only at the path /run/secrets/api_key. The mount source must be exactly /root/sec3/secret.txt.
  5. The environment variables of the sec3-file container must contain neither API_KEY nor the secret value itself.
  6. Without restarting the containers, overwrite the contents of /root/sec3/secret.txt with labhub-rotated-v2. sec3-file must see the new value, and the environment variable value of sec3-env must remain the old value. Then write your conclusion in /root/sec3/rotate.md about which method requires a restart (the text must contain the word 재시작 (the Korean word for "restart") or restart).
  7. Write /root/sec3/find-secrets.sh with execute permission. It searches the directory received as the first argument, and if it finds an access key starting with AKIA or a PRIVATE KEY block, it must exit with a nonzero code and print the path of the file where it found it; if it finds nothing, it must exit with code 0.
  8. In /root/sec3/secrets.md, write content comparing the environment variable method with the file mount method, and include the following four lines in exactly this format.
    • env_visible_in_inspect=yes
    • mount_visible_in_inspect=no
    • env_needs_restart=yes
    • mount_needs_restart=no

Notes

Secret file and permission 600

Create the /root/sec3 directory, write one line API_KEY=labhub-env-key-v1 to /root/sec3/app.env, and then change the file permission to 600.

What is graded is the file permission rather than the value itself. Only the owner should be able to read and write, and the group and other users should have no permissions at all. Create the file first, then change the permission.

Inject with env-file

Run a sec3-env container from alpine:3.20 in the background using --env-file /root/sec3/app.env. The container's API_KEY must equal the value in the file.

If you write the value directly on the command line, it stays in shell history and in the process list. Use the option that reads from a file and injects it. Grading compares the container's environment variable value with the file value, so the container must be running.

Environment variables are visible in inspect as they are

Save the API_KEY value read out with docker inspect sec3-env as it is to /root/sec3/leak-proof.txt.

Even someone who did not create the container can read this value if they can reach the daemon. Do not guess what to write; leave in the file exactly the value you actually pulled out with docker inspect.

Mount it as a file

Write labhub-file-key-v1 to /root/sec3/secret.txt, and run a sec3-file container in the background with that file bind-mounted read-only at the path /run/secrets/api_key. The mount source must be exactly /root/sec3/secret.txt.

This method attaches a single host file as it is at a specific path in the container. The mount source path and the target path are graded, so they must match exactly, and since it is a secret, it must be attached read-only.

Do not leave it in an environment variable

The environment variables of the sec3-file container must contain neither API_KEY nor the secret value itself.

Once you have switched to a file mount, there is no reason to also pass the same value as an environment variable. If you give both, the benefit of the file mount disappears — because it becomes visible in inspect again. Check that no environment variable got mixed into the step 4 container.

Rotate without a restart

Without restarting the containers, overwrite the contents of /root/sec3/secret.txt with labhub-rotated-v2. sec3-file must see the new value, and the environment variable value of sec3-env must remain the old value. Then write your conclusion in /root/sec3/rotate.md about which method requires a restart (the text must contain the word 재시작 (the Korean word for "restart") or restart).

A bind mount follows the inode. If you overwrite the file in place (with redirection), the container sees the new value immediately, but with a method that deletes the file and creates a new one (some editors or sed -i), the inode changes and the mount keeps pointing at the old file. And the environment variable side must never change — it is the control group of this step.

Secret detection script

Write /root/sec3/find-secrets.sh with execute permission. It searches the directory received as the first argument, and if it finds an access key starting with AKIA or a PRIVATE KEY block, it must exit with a nonzero code and print the path of the file where it found it; if it finds nothing, it must exit with code 0.

Scan the directory recursively for two shapes — an access key starting with AKIA and a PRIVATE KEY block header. It must end with 0 when clean and a nonzero value when it finds something, and it must print the path of the file. Also think about the limit that rules like these work well only on values with a distinct shape.

Delivery method comparison table

In /root/sec3/secrets.md, write content comparing the environment variable method with the file mount method, and include the following four lines in exactly this format.

The values of the four lines must match the results you confirmed yourself in the earlier steps. Go back over what was visible in step 3, what was not visible in step 5, and which side required a restart in step 6. The body must also mention both the environment variable method and the mount method.