next -> main (1.0.0 milestone) #118
+3
-2
@@ -67,8 +67,9 @@ RUN adduser -D -H -s /sbin/nologin pixad && \
|
|||||||
mkdir -p /var/lib/pixa /etc/pixa && \
|
mkdir -p /var/lib/pixa /etc/pixa && \
|
||||||
chown pixad:pixad /var/lib/pixa
|
chown pixad:pixad /var/lib/pixa
|
||||||
|
|
||||||
# Copy default config (edit signing_key before use)
|
# Copy the image config; signing_key comes from PIXA_SIGNING_KEY.
|
||||||
COPY config.example.yml /etc/pixa/config.yml
|
# Mount a file over /etc/pixa/config.yml to override anything else.
|
||||||
|
COPY config.docker.yml /etc/pixa/config.yml
|
||||||
|
|
||||||
USER pixad
|
USER pixad
|
||||||
WORKDIR /var/lib/pixa
|
WORKDIR /var/lib/pixa
|
||||||
|
|||||||
@@ -15,14 +15,25 @@ git clone https://git.eeqj.de/sneak/pixa.git
|
|||||||
cd pixa
|
cd pixa
|
||||||
make build
|
make build
|
||||||
|
|
||||||
# run with a config file
|
# run with a config file: copy the example and set a real signing key
|
||||||
./bin/pixad --config config.example.yml
|
# (the example placeholder is refused at startup), e.g. with
|
||||||
|
# openssl rand -base64 32
|
||||||
|
cp config.example.yml config.yml
|
||||||
|
$EDITOR config.yml # replace the signing_key placeholder
|
||||||
|
./bin/pixad --config config.yml
|
||||||
|
|
||||||
# or build and run via Docker
|
# or build and run via Docker
|
||||||
make docker
|
make docker
|
||||||
docker run -p 8080:8080 pixad:latest
|
docker run -p 8080:8080 -e PIXA_SIGNING_KEY="$(openssl rand -base64 32)" pixa:latest
|
||||||
```
|
```
|
||||||
|
|
||||||
|
A container is configured two ways. The signing key comes from the
|
||||||
|
`PIXA_SIGNING_KEY` environment variable, which the baked-in config
|
||||||
|
reads; if it is unset the container exits at startup naming the
|
||||||
|
variable. Everything else uses built-in defaults, so to change any
|
||||||
|
other setting mount your own file over `/etc/pixa/config.yml` (see
|
||||||
|
`config.example.yml` for the full set of keys).
|
||||||
|
|
||||||
## Rationale
|
## Rationale
|
||||||
|
|
||||||
Image-heavy web applications need a fast, caching reverse proxy that
|
Image-heavy web applications need a fast, caching reverse proxy that
|
||||||
|
|||||||
@@ -0,0 +1,11 @@
|
|||||||
|
# Pixa configuration baked into the Docker image.
|
||||||
|
#
|
||||||
|
# The signing key is read from the PIXA_SIGNING_KEY environment
|
||||||
|
# variable; startup aborts naming it when it is unset. Every other key
|
||||||
|
# is omitted so its default applies. Operators who need more (an
|
||||||
|
# allowlist, metrics, and so on) mount their own file over
|
||||||
|
# /etc/pixa/config.yml.
|
||||||
|
|
||||||
|
signing_key: "${ENV:PIXA_SIGNING_KEY}"
|
||||||
|
state_dir: /var/lib/pixa
|
||||||
|
port: 8080
|
||||||
@@ -44,6 +44,12 @@ const (
|
|||||||
keyCacheMaxBytes = "cache_max_bytes"
|
keyCacheMaxBytes = "cache_max_bytes"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// placeholderSigningKey is the dummy signing_key shipped in
|
||||||
|
// config.example.yml. It is 45 characters, so it passes the length
|
||||||
|
// check, but it is public in this repository and must be rejected at
|
||||||
|
// startup so no deployment ever signs URLs with it.
|
||||||
|
const placeholderSigningKey = "CHANGE_ME_generate_with_openssl_rand_base64_32"
|
||||||
|
|
||||||
// Static validation errors. Each use site attaches the offending key
|
// Static validation errors. Each use site attaches the offending key
|
||||||
// and value by wrapping these with fmt.Errorf and %w.
|
// and value by wrapping these with fmt.Errorf and %w.
|
||||||
var (
|
var (
|
||||||
@@ -61,6 +67,9 @@ var (
|
|||||||
errPortOutOfRange = errors.New("outside the valid port range")
|
errPortOutOfRange = errors.New("outside the valid port range")
|
||||||
errTooFewConnections = errors.New("must be at least 1")
|
errTooFewConnections = errors.New("must be at least 1")
|
||||||
errValueTooShort = errors.New("value too short")
|
errValueTooShort = errors.New("value too short")
|
||||||
|
errPlaceholderKey = errors.New(
|
||||||
|
"is the placeholder from config.example.yml; " +
|
||||||
|
"generate a real key with: openssl rand -base64 32")
|
||||||
errMustBeSetTogether = errors.New("must be set together")
|
errMustBeSetTogether = errors.New("must be set together")
|
||||||
errMustNotBeNegative = errors.New("must not be negative")
|
errMustNotBeNegative = errors.New("must not be negative")
|
||||||
errOverflowsInt64 = errors.New("overflows a 64-bit integer")
|
errOverflowsInt64 = errors.New("overflows a 64-bit integer")
|
||||||
@@ -341,10 +350,10 @@ func (c *Config) ensureStateDirWritable() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// validate checks that all required configuration values are set and
|
// validateSigningKey checks that the signing key is present, long
|
||||||
// that every value is within its valid range.
|
// enough, and not the public placeholder from config.example.yml. The
|
||||||
func (c *Config) validate() error {
|
// key value itself is never echoed in error messages.
|
||||||
// The signing key value is never echoed in error messages.
|
func (c *Config) validateSigningKey() error {
|
||||||
if c.SigningKey == "" {
|
if c.SigningKey == "" {
|
||||||
return fmt.Errorf("config key %q: %w", keySigningKey, errValueRequired)
|
return fmt.Errorf("config key %q: %w", keySigningKey, errValueRequired)
|
||||||
}
|
}
|
||||||
@@ -356,6 +365,21 @@ func (c *Config) validate() error {
|
|||||||
keySigningKey, errValueTooShort, minKeyLength, len(c.SigningKey))
|
keySigningKey, errValueTooShort, minKeyLength, len(c.SigningKey))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if c.SigningKey == placeholderSigningKey {
|
||||||
|
return fmt.Errorf("config key %q: %w", keySigningKey, errPlaceholderKey)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// validate checks that all required configuration values are set and
|
||||||
|
// that every value is within its valid range.
|
||||||
|
func (c *Config) validate() error {
|
||||||
|
err := c.validateSigningKey()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
const maxPort = 65535
|
const maxPort = 65535
|
||||||
if c.Port < 1 || c.Port > maxPort {
|
if c.Port < 1 || c.Port > maxPort {
|
||||||
return fmt.Errorf("config key %q: value %d is %w 1-%d",
|
return fmt.Errorf("config key %q: value %d is %w 1-%d",
|
||||||
|
|||||||
@@ -303,6 +303,11 @@ func invalidHostAndCredentialCases() []abortCase {
|
|||||||
yaml: "signing_key: short\n",
|
yaml: "signing_key: short\n",
|
||||||
wantErrSubstrings: []string{keySigningKey},
|
wantErrSubstrings: []string{keySigningKey},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "signing_key is the documented placeholder",
|
||||||
|
yaml: "signing_key: " + placeholderSigningKey + "\n",
|
||||||
|
wantErrSubstrings: []string{keySigningKey},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: "signing_key missing",
|
name: "signing_key missing",
|
||||||
yaml: "port: 8080\n",
|
yaml: "port: 8080\n",
|
||||||
|
|||||||
Reference in New Issue
Block a user