Difference between revisions of "Gitea Setup on Rocky10 with SELinux"

From Bitbull Wiki
Jump to navigation Jump to search
 
(4 intermediate revisions by the same user not shown)
Line 1: Line 1:
 +
=Setup=
 
* OS: Rocky Linux 10 minimal
 
* OS: Rocky Linux 10 minimal
 
* FQDN: gitea01.domain.tld
 
* FQDN: gitea01.domain.tld
  
=Setup=
 
 
==Disable SELinux==
 
==Disable SELinux==
 
For now, we set selinux to permissive, once all rules are set and we have no violations anymore, we set to enforce again
 
For now, we set selinux to permissive, once all rules are set and we have no violations anymore, we set to enforce again
Line 225: Line 225:
 
  dnf install -y policycoreutils-devel checkpolicy setroubleshoot-server setools-console
 
  dnf install -y policycoreutils-devel checkpolicy setroubleshoot-server setools-console
 
  > /var/log/audit/audit.log
 
  > /var/log/audit/audit.log
 +
touch /.autorelabel
 
  reboot
 
  reboot
 
  ssh -lroot $HOST -p222
 
  ssh -lroot $HOST -p222
Line 230: Line 231:
 
  mkdir -p /root/selinux/gitea
 
  mkdir -p /root/selinux/gitea
 
  cd /root/selinux/gitea
 
  cd /root/selinux/gitea
 +
 +
[[Category:Linux]]
 +
[[Category:SELinux]]
 +
[[Category:Security]]
  
 
==Generate policy default ruleset==
 
==Generate policy default ruleset==
Line 236: Line 241:
 
  for r in appstream baseos extras ; do dnf config-manager --set-enabled $r; done
 
  for r in appstream baseos extras ; do dnf config-manager --set-enabled $r; done
  
==Modify ruleset defaults
+
==Modify ruleset defaults==
 
Rename gitea_var_lib_t
 
Rename gitea_var_lib_t
 
  grep gitea_var_lib_t gitea*
 
  grep gitea_var_lib_t gitea*
Line 262: Line 267:
 
* build and load new rules
 
* build and load new rules
 
  bash gitea.sh
 
  bash gitea.sh
 +
 +
[[Category:Linux]]
 +
[[Category:SELinux]]
 +
[[Category:Security]]
  
 
==Apply new context lables==
 
==Apply new context lables==
Line 269: Line 278:
  
 
* Restart gitea  
 
* Restart gitea  
systemctl restart gitea
+
systemctl restart gitea
  
 
* Verify domain  
 
* Verify domain  
Line 292: Line 301:
 
   [...]
 
   [...]
 
</pre>
 
</pre>
 +
 +
[[Category:Linux]]
 +
[[Category:SELinux]]
 +
[[Category:Security]]
  
 
==Define additional SELinux rules==
 
==Define additional SELinux rules==
Line 322: Line 335:
 
  sed -i 's/^SELINUX=.*/SELINUX=enforcing/' /etc/selinux/config
 
  sed -i 's/^SELINUX=.*/SELINUX=enforcing/' /etc/selinux/config
 
  grubby --update-kernel ALL --remove-args selinux
 
  grubby --update-kernel ALL --remove-args selinux
 +
touch /.autorelabel
 
  reboot
 
  reboot
 
  sealert -a /var/log/audit/audit.log  
 
  sealert -a /var/log/audit/audit.log  
Line 351: Line 365:
 
ss -tan # port 2222 and 3000 are NOW listening!
 
ss -tan # port 2222 and 3000 are NOW listening!
 
</pre>
 
</pre>
 +
 +
[[Category:Linux]]
 +
[[Category:SELinux]]
 +
[[Category:Security]]
  
 
=Appendix=
 
=Appendix=

Latest revision as of 13:12, 12 July 2025

1 Setup

  • OS: Rocky Linux 10 minimal
  • FQDN: gitea01.domain.tld

1.1 Disable SELinux

For now, we set selinux to permissive, once all rules are set and we have no violations anymore, we set to enforce again

setenforce 0
sed -i 's/^SELINUX=.*/SELINUX=permissive/' /etc/selinux/config

1.2 VARS and Functions

HOST=$(hostname -f)

genpasswd() {
	local l=$1
       	[ "$l" == "" ] && l=20
      	tr -dc A-Za-z0-9_ < /dev/urandom | head -c ${l} | xargs
}

1.3 Install software

install reverse proxy and base software

dnf -y install openssl wget nginx git policycoreutils-python-utils


1.4 Move system sshd port to 222

