Security Implications of Keeping make and gcc on Production Servers: Best Practices for Deployment


2 views

Many developers face this critical infrastructure question: should build tools like make and gcc remain installed on production servers after deployment? While convenient for compiling applications directly on the server, this practice introduces potential security vectors that require careful consideration.

Maintaining build tools on production servers creates several attack surfaces:

1. Increased attack surface through:
   - Compiler vulnerabilities (e.g., GCC CVEs)
   - Makefile execution risks
   - Potential privilege escalation paths

2. Example exploit scenario:
   # Attacker could inject malicious code into Makefile
   all:
       $(shell rm -rf /important/data)

There are legitimate cases where keeping build tools makes sense:

# For dynamic module compilation (e.g., PHP extensions)
$ ./configure --with-php-config=/usr/bin/php-config
$ make && make install

# When using configuration management tools:
- Ansible: 
  yum:
    name: ["gcc", "make"]
    state: present

If you must keep build tools installed, implement these security measures:

# Example Dockerfile hardening:
FROM alpine:latest
RUN apk add --no-cache make gcc \
    && build-your-app \
    && apk del make gcc \
    && rm -rf /var/cache/apk/*

# Filesystem protections:
$ chattr +i /usr/bin/make
$ sudo chmod 750 /usr/bin/gcc

A more secure approach involves:

1. CI/CD Pipeline:
   - Build artifacts in isolated environments
   - Sign binaries with GPG
   - Deploy verified packages

2. Verification example:
   $ gpg --verify app.tar.gz.sig app.tar.gz
   $ sha256sum -c checksums.sha256

Consider these approaches to minimize risk:

# Use static binaries
$ CGO_ENABLED=0 go build -a -ldflags '-s' -o app

# Multi-stage Docker builds:
FROM golang:1.18 as builder
WORKDIR /app
COPY . .
RUN make

FROM scratch
COPY --from=builder /app/bin/app /app
CMD ["/app"]

Having build tools like make and gcc on production servers introduces several security considerations that operations teams must evaluate:

# Typical attack vectors when build tools are present:
1. Unauthorized code compilation (if attacker gains access)
2. Potential privilege escalation through Makefile exploits
3. Increased attack surface due to additional dependencies
4. Possibility of building malicious binaries directly on server

In 2021, a major cloud provider reported that 68% of compromised production servers had development tools installed unnecessarily. Attackers frequently:

  • Use make to execute arbitrary commands through crafted Makefiles
  • Compile local exploits using available compilers
  • Modify existing build processes to inject malicious code

For secure production environments, consider these approaches:

# Option 1: Build in CI/CD then deploy artifacts
docker build -t myapp .
docker save myapp > myapp.tar
scp myapp.tar production:/tmp/

# Option 2: Use multi-stage Docker builds
FROM gcc as builder
WORKDIR /app
COPY . .
RUN make

FROM alpine
COPY --from=builder /app/bin/myapp /usr/local/bin/
CMD ["myapp"]

If you absolutely need build tools in production, implement these security measures:

# Secure make usage example:
1. Set restrictive permissions:
   chmod 750 /usr/bin/make
   chmod 750 /usr/bin/gcc

2. Implement mandatory access control:
   # AppArmor example
   /usr/bin/make {
     /bin/* ix,
     /lib/* ix,
     /usr/lib/* ix,
     deny /root/**,
     deny /etc/** w
   }

For teams that need temporary build capabilities during deployment:

#!/bin/bash
# build_and_clean.sh

# Build the application
make release

# Deploy the binary
cp bin/myapp /opt/myapp/

# Remove build tools
if [ "$ENVIRONMENT" = "production" ]; then
    apt-get remove -y make gcc build-essential
    apt-get autoremove -y
    rm -rf /usr/include/*
fi

Implement these logging measures if build tools must remain:

# Auditd rules for make/gcc monitoring
-w /usr/bin/make -p x -k build_commands
-w /usr/bin/gcc -p x -k compiler_execution
-w /usr/bin/g++ -p x -k compiler_execution

Combine this with regular integrity checking of system binaries:

# Sample tripwire configuration
/usr/bin/make -> $(ReadOnly);
/usr/bin/gcc -> $(ReadOnly);
/usr/lib/gcc -> $(ReadOnly);