Skip to content

Commit

Permalink
Merge pull request #4369 from ucheNkadiCode/setFacl
Browse files Browse the repository at this point in the history
Setting File Access Lists
  • Loading branch information
Greg Van Liew authored Mar 5, 2021
2 parents 3fcecd5 + a5c6449 commit 2bb46cc
Show file tree
Hide file tree
Showing 4 changed files with 61 additions and 34 deletions.
2 changes: 1 addition & 1 deletion docs/containers/debug-python.md
Original file line number Diff line number Diff line change
Expand Up @@ -232,4 +232,4 @@ When you select **Docker: Add Docker Files to Workspace** for Django or Flask, w

Learn more about:

- [Configuring a non-root user in your container](/docs/containers/python-user-rights.md)
- [Configuring a non-root user in your container](/docs/containers/python-configure-containers.md)
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -1,15 +1,14 @@
---
Area: containers
TOCTitle: User Privileges in Python Containers
ContentId: 1ebbceb6-ae61-4b98-953d-0b18323becc4
PageTitle: User Privileges in Python Containers
DateApproved: 04/20/2020
PageTitle: Configure your Python containers
DateApproved: 03/03/2021
MetaDescription: How to setup a non-root user for VS Code Docker Extension
---

# User privileges in Python containers
# Configure your Python containers

