Arch package guidelines/Security
This page describes security packaging guidelines for Arch Linux packages. For C/C++ projects the compiler and linker can apply security hardening options. Arch Linux applies PIE, Fortify source, stack protector, nx and relro by default.
Usage
Hardening protections can be reviewed by running checksec.
$ checksec --file=/usr/bin/cat
RELRO
RELRO is a generic mitigation technique to harden the data sections of an ELF binary/process. When a program is loaded several ELF memory sections need to be written to by the linker but can be turned read-only before turning control over to the program. This prevents attackers of overriding some ELF sections. There are two different RELRO modes:
- Partial RELRO (
-Wl,-z,relro
) some sections are marked as read-only after program load except the GOT (.got.plt
) is still writeable. - Full RELRO (
-Wl,-z,now
) during program load all dynamic symbols are resolved, allowing for the complete GOT to be marked read-only.
If an application reports partial relro, investigate if the build toolchain passes our LDFLAGS or allows overriding LDFLAGS. For Go packages investigate if the build method uses build.go
as pure golang Makefile replacement which does not allow passing of LDFLAGS.
Haskell
For Haskell it is not clear how to achieve Full RELRO at the moment.
Stack canary
A stack canary is added by the compiler between the buffer and control data on the stack. If this well known value is corrupted, a buffer overflow occurred and the running program segfaults to prevent possible arbitrary code execution.
The gcc package has it enabled stack protection by default with the --enable-default-ssp compile option.
NX
C/C++
Executable-space protection marks memory regions as non-executable, such that an attempt to execute machine code in these regions will cause an exception. It makes use of hardware features such as the NX bit (no-execute bit), or in some cases software emulation of those features.
PIE
C/C++
The gcc package has it enabled by default for C/C++ with --enable-default-pie.
Golang
Pass the following flags to go build
:
export GOFLAGS='-buildmode=pie' export CGO_CPPFLAGS="-D_FORTIFY_SOURCE=2" export CGO_LDFLAGS="-Wl,-z,relro,-z,now"
Haskell
Pass the following flag to runhaskell Setup.hs configure
:
--ghc-option='-pie'
RPATH/RUNPATH
RUNPATH/RPATH provides further search paths for the object it is listed in (it can be used both for executable and for shared objects).
$ objdump -x /usr/bin/perl | egrep 'RPATH|RUNPATH'
If the RPATH value contains a path within an attackers control it can possibly execute code by installing a malicious library in that directory for example CVE-2006-1566 CVE-2005-4280. See Debian:RpathIssue.
The RPATH entry is set by the linker by passing for example the following string to LDFLAGS -Wl,-rpath -Wl,/usr/local/lib
. To make an RUNPATH entry append --enable-new-dtags
to the linker flags.
FORTIFY
Fortify source is a macro that adds buffer overflow protection in various functions that perform operations on memory and strings. It checks whether an attacker tries to copy more bytes to overflow a buffer and then stops the execution of the program. This protection is enabled with the default CPPFLAGS
:
makepkg.conf
CPPFLAGS="-D_FORTIFY_SOURCE=2"
systemd services
If a systemd service file is shipped with the package due to upstream not providing any, look into applying the following systemd service hardening features. Systemd provides a way to analyse security features which are enabled for a service.
$ systemd-analyze security reflector.service
File access
A service can be hardened by restricting file system access.
Set up a new file system namespace for the executed process and mounts private /tmp
and var/tmp
directories inside it that is not shared by processes outside the namespace. Useful for programs which write data to /tmp
.
PrivateTmp=true
ProtectSystem has three different varieties of mounting directories as read-only for the executed process. The "full" option mounts /usr
, /boot
and /etc
read only. ProtectHome makes /home
, /root
and /run/user
inaccessible to the executed process.
ProtectSystem=strict ProtectHome=true
Sets up a new /dev
namespace for the executed process and only adds API pseudo devices such as /dev/null
, /dev/zero
or /dev/random
, but not for physical devices or system memory, system ports and others. This is useful to secure the execute process from writing directly to physical devices, systemd also adds a system call filter for calls within the @raw-io
set.
PrivateDevices=true
These options make the executed process unable to change kernel variables accessible through /proc/sys
, /sys
, etc. ProtectControlGroups makes the /sys/fs/cgroup
hierarchy read-only.
ProtectKernelTunables=true ProtectControlGroups=true
Making file paths inaccessible can be done as following:
InaccessiblePaths=/etc
More detailed information can be found in systemd.exec(5).
User
Ensure that the executed process and its children can never gain new privileges through execve(2).
NoNewPrivileges=true
Memory
Prohibit attempts to create memory mappings that are both writable and executable, to change mappings to be executable or to create executable shared memory. This sandboxes a process against allowing an attacker to write in to memory which is also executed. Note that enabling this is not compatible with all applications which rely on a JIT.
MemoryDenyWriteExecute=true
System calls
Locks down the personality(2) system call so that the kernel execution domain can not be changed.
LockPersonality=true
System calls can be restricted in a service as well, systemd can display syscalls to filter on:
$ systemd-analyze syscall-filter
Predefined groups are available, e.g. to use the recommended starting point for whitelisting system calls for system services use:
SystemCallFilter=@system-service
System calls can be restricted by their architecture such as to prevent 32-bit binaries from executing on 64-bit machines (no non-native binaries):
SystemCallArchitectures=native
Network
If the running process does not require any network access it can be fully disabled by setting up a new network namespace for the process and only configuration a loopback interface.
PrivateNetwork=true
If network is required, the type of address families used can be restricted for the socket(2) system call by for example only allowing UNIX sockets.
RestrictAddressFamilies=AF_UNIX
For when only network to localhost or specific IP ranges is required a process can be restricted by only allowing network access to localhost.
IPAddressAllow=localhost IPAddressDeny=any
More information about network filtering can be found in systemd.resource-control(5).
Various
Sets up a new UTS namespace for the execute process and disallows changing the hostname or domainname.
ProtectHostname=true