Complete Guide to Installing and Configuring vsftpd on Red Hat Enterprise Linux 9 in 2025

vsftpd (Very Secure FTP Daemon) is a lightweight, secure FTP server for Red Hat Enterprise Linux (RHEL) 9, perfect for secure file transfers in enterprise environments or on Amazon EC2 instances. This comprehensive guide walks you through installing, configuring, and testing vsftpd, addressing common issues like the missing ftp_home_dir SELinux boolean, the absence of the seinfo command, and the 500 OOPS: vsftpd: refusing to run with writable root inside chroot() error. We’ll use the allow_writeable_chroot=YES fix (confirmed to work) and the public_content_rw_t SELinux context to ensure a robust setup with read/write access, compatible with tools like WinSCP on port 21.

What is vsftpd?

vsftpd is a GPL-licensed FTP server designed for Unix-like systems, known for its security, performance, and simplicity. It supports features like FTPS (FTP over SSL/TLS), chroot jails, virtual users, and IPv6. In RHEL 9, it integrates tightly with SELinux and firewalld, making it ideal for secure deployments.

Benefits of vsftpd

  • Security: Supports FTPS, chroot jails, and SELinux integration to restrict user access and encrypt transfers.
  • Performance: Handles many simultaneous connections with minimal resource usage.
  • Stability: Mature and reliable, with regular updates in RHEL 9 (e.g., version 3.0.5+ in 2025).
  • Ease of Use: Simple configuration via /etc/vsftpd/vsftpd.conf.
  • Open-Source: Free to use and modify, with strong community support.

Why vsftpd is Relevant in 2025

Despite alternatives like SFTP or cloud storage, vsftpd remains vital for:

  • Legacy Compatibility: Supports FTP/FTPS for systems requiring traditional protocols.
  • Lightweight Deployments: Ideal for IoT, web hosting, or EC2 instances with limited resources.
  • Security Updates: Ongoing patches in RHEL 9 ensure modern security standards.
  • Enterprise Use: Integrates with SELinux and firewalld, perfect for controlled environments.

Prerequisites

  • RHEL 9 system (e.g., on an EC2 instance) with an active Red Hat subscription.
  • selinux-policy-targeted package installed (sudo dnf install selinux-policy-targeted -y).
  • Root or sudo privileges.
  • SELinux in enforcing mode (check with getenforce; set with sudo setenforce 1 and edit /etc/selinux/config).
  • For EC2: Public IPv4 address and security group rules allowing TCP port 21 and passive ports (40000–50000).

Step 1: Install vsftpd

Let’s install vsftpd and ensure the system is ready.

1.1 Update the System

sudo dnf update -y

Why: Updates all packages to ensure compatibility and apply security patches. The -y flag automates prompts for a seamless update.

1.2 Install vsftpd

sudo dnf install vsftpd -y

Why: Installs vsftpd and dependencies from RHEL’s repositories, setting it up as a systemd service. If the package isn’t found, verify your subscription with sudo subscription-manager register.

1.3 Verify Installation

vsftpd -version

Why: Confirms the installed version (e.g., 3.0.5 or later in 2025). This ensures you’re using a modern release with security features like chroot restrictions.

Step 2: Configure vsftpd

We’ll configure vsftpd to allow secure logins, enable write access, and fix the 500 OOPS: refusing to run with writable root inside chroot() error.

2.1 Edit the Configuration File

sudo vi /etc/vsftpd/vsftpd.conf

Why: The configuration file (/etc/vsftpd/vsftpd.conf) defines vsftpd’s behavior. Back up the original (sudo cp /etc/vsftpd/vsftpd.conf /etc/vsftpd/vsftpd.conf.bak) to revert if needed.

Add or uncomment the following lines:

anonymous_enable=NO
local_enable=YES
write_enable=YES
chroot_local_user=YES
allow_writeable_chroot=YES
  • anonymous_enable=NO: Disables anonymous access, requiring authenticated logins for security.
  • local_enable=YES: Allows system users (e.g., created with useradd) to log in via FTP.
  • write_enable=YES: Enables file uploads, deletes, and renames, essential for writable directories.
  • chroot_local_user=YES: Confines users to their home directories (e.g., /home/ftpuser), preventing access to other system areas.
  • allow_writeable_chroot=YES: Allows chrooted directories to be writable, fixing the 500 OOPS error. Without this, vsftpd blocks logins if the chroot root is writable (e.g., /home/ftpuser).

