Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Writing files (by executing git commands) from PHP on CentOS7

I can't write files in my apache webserver directory. I'm using CentOS 7. I'm trying to execute git commands from a PHP file using exec:

exec("/usr/local/bin/git -C ../myRepo fetch 2>&1", $output, $exec_return_value);

By printing the output I see that the error manifests as:

error: cannot open .git/FETCH_HEAD: Permission denied

I discovered (with help from this blog: http://jondavidjohn.com/git-pull-from-a-php-script-not-so-simple/) that this is "expected" because Apache will execute using a different user that what I use via ssh. So I need to make sure my permissions allow that user to have write access.

I determined that my Apache installation executes as a user named "apache" by printing the output of this in my php file:

exec("whoami", $debugOutput, $debugRetVal);

I also set up SSH as recommended in that same link. But unfortunately I was still getting the same error in PHP file:

error: cannot open .git/FETCH_HEAD: Permission denied

The thing that really threw me is that I don't have any permissions issues when I run as apache from my ssh session using:

sudo -u apache /usr/local/bin/git -C ../myRepo fetch

The user "apache" seemingly has different permissions when it's executing from PHP. Can someone help me figure this out?

like image 478
Josh Avatar asked Nov 22 '25 08:11

Josh


1 Answers

The behavior I was seeing where the user "apache" seemed to have different permissions when executing from PHP vs executing from my SSH, was the result of SELinux. When SELinux is in its "enforcing" mode (which it is by default in CentOS 7) it essentially "layers" an access protection scheme on top of the standard linux user permissions. In a simplistic sense, the user "apache" actually did have different permissions when it was executing out of the httpd process.

To get more detailed the html directory that apache "serves" has an SELinux context type of "httpd_sys_content_t". So my git repo was inheriting that same context. But the way the SELinux security policy works is that httpd only has read access to the context type "httpd_sys_content_t". In order for httpd to have write access to the files I needed to change the context to "httpd_sys_rw_content_t". I did this using:

sudo chcon -R -t httpd_sys_rw_content_t ../myRepo

This finally solved my original error! Unfortunately it surfaced a new error that the link from the question also mentioned:

array(5) { [0]=> string(68) "ssh: connect to host PRIVATEGITHOST: Permission denied" [1]=> string(45) "fatal: Could not read from remote repository." [2]=> string(0) "" [3]=> string(51) "Please make sure you have the correct access rights" [4]=> string(26) "and the repository exists." } 

But as I mentioned in my question I already set up an ssh key for the "apache" user as the blog suggested. So this was actually a different error. An error once again originating from SELinux.

By default there is an SELinux boolean called "httdp_can_network_connect" that is set to off. This prevents httpd from making external connections of it's own. Which is what you want if all you're doing is serving the content that exists on your server. For my purposes I needed to set that boolean to on using:

sudo setsebool httpd_can_network_connect on

And after 2 days of pulling my hair out. I can fianlly do a "git fetch" from a php file.

I debugged the final error with the boolean using the "audit2allow" tool which looks at some SELinux logs and tells you in more or less plain english how to solve the issue.

sudo audit2allow -a

OUTPUT:
#============= httpd_t ==============
#!!!! This avc can be allowed using one of the these booleans:
#     nis_enabled, httpd_can_network_connect
allow httpd_t unreserved_port_t:tcp_socket name_connect;

If anyone is having SELinux issues relating to apache/httpd I highly recommend looking at that tool.

For the sake of completeness, I wanted to point out that changing the file context of my repository as I did above is a "temporary change" and will not persist through a system reset. In order to permanently change the context type I used:

sudo semanage fcontext -a -t httpd_sys_rw_content_t "/var/www/html/myRepo(/.*)?"

Similarly I used this to permanently change the boolean:

sudo setsebool -P httpd_can_network_connect on
like image 161
Josh Avatar answered Nov 24 '25 23:11

Josh



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!