Skip to main content

Command Palette

Search for a command to run...

Practicas seguras en Docker

Updated
3 min read
Practicas seguras en Docker

La idea es ver algunas de las practicas frecuentes (y no tan frecuentes) para poder configurar aplicaciones en contenedores, la mayoria de estas ideas aplican a kubernetes tambien pero con diferente sintaxis!

Dockerfile

Tenemos nuestro dockerfile de ejemplo, el mismo es multistage para optimizar recursos, utiliza un usuario y grupo creado para no utilizar root una vez se ejecute.

FROM rust:slim-bookworm AS build

RUN USER=root cargo new --bin sample_app
WORKDIR /tmp/app

RUN apt-get update && apt-get install -y \
    build-essential \
    ## Add additional dependencies here
    && rm -rf /var/lib/apt/lists/*

COPY . .
RUN cargo build --release --bin sample_app

FROM rust:slim-bookworm AS runtime

WORKDIR /app
COPY --from=build /tmp/app/target/release/sample_app/app/sample_app

RUN groupadd user \
&& useradd -m -g user user \
&& chown -R user:user /app

USER user
ENTRYPOINT ["/app/sample_app"]

Buildtime

De aca mismo a nivel build utilizamos root para generar nuestro binario pero jamas deberiamos utilizar ese usuario en runtime, ahora despues de instalar las dependencias necesarias tambien podriamos eliminar recursos del sistema base (runtime) que no vamos a utilizar y vienen incluidos en la imagen base que utilicemos, si nuestro entorno lo permite podemos optar por https://github.com/GoogleContainerTools/distroless

Runtime

A nivel runtime optamos por crear un grupo y usuario el cual despues vamos a gestionar sus permisos a nivel apparmor, tambien podriamos hacerlo a nivel imagen en caso que gesitone archivos u demas.

Compose

En el caso de nuestro compose

services:
  sample_app:
    image: <your_image>
    security_opt:
      - apparmor:./non-root-apparmor.profile
      - no-new-privileges:true
    cap_drop: ["ALL"]
    networks:
      - sample_app

networks:
  sample_app:
    driver: bridge

Networks

Podemos crear una red para este servicio, el cual haria que no tenga acceso a otras redes de otros contenedores en la misma situacion, el caso de usar el modo bridge es por si necesitamos comunicar un contenedor con otro den tro del mismo stack (docker-compose.yml)

No-new-privileges

Esto evita que el usuario pueda escalar mas privilegios del que se le asigno al momento de crear la imagen https://raesene.github.io/blog/2019/06/01/docker-capabilities-and-no-new-privs

Cap_drop

Las capabilities son tema de linux propio, pero que podemos aplicar a los contenedores, tanto cap_drop como cap_add permiten agregar como quitar funcionabilidades a los procesos del sistema (en este caso del contenedor), inicialmente siempre se tienen que dropear todas para que este tenga los privilegios minimos y solo agregar lo necesario ej cap_add: ["CHOWN"] https://cheatsheetseries.owasp.org/cheatsheets/Docker_Security_Cheat_Sheet.html#rule-3-limit-capabilities-grant-only-specific-capabilities-needed-by-a-container

AppArmor

Mismo que las capabilities, vamos a gestionar de forma granular segmentar los permisos del sistema operativo, esto es un concepto propio de linux el cual aplica a los sistemas de docker, en el apparmor que cree estamos dando permisos de exec solamente al binario que yo puse dentro de la carpeta /app que es donde corre nuestra aplicacion.

include <abstractions/base>

/app {
    # Denegamos escalada de privilegios
    deny capability sys_admin,
    # Le damos exec al binario que creamos
    /app/sample_app ix,
}

Conclusion

Tenemos muchas formas de aislar recursos en docker, las mismas pueden variar por app por eso es importante hacer nuestro Thread Modeling para entender la superficie de ataque para entender cual puede ser nuestra prioridad en que recursos querer cuidar.

El repositorio utilizado para la demostracion es el siguiente https://github.com/jd-apprentice/mks