Add more imagemagick security configuration

Remove default security policy, since it is overridden anyway
Update default value for max_area
Inherit animation frame count configuration for list-length
Add release document
This commit is contained in:
asonix 2024-02-05 14:30:01 -06:00
parent 0007649b68
commit 4a7a51d147
11 changed files with 169 additions and 45 deletions

View file

@ -47,6 +47,9 @@ proxy = "7d"
max_width = 10000
max_height = 10000
max_area = 40000000
memory = 256
map = 512
disk = 1024
[media.image]
max_width = 10000

View file

@ -8,7 +8,6 @@ RUN \
chown -R app:app /mnt
COPY pict-rs /usr/local/bin/pict-rs
COPY docker/prod/root/ /
USER app
EXPOSE 6669

View file

@ -2,7 +2,7 @@ version: '3.3'
services:
pictrs:
image: asonix/pictrs:0.4
image: asonix/pictrs:0.5
ports:
- "127.0.0.1:8080:8080"
restart: always

View file

@ -1,21 +0,0 @@
<policymap>
<policy domain="resource" name="memory" value="256MiB" />
<policy domain="resource" name="list-length" value="128" />
<policy domain="resource" name="width" value="10KP" />
<policy domain="resource" name="height" value="10KP" />
<policy domain="resource" name="map" value="512MiB" />
<policy domain="resource" name="area" value="40MP" />
<policy domain="resource" name="disk" value="1GiB" />
<policy domain="resource" name="file" value="768" />
<policy domain="resource" name="thread" value="2" />
<policy domain="path" rights="none" pattern="@*" />
<policy domain="coder" rights="none" pattern="*" />
<policy domain="coder" rights="read | write" pattern="{APNG,AVIF,GIF,HEIC,JPEG,JSON,JXL,PNG,WEBP,MP4,WEBM,TMP,PAM}" />
<policy domain="delegate" rights="none" pattern="*" />
<policy domain="delegate" rights="execute" pattern="ffmpeg" />
<policy domain="filter" rights="none" pattern="*" />
<policy domain="module" rights="none" pattern="*" />
<policy domain="module" rights="read | write" pattern="{APNG,AVIF,GIF,HEIC,JPEG,JSON,JXL,PNG,WEBP,TMP,PAM,PNM,VIDEO}" />
<!-- indirect reads not permitted -->
<policy domain="system" name="precision" value="6" />
</policymap>

View file

@ -47,8 +47,6 @@
tokio-console
];
MAGICK_CONFIGURE_PATH = ./docker/prod/root/usr/lib/ImageMagick-7.1.1/config-Q16HDRI;
RUST_SRC_PATH = "${pkgs.rust.packages.stable.rustPlatform.rustLibSrc}";
};
});

View file

@ -267,16 +267,36 @@ max_width = 10000
# process the image.
max_height = 10000
## Optional: maximum area, in pixels, of media that imagemagick will attempt to process
## Optional: maximum area, in pixels, of media that imagemagick will keep in memory at a time
# environment variable: PICTRS__MEDIA__MAGICK__MAX_HEIGHT
# default: 40_000_000
# default: 20_000
#
# This value should be at least as large as the greatest max_area set for images, animations, and
# videos. Any image that exceeds this limit will cause imagemagick to abort processing, which could
# lead to less clear errors, especially on image upload. In order for pict-rs to return helpful
# validation errors for images that don't meet other requirements, imagemagick must be allowed to
# process the image.
max_area = 40000000
# Images that exceed this area will have their pixels cached to disk
max_area = 20000
## Optional: maximum size, in MiB, that imagemagick is allowed to allocate in memory to store pixels
## while processing media.
# environment variable: PICTRS__MEDIA__MAGICK__MEMORY
# default: 256
#
# If this limit is exceeded, imagemagick will fall back to memory-mapped disk for storing pixels
memory = 256
## Optional: maximim size, in MiB, that imagemagick is allowed to allocate in memory-mapped disk to
## store pixels while processing media
# environment variable: PICTRS__MEDIA__MAGICK__MAP
# default: 512
#
# If this limit is exceeded, imagemagick will fall back to unmapped disk for storing pixels
map = 512
## Optional: maximum size, in MiB, that imagemagick is allowed to allocate on disk to store pixels
## while processing media
# environment variable: PICTRS__MEDIA__MAGICK__DISK
# default: 1024
#
# If this limit is exceeded, media processing will be aborted
disk = 1024
[media.image]