semanage port -a -t ssh_port_t -p tcp 222
sed -i 's/^#Port 22/Port 222/' /etc/ssh/sshd_config
systemctl restart sshd

1.5 Configure Firewall

  • open https
firewall-cmd --permanent --add-port=443/tcp
  • open new sshd port
firewall-cmd --permanent --add-port=222/tcp
  • forward port 22 to gitea
firewall-cmd --permanent --add-forward-port=port=22:proto=tcp:toport=2222
  • load new rules
firewall-cmd --reload

1.6 Install/Configure Mariadb

  • install and enable
dnf install -y mariadb-server
systemctl enable --now mariadb
  • secure mariadb
mysql_secure_installation
  • create gitea database user
MARIADB_PW=$(genpasswd)
echo MARIADB_PW=$MARIADB_PW
mysql -u root -p <<EOF
CREATE DATABASE gitea CHARACTER SET 'utf8mb4' COLLATE 'utf8mb4_unicode_ci';
CREATE USER 'gitea'@'localhost' IDENTIFIED BY '$MARIADB_PW';
GRANT ALL PRIVILEGES ON gitea.* TO 'gitea'@'localhost';
FLUSH PRIVILEGES;
EOF

1.7 Install Gitea

  • create gitea user
useradd --system --shell /usr/bin/false --comment 'Git Version Control' --create-home --home-dir /var/lib/gitea git
mkdir -p /var/lib/gitea/data
mkdir -p /etc/gitea
chown -R git:git /var/lib/gitea
chown root:git /etc/gitea
chmod 770 /etc/gitea
  • Install gitea
GITEA_VERSION=1.23.8
wget -O /usr/local/bin/gitea https://dl.gitea.com/gitea/$GITEA_VERSION/gitea-$GITEA_VERSION-linux-amd64
chown root:git /usr/local/bin/gitea
chmod 750 /usr/local/bin/gitea
  • create systemd service
cat <<EOF >/etc/systemd/system/gitea.service
[Unit]
Description=Gitea (Git with a cup of tea)
After=syslog.target
After=network.target
Requires=mariadb.service

[Service]
RestartSec=2s
Type=simple
User=git
Group=git
WorkingDirectory=/var/lib/gitea/
ExecStart=/usr/local/bin/gitea web --config /etc/gitea/app.ini
Restart=always
Environment=USER=git HOME=/var/lib/gitea GITEA_WORK_DIR=/var/lib/gitea

[Install]
WantedBy=multi-user.target
EOF
  • create gitea config
cat <<EOF >/etc/gitea/app.ini
WORK_PATH = /var/lib/gitea/

[server]
APP_NAME = Gitea: Git with a cup of tea
RUN_USER = git
RUN_MODE = prod
HTTP_PORT = 3000
ROOT_URL = https://$HOST
SSH_DOMAIN = $HOST
START_SSH_SERVER = true
SSH_PORT         = 22
SSH_LISTEN_PORT  = 2222
OFFLINE_MODE = false

[database]
DB_TYPE = mysql
HOST = 127.0.0.1:3306
NAME = gitea
USER = gitea
PASSWD = $MARIADB_PW
SSL_MODE = disable

[security]
INSTALL_LOCK = true
SECRET_KEY = $(genpasswd 32)
INTERNAL_TOKEN = $(genpasswd 32)

[lfs]
PATH = /var/lib/gitea/data/lfs

[service]
DISABLE_REGISTRATION = true

[oauth2]
JWT_SECRET = $(genpasswd 32)
EOF
chown root:git /etc/gitea/app.ini
chmod 660 /etc/gitea/app.ini
systemctl daemon-reload
systemctl enable --now gitea


1.8 nginx configuration

openssl req -x509 -newkey rsa:4096 -nodes \
  -keyout /etc/pki/tls/private/gitea.key \
  -out /etc/pki/tls/certs/gitea.crt \
  -days 3650 -subj "/CN=$HOST"
chown root:root /etc/pki/tls/private/gitea.key
chmod 600 /etc/pki/tls/private/gitea.key
openssl dhparam -out /etc/nginx/dhparam.pem 4096
cat <<EOF >/etc/nginx/conf.d/gitea.conf
server {
    listen 443 ssl;
    server_name $HOST;

    ssl_certificate /etc/pki/tls/certs/gitea.crt;
    ssl_certificate_key /etc/pki/tls/private/gitea.key;
    ssl_dhparam /etc/nginx/dhparam.pem;
    ssl_protocols TLSv1.2;
    add_header Strict-Transport-Security "max-age=31536000; 
    includeSubDomains; preload" always;
    ssl_stapling on;
    ssl_stapling_verify on;
    ssl_session_cache shared:SSL:10m;
    ssl_session_timeout 10m;
    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_prefer_server_ciphers on;

    location / {
        proxy_pass http://127.0.0.1:3000;
        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;
    }
}
EOF
systemctl enable --now nginx

