feat: add outgoingrequests config keys for centralized SSRF protection

This commit is contained in:
kolaente 2026-03-23 16:08:10 +01:00 committed by kolaente
parent 654d2c7042
commit f96b53fe99
2 changed files with 44 additions and 3 deletions

View File

@ -978,17 +978,37 @@
{
"key": "proxyurl",
"default_value": "",
"comment": "The URL of [a mole instance](https://github.com/frain-dev/mole) to use to proxy outgoing webhook requests. You should use this and configure appropriately if you're not the only one using your Vikunja instance. More info about why: https://webhooks.fyi/best-practices/webhook-providers#implement-security-on-egress-communication. Must be used in combination with `webhooks.password` (see below)."
"comment": "Deprecated: use outgoingrequests.proxyurl instead. The URL of [a mole instance](https://github.com/frain-dev/mole) to use to proxy outgoing webhook requests. You should use this and configure appropriately if you're not the only one using your Vikunja instance. More info about why: https://webhooks.fyi/best-practices/webhook-providers#implement-security-on-egress-communication. Must be used in combination with `webhooks.password` (see below)."
},
{
"key": "proxypassword",
"default_value": "",
"comment": "The proxy password to use when authenticating against the proxy."
"comment": "Deprecated: use outgoingrequests.proxypassword instead. The proxy password to use when authenticating against the proxy."
},
{
"key": "allownonroutableips",
"default_value": "false",
"comment": "If set to true, webhook target URLs may resolve to non-globally-routable IP addresses (private networks, loopback, link-local, etc). When false (the default), Vikunja blocks outgoing webhook requests to these addresses to prevent SSRF attacks. Set this to true if you need webhooks to reach services on your internal network."
"comment": "Deprecated: use outgoingrequests.allownonroutableips instead. If set to true, webhook target URLs may resolve to non-globally-routable IP addresses (private networks, loopback, link-local, etc). When false (the default), Vikunja blocks outgoing webhook requests to these addresses to prevent SSRF attacks. Set this to true if you need webhooks to reach services on your internal network."
}
]
},
{
"key": "outgoingrequests",
"children": [
{
"key": "allownonroutableips",
"default_value": "false",
"comment": "If set to true, outgoing HTTP requests (webhooks, avatar downloads, migration imports) may resolve to non-globally-routable IP addresses. When false (the default), Vikunja blocks these to prevent SSRF attacks. Set to true only if you need these to reach services on your internal network."
},
{
"key": "proxyurl",
"default_value": "",
"comment": "The URL of a proxy to use for outgoing HTTP requests. Applies to webhooks, avatar downloads, and migration imports. Must be used with `outgoingrequests.proxypassword`."
},
{
"key": "proxypassword",
"default_value": "",
"comment": "The proxy password for authenticating against the proxy."
}
]
},

View File

@ -219,6 +219,10 @@ const (
WebhooksProxyPassword Key = `webhooks.proxypassword`
WebhooksAllowNonRoutableIPs Key = `webhooks.allownonroutableips`
OutgoingRequestsAllowNonRoutableIPs Key = `outgoingrequests.allownonroutableips`
OutgoingRequestsProxyURL Key = `outgoingrequests.proxyurl`
OutgoingRequestsProxyPassword Key = `outgoingrequests.proxypassword`
AutoTLSEnabled Key = `autotls.enabled`
AutoTLSEmail Key = `autotls.email`
AutoTLSRenewBefore Key = `autotls.renewbefore`
@ -472,11 +476,28 @@ func InitDefaultConfig() {
WebhooksEnabled.setDefault(true)
WebhooksTimeoutSeconds.setDefault(30)
WebhooksAllowNonRoutableIPs.setDefault(false)
// Outgoing Requests
OutgoingRequestsAllowNonRoutableIPs.setDefault(false)
// AutoTLS
AutoTLSRenewBefore.setDefault("720h") // 30days in hours
// Plugins
PluginsEnabled.setDefault(false)
PluginsDir.setDefault(ResolvePath("plugins"))
// Migrate deprecated webhook config keys to outgoingrequests.*
// This allows removing the old keys in a single place later.
if WebhooksAllowNonRoutableIPs.GetBool() && !OutgoingRequestsAllowNonRoutableIPs.GetBool() {
log.Warningf("Config key %q is deprecated and will be removed in a future release. Please use %q instead.", WebhooksAllowNonRoutableIPs, OutgoingRequestsAllowNonRoutableIPs)
OutgoingRequestsAllowNonRoutableIPs.Set("true")
}
if proxyURL := WebhooksProxyURL.GetString(); proxyURL != "" && OutgoingRequestsProxyURL.GetString() == "" {
log.Warningf("Config key %q is deprecated and will be removed in a future release. Please use %q instead.", WebhooksProxyURL, OutgoingRequestsProxyURL)
OutgoingRequestsProxyURL.Set(proxyURL)
}
if proxyPassword := WebhooksProxyPassword.GetString(); proxyPassword != "" && OutgoingRequestsProxyPassword.GetString() == "" {
log.Warningf("Config key %q is deprecated and will be removed in a future release. Please use %q instead.", WebhooksProxyPassword, OutgoingRequestsProxyPassword)
OutgoingRequestsProxyPassword.Set(proxyPassword)
}
}
// ResolvePath resolves a path relative to service.rootpath.