When containerizing an application for production, your goal should be to port existing code into a separate runtime environment without introducing unforeseen security concerns. For this reason, you should select the default port for **Python: Django** (8000) and **Python: Flask** (5000) during execution of the **Add Dockerfiles to Workspace** command, or opt for a port **greater than** 1023. This will allow VS Code to configure the Dockerfile with non-root access and prevent a malicious user from elevating permissions in the container, ultimately [obtaining host machine root access](https://nvd.nist.gov/vuln/detail/CVE-2019-5736). When you choose **Python: General**, the Docker extension configures non-root access by default, since no port is chosen. However, in all cases, to use a non-root user within a container, you must ensure each resource your application needs to read or modify [can be accessed](#invalid-file-permissions).
When containerizing an application for production, your goal should be to port existing code into a separate runtime environment without introducing unforeseen security concerns. For this reason, we recommend selecting the default port for **Python: Django** (8000) or **Python: Flask** (5000) when executing the **Add Dockerfiles to Workspace** command, or opting for a port **greater than** 1023. This will allow VS Code to configure the Dockerfile with non-root access and prevent a malicious user from elevating permissions in the container, ultimately [obtaining host machine root access](https://nvd.nist.gov/vuln/detail/CVE-2019-5736). When you choose **Python: General**, there is no port selection, so the Docker extension configures non-root access by default. In all cases, you must ensure each resource (such as ports and files) modified or used by your application [can be accessed](#invalid-file-permissions) by a non-root user in your container.

If a user selects ports less than 1024 when adding Dockerfiles to workspace, by default, **we cannot** scaffold a Dockerfile that will run the container as a non-root user. This is because ports in this range are called **well-known** or **system** ports and must execute with root privileges in order to bind a network socket to an IP address.

Expand All @@ -31,7 +30,7 @@ If you chose **Python: General**, non-root privileges will be set up by default,

### Docker file changes

Within the Dockerfile, you must expose a non-system port, create a working directory for your app code, and then add a non-root user with access to the app directory. Lastly, ensure your exposed port **matches** the port binding of the Gunicorn command. The `CMD` command below configures Gunicorn for a Django container. For more information on configuring Gunicorn, refer to the documentation on [Gunicorn configuration for Django/Flask apps](/docs/containers/quickstart-python.md#gunicorn-modifications-for-djangoflask-apps).
Within the Dockerfile, you must expose a **non-system port**, create a working directory for your app code, and then add a non-root user with access to the app directory. Lastly, ensure your exposed port **matches** the port binding of the Gunicorn command. The `CMD` command below configures Gunicorn for a Django container. For more information on configuring Gunicorn, refer to the documentation on [Gunicorn configuration for Django/Flask apps](/docs/containers/quickstart-python.md#gunicorn-modifications-for-djangoflask-apps).

``` dockerfile
# 1024 or higher
Expand All @@ -44,7 +43,7 @@ EXPOSE 1024
WORKDIR /app
ADD . /app

# Switches to a non-root user and changes the ownership of the /app folder"
# Creates a non-root user and adds permission to access the /app folder
RUN useradd appuser && chown -R appuser /app
USER appuser

Expand All @@ -67,7 +66,7 @@ After choosing a non-system port and setting up the container to run as a non-ro
"python": {
"args": [
"runserver",
"0.0.0.0:1024", //Change the number after the colon
"0.0.0.0:1024", //<- Change the number after the colon
"--nothreading",
"--noreload"
],
Expand Down Expand Up @@ -96,7 +95,7 @@ After choosing a non-system port and setting up the container to run as a non-ro
"--no-debugger",
"--no-reload",
"--host", "0.0.0.0",
"--port", "1024" //Change this port number
"--port", "1024" //<- Change this port number
],
"module": "flask"
}
Expand All @@ -105,11 +104,11 @@ After choosing a non-system port and setting up the container to run as a non-ro

## Potential errors when running as a non-root user

Following the guide up to this point should eliminate most configuration issues caused by running as a non-root user. However, we have compiled a list (non-exhaustive) of common errors you may run into.
Following the guide up to this point should eliminate most configuration issues caused by running as a non-root user. However, we have compiled a non-exhaustive list of common errors you may run into.

If you encounter any other problems due to running as a non-root user, **please** report the issue in the [Docker Extension repository](https://github.com/microsoft/vscode-docker/issues/new). We love your feedback!

### Invalid file permissions
### Invalid file permissions in the container

If you are reading, writing, or creating a file within your container, a non-root user might not have access to folders or files in specific directories unless directly given.

Expand Down Expand Up @@ -137,41 +136,69 @@ Exception has occurred: PermissionError
To solve this issue, we need to correctly add permissions to the non-root user to gain access to this specific file or directory in the container. Within your Dockerfile, add:

```dockerfile
# Creates a non-root user and adds permission to access the /app folder
RUN useradd appuser && chown -R appuser /app

# Adds permission to appuser (non-root) for access to the /extra folder
# Adds permission for appuser (non-root) to access the /extra folder
RUN chown -R appuser /extra
```

> **Note**: This is just one example of how to add permissions. There are many ways to do so and it is your responsibility give the least permission possible to specific files and folders.
> **Note**: This is just one example of how to add permissions in a container. There are many ways to do so, and it is your responsibility give the least permission possible to specific files and folders.
### Binding to an inaccessible port
### Invalid file permission on the host (Linux)

If your container starts and stops immediately after `kb(workbench.action.debug.start)` without producing logs in the **Debug Console**, we can try to diagnose the issue through these steps:
In the previous example, we showed you how to add permissions to a file or folder on the container as a non-root user. However, if you are trying to access a folder **on the host machine** from within the container as a non-root user, the user ID or group ID in the container must have access to the files on the host. To solve this issue in Linux, you might need to set file access control lists (setfacl).

If you have a folder named `/share` on your host machine and try to access this folder before the access control list are properly set, you will likely receive this error:

```python
PermissionError: [Errno 13] Permission denied: '/share/logs/log.txt'
```

In order to give access to a non-root user `appuser` from within the container, follow these steps:

1. From the container command line, run one of these commands:

```powershell
# To find a specific user's UID
id -u appuser
# Or to find a specific user's GID
id -g username
```
1. Copy the output from the container command line.
1. From the **host machine's** command line, run one of these commands:
```powershell
# Example of giving a User ID with the value of 5678 access to the /share folder on the host machine
setfacl -m u:5678:rwx /share
# Example of giving a Group ID with the value of 6789 access to the /share folder on the host machine
setfacl -m g:6789:rwx /share
```
### Binding to a low-range port
If you hit `kb(workbench.action.debug.start)` to start your container and it immediately stops without producing any logs in the **Debug Console**, this error could mean you are exposing a system-port (ports less than 1024) while attempting to run as a non-root user. This may be hard to catch because, by default, containers are removed after debugging is stopped. To diagnose this port error, follow these steps:
1. Open and modify your `launch.json` file:
```json
{
"name": "Docker: Python - Django",
"name": "Docker: {Configuration Name}",
"type": "docker",
"request": "launch",
"preLaunchTask": "docker-run: debug",
"removeContainerAfterDebug": false, //add this line
"python": {
"pathMappings": [
{
"localRoot": "${workspaceFolder}",
"remoteRoot": "/app"
}
],
"projectType": "django"
}
"removeContainerAfterDebug": false, //<- add this line
// ... the rest of the launch configuration
}
```
1. Hit `kb(workbench.action.debug.start)` to run your container again.
1. After the container exits once more, navigate to the Docker Extension, right-click the container, and select **View Logs**.
2. Hit `kb(workbench.action.debug.start)` to run your container again.
3. After the container exits once more, navigate to the Docker Extension, right-click the container, and select **View Logs**.
![User clicking view logs on their container](images/quickstarts/python-user-rights-view-logs.png)
Expand All @@ -184,6 +211,6 @@ In a Flask app, you may see the error:
> self.socket.bind(self.server_address)
> PermissionError: [Errno 13] Permission denied
This likely means you are exposing a system-port (ports less than 1024) while attempting to run as a non-root user. This incompatible configuration is demonstrated in the image above.
The image above is a problematic configuration because a port **less than** 1024 was selected.
To solve this issue, modify your Dockerfile and `tasks.json` file in the manner shown [above](#running-your-containerized-app-as-a-nonroot-user).
To solve this issue, modify your Dockerfile and `tasks.json` file in the manner shown [here](#running-your-containerized-app-as-a-nonroot-user).
2 changes: 1 addition & 1 deletion docs/containers/quickstart-python.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ After verifying your app runs properly, you can now Dockerize your application.

>**Tip**: You may also enter the path to a folder name as long as this folder includes a `__main__.py` file.
1. If **Python: Django** or **Python: Flask** was selected, specify app port for local development. Django defaults to port 8000 while Flask defaults to port 5000; however, any unused port will work. We recommend selecting port 1024 or above to mitigate security concerns from [running as a root user](/docs/containers/python-user-rights.md).
1. If **Python: Django** or **Python: Flask** was selected, specify app port for local development. Django defaults to port 8000, while Flask defaults to port 5000; however, any unused port will work. We recommend selecting port 1024 or above to mitigate security concerns from [running as a root user](/docs/containers/python-configure-containers.md).

1. With all of this information, the Docker extension creates the following files:

Expand Down

0 comments on commit 2bb46cc

Please sign in to comment.