1.9 create gitea application admin

Change to git user, which has no sell as default

su -s /bin/bash git
genpasswd() {
  local l=$1
        [ "$l" == "" ] && l=20
        tr -dc A-Za-z0-9_ < /dev/urandom | head -c ${l} | xargs
 }

ADMIN_USER=$(genpasswd 8)
ADMIN_PW=$(genpasswd)

echo ADMIN_USER=$ADMIN_USER
echo ADMIN_PW=$ADMIN_PW
gitea admin user create --admin --username $ADMIN_USER --password "$ADMIN_PW" --email admin@domain.tld --config /etc/gitea/app.ini
exit

2 SELinux configuration

2.1 Install selinux tools as root

dnf install -y policycoreutils-devel checkpolicy setroubleshoot-server setools-console
> /var/log/audit/audit.log
touch /.autorelabel
reboot
ssh -lroot $HOST -p222
mkdir -p /root/selinux/gitea
cd /root/selinux/gitea

2.2 Generate policy default ruleset

for r in appstream baseos extras ; do dnf config-manager --set-disabled $r; done
sepolicy generate --init /usr/local/bin/gitea
for r in appstream baseos extras ; do dnf config-manager --set-enabled $r; done

2.3 Modify ruleset defaults

Rename gitea_var_lib_t

grep gitea_var_lib_t gitea*
sed -i 's/gitea_var_lib_t/gitea_data_t/g' gitea*
  • Hide permissive setting, we want to use it in enforcing mode
sed -i 's/^permissive/#permissive/' gitea.te
  • Add file context rule
echo '/etc/gitea(/.*)?                gen_context(system_u:object_r:gitea_conf_t,s0)' >> gitea.fc
  • vim gitea.te
# add in declaration
type gitea_conf_t;
files_type(gitea_conf_t)

type gitea_port_t;
corenet_port(gitea_port_t)
  • disable rpm building cmd
sed -i 's/^rpmbuild/#rpmbuild/' gitea.sh
  • build and load new rules
bash gitea.sh

2.4 Apply new context lables

  • Label ports
semanage port -a -t gitea_port_t -p tcp 2222
semanage port -a -t gitea_port_t -p tcp 3000
  • Restart gitea
systemctl restart gitea
  • Verify domain
 ps -ef -Z | grep gitea
   system_u:system_r:gitea_t:s0    git         1714       1 10 07:41 ?        00:00:01 /usr/local/bin/gitea web --config /etc/gitea/app.ini
  • verify port lables
semanage port --list | grep gitea
   gitea_port_t                   tcp      2222, 3000
  • verify file lables
ls -lZ /etc/gitea/app.ini /var/lib/gitea/data/
   -rw-rw----.  1 root git system_u:object_r:gitea_conf_t:s0  681 Jul 11 07:23 /etc/gitea/app.ini

   /var/lib/gitea/data/:
   drwxr-xr-x. 2 git git system_u:object_r:gitea_data_t:s0  6 Jul 11 07:23 actions_artifacts
   [...]

2.5 Define additional SELinux rules

  • Reset alerts and verify alerts
> /var/log/audit/audit.log
systemctl restart gitea
  • Now test the application with all its functions
  • verify new alerts
sealert -a /var/log/audit/audit.log 
grep 'run: sealert' /var/log/messages 
ausearch  --raw | audit2allow
  • Review, understand and then add this to gitea.te, gitea.fc, gitea.if
  • Then rebuild/install policy with `bash gitea.sh`
  • finally start over until you have no violations anymore

> /var/log/audit/audit.log reboot sealert -a /var/log/audit/audit.log grep 'run: sealert' /var/log/messages ausearch --raw | audit2allow ausearch -c 'git' --raw | audit2allow -R


3 ReEnable SELinux

Now we switch back to enforcing and test again

setenforce 1
sed -i 's/^SELINUX=.*/SELINUX=enforcing/' /etc/selinux/config
grubby --update-kernel ALL --remove-args selinux
touch /.autorelabel
reboot
sealert -a /var/log/audit/audit.log 

3.1 Test if rules are working as expected

