Project 2: Post-Install Baseline and Remote Access

Establish a secure baseline with a non-root admin user and working SSH access.

Quick Reference

Attribute Value
Difficulty Level 1
Time Estimate Weekend
Main Programming Language Shell (sh) (Alternatives: csh, Python)
Alternative Programming Languages sh, csh, Python
Coolness Level Level 2
Business Potential Level 1
Prerequisites Project 1 complete, basic Unix users/groups
Key Topics rc.conf, sysrc, sshd, least privilege

1. Learning Objectives

By completing this project, you will:

  1. Create an admin user with appropriate group memberships and no root password usage.
  2. Enable SSH securely via rc.conf and validate service status.
  3. Configure SSH access with safe defaults and test logs for authentication.
  4. Document a baseline configuration you can reproduce.

2. All Theory Needed (Per-Concept Breakdown)

Concept 1: FreeBSD rc System and Persistent Service Configuration

Fundamentals FreeBSD uses the rc system to manage services at boot. Instead of unit files, rc relies on startup scripts in /etc/rc.d and /usr/local/etc/rc.d, while configuration is set in /etc/rc.conf. The service command is a standardized interface to start, stop, and query services regardless of the script location. The tool sysrc safely edits rc.conf entries without manual file manipulation. This matters because on FreeBSD, “service installed” and “service enabled” are different states. A service can be present but not enabled at boot. For baseline setup, you need to ensure SSH is enabled and started, and that your settings persist after reboot. Understanding the rc model is the key to reliable operations.

Deep Dive into the concept The rc system is a sequence-driven initialization framework that executes rc scripts in dependency order. Each script declares its name, required facilities, and default state. At boot, /etc/rc parses /etc/rc.conf, merges defaults from /etc/defaults/rc.conf, and sets variables that control whether each service starts. This model emphasizes declarative configuration: you set sshd_enable="YES" and the rc script decides how to start sshd. Using sysrc instead of editing rc.conf by hand prevents syntax errors and keeps changes auditable.

Understanding the difference between the base system and ports is also important here: base services (like sshd) ship with the OS, while third-party services install their rc scripts under /usr/local/etc/rc.d. The service command hides that difference, so you can query status uniformly. When you run service sshd status, rc invokes the sshd script, which checks for a PID file and verifies the process. If a service does not start, the first step is to confirm whether it is enabled in rc.conf and whether its script exists.

The rc system is also conservative. It expects configuration in rc.conf and rarely auto-detects. This reduces surprises but requires explicit action. For example, creating a user does not automatically grant sudo-like access; you must assign groups and configure doas or sudo if you use them. Likewise, enabling SSH does not automatically secure it; you must explicitly disable root login and set authentication policies. This is a model of operational clarity: nothing is enabled unless you say so. In real environments, this predictability reduces configuration drift and accidental exposure. For this project, you are establishing a baseline by explicitly enabling only what you need, documenting it, and validating it with deterministic commands and logs.

Operationally, rc system service enablement is easiest to keep stable when you treat it as a small contract between configuration, tooling, and observable outputs. Write down the exact files that own the state and the commands that reveal the current truth. Then verify the contract at three points: immediately after you make the change, after a reboot, and after a deliberate disturbance such as restarting services or reloading modules. FreeBSD rewards this discipline because it rarely hides state; if something changes, it is usually in a file you control. Make a habit of collecting a before-and-after snapshot of commands and outputs so you can explain which change caused which effect.

At scale, rc system service enablement is also about failure containment. Identify what must remain available when something breaks and design a safe escape hatch. For example, keep console access for firewall changes, keep a previous boot environment for upgrades, or keep a dataset snapshot before risky edits. The same pattern applies across domains: define invariants, define the rollback path, and then only proceed when you can trigger that rollback quickly. Finally, test the failure path while the system is healthy; you learn more from a controlled rollback than from an emergency. This perspective turns the lab exercise into an operational capability you can trust on production systems.

How this fits on projects