2.2 Configure Passive Mode (Recommended)

For EC2 instances, passive mode is critical for WinSCP compatibility. Add to vsftpd.conf:

pasv_enable=YES
pasv_min_port=40000
pasv_max_port=50000
pasv_address=<public-ipv4-address>
  • pasv_enable=YES: Enables passive mode for better firewall/NAT compatibility.
  • pasv_min_port=40000 and pasv_max_port=50000: Defines the port range for data transfers.
  • pasv_address=<public-ipv4-address>: Specifies your EC2 instance’s public IPv4 address (e.g., 3.123.456.789). Find it in the EC2 console under Instances > Public IPv4 address. This ensures WinSCP can establish data connections.

Why: Passive mode is client-initiated, avoiding issues with active mode behind firewalls. The pasv_address is critical for EC2, as NAT requires the server to advertise its public IP.

Save and exit. Restart vsftpd later to apply changes.

Step 3: Configure SELinux

Since ftp_home_dir is missing (per your getsebool -a | grep ftp output) and seinfo is unavailable (due to missing setools), we’ll use public_content_rw_t for the FTP directory.

3.1 Verify public_content_rw_t Availability

sudo semanage fcontext -l | grep public_content_rw_t

Why: Checks if the public_content_rw_t SELinux type is defined. Output like /var/www/html(/.*)? ... public_content_rw_t confirms availability. If none, test it:

sudo mkdir /tmp/testdir
sudo semanage fcontext -a -t public_content_rw_t "/tmp/testdir(/.*)?"
sudo restorecon -R -v /tmp/testdir
ls -Z /tmp/testdir
  • Success: Output like system_u:object_r:public_content_rw_t:s0 confirms the type is available.
  • Failure: If “type not defined” appears, reinstall SELinux policies:
    sudo dnf reinstall selinux-policy selinux-policy-targeted -y
    sudo dnf update selinux-policy* -y

Optional: Install setools for future debugging:

sudo dnf install setools -y

3.2 Create an FTP User

sudo useradd -m ftpuser
sudo passwd ftpuser

Why: Creates a test user (ftpuser) with a home directory (/home/ftpuser). The password enables FTP login.

Create an FTP directory:

sudo mkdir /home/ftpuser/ftp
sudo chown ftpuser:ftpuser /home/ftpuser/ftp
sudo chmod 750 /home/ftpuser/ftp

Why: Sets up a subdirectory for FTP files. chown ensures ftpuser can read/write. chmod 750 gives owner read/write/execute, group read/execute, and no access for others, aligning with write_enable=YES.

3.3 Apply public_content_rw_t Context

sudo semanage fcontext -a -t public_content_rw_t "/home/ftpuser/ftp(/.*)?"
sudo restorecon -R -v /home/ftpuser/ftp

Why: Assigns public_content_rw_t to allow vsftpd (running as ftpd_t) to read/write the directory. Verify:

ls -Z /home/ftpuser/ftp

Expect: unconfined_u:object_r:public_content_rw_t:s0.

3.4 Enable SELinux Booleans

sudo setsebool -P ftpd_full_access on
sudo setsebool -P ftpd_anon_write on

Why: ftpd_full_access grants vsftpd broad file access, compensating for missing ftp_home_dir. ftpd_anon_write enables writes in chrooted directories. -P ensures persistence. Verify:

getsebool -a | grep ftp

3.5 Confirm SELinux Mode

getenforce

Why: Ensures SELinux is Enforcing. If not, set:

sudo setenforce 1
sudo vi /etc/selinux/config
# Set SELINUX=enforcing

Step 4: Configure the Firewall

RHEL 9 uses firewalld for firewall management, critical for EC2 connectivity.

4.1 Allow FTP Service

sudo firewall-cmd --permanent --add-service=ftp

Why: Opens ports 20 (data) and 21 (control) for FTP.

4.2 Allow Passive Mode Ports

sudo firewall-cmd --permanent --add-port=40000-50000/tcp

Why: Opens the passive port range defined in vsftpd.conf.

4.3 Reload Firewall

sudo firewall-cmd --reload

Why: Applies changes without interrupting services. Verify with sudo firewall-cmd --list-all.