chcon -t home_root_t /etc/gitea/app.ini
> /var/log/audit/audit.log
reboot
sealert -a /var/log/audit/audit.log
restorecon -FRv /etc/gitea/
> /var/log/audit/audit.log
reboot
sealert -a /var/log/audit/audit.log
semanage port -a -t ssh_port_t -p tcp 2222
semanage port -a -t ssh_port_t -p tcp 3000
> /var/log/audit/audit.log                
reboot                                    
sealert -a /var/log/audit/audit.log 
ss -tan # port 2222 and 3000 are not listening!
semanage port -a -t gitea_port_t -p tcp 2222
semanage port -a -t gitea_port_t -p tcp 3000
semanage port -l | grep gitea_
   gitea_port_t                   tcp      2222, 3000
> /var/log/audit/audit.log
reboot
sealert -a /var/log/audit/audit.log
ss -tan # port 2222 and 3000 are NOW listening!

4 Appendix

4.1 Final policy files

File: gitea.fc Modified: 2025-07-12 12:41:42

# Set the security context for the Gitea executable
/usr/local/bin/gitea		--	gen_context(system_u:object_r:gitea_exec_t,s0)
# Set the security context for Gitea data directories
/var/lib/gitea(/.*)?		gen_context(system_u:object_r:gitea_data_t,s0)
# Set the security context for Gitea configuration files
/etc/gitea(/.*)?		gen_context(system_u:object_r:gitea_conf_t,s0)

File: gitea.if Modified: 2025-07-12 12:43:07