Definitions & key terms

  • rc system -> FreeBSD startup framework.
  • rc.conf -> Primary configuration file for services.
  • sysrc -> Safe editor for rc.conf variables.
  • service -> Unified interface to rc scripts.
  • rc.d -> Directory containing rc scripts.

Mental model diagram

rc.conf -> rc system -> rc.d scripts -> service processes

How it works (step-by-step, with invariants and failure modes)

  1. System boots and rc reads rc.conf.
  2. rc decides which services are enabled.
  3. service runs rc scripts to start daemons.
  4. PID files/logs indicate status.

Invariants:

  • rc.conf syntax must be valid.
  • rc script must exist for the service.
  • Enabled services start only if dependencies are met.

Failure modes:

  • Service not enabled -> no autostart.
  • rc.conf typo -> variable ignored.
  • Missing rc script -> service cannot start.

Minimal concrete example

sysrc sshd_enable="YES"
service sshd start
service sshd status

Common misconceptions

  • “Installing a package enables it.” -> Enabling is a separate step.
  • “rc.conf is optional.” -> It is the source of truth for boot-time services.

Check-your-understanding questions

  1. Why use sysrc instead of editing rc.conf directly?
  2. How does service know where the rc script is?
  3. What happens if a service is installed but not enabled?

Check-your-understanding answers

  1. sysrc prevents syntax errors and keeps variables consistent.
  2. It searches standard rc.d locations.
  3. It will not start at boot unless you explicitly start it.

Real-world applications

  • Standardizing server baselines across a fleet.
  • Auditing which services are enabled for compliance.

Where you’ll apply it

References

  • “Absolute FreeBSD, 3rd Edition” (Ch. 14, 20)
  • FreeBSD Handbook: Service Management

Key insights FreeBSD makes service control explicit; you must enable what you want.

Summary The rc system is a declarative service controller. Mastering rc.conf and sysrc is foundational.

Homework/Exercises to practice the concept

  1. Enable and disable sshd twice and observe the effect after reboot.
  2. Use sysrc to add a variable and verify it appears in rc.conf.
  3. List all rc scripts in /etc/rc.d and /usr/local/etc/rc.d.

Solutions to the homework/exercises

  1. sshd starts only when enabled.
  2. sysrc edits rc.conf safely.
  3. Base and third-party services are separated by location.

Concept 2: Secure Remote Access and Least Privilege

Fundamentals A baseline system is not just functional; it is safe by default. The core security principle is least privilege: users should have only the permissions they need. On FreeBSD, you typically create a non-root administrative user and allow privileged actions through controlled tools like doas or sudo. SSH is the preferred remote access method, but it must be configured with secure defaults, such as disabling root login and using key-based authentication where possible. Logs in /var/log/auth.log provide visibility into authentication attempts. For this project, you need to know how to create users, assign them to groups like wheel, configure sshd, and verify remote login while preventing risky access patterns.

Deep Dive into the concept FreeBSD’s user model is straightforward: users belong to groups, and group membership controls access to administrative capabilities. By default, membership in wheel allows a user to su to root, but this is not sufficient for secure remote access. You should also configure sshd in /etc/ssh/sshd_config to disable root login and limit authentication methods. The typical hardened baseline is PermitRootLogin no and PasswordAuthentication no when you have keys available. However, in a lab setting you may temporarily allow passwords during initial setup; the important thing is to document and intentionally change the state later.

Remote access is best validated by staging: create the user locally, confirm it can log in on the console, then enable sshd, and finally connect from another host. If you skip steps, you risk locking yourself out. The safe pattern is: keep a console session open while testing SSH. This is especially critical when you later add firewalls or jails. Authentication logs become your primary troubleshooting tool: they reveal whether the user is rejected due to bad credentials, disabled root login, or missing keys.

Least privilege also applies to system configuration. Do not operate daily tasks as root. Instead, use doas or sudo with explicit rule sets. Even if you do not fully configure privilege escalation in this project, you should identify which actions truly require root and separate them from normal work. This mindset reduces damage from mistakes and is essential in production. Your baseline is not just a checklist; it is a habit of safe operations.

