An Auditor’s Checklist for Securing Ruby Backends on DigitalOcean
SSH Key Management and Access Control
A fundamental aspect of securing any cloud infrastructure, including DigitalOcean, is rigorous SSH key management. For Ruby backends, this translates to ensuring only authorized personnel can access the underlying servers where your applications are deployed. This section outlines critical checks for SSH key hygiene.
Authorized Keys Verification
Each user account on your DigitalOcean Droplets should have its SSH access controlled via the ~/.ssh/authorized_keys file. Auditors must verify that this file contains only keys belonging to active, authorized personnel. Regularly review and prune stale or unauthorized keys.
Procedure:
- Connect to a Droplet via SSH using an administrative account.
- Navigate to the user’s home directory and then to the
.sshsubdirectory. - Display the contents of the
authorized_keysfile.
Example command on a Droplet:
sudo cat /home/deploy/.ssh/authorized_keys
Auditors should cross-reference the public keys listed here with a master inventory of authorized SSH keys. Any discrepancies or keys not accounted for require immediate investigation.
SSH Daemon Configuration (sshd_config)
The SSH daemon configuration file (typically /etc/ssh/sshd_config) is a critical control point. Secure settings here prevent common attack vectors like brute-force attempts and unauthorized protocol versions.
Key Directives to Audit:
PermitRootLogin no: Disallow direct root login via SSH. All administrative tasks should be performed by a non-privileged user who then usessudo.PasswordAuthentication no: Enforce key-based authentication exclusively. This significantly mitigates brute-force password attacks.AllowUsers [user1] [user2]: Explicitly define which users are permitted to log in via SSH.Port [non-standard-port]: While not a foolproof security measure (security through obscurity), changing the default SSH port (22) can reduce automated scan traffic. Ensure this change is documented and communicated.MaxAuthTries 3: Limit the number of authentication attempts per connection.LoginGraceTime 30: Reduce the time allowed to authenticate.UsePAM yes: Ensure Pluggable Authentication Modules are enabled for centralized authentication and access control policies.
Example sshd_config snippet:
PermitRootLogin no PasswordAuthentication no AllowUsers deploy admin MaxAuthTries 3 LoginGraceTime 30 UsePAM yes
After modifying sshd_config, the SSH service must be reloaded or restarted. Auditors should verify the active configuration.
sudo systemctl reload sshd
Application-Level Security for Ruby Backends
Securing the Ruby application itself is paramount. This involves code-level vulnerabilities, dependency management, and secure configuration of the web server and application framework.
Dependency Vulnerability Scanning
Outdated or vulnerable gems are a common entry point for attackers. Regular scanning of your application’s dependencies is non-negotiable.
Tools:
bundler-audit: A gem that checks yourGemfile.lockagainst a database of known vulnerabilities.Gemnasium(now part of GitLab): Can be integrated into CI/CD pipelines.- Commercial SAST (Static Application Security Testing) tools.
Procedure using bundler-audit:
- Ensure you have the latest vulnerability database.
- Run the audit against your project’s
Gemfile.lock.
# Install bundler-audit if not already present gem install bundler-audit # Update the vulnerability database bundle exec bundler-audit --update # Perform the audit bundle exec bundler-audit
Auditors must verify that a process is in place to regularly run these scans and that identified vulnerabilities are remediated promptly. This includes establishing SLAs for patching critical and high-severity vulnerabilities.
Secure Configuration of Web Server and Application Framework
The web server (e.g., Nginx, Apache) and the Ruby framework (e.g., Rails, Sinatra) must be configured securely. This section focuses on common misconfigurations that lead to security weaknesses.
Nginx Configuration for Ruby Apps
If Nginx is used as a reverse proxy for your Ruby application (e.g., via Puma or Unicorn), its configuration is critical for security. Key areas include:
- Disabling unnecessary modules: Only load modules that are actively used.
- Rate Limiting: Protect against DoS and brute-force attacks.
- Request Header Filtering: Sanitize or reject malicious headers.
- SSL/TLS Configuration: Enforce strong ciphers and protocols.
- Preventing Directory Traversal: Ensure
locationblocks don’t expose sensitive files.
Example Nginx configuration snippet for a Rails app:
# Basic security headers
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header X-Content-Type-Options "nosniff" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;
# Prevent access to hidden files and sensitive directories
location ~ /\. {
deny all;
return 404;
}
location / {
# ... proxy_pass configuration ...
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
# Rate limiting example (requires http_limit_req_module)
limit_req_zone $binary_remote_addr zone=mylimit:10m rate=5r/s;
location /login {
limit_req zone=mylimit burst=20 nodelay;
# ... proxy_pass configuration ...
}
# SSL configuration (ensure this is in your SSL server block)
ssl_protocols TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers on;
ssl_ciphers 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384';
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;
ssl_session_tickets off;
ssl_stapling on;
ssl_stapling_verify on;
resolver 8.8.8.8 valid=300s; # Example DNS resolver
resolver_timeout 5s;
Auditors should verify that Nginx is configured to serve static assets directly and proxy dynamic requests to the application server. They must also check that SSL/TLS is correctly implemented with strong configurations.
Rails Application Security Best Practices
Ruby on Rails, being a popular framework, has well-documented security considerations. Auditors should check for adherence to these:
- CSRF Protection: Ensure Rails’ built-in Cross-Site Request Forgery protection is enabled and correctly implemented for all forms and AJAX requests.
- Mass Assignment Vulnerabilities: Use strong parameters (
strong_parameters) in controllers to explicitly permit attributes for mass assignment, preventing unauthorized attribute updates. - SQL Injection Prevention: Use ActiveRecord’s query interface and avoid constructing SQL queries with raw user input. Use parameterized queries or ActiveRecord’s built-in methods.
- XSS Prevention: Rails’ ERB templating automatically HTML-escapes output by default. Ensure this behavior hasn’t been overridden or bypassed. Use
html_escapeorhhelper where necessary. - Secure Session Management: Ensure session cookies are set with appropriate flags (
HttpOnly,Secure,SameSite). - Secret Key Management: The
secret_key_baseinconfig/secrets.yml(or equivalent in newer Rails versions) must be kept secret and rotated periodically. It should not be committed to version control.
Example of Strong Parameters in a Rails Controller:
class PostsController < ApplicationController
before_action :set_post, only: [:show, :edit, :update, :destroy]
def create
@post = Post.new(post_params)
if @post.save
redirect_to @post, notice: 'Post was successfully created.'
else
render :new
end
end
def update
if @post.update(post_params)
redirect_to @post, notice: 'Post was successfully updated.'
else
render :edit
end
end
private
def set_post
@post = Post.find(params[:id])
end
# Whitelisting attributes for mass assignment
def post_params
params.require(:post).permit(:title, :body, :author_id, tag_ids: [])
end
end
Auditors should review controller code for proper use of strong_parameters and examine database interaction logic for potential SQL injection vulnerabilities. They should also verify that sensitive configuration values like secret_key_base are managed securely, ideally through environment variables or a secrets management system.
DigitalOcean Infrastructure Security
Beyond the application and server access, the underlying DigitalOcean infrastructure itself requires security hardening. This section covers Droplet security, network configurations, and DigitalOcean-specific security features.
Firewall Rules (UFW/iptables)
A properly configured firewall is essential to restrict network access to only necessary ports and services. Uncomplicated Firewall (UFW) is a user-friendly frontend for iptables commonly used on Ubuntu-based Droplets.
Procedure:
- Verify that UFW is enabled.
- Ensure only essential ports are open (e.g., 22 for SSH, 80 for HTTP, 443 for HTTPS, and the port your Ruby application server listens on, e.g., 3000 for Puma).
- Deny all other incoming traffic by default.
Example UFW commands:
# Check UFW status sudo ufw status verbose # Enable UFW (if not already enabled) sudo ufw enable # Set default policies sudo ufw default deny incoming sudo ufw default allow outgoing # Allow essential services sudo ufw allow ssh # Or your custom SSH port sudo ufw allow http sudo ufw allow https sudo ufw allow 3000/tcp # Example for Puma on port 3000 # Deny specific ports if necessary (e.g., if a service was previously exposed) # sudo ufw deny 8080/tcp # Reload UFW after changes (though 'allow' and 'deny' usually take effect immediately) # sudo ufw reload
Auditors should confirm that the firewall rules align with the principle of least privilege, allowing only the minimum necessary network access for the application to function.
DigitalOcean Cloud Firewall
DigitalOcean offers a managed Cloud Firewall service that can be applied to Droplets. This provides an additional layer of network security at the infrastructure level, independent of the Droplet’s operating system.
Audit Checklist:
- Verify that a Cloud Firewall is implemented for your Droplets.
- Review the inbound and outbound rules. Ensure they are restrictive and align with the application’s network requirements.
- Check that the firewall is applied to the correct Droplets or Droplet tags.
- Confirm that rules are specific (e.g., allow traffic only from trusted IP ranges if applicable).
Auditors should compare the DigitalOcean Cloud Firewall rules with the Droplet’s local firewall (UFW/iptables) to ensure consistency and identify any potential gaps or overly permissive settings.
Droplet Security Updates and Patching
Operating system vulnerabilities are a significant risk. A robust patching strategy is crucial for maintaining a secure environment.
Procedure:
- Verify that Droplets are running up-to-date operating systems.
- Confirm that an automated or manual process is in place for applying security patches.
- Check logs for evidence of regular system updates.
- For Ubuntu/Debian systems, ensure
unattended-upgradesis configured and active for security updates.
Example configuration for unattended-upgrades (on Debian/Ubuntu):
# /etc/apt/apt.conf.d/50unattended-upgrades
Unattended-Upgrade::Allowed-Origins {
"${distro_id}:${distro_codename}";
"${distro_id}:${distro_codename}-security";
// "${distro_id}:${distro_codename}-updates";
// "${distro_id}:${distro_codename}-proposed";
// "${distro_id}:${distro_codename}-backports";
}
Unattended-Upgrade::Package-Blacklist {
// "vim";
// "libc6";
// "libc6-dev";
// "libc6-i686";
};
Unattended-Upgrade::Automatic-Reboot "false"; # Or configure as needed, with notification
Unattended-Upgrade::Automatic-Reboot-Time "02:00";
Auditors should confirm that the patching strategy includes a testing phase before deploying updates to production and a rollback plan. For critical systems, manual review of patches might be preferred over full automation.
Logging, Monitoring, and Auditing
Effective logging, monitoring, and auditing are vital for detecting and responding to security incidents. This section details what to look for in a secure Ruby backend environment on DigitalOcean.
Application and System Logs
Comprehensive logs provide visibility into application behavior and system events. Auditors must ensure that logs are:
- Enabled and Configured: Application frameworks (like Rails) and system services (SSH, web server) should have logging enabled.
- Centralized: Logs from multiple Droplets and applications should be aggregated into a central location (e.g., ELK stack, Splunk, cloud-native logging services) for easier analysis and correlation.
- Retained: Logs should be stored for a defined period, compliant with regulatory requirements and incident response needs.
- Protected: Log files and the logging system itself should be protected against tampering and unauthorized access.
Example Rails Log Configuration (config/environments/production.rb):
# Log to STDOUT in production. This is useful for containerized environments
# and for sending logs to external services.
config.logger = ActiveSupport::Logger.new(STDOUT)
config.logger = ActiveSupport::Logger.new("log/production.log") # Or to a file
# Log level: :debug, :info, :warn, :error, :fatal
config.log_level = :info
# Rotate logs if writing to a file
# config.logger = ActiveSupport::Logger.new("log/#{Rails.env}.log", 10, 10.megabytes)
Auditors should verify that sensitive information (like passwords, API keys) is not logged. They should also check that log rotation is configured correctly if logs are stored on the Droplet to prevent disk space exhaustion.
Monitoring and Alerting
Proactive monitoring helps detect anomalies and potential security incidents before they escalate. Key aspects include:
- System Metrics: Monitor CPU, memory, disk I/O, and network traffic for unusual spikes or patterns.
- Application Performance: Track request latency, error rates, and resource utilization specific to the Ruby application.
- Security Events: Monitor for failed login attempts (SSH, application), suspicious network connections, and unauthorized file modifications.
- Alerting: Configure alerts for critical thresholds and security events, ensuring they are routed to the appropriate personnel.
DigitalOcean’s monitoring tools (or integrated third-party solutions) should be configured to provide these insights. Auditors should review the configured alerts and the incident response procedures associated with them.
Audit Trails
Audit trails provide a record of who did what, when, and where. This is crucial for accountability and forensic analysis.
- System-level Auditing: Ensure Linux auditing tools (like
auditd) are configured to log critical system events, such as file access, privilege escalation (sudousage), and configuration changes. - Application-level Auditing: Implement audit logging within the Ruby application for significant business events, such as user authentication, data modifications, and access to sensitive resources.
- Access Control Logs: Review logs for administrative access to DigitalOcean resources (Droplets, firewalls, databases).
Example auditd rule to log sudo usage:
-w /usr/bin/sudo -p x -k sudo_execution
Auditors must verify that audit trails are comprehensive, tamper-evident, and regularly reviewed. The retention policy for audit logs should be clearly defined and enforced.