# Interface to allow domain transition to gitea_t when executing gitea_exec_t
interface(`gitea_domtrans',`
	gen_require(`
		type gitea_t, gitea_exec_t;
	')

	corecmd_search_bin($1)
	domtrans_pattern($1, gitea_exec_t, gitea_t)
')

# Interface to allow execution of gitea_exec_t in the caller's domain
interface(`gitea_exec',`
	gen_require(`
		type gitea_exec_t;
	')

	corecmd_search_bin($1)
	can_exec($1, gitea_exec_t)
')

# Interface to allow searching of Gitea library directories
interface(`gitea_search_lib',`
	gen_require(`
		type gitea_data_t;
	')

	allow $1 gitea_data_t:dir search_dir_perms;
	files_search_var_lib($1)
')

# Interface to allow reading of Gitea library files
interface(`gitea_read_lib_files',`
	gen_require(`
		type gitea_data_t;
	')

	files_search_var_lib($1)
	read_files_pattern($1, gitea_data_t, gitea_data_t)
')

# Interface to allow management of Gitea library files
interface(`gitea_manage_lib_files',`
	gen_require(`
		type gitea_data_t;
	')

	files_search_var_lib($1)
	manage_files_pattern($1, gitea_data_t, gitea_data_t)
')

# Interface to allow management of Gitea library directories
interface(`gitea_manage_lib_dirs',`
	gen_require(`
		type gitea_data_t;
	')

	files_search_var_lib($1)
	manage_dirs_pattern($1, gitea_data_t, gitea_data_t)
')


# Interface to allow administration of a Gitea environment
interface(`gitea_admin',`
	gen_require(`
		type gitea_t;
		type gitea_data_t;
	')

	allow $1 gitea_t:process { signal_perms };
	ps_process_pattern($1, gitea_t)

    tunable_policy(`deny_ptrace',`',`
        allow $1 gitea_t:process ptrace;
    ')

	files_search_var_lib($1)
	admin_pattern($1, gitea_data_t)
	optional_policy(`
		systemd_passwd_agent_exec($1)
		systemd_read_fifo_file_passwd_run($1)
	')
')

File: gitea.te Modified: 2025-07-12 12:44:36

# Define the Gitea SELinux policy module version
policy_module(gitea, 1.0.0)

########################################
# Required types and classes for Gitea SELinux policy
require {
  type gitea_t;
  type gitea_data_t;
  type gitea_port_t;                      
  type gitea_conf_t;                               
  class dir search;
  class process setpgid;
  class file { getattr map open read execute execute_no_trans};
  class tcp_socket { accept bind listen name_bind name_connect connect create getattr getopt setopt read write};
  class udp_socket { connect create getattr setopt };
  type tmp_t;
  type httpd_t;
} 

type gitea_t;
type gitea_exec_t;
# Initialize the Gitea daemon domain
init_daemon_domain(gitea_t, gitea_exec_t)

# permissive gitea_t;

type gitea_data_t;
files_type(gitea_data_t)

type gitea_conf_t;
files_type(gitea_conf_t)

type gitea_port_t;
corenet_port(gitea_port_t)

########################################
# gitea policy definitions #############
# Allow Gitea to read and write to its own FIFO files
allow gitea_t self:fifo_file rw_fifo_file_perms;
# Allow Gitea to create and use Unix stream sockets
allow gitea_t self:unix_stream_socket create_stream_socket_perms;

# Allow Gitea to search its configuration directory
allow gitea_t gitea_conf_t:dir search;             
# Allow Gitea to get attributes, open, and read its configuration files
allow gitea_t gitea_conf_t:file { getattr open read };

# Allow HTTPD to connect to Gitea's TCP socket
allow httpd_t gitea_port_t:tcp_socket name_connect;

# Allow Gitea to manage its data directories
manage_dirs_pattern(gitea_t, gitea_data_t, gitea_data_t)
# Allow Gitea to manage its data files
manage_files_pattern(gitea_t, gitea_data_t, gitea_data_t)
# Allow Gitea to manage its symbolic link files
manage_lnk_files_pattern(gitea_t, gitea_data_t, gitea_data_t)
# Allow file transitions for Gitea in /var/lib
files_var_lib_filetrans(gitea_t, gitea_data_t, { dir file lnk_file })

# Allow Gitea to use interactive file descriptors
domain_use_interactive_fds(gitea_t)

# Allow Gitea to read files in /etc
files_read_etc_files(gitea_t)

# Allow Gitea to read localization files
miscfiles_read_localization(gitea_t)

# Define file transition patterns for Gitea
filetrans_pattern(gitea_t, gitea_data_t, gitea_data_t, file);
filetrans_pattern(gitea_t, gitea_data_t, gitea_data_t, dir);
filetrans_pattern(gitea_t, tmp_t, gitea_data_t, { file dir });

# Allow Gitea to map its data files
allow gitea_t gitea_data_t:file map;
# Allow Gitea to bind to its TCP socket
allow gitea_t gitea_port_t:tcp_socket name_bind;
# Allow Gitea to set process group ID
allow gitea_t self:process setpgid;
# Allow Gitea to perform operations on its TCP socket
allow gitea_t self:tcp_socket { accept bind listen read write};
# Allow Gitea to read the password file
auth_read_passwd_file(gitea_t)
# Allow Gitea to execute shell commands
corecmd_check_exec_shell(gitea_t)
# Allow Gitea to execute binaries
corecmd_exec_bin(gitea_t)
# Allow Gitea to bind to generic TCP nodes
corenet_tcp_bind_generic_node(gitea_t)
# Allow Gitea to read from sysfs
dev_read_sysfs(gitea_t)
# Allow Gitea to read network sysctl settings
kernel_read_net_sysctls(gitea_t)
# Allow Gitea to search network sysctl settings
kernel_search_network_sysctl(gitea_t)

# Allow Gitea to execute its own executable without transition
allow gitea_t gitea_exec_t:file execute_no_trans;
# Allow Gitea to execute its data files
allow gitea_t gitea_data_t:file execute;
# Allow Gitea to execute its data files without transition
allow gitea_t gitea_data_t:file execute_no_trans;
# Allow Gitea to connect to its TCP socket
allow gitea_t gitea_port_t:tcp_socket name_connect;  
# Allow Gitea to perform various operations on its TCP socket
allow gitea_t self:tcp_socket { connect create getattr getopt setopt };
# Allow Gitea to perform various operations on its UDP socket
allow gitea_t self:udp_socket { connect create getattr setopt };
# Allow Gitea to connect to HTTP ports
corenet_tcp_connect_http_port(gitea_t);
# Allow Gitea to connect to MySQL ports
corenet_tcp_connect_mysqld_port(gitea_t)
# Allow Gitea to read generic certificates
miscfiles_read_generic_certs(gitea_t);
# Allow Gitea to read system network configuration
sysnet_read_config(gitea_t);


4.2 Boolean Analysis: domain_can_mmap_files

  • Lets see what domain_can_mmap_files contains
getsebool -a | grep domain_can_mmap_files
  domain_can_mmap_files --> off


semanage boolean -l | grep domain_can_mmap_files
   domain_can_mmap_files          (off  ,  off)  Allow any process to mmap any file on system with attribute file_type.

sesearch -b domain_can_mmap_files -A
   allow domain file_type:blk_file map; [ domain_can_mmap_files ]:True
   allow domain file_type:chr_file map; [ domain_can_mmap_files ]:True
   allow domain file_type:file map; [ domain_can_mmap_files ]:True
   allow domain file_type:lnk_file map; [ domain_can_mmap_files ]:True