Operationally, secure remote access and least privilege is easiest to keep stable when you treat it as a small contract between configuration, tooling, and observable outputs. Write down the exact files that own the state and the commands that reveal the current truth. Then verify the contract at three points: immediately after you make the change, after a reboot, and after a deliberate disturbance such as restarting services or reloading modules. FreeBSD rewards this discipline because it rarely hides state; if something changes, it is usually in a file you control. Make a habit of collecting a before-and-after snapshot of commands and outputs so you can explain which change caused which effect.

At scale, secure remote access and least privilege is also about failure containment. Identify what must remain available when something breaks and design a safe escape hatch. For example, keep console access for firewall changes, keep a previous boot environment for upgrades, or keep a dataset snapshot before risky edits. The same pattern applies across domains: define invariants, define the rollback path, and then only proceed when you can trigger that rollback quickly. Finally, test the failure path while the system is healthy; you learn more from a controlled rollback than from an emergency. This perspective turns the lab exercise into an operational capability you can trust on production systems.

Also, document the specific signals you will treat as success or failure for secure remote access and least privilege. For access or policy topics, that might be a deliberate allow case and a deliberate deny case that is correctly logged. For workflow topics, it might be a version change plus a service health check. Writing down these signals forces you to define what working actually means and prevents you from moving forward on assumptions.

How this fits on projects

Definitions & key terms

  • Least privilege -> Grant only the access required.
  • wheel group -> Administrative group for su access.
  • sshd_config -> SSH server configuration file.
  • PermitRootLogin -> sshd setting for root access.

Mental model diagram

User -> (wheel membership) -> privileged actions
SSH -> sshd_config -> allowed authentication
Logs -> auth.log -> verify access

How it works (step-by-step, with invariants and failure modes)

  1. Create user and set password.
  2. Add user to wheel group.
  3. Enable sshd in rc.conf and start service.
  4. Configure sshd to disable root login.
  5. Test login from another host and review logs.

Invariants:

  • A non-root user exists and can log in locally.
  • sshd must be running for remote access.
  • Root login is disabled unless explicitly allowed.

Failure modes:

  • User not in wheel -> cannot elevate privileges.
  • sshd not enabled -> connection refused.
  • Misconfigured sshd_config -> login denied.

Minimal concrete example

pw useradd admin -m -G wheel -s /bin/sh
sysrc sshd_enable="YES"
service sshd start

Common misconceptions

  • “Disabling root SSH is optional.” -> It is the baseline expectation.
  • “Console access equals remote safety.” -> Remote access has different risks.

Check-your-understanding questions

  1. Why keep a console session open when testing SSH?
  2. What does wheel membership grant you?
  3. Which log shows SSH authentication attempts?

Check-your-understanding answers

  1. To recover if you misconfigure sshd and lock yourself out.
  2. The ability to use su to become root.
  3. /var/log/auth.log.

Real-world applications

  • Secure administration of servers without exposing root.
  • Audit trails for access attempts.

Where you’ll apply it

References

  • “Absolute FreeBSD, 3rd Edition” (Ch. 9)
  • FreeBSD Handbook: OpenSSH

Key insights A baseline is secure only when root access is explicit and controlled.

Summary Least privilege and SSH hardening are the difference between a lab VM and a secure system.

Homework/Exercises to practice the concept

  1. Create a second user without wheel membership and compare privileges.
  2. Disable root SSH login and verify failures in auth.log.
  3. Document your SSH configuration changes.

Solutions to the homework/exercises

  1. The non-wheel user cannot become root.
  2. Root login attempts are rejected and logged.
  3. Documentation makes changes auditable.

3. Project Specification

3.1 What You Will Build

A baseline FreeBSD system with a non-root admin user, SSH access enabled, root SSH disabled, and a documented rc.conf baseline. You will verify service status and authentication logs.

3.2 Functional Requirements

  1. Admin user created: non-root user exists with home directory.
  2. Wheel membership: admin user can elevate privileges.
  3. SSHD enabled: service starts at boot.
  4. Root login disabled: sshd_config denies root access.
  5. Remote login tested: SSH succeeds from another host.

