Page MenuHomePhabricator

SELinux / AppArmor ruin everything all the time
Open, NormalPublic

Description

It would be nice to, e.g., detect that SELinux or AppArmor are enabled and tell the user that nothing will work.

Ideally we should also say "here's how to fix it" but I've never known anyone to actually do that.

Event Timeline

epriestley raised the priority of this task from to Normal.
epriestley updated the task description. (Show Details)
epriestley added a project: Setup.
epriestley added a subscriber: epriestley.

PROBLEM

Trying to look at the repo through diffusion causes errors listed below; however the commit does go properly (through command line) and one can see the commit number in phabricator.

ERRORS PRODUCED

Command failed with error #128! COMMAND git cat-file --batch-check STDOUT (empty) STDERR fatal: Not a git repository (or any of the parent directories): .git

Command failed with error #128! COMMAND git cat-file --batch-check STDOUT (empty) STDERR fatal: Not a git repository (or any of the parent directories): .git

Command failed with error #128! COMMAND git for-each-ref --sort='-creatordate' --format='%(objectname)%01%(objecttype)%01%(refname)%01%(*objectname)%01%(*objecttype)%01%(subject)%01%(creator)' 'refs/tags/' STDOUT (empty) STDERR fatal: Not a git repository (or any of the parent directories): .git

Command failed with error #128! COMMAND git for-each-ref --sort='-creatordate' --format='%(objectname)%01%(objecttype)%01%(refname)%01%(*objectname)%01%(*objecttype)%01%(subject)%01%(creator)' 'refs/heads/' STDOUT (empty) STDERR fatal: Not a git repository (or any of the parent directories): .git

CAUSE

This issue happens due to SELinux being enabled, you can verify by temporarily setting selinux to not enforce, check and if it works then re-enable and follow resolution below.

setenforce 0

Alternatively you can use audit2why to check for git errors:

grep git /var/log/audit/audit.log | audit2why

Please remember to turn on SELinux after.

SUGGESTION FOR PHABRICATOR DETECTION

Much like phabricator throws a setup error once it detects files can't be uploaded we should do the same thing here. On error produced we should throw a setup issue to resolve. Also this can be made into a script and made available to the user.

RESOLUTION

To fix this issue you need to create an SELinux policy to allow everything through.

Generate a policy rule to allow phabricator to run locally using git (webserver):

rep git /var/log/audit/audit.log | audit2allow -m phabgitlocal > phabgitlocal.te

You can view the policy rules by using cat:

# bash: cat phabgitlocal.te

module phabgitlocal 1.0;

require {
	type httpd_t;
	type var_t;
	class file { read getattr open };
}

#============= httpd_t ==============
allow httpd_t var_t:file { read getattr open };

The next step, now that the above looks okay, is to create a policy module that you can install:

#bash: grep git /var/log/audit/audit.log | audit2allow -M phabgitlocal.te

******************** IMPORTANT ***********************
To make this policy package active, execute:

semodule -i phabgitlocal.te.pp

And finally lets go ahead and install it:

#bash: selinux -i phabgitlocal.te.pp
#bash:

The first code block underneath the Resolution heading is missing the starting 'g' in grep.

This solution was very helpful on a CentOS 7 install. Thank you.

A related issue. /notification/status just returns:

[cURL/7] (http://127.0.0.1:22281/status/) <CURLE_COULDNT_CONNECT> The cURL library raised an error while making a request. You may be able to find more information about this error (error code: 7) on the cURL site: http://curl.haxx.se/libcurl/c/libcurl-errors.html#CURLECOULDNTCONNECT

This is caused by SELinux here on my instance running on a plain CentOS 7.
It can be (temporarily) worked around using: setsebool -P httpd_can_network_connect on

I'll try to come up with a proper permanent solution.

Just throwing this on here in case it helps anyone. I'm working through an SELinux + phabricator install with hosting over SSH instead of HTTP, and so far I've had to do this to get clone working:

[root@phab]# ausearch -m avc -ts recent | audit2allow -v


#============= sshd_t ==============
# src="sshd_t" tgt="mysqld_t" class="unix_stream_socket", perms="connectto"
# comm="php" exe="" path=""
#!!!! This avc can be allowed using the boolean 'daemons_enable_cluster_mode'
allow sshd_t mysqld_t:unix_stream_socket connectto;
# src="sshd_t" tgt="mysqld_var_run_t" class="sock_file", perms="write"
# comm="php" exe="" path=""
#!!!! This avc is allowed in the current policy
allow sshd_t mysqld_var_run_t:sock_file write;
[root@phab]# ausearch -m avc -ts recent | audit2allow -M sshd_mysql2
[root@phab]# semodule -i sshd_mysql2.pp

This is in addition to using setfacl to allow my vcs-user rwx in the arcanist, libphutil, and phabricator directories under the phabricator root.

Anyone ever systematically worked through this? it's quite a tangled mess.

@storrgie - SELinux is very nice for tightening security, but unless you are very well versed in it is a pain in arse... and ruins everything you love. Phabricator + SE Linux.... Well, either disable SELinux or happy hunting!

It wasn't too much of a hunt, but I did go easy mode towards the end (audit2allow). For others he is my basic install notes on Fedora 23 server:

phabricator

Lots adapted from the following resources:

user

adduser --system phacility

database

dnf install mariadb-server
systemctl start mariadb.service
systemctl enable mariadb.service
sudo mysql_secure_installation

We must make a user account and grant it way too much privileges because phabricator installs so many schemas:

mysql -u root -p
GRANT ALL PRIVILEGES ON *.* TO "phabricator"@"localhost" IDENTIFIED BY "<pass>";
FLUSH PRIVILEGES;
EXIT;

dependencies

dnf install php php-cli php-common php-mysql php-process php-devel php-gd php-mbstring php-xml php-opcache php-pecl-apcu python-pygments

php-fpm + nginx

Now we will get php working. We're going to use a fastcgi process manager called
php-fpm.

dnf install php-fpm

The default behavior of the php interpreter is to do its best to process the
file that is as near to the requested file as possible. This is a possible
security risk. We will change the default configuration so that the interpreter
will only process the exact file path.

vim /etc/php.ini
    # Find the line cgi.fix_pathinfo, uncomment, change to 0

By default php-fpm is listening on a socket, this is better (in our case) than
tcp because there is no overhead.

vim /etc/nginx/nginx.conf

`````
server {
    listen       443 ssl;
    listen       [::]:443 ssl;
    server_name  phabricator.domain.tld;
    root /opt/phacility/phabricator/webroot;

    ssl_certificate "/etc/pki/tls/certs/orolo.domain.tld/phabricator.domain.tld.crt";
    ssl_certificate_key "/etc/pki/tls/certs/orolo.domain.tld/phabricator.domain.tld.key";
    ssl_client_certificate "/etc/pki/tls/certs/orolo.domain.tld/ca.domain.tld.crt";
    ssl_verify_client on;

    ssl_session_cache shared:SSL:1m;
    ssl_session_timeout  10m;
    ssl_ciphers PROFILE=SYSTEM;
    ssl_prefer_server_ciphers on;

    access_log /var/log/nginx/phabricator.domain.tld.access.log main;

    index index.php;

    client_max_body_size 512m;

    location / {
        index index.php;
        rewrite ^/(.*)$ /index.php?__path__=/$1 last;
    }

    location ~ \.php$ {
        # SECURITY : Zero day Exploit Protection
        try_files $uri =404;
        # ENABLE : Enable PHP, listen fpm sock
        fastcgi_split_path_info ^(.+\.php)(/.+)$;
        fastcgi_pass unix:/run/php-fpm/www.sock;
        fastcgi_index index.php;
        include fastcgi.conf;


        #required if PHP was built with --enable-force-cgi-redirect
        fastcgi_param  REDIRECT_STATUS    200;

        #variables to make the $_SERVER populate in PHP
        fastcgi_param  SCRIPT_FILENAME    $document_root$fastcgi_script_name;
        fastcgi_param  QUERY_STRING       $query_string;
        fastcgi_param  REQUEST_METHOD     $request_method;
        fastcgi_param  CONTENT_TYPE       $content_type;
        fastcgi_param  CONTENT_LENGTH     $content_length;
        fastcgi_param  SCRIPT_NAME        $fastcgi_script_name;
        fastcgi_param  GATEWAY_INTERFACE  CGI/1.1;
        fastcgi_param  SERVER_SOFTWARE    nginx/$nginx_version;
        fastcgi_param  REMOTE_ADDR        $remote_addr;
    }
}
`````

firewall-cmd --permanent --zone=FedoraServer --add-port=443/tcp
firewall-cmd --zone=FedoraServer --list-all
firewall-cmd --complete-reload
firewall-cmd --zone=FedoraServer --list-all

phabricator

mkdir /opt/phacility
chown root:root /opt/phacility
chmod 755 /opt/phacility

cd /opt/phacility
git clone https://github.com/phacility/libphutil.git
cd /opt/phacility/libphutil
git checkout stable
git clone https://github.com/phacility/arcanist.git
cd /opt/phacility/arcanist
git checkout stable
git clone https://github.com/phacility/phabricator.git
cd /opt/phacility/phabricator
git checkout stable

mkdir /opt/phacility/files
chmod -R 777 /opt/phacility/files
mkdir /opt/phacility/git
chown -R phacility:phacility /opt/phacility/*    

semanage fcontext -a -s system_u -t httpd_sys_content_t "/opt/phacility(/.*)?"
semanage fcontext -a -t httpd_sys_rw_content_t "/opt/phacility/files(/.*)?"
restorecon -F -R -v /opt/phacility

su phacility  # because we want the /<phab>/config/local/local.json to be created with proper ownership
cd /opt/phacility/phabricator
./bin/config set mysql.user phabricator
./bin/config set mysql.pass <pass>
./bin/config set phabricator.base-uri 'https://phabricator.domain.tld/'
./bin/config storage.local-disk.path /opt/phacility/files/
exit # back to root

phabricator-phd

cd /opt/phacility/phabricator
./bin/config set phd.user phacility
vim /lib/systemd/system/phabricator-phd.service

```
[Unit]
Description=phabricator-phd
After=network.target nginx.service php-fpm.service mysqld.service

[Service]
User=phacility
Group=phacility
Type=oneshot
Environment="PATH=/sbin:/usr/sbin:/usr/local/sbin:/usr/local/bin:/usr/bin:/bin"
ExecStart=/opt/phacility/phabricator/bin/phd start
ExecStop=/opt/phacility/phabricator/bin/phd stop
RemainAfterExit=yes

[Install]
WantedBy=multi-user.target
```

systemctl start phabricator-phd.service
systemctl enable phabricator-phd.service

sshd + git

phabricator runs its own ssh daemon on 22 by default, which gives us that nice looking git@phabricator.domain.tld.

adduser --system git
visudo
    git ALL=(phacility) SETENV: NOPASSWD: /bin/git-upload-pack, /bin/git-receive-pack
cd /opt/phacility/phabricator
./bin/config set diffusion.ssh-user git
mkdir /opt/phacility/git
chown phacility:phacility /opt/phacility/git
./bin/config set repository.default-local-path /opt/phacility/git

cp /usr/lib/systemd/system/sshd.service /usr/lib/systemd/system/phabricator-sshd.service
vim /usr/lib/systemd/system/phabricator-sshd.service

```
[Unit]
Description=OpenSSH server daemon for Phabricator Diffusion
After=sshd.service

[Service]
EnvironmentFile=-/etc/sysconfig/sshd
ExecStart=/usr/sbin/sshd -D -f /etc/ssh/phabricator_sshd_config $OPTIONS
ExecReload=/bin/kill -HUP $MAINPID
KillMode=process
Restart=on-failure
RestartSec=42s

[Install]
WantedBy=multi-user.target
```

vim /etc/ssh/phabricator_sshd_config

```
AuthorizedKeysCommand /opt/phacility/phabricator-ssh-hook.sh
AuthorizedKeysCommandUser git
AllowUsers git

# You may need to tweak these options, but mostly they just turn off everything
# dangerous.

Port 22
Protocol 2

HostKey /etc/ssh/ssh_host_rsa_key
HostKey /etc/ssh/ssh_host_ecdsa_key
HostKey /etc/ssh/ssh_host_ed25519_key

PermitRootLogin no
AllowAgentForwarding no
AllowTcpForwarding no
PrintMotd no
PrintLastLog no
PasswordAuthentication no
AuthorizedKeysFile none

PidFile /var/run/phabricator-sshd.pid
```

semanage fcontext -a -s system_u -t etc_t /etc/ssh/phabricator_sshd_config
restorecon -F -v /etc/ssh/phabricator_sshd_config
mkdir /opt/phacility/sshd
vim /opt/phacility/sshd/phabricator-ssh-hook.sh

```
#!/bin/sh

# NOTE: Replace this with the username that you expect users to connect with.
VCSUSER="git"

# NOTE: Replace this with the path to your Phabricator directory.
ROOT="/opt/phacility/phabricator"

if [ "$1" != "$VCSUSER" ];
then
  exit 1
fi

exec "$ROOT/bin/ssh-auth" $@
```

chown -R root:root /opt/phacility/sshd
chmod -R 755 /opt/phacility/sshd

firewall-cmd --permanent --zone=FedoraServer --add-port=22/tcp
firewall-cmd --zone=FedoraServer --list-all
firewall-cmd --complete-reload
firewall-cmd --zone=FedoraServer --list-all

systemctl enable phabricator-sshd.service
systemctl start phabricator-sshd.service
systemctl restart phabricator-phd.service
systemctl restart php-fpm.service
systemctl restart nginx.service

SELinux

Phab has a lot of things going (especially due to that git hook), so we're going to ultimately rely on audit2allow to help us.

setenforce 0
getenforce  # verify

Now within phabricator create a repository, add you pub key, and clone that repository to your developmental machine.

Now back to the server

mkdir /opt/phacility/selinux
cd /opt/phacility/selinux
ausearch -m avc -ts recent | audit2allow -M phacility
semodule -i phacility.pp
setenforce 1
getenforce  # verify

You can examine the .te of that file to see what audit2allow produced.

upgrading

systemctl stop nginx.service
systemctl stop php-fpm.service
systemctl stop phabricator-phd.service
cd /opt/phacility/arcanist/
git pull
cd /opt/phacility/libphutil/
git pull
cd /opt/phacility/phabricator
git pull
./bin/storage upgrade --user phabricator --password <pass>
systemctl start nginx.service
systemctl start php-fpm.service
systemctl start phabricator-phd.service