Module 0.2: Environment & Permissions (Who You Are & Where You Are)
Everyday Use | Complexity:
[QUICK]| Time: 45 min
Prerequisites
Section titled “Prerequisites”Before starting this module:
- Required: Module 0.1: The CLI Power User
- Environment: Any Linux system (VM, WSL, or native)
What You’ll Be Able to Do
Section titled “What You’ll Be Able to Do”After this module, you will be able to:
- Configure shell environment variables (PATH, HOME, PS1) and explain how they’re inherited
- Debug “command not found” errors by tracing the PATH variable
- Use sudo safely and explain why running as root is dangerous
- Manage file ownership and permissions across users and groups
Why This Module Matters
Section titled “Why This Module Matters”Picture this. You download a script from a tutorial. You type ./deploy.sh. The terminal spits back:
bash: ./deploy.sh: Permission deniedSo you try again with deploy.sh (without the ./). Now you get:
bash: deploy.sh: command not foundYou stare at the screen. The file is right there. You can see it with ls. Why does Linux pretend it does not exist? And why, when you point directly at it, does Linux refuse to run it?
These two errors — “Permission denied” and “command not found” — are probably the most common frustrations for Linux beginners. They feel random and unfair. But they are not random at all. They come from two systems that are working exactly as designed:
- The Environment — a collection of settings that tells your shell where to find programs, who you are, and how to behave
- Permissions — a security system that controls who can read, write, and execute every single file on the system
Once you understand these two systems, those cryptic errors transform from brick walls into helpful signposts. You will know exactly what is wrong and exactly how to fix it. More importantly, when you start working with Kubernetes, you will understand why containers run as non-root, why ServiceAccounts exist, and why RBAC matters — because they are all built on these same permission concepts.
Did You Know?
Section titled “Did You Know?”-
The
$PATHvariable was introduced in Unix Version 7 in 1979. Before that, you had to type the full path to every single command — imagine typing/usr/bin/lsevery time you wanted to list files. -
The numeric permission system (like
chmod 755) is based on octal (base-8) numbers. Each digit represents three binary bits — one for read, one for write, one for execute. It is literally binary math you can do in your head. -
The
sudocommand logs every single invocation to/var/log/auth.log(or/var/log/secureon RHEL-based systems). Your sysadmin can see exactly what you ran and when. There are no secrets withsudo. -
On most Linux distributions, the root user’s home directory is
/root, not/home/root. Root is so special it does not even live in the same neighborhood as regular users.
1. Environment Variables: Your Terminal’s Settings Panel
Section titled “1. Environment Variables: Your Terminal’s Settings Panel”Think of environment variables like the Settings app on your phone. Your phone stores your language preference, your default browser, your wallpaper choice — all so that every app knows how to behave without asking you each time. Environment variables do the same thing for your terminal and every program that runs inside it.
An environment variable is simply a name=value pair stored in memory. By convention, the names use ALL_CAPS with underscores:
# See ALL your environment variables (there are a lot!)env
# Or use printenv for the same thingprintenv
# See just one specific variable — the $ says "give me the value"echo $USERecho $HOMEThe Essential Variables You Should Know
Section titled “The Essential Variables You Should Know”| Variable | What It Stores | Example Value |
|---|---|---|
$USER | Your current username | alice |
$HOME | Path to your home directory | /home/alice |
$SHELL | Your default shell program | /bin/bash |
$PWD | Your current working directory | /home/alice/projects |
$EDITOR | Your preferred text editor | vim or nano |
$LANG | Your language and encoding | en_US.UTF-8 |
$HOSTNAME | The name of this machine | web-server-01 |
$TERM | Your terminal type | xterm-256color |
$PATH | Where to find commands | (see next section) |
Try them right now:
echo "Hello, $USER! You are on $HOSTNAME."echo "Your home is $HOME and your shell is $SHELL."echo "You are currently in $PWD."2. $PATH — The Most Important Variable You Will Ever Meet
Section titled “2. $PATH — The Most Important Variable You Will Ever Meet”When you type ls and press Enter, how does your shell know where the ls program lives? It does not search the entire hard drive — that would take forever. Instead, it checks a specific list of directories, in order, and runs the first match it finds. That list is your $PATH.
echo $PATHYou will see something like this (directories separated by colons):
/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/home/alice/binHere is how the shell uses it when you type a command:
You type: kubectl
Shell searches $PATH directories left to right:
/usr/local/bin/kubectl → does this exist? NO → keep looking /usr/bin/kubectl → does this exist? NO → keep looking /bin/kubectl → does this exist? NO → keep looking /usr/sbin/kubectl → does this exist? NO → keep looking /sbin/kubectl → does this exist? NO → keep looking /home/alice/bin/kubectl → does this exist? YES → RUN IT!
If nothing found in any directory: → "bash: kubectl: command not found"Pause and predict: If you type
kubectland the shell searches through all directories in your$PATHbut doesn’t find it, what exact error message will it print?
This is why ./deploy.sh works but deploy.sh does not. The current directory (.) is not in your $PATH by default. When you type deploy.sh, the shell looks through every $PATH directory, never finds it, and gives up. When you type ./deploy.sh, you are giving an explicit path — you are saying “run the file right here” — so the shell does not need $PATH at all.
Finding Where Commands Live
Section titled “Finding Where Commands Live”# which — shows the full path of a commandwhich lswhich python3# Output: /usr/bin/python3
# type — shows what the shell thinks a command istype ls# Output: ls is aliased to 'ls --color=auto' (if aliased)# Output: ls is /usr/bin/ls (if not)
type cd# Output: cd is a shell builtin (built into bash itself)Adding a Directory to $PATH
Section titled “Adding a Directory to $PATH”Say you put custom scripts in ~/bin. You need to add that directory to your $PATH:
# Temporary — lasts until you close the terminalexport PATH="$HOME/bin:$PATH"
# Verify it workedecho $PATH# Now /home/alice/bin appears at the frontPutting your directory at the front means your custom scripts get found first, before system commands with the same name. Putting it at the end means system commands take priority.
To make it permanent, add the export PATH=... line to your shell config file (covered in Section 4).
Trade-off: Convenience vs. Security in $PATH
Section titled “Trade-off: Convenience vs. Security in $PATH”It might be tempting to add the current directory (.) to your $PATH like this: export PATH=".:$PATH". This way, you could just type deploy.sh instead of ./deploy.sh.
War Story: A sysadmin once added . to the beginning of their root user’s $PATH for convenience. An attacker created a malicious script named ls and placed it in a world-writable directory like /tmp. When the sysadmin cd’d into /tmp and typed ls, the shell searched . first, found the malicious script, and executed it as root. The server was instantly compromised. The trade-off is clear: saving two keystrokes (./) is never worth giving an attacker an easy execution vector. Always keep . out of your $PATH and use explicit paths for local files.
3. Setting Variables: export vs No export
Section titled “3. Setting Variables: export vs No export”This distinction trips up almost everyone.
Stop and think: If you set
API_KEY="12345"in your terminal withoutexport, and then run a deployment script that needs to read$API_KEY, will the script succeed? Why or why not?
Watch carefully:
# Setting a variable WITHOUT exportGREETING="Hello from parent"echo $GREETING # Works! Prints: Hello from parentbash # Start a child shell (a new process)echo $GREETING # Nothing! Empty! The child does not know about itexit # Return to parent shell
# Setting a variable WITH exportexport GREETING="Hello from parent"echo $GREETING # Works! Prints: Hello from parentbash # Start a child shellecho $GREETING # Works! Prints: Hello from parentexit # Return to parent shellWhy does this matter? Because every command you run is a child process of your shell. When you run a Python script, a Docker command, or kubectl, they are all child processes. If you set a variable without export, those programs cannot see it.
The rule is simple:
- No
export: Variable exists only in your current shell session. Use this for quick throwaway values. - With
export: Variable is inherited by every child process. Use this for settings that programs need to see (like$KUBECONFIG,$EDITOR,$JAVA_HOME).
# Common exports you will see in Kubernetes workexport KUBECONFIG=~/.kube/configexport EDITOR=vimexport JAVA_HOME=/usr/lib/jvm/java-17
# Quick throwaway — no export neededBACKUP_DATE=$(date +%Y-%m-%d)echo "Backing up for $BACKUP_DATE"Unsetting Variables
Section titled “Unsetting Variables”# Remove a variable entirelyunset GREETINGecho $GREETING # Nothing — it is gone4. Shell Config Files: Making Changes Permanent
Section titled “4. Shell Config Files: Making Changes Permanent”Every change you make in the terminal is temporary — it vanishes when you close the window. To make environment variables, aliases, and $PATH changes permanent, you need to add them to a shell config file that runs automatically when a new shell starts.
But which file? This is where it gets confusing, because there are several and they run at different times.
When Each File Runs
Section titled “When Each File Runs”┌──────────────────────────────────────────────────────────┐│ LOGIN SHELL ││ (SSH session, first terminal on Linux) ││ ││ Runs: /etc/profile ││ → then the FIRST one found of: ││ ~/.bash_profile ││ ~/.bash_login ││ ~/.profile ││ ││ Think: "Welcome! Let me set up your entire session." │└──────────────────────────────────────────────────────────┘
┌──────────────────────────────────────────────────────────┐│ INTERACTIVE NON-LOGIN SHELL ││ (new terminal tab/window on desktop, typing bash) ││ ││ Runs: ~/.bashrc ││ ││ Think: "Just another shell, here are your shortcuts." │└──────────────────────────────────────────────────────────┘In practice, most people want their settings in every shell. The standard trick is:
- Put all your settings in
~/.bashrc - Have
~/.bash_profilesource it:
# Contents of ~/.bash_profileif [ -f ~/.bashrc ]; then source ~/.bashrcfiThis way, login shells load .bashrc too, and you only maintain one file.
For Zsh users (default on macOS): The equivalent is ~/.zshrc. Zsh reads it for every interactive shell, login or not — much simpler.
Reloading After Changes
Section titled “Reloading After Changes”After editing your config file, you do NOT need to close and reopen the terminal:
# Reload .bashrc immediatelysource ~/.bashrc
# Shorthand (does the same thing). ~/.bashrc5. Aliases: Your Custom Shortcut Commands
Section titled “5. Aliases: Your Custom Shortcut Commands”An alias is a custom shortcut that expands into a longer command. They save you keystrokes every single day.
# Create an aliasalias ll='ls -la'alias ..='cd ..'alias ...='cd ../..'alias cls='clear'alias ports='ss -tulnp'alias myip='curl -s ifconfig.me'Practical Aliases for DevOps and Kubernetes
Section titled “Practical Aliases for DevOps and Kubernetes”# Kubernetes — the k alias is used throughout KubeDojoalias k='kubectl'alias kgp='kubectl get pods'alias kgs='kubectl get svc'alias kgn='kubectl get nodes'alias kaf='kubectl apply -f'alias kdel='kubectl delete -f'alias klog='kubectl logs -f'
# Dockeralias dps='docker ps'alias dimg='docker images'alias dex='docker exec -it'
# Safety nets — ask before overwritingalias cp='cp -i'alias mv='mv -i'alias rm='rm -i'
# Quick system infoalias meminfo='free -h'alias diskinfo='df -h'alias cpuinfo='lscpu'To make aliases permanent, add them to your ~/.bashrc:
# Open your .bashrc and add aliases at the bottomnano ~/.bashrc
# After adding aliases, reloadsource ~/.bashrcChecking and Removing Aliases
Section titled “Checking and Removing Aliases”# See all your current aliasesalias
# See what a specific alias expands toalias ll# Output: alias ll='ls -la'
# Temporarily bypass an alias (use the real command)\ls # The backslash skips the aliascommand ls # Another way to skip
# Remove an alias for this sessionunalias ll6. File Permissions: The rwx System
Section titled “6. File Permissions: The rwx System”Linux is a multi-user operating system. Even if you are the only human using the machine, there are dozens of system users (like www-data for your web server, postgres for your database). Permissions ensure that your web server cannot read your SSH keys and your database cannot modify your application code.
Run ls -l in any directory:
ls -l /etc/passwd /bin/ls /home
# Output looks like:# -rwxr-xr-x 1 root root 142144 Sep 5 2023 /bin/ls# -rw-r--r-- 1 root root 2775 Mar 10 14:22 /etc/passwd# drwxr-xr-x 3 root root 4096 Mar 10 14:22 /homeLet us decode that first column character by character:
- r w x r - x r - x │ │ │ │ │ │ │ │ │ │ │ └──┴──┘ └──┴──┘ └──┴──┘ │ │ │ │ │ OWNER GROUP OTHERS │ (user) │ └── File type - = regular file d = directory l = symbolic linkWhat r, w, x Actually Mean
Section titled “What r, w, x Actually Mean”| Permission | On a File | On a Directory |
|---|---|---|
r (read) | View the file contents (cat, less) | List the directory contents (ls) |
w (write) | Modify or overwrite the file | Create, rename, or delete files inside it |
x (execute) | Run the file as a program | Enter the directory (cd) |
- (none) | Cannot do the action | Cannot do the action |
The directory permissions catch people off guard. A directory without x is like a room with a locked door — you cannot walk in, even if you know what is inside. A directory without r but with x is like a dark room — you can walk in and grab files if you know their names, but you cannot turn on the lights to see what is there.
Reading Permission Strings — Practice
Section titled “Reading Permission Strings — Practice”| String | Owner | Group | Others | Meaning |
|---|---|---|---|---|
-rwxr-xr-x | rwx | r-x | r-x | Typical program — everyone can run it, only owner can edit |
-rw-r--r-- | rw- | r— | r— | Typical config file — everyone can read, only owner can edit |
-rw------- | rw- | --- | --- | Private file — only owner can read and write |
-rwx------ | rwx | --- | --- | Private script — only owner can run it |
drwxr-xr-x | rwx | r-x | r-x | Typical directory — everyone can enter and list, only owner can modify |
drwx------ | rwx | --- | --- | Private directory — only owner can enter |
7. Changing Permissions with chmod
Section titled “7. Changing Permissions with chmod”chmod (change mode) modifies permissions. You can use it in two ways.
Symbolic Mode — Human-Readable
Section titled “Symbolic Mode — Human-Readable”Format: chmod [who][operator][permission] file
- Who:
u(user/owner),g(group),o(others),a(all three) - Operator:
+(add),-(remove),=(set exactly) - Permission:
r,w,x
# Make a script executable for the ownerchmod u+x deploy.sh
# Remove write permission from group and otherschmod go-w config.yaml
# Give everyone read permissionchmod a+r README.md
# Set exact permissions — owner gets rwx, everyone else gets nothingchmod u=rwx,go= secret-script.sh
# Add execute for everyonechmod +x run-tests.sh # Without specifying who, + applies to all
# Remove all permissions from otherschmod o= private-notes.txt # = with nothing after it means "set to nothing"Numeric Mode — Fast and Precise
Section titled “Numeric Mode — Fast and Precise”Each permission has a number: read = 4, write = 2, execute = 1. Add them up for each position (owner, group, others).
Permission Binary Decimal--------- ----- ------- --- 000 0 (no permissions) --x 001 1 (execute only) -w- 010 2 (write only) -wx 011 3 (write + execute) r-- 100 4 (read only) r-x 101 5 (read + execute) rw- 110 6 (read + write) rwx 111 7 (read + write + execute)You specify three digits: owner, group, others.
# 755 — owner: rwx (7), group: r-x (5), others: r-x (5)# Standard for scripts and programschmod 755 deploy.sh
# 644 — owner: rw- (6), group: r-- (4), others: r-- (4)# Standard for regular fileschmod 644 config.yaml
# 600 — owner: rw- (6), group: --- (0), others: --- (0)# Private files (SSH keys, passwords)chmod 600 ~/.ssh/id_rsa
# 700 — owner: rwx (7), group: --- (0), others: --- (0)# Private directories, private scriptschmod 700 ~/.ssh
# 444 — owner: r-- (4), group: r-- (4), others: r-- (4)# Read-only for everyone (like a museum exhibit — look but do not touch)chmod 444 important-record.txtPause and predict: You have a directory. You want users to be able to
cdinto it andlsits contents, but NOT be able to create or delete files. What is the numericchmodvalue for this exact set of permissions?
The Most Common Permission Patterns
Section titled “The Most Common Permission Patterns”| Pattern | Numeric | Use Case |
|---|---|---|
rwxr-xr-x | 755 | Programs, scripts, directories |
rw-r--r-- | 644 | Regular files, config files |
rw------- | 600 | SSH private keys, secrets |
rwx------ | 700 | SSH directory, private scripts |
rwxrwxr-x | 775 | Shared project directories |
rw-rw-r-- | 664 | Shared project files |
8. Ownership with chown and chgrp
Section titled “8. Ownership with chown and chgrp”Every file has two ownership attributes: a user (owner) and a group.
# Check ownershipls -l myfile.txt# -rw-r--r-- 1 alice developers 1024 Mar 10 14:22 myfile.txt# ^^^^^ ^^^^^^^^^^# owner groupChanging Ownership
Section titled “Changing Ownership”# Change owner (requires sudo because you are giving away a file)sudo chown bob myfile.txt
# Change group onlysudo chgrp developers myfile.txt
# Change both at once — user:groupsudo chown bob:developers myfile.txt
# Change ownership of a directory and EVERYTHING inside it (-R = recursive)sudo chown -R alice:webteam /var/www/mysite
# Change only the group (useful if you are a member of the target group)chgrp devops deployment.yaml # No sudo needed if you belong to "devops"Why Ownership Matters for Kubernetes
Section titled “Why Ownership Matters for Kubernetes”When a container runs, it runs as a specific user (often root by default, which is a security risk). Kubernetes securityContext lets you set runAsUser and runAsGroup — the exact same user/group ownership concept you are learning here, just applied inside a container.
9. The Root User and sudo
Section titled “9. The Root User and sudo”Root: The All-Powerful Superuser
Section titled “Root: The All-Powerful Superuser”Linux has one special user called root (UID 0). Root can:
- Read, write, and execute any file regardless of permissions
- Kill any process
- Modify any system configuration
- Bind to privileged ports (below 1024)
- Format disks, mount filesystems, do absolutely anything
This is why running as root is dangerous. A typo like rm -rf / (instead of rm -rf ./) would wipe the entire system. There is no “Are you sure?” prompt, no recycle bin, no undo.
sudo: Borrow Root Power Safely
Section titled “sudo: Borrow Root Power Safely”sudo stands for “superuser do.” It lets an authorized regular user run a single command with root privileges:
# Install a package (requires root)sudo apt updatesudo apt install nginx
# Edit a system file (requires root)sudo nano /etc/hosts
# Restart a service (requires root)sudo systemctl restart nginx
# See who you become when you use sudosudo whoami# Output: rootWhen you run sudo, it asks for your password (not root’s password). This confirms that the person at the keyboard is actually you and not someone who walked up to your unlocked laptop.
The sudoers File
Section titled “The sudoers File”Not everyone can use sudo. The file /etc/sudoers controls who is allowed. You should never edit it directly — always use the special visudo command, which checks for syntax errors before saving (a broken sudoers file can lock you out of root access entirely).
# See your sudo privilegessudo -l
# Common output shows something like:# (ALL : ALL) ALL — you can do anything as any user# (ALL) NOPASSWD: ALL — you can do anything without a password (common on cloud VMs)On most systems, you get sudo access by being in a specific group:
- Ubuntu/Debian: The
sudogroup - RHEL/CentOS/Fedora: The
wheelgroup
# Check what groups you belong togroups# Output: alice sudo docker
# If "sudo" or "wheel" is in the list, you can use sudoBest Practices with sudo
Section titled “Best Practices with sudo”# DO: Use sudo for specific commands that need itsudo systemctl restart nginx
# DO NOT: Start a root shell and work in itsudo -i # Avoid this — you lose the safety net of per-command authorizationsudo su - # Avoid this too — same problem
# DO NOT: Use sudo for things that do not need itsudo cat myfile.txt # If you own the file, just use cat!sudo vim notes.txt # The file will end up owned by root — now YOU cannot edit itWar Story: A junior engineer once ran sudo vim to edit a config file in their home directory. The file’s ownership changed to root. Later, the application that needed to read that config file (running as a normal user) got “Permission denied” and crashed in production. The fix was a simple chown, but finding the cause took hours of debugging. The lesson: only use sudo when you actually need root privileges.
Common Mistakes
Section titled “Common Mistakes”| Mistake | Why It Happens | How to Fix It |
|---|---|---|
| ”command not found” for a script in the current directory | The current directory . is not in $PATH. Typing script.sh makes the shell search $PATH only. | Run it with ./script.sh (explicit path) or add its directory to $PATH. |
| ”Permission denied” when running a script | The file does not have execute (x) permission. Linux requires it explicitly. | Run chmod u+x script.sh then try again. |
| Alias disappears after closing the terminal | You defined it in the shell but not in ~/.bashrc. Shell settings do not persist automatically. | Add the alias line to ~/.bashrc and run source ~/.bashrc. |
export in .bashrc does not take effect | You edited the file but did not reload it. The running shell does not watch for file changes. | Run source ~/.bashrc or open a new terminal. |
Using sudo vim to edit files you own | Creates the file as root-owned. Now your normal user cannot modify it. | Use vim without sudo. If already owned by root, run sudo chown $USER file. |
Running as root all the time (sudo su -) | Feels convenient but removes all safety nets. One wrong rm can destroy everything. | Use sudo per command. Exit root shells immediately when done. |
chmod 777 on everything to “fix” permission errors | Gives everyone full access — a massive security hole. | Figure out which specific permission is missing and grant only that. Usually chmod 755 or 644 is correct. |
Forgetting the -R flag with chown or chmod | Only changes the directory itself, not the files inside it. | Use -R for recursive: sudo chown -R alice:devs /project. |
Quiz (Testing Your Outcomes)
Section titled “Quiz (Testing Your Outcomes)”Test yourself. Try to answer before revealing the solution.
Q1 [Outcome: Debug "command not found"]: You are helping a junior developer. They downloaded a binary called kubens into ~/downloads and typed kubens. The terminal returns "command not found". They prove the file is there by running ls ~/downloads and seeing kubens listed. Why did this happen and how do they fix it?
The shell only looks for commands in the directories listed in the $PATH environment variable. By default, ~/downloads is not in $PATH, and the current directory (.) is also not checked automatically for security reasons. Because of this, the shell cannot resolve the command name to the actual file location on disk. To fix this, the developer must either provide the explicit relative or absolute path (e.g., ./kubens or ~/downloads/kubens) or move the binary to a directory that is already in $PATH, like /usr/local/bin.
Q2 [Outcome: Configure environment]: You set a database password using DB_PASS=secret in your terminal. You then run a Python script python3 connect.py that reads the DB_PASS environment variable, but the script crashes complaining that the variable is missing. You type echo $DB_PASS and see "secret". What is happening?
The variable was set locally in the current shell, but it was not exported to the environment. When you run a script or program, it spawns as a child process of your current shell. Child processes only inherit environment variables that have been explicitly flagged with the export command. Because it was missing, the Python script spawned with an environment that did not contain DB_PASS. To fix this, you need to run export DB_PASS=secret before running the Python script, or pass it inline like DB_PASS=secret python3 connect.py.
Q3 [Outcome: Manage permissions]: A web server running as the user www-data needs to read a configuration file app.conf. The file is currently owned by alice:developers with permissions -rw-r-----. The www-data user is not part of the developers group. What is the most secure way to grant the web server read access without exposing the file to every user on the system?
The most secure approach is to change the group ownership of the file to a group that www-data already belongs to, or change the owner directly to www-data. You should avoid using broad permissions like chmod 777 or chmod o+r (which results in 644), as these would allow any other user on the system to read the potentially sensitive configuration. By using a command like chown alice:www-data app.conf (assuming www-data acts as a group), the group permissions (r--) will apply exclusively to the web server. This keeps the “others” permissions at zero (---), ensuring the principle of least privilege is maintained.
Q4 [Outcome: Manage permissions]: You have a directory /opt/scripts/ with permissions drwxr-x--- owned by root:devops. You are logged in as bob, who is a member of the devops group. You try to create a new script inside this directory using touch /opt/scripts/new.sh, but you receive a "Permission denied" error. Why?
File creation within a Linux directory is governed by the write (w) permission of the directory itself, not the permissions of the files inside it. As a member of the devops group, the directory’s group permissions r-x apply to your user. The r (read) allows you to list the directory contents, and the x (execute) allows you to enter the directory using the cd command. However, since the group lacks the w (write) permission, you are forbidden from modifying the directory’s inventory, which means you cannot create, delete, or rename files within it.
Q5 [Outcome: Use sudo safely]: You need to append a new line to a protected system file /etc/hosts. You try running sudo echo "10.0.0.5 myserver" >> /etc/hosts, but you get a "Permission denied" error, even though you used sudo. Why did this fail, and how do you fix it?
The failure happens because the shell handles output redirection (>>) before sudo ever executes the command. As a result, sudo only applies to the echo command itself, while your current, unprivileged shell attempts to open /etc/hosts for writing, which it lacks the permissions to do. To fix this safely without switching entirely to a root shell, you can use the tee command combined with sudo. Running echo "10.0.0.5 myserver" | sudo tee -a /etc/hosts executes tee with root privileges, allowing it to successfully append the text to the protected file.
Q6 [Outcome: Use sudo safely]: An application running on your server is crashing, and the logs are in /var/log/app/error.log, which is owned by root:root with -rw------- permissions. You type sudo vim /var/log/app/error.log to investigate. Why is this a bad practice, and what should you do instead?
Using an interactive editor like vim with sudo simply to view a file is highly dangerous because it runs the entire editor program with root privileges. If you accidentally bump the keyboard, you might unintentionally modify critical system logs or configuration files. Furthermore, if the editor contains vulnerabilities or automatically executes macros, those actions will execute with full system-level authority. Instead, you should always use commands specifically designed for read-only viewing, such as sudo less, sudo cat, or sudo tail, which eliminate the risk of accidental modification or privilege escalation.
Q7 [Outcome: Manage permissions]: You downloaded a bash script setup.sh that automates a complex installation. When you run ./setup.sh, the system says "Permission denied". You run ls -l and see -rw-r--r--. You are tempted to run chmod 777 setup.sh to get it working quickly. What is the numeric permission you should actually use, and why is 777 a bad idea?
The correct numeric permission for a script you want to run is 755 (or simply chmod u+x if only the owner needs to execute it). Using chmod 777 is a massive security risk because it grants read, write, and execute permissions to every single user on the entire system. This means any other user could maliciously modify the script to inject harmful commands, which you would unknowingly execute the next time you ran it. By using 755 (-rwxr-xr-x), you ensure that only the owner can modify the file, while everyone else is restricted to merely reading and executing it.
Q8 [Outcome: Configure environment]: You want to override the system's version of python3 (located in /usr/bin/python3) with a newer version you compiled yourself and placed in /opt/custom/bin/python3. However, when you type python3 --version, it still shows the old system version. How do you modify your environment to fix this?
The shell searches through the directories listed in your $PATH variable from left to right and immediately stops at the first matching executable it finds. Because /usr/bin is already present in your default $PATH and your custom directory is not (or is placed at the very end), the shell discovers the system version first. To override this behavior, you must prepend your custom directory to the absolute front of the $PATH by adding a line like export PATH="/opt/custom/bin:$PATH" to your ~/.bashrc. This guarantees that the shell evaluates /opt/custom/bin prior to /usr/bin, allowing your custom compiled version to take precedence.
Hands-On Exercise: Environment and Permissions Boot Camp
Section titled “Hands-On Exercise: Environment and Permissions Boot Camp”Scenario: You are setting up a development environment on a new server. You need to configure your shell, create a project with proper permissions, and set up scripts that your team can use.
Part 1: Environment Variables
Section titled “Part 1: Environment Variables”# 1. Display your current username, home directory, and shellecho "User: $USER"echo "Home: $HOME"echo "Shell: $SHELL"
# 2. See your entire $PATH, one directory per line (easier to read)echo $PATH | tr ':' '\n'
# 3. Set a variable WITHOUT export — verify it does not reach child processesPROJECT_NAME="kubedojo-lab"echo $PROJECT_NAME # Should print: kubedojo-labbash -c 'echo $PROJECT_NAME' # Should print nothing (empty)
# 4. Now export it and verify the child process CAN see itexport PROJECT_NAME="kubedojo-lab"bash -c 'echo $PROJECT_NAME' # Should print: kubedojo-labPart 2: Shell Configuration
Section titled “Part 2: Shell Configuration”# 5. Add useful aliases to your .bashrccat >> ~/.bashrc << 'EOF'
# --- KubeDojo Lab Aliases ---alias ll='ls -la'alias cls='clear'alias ..='cd ..'alias k='kubectl'EOF
# 6. Reload your config and testsource ~/.bashrcll # Should show detailed listing with hidden filesPart 3: Permissions
Section titled “Part 3: Permissions”# 7. Create a project directory structuremkdir -p ~/lab-project/{scripts,config,secrets}
# 8. Create a deploy scriptcat > ~/lab-project/scripts/deploy.sh << 'EOF'#!/bin/bashecho "Deploying $PROJECT_NAME..."echo "Deploy complete at $(date)"EOF
# 9. Try to run it — observe the error~/lab-project/scripts/deploy.sh# Expected: Permission denied
# 10. Check current permissionsls -l ~/lab-project/scripts/deploy.sh# Expected: -rw-r--r-- or -rw-rw-r-- (no x anywhere)
# 11. Add execute permission for the owner onlychmod u+x ~/lab-project/scripts/deploy.sh
# 12. Verify the permission changedls -l ~/lab-project/scripts/deploy.sh# Expected: -rwxr--r-- (x added for owner)
# 13. Run it successfully~/lab-project/scripts/deploy.sh# Expected: "Deploying kubedojo-lab..." and timestampPart 4: Securing Files
Section titled “Part 4: Securing Files”# 14. Create a "secret" config fileecho "DB_PASSWORD=supersecret123" > ~/lab-project/secrets/db.env
# 15. Lock it down — only you can read and write (numeric mode)chmod 600 ~/lab-project/secrets/db.env
# 16. Verifyls -l ~/lab-project/secrets/db.env# Expected: -rw------- (only owner has rw)
# 17. Set the secrets directory so only you can enter itchmod 700 ~/lab-project/secrets/
# 18. Verify the full structurels -la ~/lab-project/ls -la ~/lab-project/scripts/ls -la ~/lab-project/secrets/Success Criteria
Section titled “Success Criteria”You have completed this exercise successfully if you can demonstrate the following module outcomes:
- [Outcome: Debug “command not found”] You can explain what
$PATHdoes and why./script.shworks butscript.shdoes not. - [Outcome: Configure environment] Your aliases and environment variables are saved in
~/.bashrcand persist after runningsource ~/.bashrc. - [Outcome: Manage permissions]
deploy.shhas execute permission for the owner (-rwxr--r--or similar). - [Outcome: Manage permissions]
db.envis locked down to owner-only access (-rw-------/600) and thesecrets/directory is locked to owner-only (drwx------/700). - [Outcome: Use sudo safely] You did not use
sudofor anything in this exercise, proving you understand it is not needed for files you own.
Cleanup
Section titled “Cleanup”# When you are done experimentingrm -rf ~/lab-project# Optionally remove the aliases from ~/.bashrc if you do not want themNext Up: Module 0.3: Process & Resource Survival Guide — Learn how to find running processes, monitor system resources, and stop runaway programs before they cause trouble.