3.3 Non-Functional Requirements

  • Performance: SSH login completes in under 3 seconds on LAN.
  • Reliability: SSH still works after reboot.
  • Usability: rc.conf baseline is readable and documented.

3.4 Example Usage / Output

$ service sshd status
sshd is running as pid 1234.

$ ssh admin@vm
Last login: Tue Jan 07 12:00:00 2025 from 192.168.1.20

3.5 Data Formats / Schemas / Protocols

  • rc.conf
    sshd_enable="YES"
    
  • sshd_config excerpt
    PermitRootLogin no
    PasswordAuthentication yes
    

3.6 Edge Cases

  • User created without home directory.
  • SSH enabled but blocked by firewall.
  • Root login disabled before a non-root user exists.

3.7 Real World Outcome

A VM that is safe to administer remotely, with logs confirming access and a baseline you can reproduce.

3.7.1 How to Run (Copy/Paste)

pw useradd admin -m -G wheel -s /bin/sh
sysrc sshd_enable="YES"
service sshd start
ssh admin@192.168.1.50

3.7.2 Golden Path Demo (Deterministic)

  • Use a fixed username admin.
  • Use TZ=UTC for consistent log timestamps.

3.7.3 If CLI: provide an exact terminal transcript

$ TZ=UTC service sshd status
sshd is running as pid 1234.

$ TZ=UTC ssh admin@192.168.1.50
Last login: Tue Jan 07 12:00:00 2025 from 192.168.1.20

$ echo $?
0

Failure demo (deterministic)

$ TZ=UTC ssh root@192.168.1.50
Permission denied (publickey,password).

$ echo $?
255

4. Solution Architecture

4.1 High-Level Design

+--------------+
| Users/Groups |
+------+-------+
 |
 v
+--------------+
| rc.conf |
+------+-------+
 |
 v
+--------------+
| sshd service |
+------+-------+
 |
 v
+--------------+
| Remote login |
+--------------+

4.2 Key Components

Component Responsibility Key Decisions
User accounts Admin identity Non-root admin user
rc.conf Service enablement Use sysrc
sshd_config SSH policy Disable root login
Logs Audit trail Use auth.log

4.3 Data Structures (No Full Code)

BaselineConfig
- admin_user: "admin"
- groups: ["wheel"]
- sshd_enabled: true
- root_login: false

4.4 Algorithm Overview

Key Algorithm: Baseline Setup

  1. Create admin user and verify login.
  2. Enable and start sshd.
  3. Harden sshd_config.
  4. Test remote login and review logs.

Complexity Analysis:

  • Time: O(number of config changes)
  • Space: O(log size)

5. Implementation Guide

5.1 Development Environment Setup

# Ensure you can reach the VM on the LAN
ping -c 1 192.168.1.50

5.2 Project Structure

freebsd-baseline/
+-- rc.conf.baseline
+-- sshd_config.baseline
+-- logs/
 +-- auth.log.sample

5.3 The Core Question You’re Answering

“How do I make a FreeBSD system usable day-to-day without using root?”

5.4 Concepts You Must Understand First

Stop and research these before coding:

  1. rc.conf and service enablement
  2. Least privilege and SSH hardening

5.5 Questions to Guide Your Design

  1. Which groups should your admin user join?
  2. Will you use password or key authentication initially?
  3. What exact sshd_config changes will you document?

5.6 Thinking Exercise

Minimal Access Principle

List every privilege your admin user needs and remove the rest.

5.7 The Interview Questions They’ll Ask

  1. How do you enable a service at boot on FreeBSD?
  2. What is sysrc and why use it?
  3. Where do you configure SSH?
  4. How do you check SSH logs?
  5. What is the difference between rc.conf and rc.conf.local?

5.8 Hints in Layers

Hint 1: Start with a console login Verify the admin user can log in locally.

Hint 2: Enable sshd explicitly Use sysrc and check status.

Hint 3: Harden before remote Disable root login before testing remote access.

Hint 4: Read the logs Use auth.log to confirm authentication outcomes.

5.9 Books That Will Help

