Hacker News new | ask | show | jobs
by adamddev1 21 days ago
I enjoyed my foray into trying FreeBSD for my personal server. There's something cool, clean, simple and "punk rock" about it. But I gave up as my main pain points were:

- PM2 was buggy on FreeBSD, which I used to manage my processes

- An alternative, using `rc.d` to run daemons was just so hard to get logs working.

- The firewall required too much self configuration to get it right with all the best security practices (ie. What does one do with ICMP.) I was missing something like a template with the defaults that come with UFW, for instance.

5 comments

> I was missing something like a template with the defaults that come with UFW, for instance.

FreeBSD does include this! It's implemented using IPFW instead of PF. Check out `firewall_type` key in `rc.conf`: https://cgit.freebsd.org/src/tree/libexec/rc/rc.conf?id=8e08...

For a very easy single-machine firewall, one could set `firewall_type=client` or `firewall_type=workstation` if you want to host anything. For the latter, `firewall_myservices` and `firewall_allowservices` control what ports are enabled and who (other networks/IPs) have access to them.

For a very simple NAT gateway, one could set `firewall_type=simple` and then `firewall_simple_(iif|inet|oif|onet)(_ipv6)?` to configure the ISP-side and internal-side interface names and IPv4 and IPv6 network ranges for each.

For more details and to see exactly what each option actually does, check out `/etc/rc.firewall` where this is all implemented: https://cgit.freebsd.org/src/tree/libexec/rc/rc.firewall?id=...

> - PM2 was buggy on FreeBSD, which I used to manage my processes

For supervision?

> - An alternative, using `rc.d` to run daemons was just so hard to get logs working.

The unix way is to use logger(1) If you only want some simple message, or redirect to files using newsyslog(8) for managing the sizes of the files.

> The firewall required too much self configuration to get it right with all the best security practices (ie. What does one do with ICMP.) I was missing something like a template with the defaults that come with UFW, for instance.

I would recommend The Book of PF[0]. While FreeBSD has syntax difference with OpenBSD's pf, this should give you enough insight on how a firewall operates to get a sense of what rules to write.

[0]: https://nostarch.com/book-of-pf-4e

My main pain points were that it doesn't survive power hits. If your power goes out, it will reboot and ask you to manually fsck the filesystem.
You didn’t use ZFS with FreeBSD?
Now I do, but I built a few headless media servers for friends and was very disappointed when they stopped working after power outages.
pm2 has been buggy every time I’ve used it, no matter the OS. Incredibly convenient to begin with but simultaneously unpleasant to use software. Updating environment variables with a deployment has not once ever worked as intended.
> An alternative, using `rc.d` to run daemons was just so hard to get logs working.

I actually just did this for a web application on FreeBSD 15. It wasn't too bad, but I used `daemon` to send stdout & stderr into syslog and then I just added a syslog config to send that to it's own file. Roughly, I did this:

    # /usr/local/etc/rc.d/app:

    # PROVIDE: app
    # KEYWORD: shutdown
    
    . /etc/rc.subr
    
    name="app"
    rcvar="app_enable"
    
    load_rc_config $name
    
    : ${app_enable:="NO"}
    : ${app_user:="www"}
    : ${app_database_url:=""}
    
    app_command="/usr/local/bin/app"
    
    cpidfile="/var/run/${name}/${name}.pid"
    pidfile="/var/run/${name}/${name}d.pid"
    logfile="/var/log/${name}.log"
    command=/usr/sbin/daemon
    command_args="-P ${pidfile} -p ${cpidfile} -S -t ${name}-super -T ${name} ${app_command} start"
    
    start_precmd="${name}_prestart"

    app_prestart()
    {
        # ... Set environment variables here with EXPORT, using values from rc.conf
        export DATABASE_URL=${app_database_url}

        # This is also a good place to use install(1) to create 
        #   config files and log directories if they don't exist
    }

    run_rc_command "$1"

Note that -S to `daemon` instructs it to use syslog, and the -T sets the syslog tag which controls how messages are routed. See for more information on daemon: https://man.freebsd.org/cgi/man.cgi?query=daemon&apropos=0&s...

After sending the log messages to syslog, it's a simple matter of routing them to the desired destination. That's easy enough to do by creating a file as follows:

    # /usr/local/etc/syslog.d/app.conf:
    !app
    *.*    /var/log/app.log

in this, the !app indicates this rule is for all items tagged "app", then the *.* matches "all facilities" and "all levels". The last bit indicates the file to route those messages to. After that, it pretty much runs itself. You can start and stop the service with `service app start` and `service app stop` and all log messages get forwarded to `/var/log/app.log`. You do need to make sure the log file exists and has appropriate permissions (usually 600 or 644, owned by root & the wheel group).

You can additionally set up log rotation with:

    # /usr/local/etc/newsyslog.conf.d/app.conf:
    /var/log/blog.log  640  7  *  @T00  Z

Oh and I believe you do need to reload syslog with `service syslogd reload` after this.