Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Provide configuration to allow camo-media proxying #12802

Merged
merged 13 commits into from
Mar 29, 2022
7 changes: 7 additions & 0 deletions custom/conf/app.example.ini
Original file line number Diff line number Diff line change
Expand Up @@ -423,6 +423,13 @@ INTERNAL_TOKEN=
;; Cache successful token hashes. API tokens are stored in the DB as pbkdf2 hashes however, this means that there is a potentially significant hashing load when there are multiple API operations.
;; This cache will store the successfully hashed tokens in a LRU cache as a balance between performance and security.
;SUCCESSFUL_TOKENS_CACHE_SIZE = 20
;;
;; Use a camo image proxy - leave empty to not use
lunny marked this conversation as resolved.
Show resolved Hide resolved
;CAMO_SERVER_URL =
;; HMAC to encode urls with
;CAMO_HMAC_KEY =
;; Set to true to use camo for https too lese only non https urls are proxyed
;CAMO_ALLWAYS = false

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
Expand Down
5 changes: 4 additions & 1 deletion docs/content/doc/advanced/config-cheat-sheet.en-us.md
Original file line number Diff line number Diff line change
Expand Up @@ -513,7 +513,10 @@ Certain queues have defaults that override the defaults set in `[queue]` (this o
- spec - use one or more special characters as ``!"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~``
- off - do not check password complexity
- `PASSWORD_CHECK_PWN`: **false**: Check [HaveIBeenPwned](https://haveibeenpwned.com/Passwords) to see if a password has been exposed.
- `SUCCESSFUL_TOKENS_CACHE_SIZE`: **20**: Cache successful token hashes. API tokens are stored in the DB as pbkdf2 hashes however, this means that there is a potentially significant hashing load when there are multiple API operations. This cache will store the successfully hashed tokens in a LRU cache as a balance between performance and security.
- `SUCCESSFUL_TOKENS_CACHE_SIZE`: **20**: Cache successful token hashes. API tokens are stored in the DB as pbkdf2 hashes however, this means that there is a potentially significant hashing load when there are multiple API operations. This cache will store the successfully hashed tokens in a LRU cache as a balance between performance and security.
- `CAMO_SERVER_URL`: **<empty>**: If you would like to use a camo proxy to proxy images from rendered content, set the camo server url here
lunny marked this conversation as resolved.
Show resolved Hide resolved
- `CAMO_HMAC_KEY`: **<empty>**: Provide the HMAC key for encoding urls
- `CAMO_ALLWAYS`: **false**: Set to true to use camo for https too lese only non https urls are proxyed

## OpenID (`openid`)

Expand Down
45 changes: 45 additions & 0 deletions modules/markup/camo.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
// Copyright 2022 The Gitea Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.

package markup

import (
"crypto/hmac"
"crypto/sha1"
"encoding/base64"
"net/url"
"strings"

"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/util"
)

// CamoEncode encodes a lnk to fit with the go-camo and camo proxy links
wxiaoguang marked this conversation as resolved.
Show resolved Hide resolved
func CamoEncode(link string) string {
if strings.HasPrefix(link, setting.CamoServerURL) {
return link
}

mac := hmac.New(sha1.New, []byte(setting.CamoHMACKey))
_, _ = mac.Write([]byte(link)) // hmac does not return errors
macSum := b64encode(mac.Sum(nil))
encodedURL := b64encode([]byte(link))

return util.URLJoin(setting.CamoServerURL, macSum, encodedURL)
}

func b64encode(data []byte) string {
return strings.TrimRight(base64.URLEncoding.EncodeToString(data), "=")
}

func camoHandleLink(link string) string {
if setting.CamoEnabled {
lnkURL, err := url.Parse(link)
if err == nil && lnkURL.IsAbs() && !strings.HasPrefix(link, setting.AppURL) &&
(setting.CamoAllways || lnkURL.Scheme != "https") {
return CamoEncode(link)
}
}
return link
}
44 changes: 44 additions & 0 deletions modules/markup/camo_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
// Copyright 2022 The Gitea Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.

package markup

import (
"testing"

"code.gitea.io/gitea/modules/setting"
"github.com/stretchr/testify/assert"
)

func TestCamoHandleLink(t *testing.T) {
setting.AppURL = "https://gitea.com"
// Test media proxy
setting.CamoEnabled = true
setting.CamoServerURL = "https://image.proxy"
setting.CamoHMACKey = "geheim"

assert.Equal(t,
"https://gitea.com/img.jpg",
camoHandleLink("https://gitea.com/img.jpg"))
assert.Equal(t,
"https://testimages.org/img.jpg",
camoHandleLink("https://testimages.org/img.jpg"))
assert.Equal(t,
"https://image.proxy/eivin43gJwGVIjR9MiYYtFIk0mw/aHR0cDovL3Rlc3RpbWFnZXMub3JnL2ltZy5qcGc",
camoHandleLink("http://testimages.org/img.jpg"))

setting.CamoAllways = true
assert.Equal(t,
"https://gitea.com/img.jpg",
camoHandleLink("https://gitea.com/img.jpg"))
assert.Equal(t,
"https://image.proxy/tkdlvmqpbIr7SjONfHNgEU622y0/aHR0cHM6Ly90ZXN0aW1hZ2VzLm9yZy9pbWcuanBn",
camoHandleLink("https://testimages.org/img.jpg"))
assert.Equal(t,
"https://image.proxy/eivin43gJwGVIjR9MiYYtFIk0mw/aHR0cDovL3Rlc3RpbWFnZXMub3JnL2ltZy5qcGc",
camoHandleLink("http://testimages.org/img.jpg"))

// Restore previous settings
setting.CamoEnabled = false
}
1 change: 1 addition & 0 deletions modules/markup/html.go
Original file line number Diff line number Diff line change
Expand Up @@ -386,6 +386,7 @@ func visitNode(ctx *RenderContext, procs, textProcs []processor, node *html.Node

attr.Val = util.URLJoin(prefix, attr.Val)
}
attr.Val = camoHandleLink(attr.Val)
node.Attr[i] = attr
}
} else if node.Data == "a" {
Expand Down
15 changes: 15 additions & 0 deletions modules/setting/setting.go
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,10 @@ var (
PasswordHashAlgo string
PasswordCheckPwn bool
SuccessfulTokensCacheSize int
CamoEnabled bool
CamoServerURL string
CamoHMACKey string
CamoAllways bool

// UI settings
UI = struct {
Expand Down Expand Up @@ -914,6 +918,17 @@ func loadFromConf(allowEmpty bool, extraConfig string) {
PasswordCheckPwn = sec.Key("PASSWORD_CHECK_PWN").MustBool(false)
SuccessfulTokensCacheSize = sec.Key("SUCCESSFUL_TOKENS_CACHE_SIZE").MustInt(20)

CamoServerURL = sec.Key("CAMO_SERVER_URL").MustString("")
CamoHMACKey = sec.Key("CAMO_HMAC_KEY").MustString("")
CamoAllways = sec.Key("CAMO_ALLWAYS").MustBool(false)
if CamoServerURL != "" {
if CamoHMACKey == "" {
log.Error("CAMO_SERVER_URL is set but CAMO_HMAC_KEY is empty, skip media proxy settings")
} else {
CamoEnabled = true
}
}

InternalToken = loadInternalToken(sec)
if InstallLock && InternalToken == "" {
// if Gitea has been installed but the InternalToken hasn't been generated (upgrade from an old release), we should generate
Expand Down