76
releases/0.5.6.md Normal file
View file

@ -0,0 +1,76 @@
# pict-rs 0.5.6
## Overview
pict-rs 0.5.6 adds more configuration for imagemagick security policies and updates the default
value for `max_area`
### Features
- [More Imagemagick Configuration](#more-imagemagick-configuration)
### Changes
- [Imagemagick Area Defaults](#imagemagick-area-defaults)
- [Imagemagick Frame Configuration](#imagemagick-frame-configuration)
## Upgrade Notes
There's no significant changes from 0.5.5, so upgrading should be as simple as pulling a new version
of pict-rs.
## Descriptions
### More Imagemagick Configuration
Three new configuration values have been added to the imagemagick security configuration for
pict-rs: `memory`, `map`, and `disk`. These options describe sizes for three tiers of storage that
imagemagick is allowed to use when processing media. The first is `memory`, this is a simple value
that represents how much RAM imagemagick is allowed to use to store image pixels. If this size is
exceeded, it will start using the next tier of storage for image pixels, which is `map`. `map`
represents space on disk that's mapped into RAM for quicker access. Since it's disk-backed, it can
be larger than `memory`. Finally, if `map` is exceeded, imagemagick will start using the `disk` for
storing pixels without mapping into memory. If the `disk` size is exceeded, media processing is
aborted.
The configuration for these values can be set via the pict-rs.toml file, via environment variables,
or via the commandline.
```toml
# pict-rs.toml
# values are in MiB
[media.magick]
memory = 256
map = 512
disk = 1024
```
```bash
# environment variables
# values are in MiB
PICTRS__MEDIA__MAGICK__MEMORY=256
PICTRS__MEDIA__MAGICK__MAP=512
PICTRS__MEDIA__MAGICK__DISK=1024
```
```bash
# commandline
# values are in MiB
pict-rs run \
--media-magick-memory 256 \
--media-magick-map 512 \
--media-magick-disk 1024
```
### Imagemagick Area Defaults
The default value for `max_area` has been decreased from 40 million to 20 thousand. The reason for
this is it doesn't impose a hard limit on the area of uploaded images, it instead imposes a limit on
how much of an image can be held in memory at a time, with the rest of the image residing on disk.
### Imagemagick Frame Configuration
Imagemagick now inherits pict-rs' animation `max_frame_count` value to set it's maximum
`list-length`, which should allow longer animations to be configured.

View file

@ -70,6 +70,9 @@ impl Args {
media_magick_max_width,
media_magick_max_height,
media_magick_max_area,
media_magick_memory,
media_magick_map,
media_magick_disk,
media_image_max_width,
media_image_max_height,
media_image_max_area,
@ -144,6 +147,9 @@ impl Args {
max_width: media_magick_max_width,
max_height: media_magick_max_height,
max_area: media_magick_max_area,
memory: media_magick_memory,
map: media_magick_map,
disk: media_magick_disk,
};
let image_quality = ImageQuality {
@ -658,12 +664,22 @@ struct Magick {
max_height: Option<u16>,
#[serde(skip_serializing_if = "Option::is_none")]
max_area: Option<u32>,
#[serde(skip_serializing_if = "Option::is_none")]
memory: Option<u32>,
#[serde(skip_serializing_if = "Option::is_none")]
map: Option<u32>,
#[serde(skip_serializing_if = "Option::is_none")]
disk: Option<u32>,
}
impl Magick {
fn set(self) -> Option<Self> {
let any_set =
self.max_width.is_some() || self.max_height.is_some() || self.max_area.is_some();
let any_set = self.max_width.is_some()
|| self.max_height.is_some()
|| self.max_area.is_some()
|| self.memory.is_some()
|| self.map.is_some()
|| self.disk.is_some();
if any_set {
Some(self)
@ -1033,9 +1049,22 @@ struct Run {
/// The maximum height, in pixels, for uploaded media that imagemagick will attempt to process
#[arg(long)]
media_magick_max_height: Option<u16>,
/// The maximum area, in pixels, for uploaded media that imagemagick will attempt to process
/// The maximum area, in pixels, for uploaded media that imagemagick will keep in memory at a
/// time. Larger images will be cached to disk.
#[arg(long)]
media_magick_max_area: Option<u32>,
/// The maximum size, in MiB, that imagemagick is allowed to use to store pixels in memory. If
/// this limit is exceeded the pixels are moved to memory-mapped disk
#[arg(long)]
media_magick_memory: Option<u32>,
/// The maximum size, in MiB, that imagemagick is allowed to use to store pixels in
/// memory-mapped disk. If this limit is exceeded the pixels are moved to unmapped disk
#[arg(long)]
media_magick_map: Option<u32>,
/// The maximum size, in MiB, that imagemagick is allowed to use to store pixels on disk. If
/// this limit is exceeded, the media processing will abort.
#[arg(long)]
media_magick_disk: Option<u32>,
/// The maximum width, in pixels, for uploaded images
#[arg(long)]

View file

@ -96,6 +96,9 @@ struct MagickDefaults {
max_width: u16,
max_height: u16,
max_area: u32,
memory: u32,
map: u32,
disk: u32,
}
#[derive(Clone, Debug, serde::Serialize)]
@ -287,7 +290,10 @@ impl Default for MagickDefaults {
Self {
max_width: 10_000,
max_height: 10_000,
max_area: 40_000_000,
max_area: 20_000,
memory: 256,
map: 512,
disk: 1024,
}
}
}

View file

@ -230,6 +230,12 @@ pub(crate) struct Magick {
pub(crate) max_height: u16,
pub(crate) max_area: u32,
pub(crate) memory: u32,
pub(crate) map: u32,
pub(crate) disk: u32,
}
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]

View file

@ -258,29 +258,37 @@ fn generate_policy(media: &Media) -> String {
let width = media.magick.max_width;
let height = media.magick.max_height;
let area = media.magick.max_area;
let memory = media.magick.memory;
let map = media.magick.map;
let disk = media.magick.disk;
let frames = media.animation.max_frame_count;
let timeout = media.process_timeout;
format!(
r#"<policymap>
<policy domain="resource" name="width" value="{width}" />
<policy domain="resource" name="height" value="{height}" />
<policy domain="resource" name="area" value="{area}" />
<policy domain="resource" name="width" value="{width}P" />
<policy domain="resource" name="height" value="{height}P" />
<policy domain="resource" name="area" value="{area}P" />
<policy domain="resource" name="list-length" value="{frames}" />
<policy domain="resource" name="time" value="{timeout}" />
<policy domain="resource" name="memory" value="256MiB" />
<policy domain="resource" name="list-length" value="128" />
<policy domain="resource" name="map" value="512MiB" />
<policy domain="resource" name="disk" value="1GiB" />
<policy domain="resource" name="memory" value="{memory}MiB" />
<policy domain="resource" name="map" value="{map}MiB" />
<policy domain="resource" name="disk" value="{disk}MiB" />
<policy domain="resource" name="file" value="768" />
<policy domain="resource" name="thread" value="2" />
<policy domain="cache" name="memory-map" value="anonymous"/>
<policy domain="cache" name="synchronize" value="true"/>
<policy domain="path" rights="none" pattern="@*" />
<policy domain="coder" rights="none" pattern="*" />
<policy domain="coder" rights="read | write" pattern="{{APNG,AVIF,GIF,HEIC,JPEG,JSON,JXL,PNG,WEBP,MP4,WEBM,TMP,PAM}}" />
<policy domain="delegate" rights="none" pattern="*" />
<policy domain="delegate" rights="execute" pattern="ffmpeg" />
<policy domain="delegate" rights="execute" pattern="FFMPEG" />
<policy domain="filter" rights="none" pattern="*" />
<policy domain="module" rights="none" pattern="*" />
<policy domain="module" rights="read | write" pattern="{{APNG,AVIF,GIF,HEIC,JPEG,JSON,JXL,PNG,WEBP,TMP,PAM,PNM,VIDEO}}" />
<!-- indirect reads not permitted -->
<policy domain="system" name="max-memory-request" value="256MiB"/>
<policy domain="system" name="memory-map" value="anonymous"/>
<policy domain="system" name="precision" value="6" />
</policymap>"#
)