Store server IPs in one form (no brackets, short lower-case IPv6) and
write each address in the form its context needs:
- URLs and host:port (Sentinel, database URLs, SFTP backup download,
DNS servers, SMTP, webhook base URLs, links, health checks) get
brackets through formatHostForUrl().
- Traefik Host() rules use the address without brackets, so IPv6
domains no longer return 404; saved labels are fixed at deploy.
Caddy keeps the brackets.
- SSH keeps the plain address.
- Hetzner gives an IPv6 /64 network; IPv6-only servers now get the
server address (prefix::1) instead of the unusable prefix::64.
- sslip labels, registry image names with an IPv6 host, the API domain
list, allowlist matching and duplicate checks handle IPv6.
A migration normalizes stored IPv6 server addresses, repairs Hetzner
servers saved as ::64 that are not reachable, and adds brackets to
saved Sentinel URLs.
Accept HTTP(S), git:// and SCP-style SSH repository URLs whose host is an
IP address, including bracketed IPv6 such as git@[2a01:4f8::1]:org/repo.git.
The clone runs on the user's deployment server, so the IP block did not
protect Coolify, and SSH URLs with IPv4 already passed.
Loopback, link-local and other reserved addresses are still rejected as
internal hosts. Brackets are allowed only around a valid IPv6 host.
New or changed manual webhook secrets (GitHub, GitLab, Bitbucket,
Gitea) must have at least 16 characters, in the Webhooks page and in
the application API. Stored secrets keep working: an unchanged value
passes, and an empty value still disables the webhook. Each secret
field gets a Generate button that fills a random 40-character secret;
the user saves it after adding it to the Git provider.
Git source HTTP clients previously disabled redirects entirely, so API
calls for renamed repositories (e.g. GitHub 301 to /repositories/{id})
failed. Add SafeExternalUrl::sameOriginRedirectOptions() and use it in
the GitSource HTTP macro to follow up to five strict redirects that stay
on the same scheme, host and port.
A redirect to a different origin gets a RequestException, because the
DNS pin covers only the original host and port. This blocks SSRF via
redirects to metadata or attacker hosts. Authorization headers and the
original request method and body are kept on same-origin redirects.
Tests cover following same-host redirects, keeping POST on a redirect,
refusing cross-host/scheme/port redirects and the five-redirect cap.
Since GitHub App and GitLab API calls use the outbound URL guard,
GitHub Enterprise or GitLab on a private network failed with "Webhook
URL resolved to an unsafe IP address" unless an admin allow-listed it.
On self-hosted instances, Git source URLs and requests now allow
private (RFC 1918), CGNAT (100.64/10, Tailscale), and IPv6 unique local
addresses, plus internal hostnames such as .internal, .local, and
container names. Loopback, localhost, link-local (cloud metadata),
0.0.0.0, and other reserved targets stay blocked. Redirects stay off
and DNS stays pinned. Coolify Cloud and all other outbound URLs keep
the strict rules.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
S3-compatible and legacy buckets may include uppercase letters. Relax the bucket name pattern, validation messages, and tests so those names are accepted while still rejecting invalid characters and formats.
Centralize scp-style Git URL parsing so user@host:path (including custom
usernames and embedded ports) is accepted and converted to HTTPS for
public clones, API create, webhooks, validation, and commit/branch links.
libcurl overrides an existing host:port DNS cache entry each time a new
one is added, so one entry per address left only the last pinned. That is
IPv6 whenever the target has AAAA records, which broke every request on
hosts without IPv6. Join them into one comma separated entry instead.
S3 backup uploads fail when the endpoint has an AAAA record: mc parses
--resolve values with netip.ParseAddr and rejects the bracketed IPv6
form. Its resolver map also keeps only one IP per host, so emit a single
entry, preferring IPv4, without brackets.
Split V5 dashboard behavior into domain controllers and policies,
add agent token rotation/revocation, status reconciliation jobs,
ingress firewall syncing, and canvas connection APIs.
Add migrations for V5 status tracking, server capabilities, resource
connection aliases, and revoked agent tokens.
Reuse the existing SafeWebhookUrl rule on the S3 Storage endpoint field so
the create and edit forms go through the same URL-normalization path as
webhook settings. Adds a matching guard inside S3Storage::testConnection()
so background callers (scheduled backups, database import reuse) also
validate the endpoint before building the S3 client.
Also fixes an IPv6-bracket edge case in SafeWebhookUrl so `http://[::1]`
style hosts are normalized before the filter_var IP check — the rule's
own loopback test was already asserting this behaviour.
Add SafeWebhookUrl validation rule to notification webhook URL fields
(Slack, Discord, custom webhook) to enforce safe URL patterns including
scheme validation and hostname checks.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add SafeExternalUrl validation rule that ensures URLs point to
publicly-routable hosts. Apply to all GitHub source entry points
(Livewire Create, Livewire Change, API create and update).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Normalize hostnames to lowercase for RFC 1123 compliance while accepting uppercase input
- Expand NAME_PATTERN to allow parentheses, hash, comma, colon, and plus characters
- Add fallback to random name generation when application name doesn't meet minimum requirements
- Add comprehensive test coverage for validation patterns and edge cases
Update server limit enforcement to re-enable force-disabled servers when the
team is at or under its limit (`<= 0` condition).
Improve allowlist validation and matching by:
- supporting IPv6 CIDR mask ranges up to `/128`
- adding IPv6-aware CIDR matching in `checkIPAgainstAllowlist`
- normalizing/deduplicating redundant allowlist entries before saving
Add feature tests for `ServerLimitCheckJob` covering under-limit, at-limit,
over-limit, and no-op scenarios.
Adds a new Laravel validation rule to prevent path traversal, hidden files, and invalid filenames in the dynamic proxy configuration feature. Validates filenames to ensure they contain only safe characters, don't exceed filesystem limits, and don't use reserved names.
- New Rule: ValidProxyConfigFilename with comprehensive validation
- Updated: NewDynamicConfiguration to use the new rule
- Added: 13 unit tests covering all validation scenarios
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Refactored DockerImage component to use separate properties for image name, tag, and SHA256 digest.
- Introduced DockerImageFormat validation rule to enforce correct image format.
- Updated DockerImageParser to handle new parsing logic for image tags and SHA256 hashes.
- Enhanced UI to separate input fields for image name, tag, and SHA256 digest, improving user experience.
- Added comprehensive tests for DockerImageParser to ensure accurate parsing and validation of image formats.
- Introduced `ValidGitRepositoryUrl` and `ValidGitBranch` validation rules to ensure safe and valid input for Git repository URLs and branch names.
- Updated relevant Livewire components and API controllers to utilize the new validation rules, enhancing security against command injection and invalid inputs.
- Refactored existing validation logic to improve consistency and maintainability across the application.