Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(adr): Docker container guidelines #220

Merged
merged 1 commit into from
Oct 26, 2020

Conversation

brian-intel
Copy link
Contributor

ADR: Docker container guidelines
close #206

Signed-off-by: Brian McGinn [email protected]

PR Checklist

Please check if your PR fulfills the following requirements:

PR Type

What kind of change does this PR introduce?
New ADR for docker security guidelines

  • Refactoring (no functional changes, no api changes)
  • Build related changes
  • CI related changes
  • Documentation content changes
  • Other... Please describe:

What is the current behavior?

Current docker container deployments lack updated security guidelines

Issue Number: 206

What has been updated?

Are there any specific instructions or things that should be known prior to reviewing?

Other information


## containers for the edge

what's the standard for edgex containers
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add a references section. Include the following URLs:

Also provide cross-references for each recommendation above.


When deploying Docker images, the following flags should be set for heightened security.

- To avoid escalation of privileges each docker container should use the `--security-opt=no-new-privileges` flag. More details about this flag can be found [here](https://docs.docker.com/engine/reference/run/#security-configuration)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

EdgeX has no control over the user's docker daemon. We are only allowed to exercise control via Dockerfile or docker-compose. In this case, we want

    security_opt:
      - "no-new-privileges:true"


- To avoid escalation of privileges each docker container should use the `--security-opt=no-new-privileges` flag. More details about this flag can be found [here](https://docs.docker.com/engine/reference/run/#security-configuration)

- To further prevent privilege escalation attacks the user should be set for the docker container using the `--user=<userid>` or `-u=<userid>` flag. More details about this flag can be found [here](https://docs.docker.com/engine/reference/run/#user)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

EdgeX does not launch containers via docker run CLI. Rewrite to focus on what is available in the docker-compose.

Please also provide guidance on what user ID's will be used in the docker-compose.


- To avoid a faulty or compromised containers from consuming excess amounts of the host of its resources `resource limits` should be set for each container. more details about `resource limits` can be found [here](https://docs.docker.com/config/containers/resource_constraints/)

- To avoid attackers from writing data to the containers and modifying their files the `--read_only` flag should be set. More details about this flag can be found [here](https://docs.docker.com/compose/compose-file/#domainname-hostname-ipc-mac_address-privileged-read_only-shm_size-stdin_open-tty-user-working_dir)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Apply to docker-compose not docker run.

- To avoid attackers from writing data to the containers and modifying their files the `--read_only` flag should be set. More details about this flag can be found [here](https://docs.docker.com/compose/compose-file/#domainname-hostname-ipc-mac_address-privileged-read_only-shm_size-stdin_open-tty-user-working_dir)

> NOTE: exception
If a container is required to have write permission to function, then this flag will not work. For example, the vault requires write permission to write secrets. In this case the `--read_only` flag will not be used.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Update technical explanation. Vault needs to run setcap in order to lock pages in memory, but will otherwise run fine if memory page locking is disabled. In many cases, mounting a writable docker volume in a strategic place will resolve the issue.

@jpwhitemn jpwhitemn added the ADR Architecture Decision Record label Sep 1, 2020
Copy link
Contributor

@jim-wang-intel jim-wang-intel left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some minor changes needed

jim-wang-intel
jim-wang-intel previously approved these changes Sep 11, 2020
Copy link
Contributor

@jim-wang-intel jim-wang-intel left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

bnevis-i
bnevis-i previously approved these changes Sep 11, 2020
lenny-goodell
lenny-goodell previously approved these changes Sep 11, 2020
Copy link
Member

@lenny-goodell lenny-goodell left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@jamesrgregg jamesrgregg self-requested a review September 16, 2020 16:56
jamesrgregg
jamesrgregg previously approved these changes Sep 16, 2020
Copy link

@jamesrgregg jamesrgregg left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM - we should be able to validate the configuration within a running Jenkins Pipeline using Docker Security Bench. Just wondering if this was already looked at during the work completed here.

Copy link
Member

@jpwhitemn jpwhitemn left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this document is good - but to clarify, this is just guidance (correct??). Is there any of this that we plan to incorporate into our Docker Compose files?

With regard to the no-new-privileges options, I think we should note that this may be more difficult when dealing with a device-service that may require privilege access in order to get connected to drivers/etc for sensors/devices.

@bnevis-i
Copy link
Collaborator

I think this document is good - but to clarify, this is just guidance (correct??). Is there any of this that we plan to incorporate into our Docker Compose files?

The intent is to use this ADR to justify a number of the items in the security backlog that will implement these recommendations. So yes, there are active, in-process plans to incorporate this guidance.

With regard to the no-new-privileges options, I think we should note that this may be more difficult when dealing with a device-service that may require privilege access in order to get connected to drivers/etc for sensors/devices.

No-new-privileges is used to block things like "sudo" in a container. If a container starts with privilege, then no-new-privileges won't prevent the container from retaining privilege. So if there was a device driver that needed root privilege to talk, it could still specify no-new-privilege and start as root.

@jpwhitemn jpwhitemn self-requested a review September 16, 2020 18:53
jpwhitemn
jpwhitemn previously approved these changes Sep 16, 2020
Copy link
Member

@tonyespy tonyespy left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Overall, the recommendations seem fine, but I did have a few comments/questions.

Two other global comments, it seems odd that this ADR falls under system-management, as it's more about deployment than management. Second, maybe I misunderstood, but I thought we sequentially number all ADRs, is it really the case that we number them in series per domain? This doesn't seem to be the case with Core, which only has a single ADR which is 0003:

https://github.com/edgexfoundry/edgex-docs/blob/master/docs_src/design/adr/core/0003-V2-API-Principles.md

services:
device-virtual:
image: ${REPOSITORY}/docker-device-virtual-go${ARCH}:${DEVICE_VIRTUAL_VERSION}
user: 4000:4000 # user option using an unprivileged user
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are there any guidelines as to what UID ranges should be used? On many distros system user UIDs (i.e. used by services) range from 100-999, and regular user UIDs 1000+.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Docker user NS remapping, when enabled, usually uses a very high user range.

I am a proponent of giving each service its own UID (maybe == its port number) so that the secret store tokens can be individually ACLed.

> NOTE: exception
Sometimes containers will require root access to perform their fuctions. For example the System Management Agent requires root access to control other Docker containers. In this case you would allow it run as default root user.

- To avoid a faulty or compromised containers from consuming excess amounts of the host of its resources `resource limits` should be set for each container. More details about `resource limits` can be found [here](https://docs.docker.com/config/containers/resource_constraints/). This follows Rule #7 for Docker security found [here](https://cheatsheetseries.owasp.org/cheatsheets/Docker_Security_Cheat_Sheet.html#rule-7-limit-resources-memory-cpu-file-descriptors-processes-restarts).
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this ADR give some recommendations as to what these resource limitations should be? I also imagine the limits might be different for some of our runtime dependencies (e.g. kong, redis, ...).

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't think we can as they will be system and use case dependent.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we're not going to provide any guidance (e.g. minimum memory requirement), then we should add some verbiage that this is an exercise left to the end-user base on system resources and use cases.

Also I'll note that the docker resource constraints link above is for the docker command itself, not docker-compose. Does compose support YAML specified resource constraints?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does compose support YAML specified resource constraints?

https://docs.docker.com/compose/compose-file/#resources

If a container is required to have write permission to function, then this flag will not work. For example, the vault needs to run setcap in order to lock pages in memory. In this case the `--read_only` flag will not be used.

> NOTE: alternatives
If writing non-persistent data is required (ex. a config file) then a temporary filesystem mount can be used to accomplish this goal while still enforcing `--read_only`. Mounting a `tmpfs` in Docker gives the container a temporary location in the host systems memory to modify files. This location will be removed once the container is stopped. More details about `tmpfs` can be found [here](https://docs.docker.com/storage/tmpfs/)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What about our persistence later (e.g. redis)?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agree with Tony. Should also mention plain 'old volumes for normal persistence needs.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Interestingly, Kubernetes might be an issue here. It just doesn't support volumes in the normal way that Docker does.


When deploying Docker images, the following flags should be set for heightened security.

- To avoid escalation of privileges each docker container should use the `no-new-privileges` option in their Docker compose file (example below). More details about this flag can be found [here](https://docs.docker.com/engine/reference/run/#security-configuration). This follows Rule #4 for Docker security found [here](https://cheatsheetseries.owasp.org/cheatsheets/Docker_Security_Cheat_Sheet.html#rule-4-add-no-new-privileges-flag).
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We've also had instances of device services using the --privileged flag in order to access DBus and/or actual hardware devices. It'd be nice if this was called out explicitly as not allowed. In addition, we should provide some guidelines as to how to properly allow hardware access (e.g. writing custom AppArmor rules) from within a container.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@tonyespy Jim White I believe commented on this earlier. I will repeat what I said before.

no-new-privileges doesn't prevent one from running privileged containers. What it prevents is tools like "sudo" from running in a container thus a going from user mode to root mode. It also prevents capabilities defined on extended file attributes from being effected. (For example, the capability to bind to a low-numbered port.).

Docker-compose v3 doesn't allow one to customize cgroups like the v2 did. For example, you can no longer pass in device_cgroup_rules. So if you are accessing a device that isn't in docker's built-in allow list, privileged mode is one way to allow device access to continue to function. (Curious myself if this is the only way, or maybe we should downrev the docker-compose to v2... hmmm.)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @bnevis-i. Two additional points...

  • The device-bluetooth-c (still in holding) device service recommends running the service using docker run --privileged, and on systems where AppArmor is supported, includes an example of how to pass an AppArmor profile to docker run. Do you know if compose v2 also allowed AppArmor profiles to be specified?
  • Frankly, if we're going to suggest that someone run a service with --privileged, I'm not sure why we're even bothering to run the service in a container in the first place.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@brian-intel

Are you up to adding a recommendation to prefer

security_opt: [ "apparmor:unconfined" ]

or a custom apparmor profile vs running --privileged

This doesn't negate the guidance regarding no-new-privileges, but it does add additional (and worthy) guidance to not use --privileged at all.

cloudxxx8
cloudxxx8 previously approved these changes Oct 21, 2020
Copy link
Member

@cloudxxx8 cloudxxx8 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM
Thanks, this is helpful

@jamesrgregg
Copy link

Specific Docker Security Bench checks as referenced in this ADR are:
2 - Docker daemon configuration
2.8 Enable user namespace support (Scored)
Same as Rule #2 - https://cheatsheetseries.owasp.org/cheatsheets/Docker_Security_Cheat_Sheet.html#rule-2-set-a-user

2.17 Ensure containers are restricted from acquiring new privileges (Scored)
Same as Rule #4 - https://cheatsheetseries.owasp.org/cheatsheets/Docker_Security_Cheat_Sheet.html#rule-4-add-no-new-privileges-flag

4 - Container Images and Build File
ADR does not specifically call out a need to run specific checks for root vs. non-root. The only call out is that this note.
NOTE: exception Sometimes containers will require root access to perform their fuctions. For example the System Management Agent requires root access to
control other Docker containers. In this case you would allow it run as default root user.

5 - Container Runtime
5.10 Ensure that the memory usage for containers is limited (Scored)
Same as Rule #7 - https://cheatsheetseries.owasp.org/cheatsheets/Docker_Security_Cheat_Sheet.html#rule-7-limit-resources-memory-cpu-file-descriptors-processes-restarts

5.12 Ensure that the container's root filesystem is mounted as read only
(Scored)
Same as Rule #8 - https://cheatsheetseries.owasp.org/cheatsheets/Docker_Security_Cheat_Sheet.html#rule-8-set-filesystem-and-volumes-to-read-only

@bnevis-i
Copy link
Collaborator

bnevis-i commented Oct 21, 2020

I think we should start with the following checks and known exceptions:

check_4_1,check_5_3,check_5_4,check_5_5,check_5_6,check_5_7,check_5_9,check_5_12,check_5_15,check_5_16,check_5_19,check_5_20,check_5_21,check_5_24,check_5_25,check_5_29,check_5_30,check_5_31

Exclusions:
* Capabilities added: CapAdd=[IPC_LOCK] to edgex-vault
* Container running with root FS mounted R/W: edgex-vault
* Docker socket shared: edgex-sys-mgmt-agent

2.8 Enable user namespace support (Scored)
Same as Rule #2 - https://cheatsheetseries.owasp.org/cheatsheets/Docker_Security_Cheat_Sheet.html#rule-2-set-a-user

We have to to exclude all of section 2 because EdgeX doesn't install or configure the users's docker daemon.

2.17 Ensure containers are restricted from acquiring new privileges (Scored)

We can't check this at daemon level, but we can code it in using security_opt (no checker for it).

4 - Container Images and Build File

Suggest we run check 4.1 (the only scored checker in the section) and add exceptions for the containers requiring root.
Right now this would be all of them, but we can remove the exceptions as we implement the non-root enhancements.

5.10 Ensure that the memory usage for containers is limited (Scored)
Same as Rule #7 - https://cheatsheetseries.owasp.org/cheatsheets/Docker_Security_Cheat_Sheet.html#rule-7-limit-resources-memory-cpu-file-descriptors-processes-restarts

Suggest hold off adding the checker until we have syntax in the docker-compose to set limits.

5.12 Ensure that the container's root filesystem is mounted as read only
(Scored)
Same as Rule #8 - https://cheatsheetseries.owasp.org/cheatsheets/Docker_Security_Cheat_Sheet.html#rule-8-set-filesystem-and-volumes-to-read-only

Good check. One exception so far.

@bnevis-i
Copy link
Collaborator

@jamesrgregg

Current output of the above proposal.

# ------------------------------------------------------------------------------
# Docker Bench for Security v1.3.4
#
# Docker, Inc. (c) 2015-
#
# Checks for dozens of common best-practices around deploying Docker containers in production.
# Inspired by the CIS Docker Community Edition Benchmark v1.1.0.
# ------------------------------------------------------------------------------

Initializing Wed Oct 21 21:46:55 UTC 2020

[WARN] 4.1  - Ensure a user for the container has been created
[WARN]      * Running as root: edgex-kuiper
[WARN]      * Running as root: edgex-device-virtual
[WARN]      * Running as root: edgex-device-rest
[WARN]      * Running as root: edgex-app-service-configurable-rules
[WARN]      * Running as root: edgex-sys-mgmt-agent
[WARN]      * Running as root: edgex-core-command
[WARN]      * Running as root: edgex-core-data
[WARN]      * Running as root: edgex-core-metadata
[WARN]      * Running as root: edgex-support-scheduler
[WARN]      * Running as root: edgex-support-notifications
[WARN]      * Running as root: edgex-redis
[WARN]      * Running as root: edgex-vault-worker
[WARN]      * Running as root: edgex-vault
[WARN]      * Running as root: edgex-core-consul
[WARN]      * Running as root: kong-db
[WARN] 5.3  - Ensure Linux Kernel Capabilities are restricted within containers
[WARN]      * Capabilities added: CapAdd=[IPC_LOCK] to edgex-vault
[PASS] 5.4  - Ensure privileged containers are not used
[PASS] 5.5  - Ensure sensitive host system directories are not mounted on containers
[PASS] 5.6  - Ensure ssh is not run within containers
[PASS] 5.7  - Ensure privileged ports are not mapped within containers
[PASS] 5.9  - Ensure the host's network namespace is not shared
[WARN] 5.12  - Ensure the container's root filesystem is mounted as read only
[WARN]      * Container running with root FS mounted R/W: edgex-kuiper
[WARN]      * Container running with root FS mounted R/W: edgex-device-virtual
[WARN]      * Container running with root FS mounted R/W: edgex-vault
[PASS] 5.15  - Ensure the host's process namespace is not shared
[PASS] 5.16  - Ensure the host's IPC namespace is not shared
[PASS] 5.19  - Ensure mount propagation mode is not set to shared
[PASS] 5.20  - Ensure the host's UTS namespace is not shared
[PASS] 5.21  - Ensure the default seccomp profile is not Disabled
[PASS] 5.24  - Ensure cgroup usage is confirmed
[WARN] 5.25  - Ensure the container is restricted from acquiring additional privileges
[WARN]      * Privileges not restricted: edgex-kuiper
[WARN]      * Privileges not restricted: edgex-device-virtual
[WARN]      * Privileges not restricted: edgex-device-rest
[WARN]      * Privileges not restricted: edgex-app-service-configurable-rules
[WARN]      * Privileges not restricted: edgex-sys-mgmt-agent
[WARN]      * Privileges not restricted: edgex-core-command
[WARN]      * Privileges not restricted: edgex-core-data
[WARN]      * Privileges not restricted: edgex-core-metadata
[WARN]      * Privileges not restricted: edgex-support-scheduler
[WARN]      * Privileges not restricted: edgex-support-notifications
[WARN]      * Privileges not restricted: edgex-redis
[WARN]      * Privileges not restricted: edgex-vault-worker
[WARN]      * Privileges not restricted: kong
[WARN]      * Privileges not restricted: edgex-vault
[WARN]      * Privileges not restricted: edgex-core-consul
[WARN]      * Privileges not restricted: kong-db
[WARN]      * Privileges not restricted: edgex-secrets-setup
[PASS] 5.29  - Ensure Docker's default bridge docker0 is not used
[PASS] 5.30  - Ensure the host's user namespaces is not shared
[WARN] 5.31  - Ensure the Docker socket is not mounted inside any containers
[WARN]      * Docker socket shared: edgex-sys-mgmt-agent

[INFO] Checks: 19
[INFO] Score: 6

@jamesrgregg
Copy link

Thank you @bnevis-i
These checks are exactly what I was hoping for as we have a working demo of this running on the EdgeX Sandbox and can demo with these checks next week.

@bnevis-i
Copy link
Collaborator

@brian-intel Please change the commit message to "feat(adr):" instead of "ADR:"

@bnevis-i bnevis-i changed the title ADR: Docker container guidelines feat(adr): Docker container guidelines Oct 26, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
ADR Architecture Decision Record
Projects
None yet
Development

Successfully merging this pull request may close these issues.

ADR for docker image guidelines
8 participants