LFCS 67 🐧 Cron and At Access Control
Access control for scheduled jobs is a deceptively simple topic with a critical distinction: the files that restrict who may create a job are not the same as the files that control whether an existing job runs. For the LFCS exam, this distinction is the difference between answering the objective correctly and creating a false sense of security. Both cron and at use an allow/deny file pair, and both follow the same precedence rules—but cron‘s restriction applies only to the crontab command, not to the execution of jobs already in the spool .
Key point: /etc/cron.allow and /etc/cron.deny restrict access to the crontab command. They do not stop an existing crontab from executing. Removing a user from cron.allow prevents them from editing their schedule; it does not cancel what is already scheduled.
Why access control exists
The privilege escalation problem. Cron jobs run with the privileges of the user who owns the crontab. A user who can schedule arbitrary commands can schedule a command that runs as themselves—which is not privilege escalation—but on shared systems, the ability to schedule persistent background work is itself a resource that administrators may need to gate. Access control files exist to make that gate explicit and auditable.
The audit and compliance problem. Regulated environments require knowing who can introduce scheduled execution into a system. The allow/deny files provide a single, readable answer: if /etc/cron.allow exists and a username is not in it, that user cannot create a cron job. This is simpler to audit than inspecting every user’s crontab individually.
The accidental denial problem. A misconfigured access control file can silently block every non-root user from scheduling jobs. Understanding the precedence rules—allow wins over deny, missing files have different meanings than empty files—prevents outages caused by a well-intentioned security change.
The at/cron consistency problem. The at and cron systems use parallel file naming (at.allow/at.deny versus cron.allow/cron.deny) and identical precedence rules, but they govern different commands and have different execution implications. Learning them together reveals both the pattern and the trap.
a. Cron Access Control
The /etc/cron.allow and /etc/cron.deny files determine which users may use the crontab command to install, edit, or remove their own crontab files . The format is one username per line, with no whitespace.
The precedence rules are as follows. If /etc/cron.allow exists, only users listed in it may use crontab. If cron.allow does not exist but cron.deny does, every user not listed in cron.deny may use crontab. If neither file exists, the default depends on the distribution: on many systems only root may use crontab, while Debian and Ubuntu default to allowing all users . If both files exist, cron.allow takes precedence and cron.deny is ignored entirely .
The critical caveat is that these files control the crontab command, not the execution of jobs already in the spool. A user who is removed from cron.allow can no longer run crontab -e or crontab -r, but their existing crontab continues to execute until it is manually removed by root . This means access control is a prevention mechanism for new schedules, not a revocation mechanism for existing ones.
# Restrict crontab usage to a specific list of users
# Create cron.allow with the permitted usernames
sudo tee /etc/cron.allow > /dev/null <<'EOF'
alice
bob
EOF
# Ensure the file is readable; cron treats unreadable files as nonexistent
sudo chmod 644 /etc/cron.allow
# If cron.deny exists alongside cron.allow, it is ignored
# Remove it to avoid confusion
sudo rm -f /etc/cron.deny
The permission requirement is worth noting: if the access control files are not readable by the invoking user (or the crontab group on Debian systems), cron treats them as nonexistent, which changes the effective policy .
b. At Access Control
The /etc/at.allow and /etc/at.deny files follow the same pattern for the at and batch commands . If at.allow exists, only listed users may submit at jobs. If it does not exist but at.deny does, all users not listed in at.deny may submit jobs. If neither exists, only root may use at. An empty at.deny means every user may use at .
Unlike the cron situation, the at access control files govern submission of new jobs, and there is no equivalent caveat about already-queued jobs continuing to run—though in practice, a queued at job will still execute even if the user is later denied access. The files affect the ability to submit, not the execution of what is already in the queue.
# Block a specific user from submitting at jobs
# If at.allow does not exist, at.deny is checked
echo "contractor" | sudo tee -a /etc/at.deny
# Verify the file contents
cat /etc/at.deny
# If at.allow exists, at.deny is ignored entirely
# Check which file is actually in effect
ls -l /etc/at.allow /etc/at.deny 2>/dev/null
The parallel structure between cron and at is intentional: both systems use a single decision file if allow exists, fall back to deny if it does not, and default to root-only if neither exists. The practical difference is the command being restricted (crontab versus at) and the execution implications for already-scheduled work.
Complete Example Session
# ============================================
# PART 1: INSPECT CURRENT ACCESS CONTROL STATE
# ============================================
# Check which access control files exist
ls -l /etc/cron.allow /etc/cron.deny /etc/at.allow /etc/at.deny 2>/dev/null
# Files that do not exist produce no output
# ============================================
# PART 2: DETERMINE EFFECTIVE CRON POLICY
# ============================================
# If cron.allow exists, it is the only file that matters
# If it does not, cron.deny is checked
# If neither exists, the distribution default applies
test -f /etc/cron.allow && echo "cron.allow governs" || echo "cron.allow absent"
test -f /etc/cron.deny && echo "cron.deny present" || echo "cron.deny absent"
# ============================================
# PART 3: RESTRICT CRONTAB TO SPECIFIC USERS
# ============================================
# Create cron.allow with the permitted users
sudo tee /etc/cron.allow > /dev/null <<'EOF'
alice
bob
EOF
# Set readable permissions so cron does not ignore the file
sudo chmod 644 /etc/cron.allow
# Remove cron.deny if it exists; it would be ignored anyway
sudo rm -f /etc/cron.deny
# ============================================
# PART 4: VERIFY THE RESTRICTION
# ============================================
# As a permitted user, crontab -l should work
sudo -u alice crontab -l
# As a denied user, crontab should fail with a permission error
sudo -u carol crontab -l
# Expected: "You (carol) are not allowed to use this program (crontab)"
# ============================================
# PART 5: UNDERSTAND THE EXECUTION CAVEAT
# ============================================
# A crontab installed before the restriction continues to run
# Removing a user from cron.allow does NOT cancel their jobs
# To stop execution, root must remove the crontab itself
sudo crontab -u carol -r
# ============================================
# PART 6: RESTRICT AT SUBMISSION
# ============================================
# Block a user from submitting at jobs
echo "contractor" | sudo tee /etc/at.deny
# If at.allow exists, at.deny is ignored; check first
test -f /etc/at.allow && echo "at.allow governs; at.deny ignored"
# ============================================
# PART 7: VERIFY AT RESTRICTION
# ============================================
# As a blocked user, at should fail
sudo -u contractor at now + 5 minutes <<< "echo test"
# Expected: "You do not have permission to use at."
# As an unlisted user, at should work (at.deny present, at.allow absent)
sudo -u alice at now + 5 minutes <<< "echo test"
# ============================================
# PART 8: CHECK FILE PERMISSIONS
# ============================================
# Unreadable access files are treated as nonexistent
ls -l /etc/cron.allow /etc/at.deny
# Ensure 644 or at least readable by the relevant group
# ============================================
# PART 9: DOCUMENT THE EFFECTIVE POLICY
# ============================================
# Summarize which file governs each system
for f in /etc/cron.allow /etc/cron.deny /etc/at.allow /etc/at.deny; do
if [ -f "$f" ]; then echo "PRESENT: $f"; else echo "ABSENT: $f"; fi
done
# ============================================
# PART 10: CLEAN UP TEST RESTRICTIONS
# ============================================
# Remove the test files to restore default behavior
sudo rm -f /etc/cron.allow /etc/at.deny
# Verify restoration
ls -l /etc/cron.allow /etc/at.allow 2>/dev/null
The ten parts demonstrate the full workflow: inspecting existing state, understanding precedence, applying restrictions, verifying them as different users, and confronting the cron execution caveat. The verification steps are as important as the configuration steps—an access control change that is not tested is an assumption, not a policy.
Quick Reference
Access Control File Precedence
| File State | Cron Effect | At Effect |
|---|---|---|
allow exists | Only listed users may use command | Only listed users may use command |
allow absent, deny exists | Listed users blocked; others allowed | Listed users blocked; others allowed |
| Neither exists | Distribution default (often root-only) | Root-only |
| Both exist | allow wins; deny ignored | allow wins; deny ignored |
deny empty | All users allowed | All users allowed |
File Locations and Scope
| File | Controls | Command Affected |
|---|---|---|
/etc/cron.allow | Who may use crontab | crontab only |
/etc/cron.deny | Who may not use crontab | crontab only |
/etc/at.allow | Who may submit at jobs | at, batch |
/etc/at.deny | Who may not submit at jobs | at, batch |
Critical Distinctions
| Distinction | Cron | At |
|---|---|---|
| What is restricted | crontab command usage | at job submission |
| Existing jobs continue? | Yes, until removed by root | Yes, already queued jobs run |
| Default when no files | Distribution-dependent | Root-only |
| Permission requirement | Must be readable | Must be readable |
Best Practices
✅ Do This:
# Check which file exists before editing
ls -l /etc/cron.allow /etc/cron.deny 2>/dev/null # ✅
# Set readable permissions on access files
sudo chmod 644 /etc/cron.allow # ✅
# Test restrictions as the affected user
sudo -u alice crontab -l # ✅
# Remove the less-preferred file to avoid confusion
sudo rm -f /etc/cron.deny # ✅
# Remember that cron restrictions do not cancel existing jobs
sudo crontab -u user -r # ✅
# Use at.allow when a strict whitelist is required
echo "admin" | sudo tee /etc/at.allow # ✅
❌ Don’t Do This:
# Assume cron.deny applies when cron.allow exists
# cron.deny is ignored in that case # ❌
# Expect cron.allow to stop an existing crontab
# It only blocks the crontab command # ❌
# Leave access files with restrictive permissions
sudo chmod 600 /etc/cron.allow # ❌
# Create both allow and deny files
# This creates ambiguity; only allow is read # ❌
# Assume neither file means all users are allowed
# Many systems default to root-only # ❌
# Block at with at.deny when at.allow exists
# at.deny is ignored in that case # ❌
Common Pitfalls
| Pitfall | Why It Happens | Fix |
|---|---|---|
| Restriction has no effect | Editing deny while allow exists | Check which file governs; edit allow |
| Crontab still runs after user is blocked | Access files only restrict crontab command | Remove the crontab with crontab -u user -r |
Users cannot use crontab at all | Access file permissions are too restrictive | Set mode 644 |
at.deny is ignored | at.allow exists | Remove at.allow or add users to it |
| Neither file exists but jobs still blocked | Distribution default is root-only | Create an allow file with permitted users |
Empty deny blocks everyone | Misunderstanding: empty deny allows all | Verify file is actually empty, not absent |
Real-World Examples
1. Restrict Crontab to Two Users
printf "alice\nbob\n" | sudo tee /etc/cron.allow
sudo chmod 644 /etc/cron.allow
2. Block One User from At
echo "intern" | sudo tee /etc/at.deny
3. Check Effective Cron Policy
test -f /etc/cron.allow && cat /etc/cron.allow || cat /etc/cron.deny 2>/dev/null
4. Remove a User’s Crontab After Blocking
sudo crontab -u blockeduser -r
5. Verify At Restriction
sudo -u intern at now + 1 minute <<< "true"
6. Restore Default Access
sudo rm -f /etc/cron.allow /etc/cron.deny /etc/at.allow /etc/at.deny
7. Whitelist At Users
printf "admin\nops\n" | sudo tee /etc/at.allow
8. Confirm Which File Governs
test -f /etc/at.allow && echo "allow governs" || echo "deny governs"
9. Fix Unreadable Access File
sudo chmod 644 /etc/cron.allow
10. Document Policy State
for f in /etc/cron.{allow,deny} /etc/at.{allow,deny}; do [ -f "$f" ] && echo "$f exists"; done
Visual
Cron Access Control Decision Flow
┌──────────────────────────────────────────────────────────────┐
│ USER RUNS crontab │
│ │
│ ┌─────────────────┐ │
│ │ /etc/cron.allow │────► EXISTS: Only listed users │
│ │ exists? │ may use crontab │
│ └────────┬────────┘ (deny ignored) │
│ │ NO │
│ ▼ │
│ ┌─────────────────┐ │
│ │ /etc/cron.deny │────► EXISTS: Listed users blocked │
│ │ exists? │ Others allowed │
│ └────────┬────────┘ │
│ │ NO │
│ ▼ │
│ ┌─────────────────┐ │
│ │ Distribution │────► Often root-only, │
│ │ default │ Debian allows all │
│ └─────────────────┘ │
│ │
│ CRITICAL: These files do NOT stop existing crontabs. │
│ A blocked user's jobs continue until root removes them. │
│ │
└──────────────────────────────────────────────────────────────┘
At Access Control Decision Flow
┌──────────────────────────────────────────────────────────────┐
│ USER RUNS at │
│ │
│ ┌─────────────────┐ │
│ │ /etc/at.allow │────► EXISTS: Only listed users │
│ │ exists? │ may submit jobs │
│ └────────┬────────┘ (deny ignored) │
│ │ NO │
│ ▼ │
│ ┌─────────────────┐ │
│ │ /etc/at.deny │────► EXISTS: Listed users blocked │
│ │ exists? │ Empty file = all allowed │
│ └────────┬────────┘ │
│ │ NO │
│ ▼ │
│ ┌─────────────────┐ │
│ │ Only root may │ Default state │
│ │ submit jobs │ │
│ └─────────────────┘ │
│ │
│ Same precedence pattern as cron. │
│ Different command, same logic. │
│ │
└──────────────────────────────────────────────────────────────┘
The Execution Caveat
┌──────────────────────────────────────────────────────────────┐
│ WHAT cron.allow CONTROLS │
│ │
│ ┌─────────────────┐ ┌─────────────────┐ │
│ │ crontab -e │ │ Existing │ │
│ │ crontab -l │ │ crontab entry │ │
│ │ crontab -r │ │ in spool │ │
│ └────────┬────────┘ └────────┬────────┘ │
│ │ │ │
│ ▼ ▼ │
│ ┌─────────────────┐ ┌─────────────────┐ │
│ │ BLOCKED by │ │ STILL RUNS │ │
│ │ cron.allow │ │ regardless of │ │
│ │ │ │ cron.allow │ │
│ └─────────────────┘ └─────────────────┘ │
│ │
│ Access control prevents NEW schedules. │
│ It does not revoke EXISTING schedules. │
│ Revocation requires: crontab -u user -r │
│ │
└──────────────────────────────────────────────────────────────┘
File Precedence Summary
┌──────────────────────────────────────────────────────────────┐
│ ALLOW FILE DENY FILE EFFECTIVE POLICY │
│ │
│ exists ignored allow list only │
│ absent exists deny list only │
│ absent absent root only │
│ exists exists allow wins │
│ absent empty all users allowed │
│ │
│ Applies identically to: │
│ /etc/cron.allow + /etc/cron.deny │
│ /etc/at.allow + /etc/at.deny │
│ │
│ The pattern is consistent across both systems. │
│ The execution implication differs for cron. │
│ │
└──────────────────────────────────────────────────────────────┘
Summary
| Item | Value |
|---|---|
| Cron access files | /etc/cron.allow, /etc/cron.deny |
| At access files | /etc/at.allow, /etc/at.deny |
| Precedence | allow exists: deny ignored |
| Neither exists | Cron: distribution default; At: root-only |
Empty deny | All users allowed |
| Cron restriction scope | crontab command only, not job execution |
| At restriction scope | at/batch submission |
| Permission requirement | Files must be readable or treated as absent |
| Revoking cron jobs | Must remove crontab with crontab -u user -r |
Key takeaways:
allowalways wins. If both files exist,denyis ignored entirely. The presence ofcron.alloworat.allowmakes the correspondingdenyfile irrelevant.- Cron access control does not stop execution. Removing a user from
cron.allowprevents them from editing their schedule but does not cancel jobs already in the spool. Revocation requires root to remove the crontab explicitly . - Neither file means root-only for at. For
at, the absence of bothat.allowandat.denyrestricts usage to root. For cron, the default varies by distribution. - Empty
denymeans everyone allowed. An empty/etc/cron.denyor/etc/at.denyis not a lockout—it permits all users. - Permissions matter. Access control files that are not readable by the invoking user (or the
crontabgroup on Debian) are treated as nonexistent, which silently changes the policy . - Test as the affected user. A restriction that is not verified from the restricted user’s perspective is an assumption, not a confirmed policy.
Remember: The access control files for cron and at share a single design: an allow file overrides a deny file, a missing allow file falls back to the deny file, and a missing deny file falls back to a default. The trap is the cron execution caveat—the files govern the crontab command, not the scheduler’s execution of existing entries. An administrator who blocks a user from crontab has not stopped that user’s jobs; they have only stopped the user from changing them. The complete action requires both the access control change and the removal of the existing crontab. For the LFCS exam, the precedence rules, the file locations, and this execution distinction are the facts to retain.
Stop using slow, ad-bloated tool sites! 🤮
🔎 Search “KandZ Tools” on Google to use many professional utilities for free.
KandZ.me is the ultimate minimalist hub for:
✅ Finance (Mortgage, Interest, Inflation)
✅ Tech (Base64, JSON, Dev Suite, IP)
✅ Health (BMI, BMR, TDEE)
✅ Productivity (Timer, Workspace, QR)
⚡️ Fast & Private
🔒 No data leaves your device
💎 100% Free
🔗 Use it now: https://tools.kandz.me
🔖 Bookmark it—you’ll need it later!