Step 5: Configure EC2 Security Group

For EC2, security groups act as a virtual firewall.

  1. Go to EC2 > Security Groups in the AWS Console.
  2. Select the security group for your instance.
  3. Add inbound rules:
    • Type: Custom TCP, Port Range: 21, Source: 0.0.0.0/0 (or your IP, e.g., 203.0.113.1/32).
    • Type: Custom TCP, Port Range: 40000–50000, Source: 0.0.0.0/0.
  4. Save rules.

Or via CLI:

aws ec2 authorize-security-group-ingress --group-id <security-group-id> --protocol tcp --port 21 --cidr 0.0.0.0/0
aws ec2 authorize-security-group-ingress --group-id <security-group-id> --protocol tcp --port 40000-50000 --cidr 0.0.0.0/0

Why: Allows FTP control and passive data connections from WinSCP.

Step 6: Start and Enable vsftpd

6.1 Start the Service

sudo systemctl start vsftpd

Why: Launches vsftpd, binding to port 21.

6.2 Enable on Boot

sudo systemctl enable vsftpd

Why: Ensures vsftpd starts on reboot.

6.3 Check Status

sudo systemctl status vsftpd

Why: Confirms the service is “active (running).” If not, check logs.

Step 7: Test the vsftpd Setup

7.1 Local Test

ftp localhost

Log in with ftpuser and password. Run:

ls
put testfile.txt
get testfile.txt

Why: Verifies login, directory listing, and read/write access. The allow_writeable_chroot=YES fix ensures no chroot errors.

7.2 Remote Test with WinSCP

  1. Open WinSCP.
  2. Create a new site:
    • File Protocol: FTP
    • Host name: Your EC2 public IPv4 address (e.g., 3.123.456.789)
    • Port number: 21
    • User name: ftpuser
    • Password: Your user’s password
  3. Go to Advanced > Connection, ensure Passive mode is checked.
  4. Click Login.

Why: Tests connectivity from your local machine to the EC2 instance.

7.3 Troubleshoot

  • Logs: Check vsftpd logs:
    sudo journalctl -u vsftpd
  • SELinux Denials: Inspect:
    sudo ausearch -m avc -ts recent
    Create custom policy if needed:
    sudo ausearch -m avc -ts recent | audit2allow -M myvsftpd
    sudo semodule -i myvsftpd.pp
  • Firewall: Verify ports:
    sudo firewall-cmd --list-all
  • EC2 Connectivity: Test port 21:
    telnet <public-ipv4-address> 21

Step 8: Secure vsftpd (Optional)

8.1 Enable FTPS

Add to vsftpd.conf:

ssl_enable=YES
allow_anon_ssl=NO
force_local_data_ssl=YES
force_local_logins_ssl=YES
ssl_tlsv1=YES
ssl_sslv2=NO
ssl_sslv3=NO
rsa_cert_file=/etc/ssl/certs/vsftpd.pem
rsa_private_key_file=/etc/ssl/private/vsftpd.key

Generate certificate:

sudo openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout /etc/ssl/private/vsftpd.key -out /etc/ssl/certs/vsftpd.pem

Why: Encrypts FTP sessions with TLS. In WinSCP, set Encryption to TLS/SSL Explicit encryption.

8.2 Restrict Users

echo "ftpuser" | sudo tee -a /etc/vsftpd/user_list

Add to vsftpd.conf:

userlist_enable=YES
userlist_file=/etc/vsftpd/user_list
userlist_deny=NO

Why: Whitelists ftpuser, limiting access to authorized users.

Conclusion

This guide sets up vsftpd on RHEL 9, fixing the 500 OOPS: refusing to run with writable root inside chroot() error with allow_writeable_chroot=YES. It uses public_content_rw_t and SELinux booleans to handle the missing ftp_home_dir and works without seinfo. The EC2-specific pasv_address ensures WinSCP connectivity. Test thoroughly and share logs in the comments if issues persist!

Posted on August 29, 2025 | Tags: vsftpd, RHEL 9, FTP, SELinux, EC2, WinSCP

SUBSCRIBE OUR NEWSLETTER
I agree to have my personal information transfered to MailChimp ( more information )
Join us by subscribing to our newsletter and learn IT subjects for free
We hate spam. Your email address will not be sold or shared with anyone else.

Leave a Comment

Scroll to Top