Topic Book Chapter
rc.conf “Absolute FreeBSD, 3rd Edition” Ch. 14
Security “Absolute FreeBSD, 3rd Edition” Ch. 9

5.10 Implementation Phases

Phase 1: Accounts (1-2 hours)

Goals: Create admin user and verify local login. Tasks:

  1. Create user and home directory.
  2. Confirm shell login at console. Checkpoint: Local login works.

Phase 2: SSH Enablement (2-3 hours)

Goals: Enable and start sshd. Tasks:

  1. Add sshd_enable to rc.conf.
  2. Start sshd and check status. Checkpoint: service sshd status reports running.

Phase 3: Hardening (2-3 hours)

Goals: Secure SSH access. Tasks:

  1. Disable root login.
  2. Test remote login. Checkpoint: Root login fails, admin login succeeds.

5.11 Key Implementation Decisions

Decision Options Recommendation Rationale
Auth method Password, Keys Password first, then keys Easier for initial lab
Root login Allow, Deny Deny Baseline security
Privilege tool su, doas, sudo su + wheel Simplest for baseline

6. Testing Strategy

6.1 Test Categories

Category Purpose Examples
Account Tests Verify user and groups id admin
Service Tests SSH availability service sshd status
Auth Tests Remote login ssh admin@vm

6.2 Critical Test Cases

  1. Admin login: User can log in locally and via SSH.
  2. Root login denied: root SSH is rejected.
  3. Service persistence: sshd stays running after reboot.

6.3 Test Data

Username: admin
Expected SSH failure user: root

7. Common Pitfalls & Debugging

7.1 Frequent Mistakes

Pitfall Symptom Solution
Forgot to enable sshd Connection refused sysrc sshd_enable=YES
Root login disabled too early Lockout risk Keep console session open
auth.log ignored Unknown failures Tail /var/log/auth.log

7.2 Debugging Strategies

  • Log-first troubleshooting: always read /var/log/auth.log.
  • Console safety net: keep local access while testing.

7.3 Performance Traps

  • Repeated SSH failures can trigger delays due to auth throttling.

8. Extensions & Challenges

8.1 Beginner Extensions

  • Create a second admin user and compare group access.
  • Document a baseline rc.conf template.

8.2 Intermediate Extensions

  • Switch to SSH key authentication only.
  • Add a simple login banner.

8.3 Advanced Extensions

  • Configure doas with least-privilege rules.
  • Enable fail2ban-like protections using pf or ipfw.

9. Real-World Connections

9.1 Industry Applications

  • Server baselines: standard user and SSH policies across fleets.
  • Security audits: verifying that root login is disabled.
  • OpenSSH: secure remote access implementation.
  • doas: minimal privilege escalation tool.

9.3 Interview Relevance

  • Service management without systemd.
  • SSH hardening and least privilege concepts.

10. Resources

10.1 Essential Reading

  • “Absolute FreeBSD, 3rd Edition” by Michael W. Lucas - Ch. 9, 14
  • FreeBSD Handbook - OpenSSH and Service Management

10.2 Video Resources

  • “FreeBSD SSH Basics” - community tutorials

10.3 Tools & Documentation

  • sysrc: rc.conf configuration utility
  • OpenSSH: sshd_config reference

11. Self-Assessment Checklist

11.1 Understanding

  • I can explain how rc.conf controls services.
  • I understand why root SSH login is dangerous.
  • I can interpret auth.log entries.

11.2 Implementation

  • Admin user created and tested.
  • sshd enabled and running.
  • Root login disabled and confirmed.

11.3 Growth

  • I documented baseline configuration.
  • I can reproduce these steps quickly.
  • I can teach the process to someone else.

12. Submission / Completion Criteria

Minimum Viable Completion:

  • Non-root admin user created.
  • sshd enabled and running.
  • Root SSH login disabled.

Full Completion:

  • All minimum criteria plus:
  • Remote login tested from another host.
  • Baseline rc.conf saved and documented.

Excellence (Going Above & Beyond):

  • SSH keys enforced and passwords disabled.
  • Privilege escalation rules documented and tested.