Dnsmasq: cannot open log /var/log/pihole.log: Permission denied

Installed pi-hole today CentOS 7. It appears to have installed correctly, however, the dnsmasq service will not start because of the following error:

dnsmasq: cannot open log /var/log/pihole.log: Permission denied

As far as I know, that's the default location with default permisssions:

ls -l /var/log/pihole.log
-rw-r--r--. 1 nobody root 0 Jan 21 12:39 /var/log/pihole.log

Any suggestions?

Hi, can you run pihole -d and get us the token so we can take a look. I just ran through an install on a Centos 7.3.1161 and confirmed that the permissions are correct and the user is right. The debug log should show us some more information though.

Your debug token is : xodz9hm7xw

Please go to your dnsmasq config /etc/dnsmasq.d/01-pihole.conf and comment out the line

log-facility=/var/log/pihole.log

Then, start the dnsmasq service and run

ps aux|grep dnsmasq|grep -v grep

and provide the output. This will tell us which user your dnsmasq process likes to use.

It started cleanly.

ps aux | grep dnsmasq | grep -v grep
nobody 11889 0.8 0.0 25788 11504 ? Ss 14:25 0:00 /usr/sbin/dnsmasq -k

This circumvents the issue you are seeing as it prevents dnsmasq from trying to access the log file. Unfortunately, this means that logging is now disabled.

By any chance, do you use SELinux on this machine?

I think so. It's just basic CentOS install so if SELinux is installed by default...

One way we can check is via sestatus, if you run that command you should get back the status of the SELinux security. So that's next to check.

sestatus

SELinux status: enabled
SELinuxfs mount: /sys/fs/selinux
SELinux root directory: /etc/selinux
Loaded policy name: targeted
Current mode: enforcing
Mode from config file: enforcing
Policy MLS status: enabled
Policy deny_unknown status: allowed
Max kernel policy version: 28

Okay, and then ls -laZ /var/log/pihole.log to see if the file is set to be permissive read/write.

# ls -laZ /var/log/pihole.log
-rw-r--r--. nobody root unconfined_u:object_r:var_log_t:s0 /var/log/pihole.log

Okay, we do have a warning on install for some recent releases that notes for SELinux enabled systems you would need to set the confinements for files in order to work properly. It's possible to run with SELinux in a degraded security mode, but it's not suggested.

In order to run with SEL enabled, you would need to set the rules on your system to allow dnsmasq to access that file.

Ok. I can look into that. This is what I get when I check dnsmasq:

# ps auxZ | grep dnsmasq | grep -v grep
system_u:system_r:dnsmasq_t:s0 nobody 11889 0.0 0.0 25788 11504 ? Ss 14:25 0:00 /usr/sbin/dnsmasq -k

Once you have the constraints set on that file, then it should start up with no problems. Since it's modifying the security of the operating system, we decided a few releases back to just warn the user and let them modify if they chose, instead of installing a custom security policy. If we can be of assistance, let us know, however the latest policy that we have needs to be tuned and cleaned up, it was a little too permissive for our likes.

Where would I find that policy? Could I use it as a starting point?

I'm guessing that this isn't the only issue I'll run into with SELinux and Pi-Hole.

It should be the only issue, that's the only log change and redirection of the file. We used to have the policy in the script, it was at: pi-hole/pihole.te at 420158494dd3947d1dd7b2011134990a82336651 · pi-hole/pi-hole · GitHub for the template.

If anyone else is looking at this, I ended up using the following to to allow dnsmasq to start with the default config:

# chcon -v --type=dnsmasq_var_log_t /var/log/pihole.log

I don't think it's permanent though. I'll post back here once I figure that part out.

Our Fedora/CentOS guy has suggested this Pull Request as a point to help getting a policy in place. Selinux rewrite by bcambl · Pull Request #900 · pi-hole/pi-hole · GitHub The template has been tightened up a little more from the one I linked you to.

I've done my initial bit of getting PiHole up and running against Fedora29. I'm not entirely happy as this gives way to much power to the httpd process - in a next iteration I'm going to add some custom types to limit this power.

This is what I put:

booleans:

# setsebool -P httpd_can_network_connect 1
# setsebool -P httpd_setrlimit 1
# setsebool -P httpd_mod_auth_pam 1
# setsebool -P httpd_execmem 1

contexts:

# semanage fcontext -a -t httpd_sys_content_t "/html(/.*)?"
# semanage fcontext -a -t httpd_log_t "/var/log/pihole-FTL.log"
# semanage fcontext -a -t httpd_log_t "/var/log/pihole.log"
# semanage fcontext -a -t httpd_var_run_t "/var/run/pihole(/.*)?"
# semanage fcontext -a -t httpd_var_run_t "/var/run/pihole-FTL.*"

I've added following modules:

module pihole-custom 1.0;

require {                                                                                                           
        type httpd_t;                                                                                                                                               
        type httpd_sys_content_t;                                                                                                                                                                                  
        type dnsmasq_etc_t;                                                                                                                                                                 
        type etc_t;                                                                                                                                         
        type user_tmp_t;                                                                                                                                                        
        type httpd_log_t;
        type logrotate_exec_t;
        type var_log_t;
        type initrc_exec_t;
        type bin_t;
        type initrc_t;

        class process signal;                                                                                                                                                                     
        class dir { add_name write remove_name };                                                                                                                                                                  
        class file { read create write unlink setattr getattr rename append open ioctl getattr execmod };                                                                                                                                                                        
        class service { start };
        class capability { sys_ptrace };
}                                                                                   

allow httpd_t httpd_sys_content_t:dir { add_name write remove_name };                                                                                                                                              
allow httpd_t httpd_sys_content_t:file { create write unlink };                                                                                                                                                    
                                                                                                                                                      
allow httpd_t dnsmasq_etc_t:dir { add_name write remove_name };                                                                                         
allow httpd_t dnsmasq_etc_t:file { create append getattr open read ioctl write setattr rename unlink };

allow httpd_t etc_t:dir { add_name write remove_name };
allow httpd_t etc_t:file { create append write rename unlink setattr };

allow httpd_t user_tmp_t:file unlink;

allow httpd_t httpd_log_t:file write;
allow httpd_t logrotate_exec_t:file { getattr read };
allow httpd_t var_log_t:file { write open };

allow httpd_t bin_t:file execmod;
allow httpd_t initrc_exec_t:file getattr;
allow httpd_t initrc_exec_t:service start;
allow httpd_t self:capability sys_ptrace;

allow httpd_t initrc_t:process signal;

And this module to allow it to get info from other domains (so pidof/ps work):
policy_module(pihole-allow-state, 1.0.0)

gen_require(`
    type httpd_t;
')

domain_read_all_domains_state(httpd_t);

The combination of all above gives me a working PiHole. I haven't incorporated the restart / poweroff server as I find this goes a bit too far.