Skip to content

Commit d3e7cc8

Browse files
committed
add encryption
1 parent 78d1f24 commit d3e7cc8

9 files changed

Lines changed: 237 additions & 30 deletions

File tree

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
release_notes.md
22
target
33
.DS_STORE
4-
.vscode/launch.json
4+
.vscode/launch.json
5+
.env

CHANGELOG.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,11 @@
1+
## 1.39.0 - 2024-11-15
2+
### Added
3+
- Added encryption integration
4+
5+
### Changed
6+
- Removed support for Redis TLS config
7+
8+
19
## 1.38.0 - 2024-11-09
210
### Added
311
- Added support for `claude-3-5-haiku`

cmd/bricksllm/.env

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,6 @@ POSTGRESQL_PASSWORD=
77
POSTGRESQL_SSL_MODE=disable
88
POSTGRESQL_PORT=5432
99
REDIS_HOSTS=localhost
10-
REDIS_ENABLE_TLS=false
11-
REDIS_INSECURE_SKIP_VERIFY=false
1210
REDIS_PORT=6379
1311
REDIS_USERNAME=
1412
REDIS_PASSWORD=

cmd/bricksllm/config_local.json

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,6 @@
77
"postgresql_port": "5432",
88
"redis_hosts": "localhost",
99
"redis_port": "6379",
10-
"redis_enable_tls": false,
11-
"redis_insecure_skip_verify": false,
1210
"redis_username": "",
1311
"redis_password": "",
1412
"redis_read_time_out": "1s",

cmd/bricksllm/main.go

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ package main
22

33
import (
44
"context"
5-
"crypto/tls"
65
"flag"
76
"fmt"
87
"os"
@@ -13,6 +12,7 @@ import (
1312
auth "github.com/bricks-cloud/bricksllm/internal/authenticator"
1413
"github.com/bricks-cloud/bricksllm/internal/cache"
1514
"github.com/bricks-cloud/bricksllm/internal/config"
15+
"github.com/bricks-cloud/bricksllm/internal/encryptor"
1616
"github.com/bricks-cloud/bricksllm/internal/logger/zap"
1717
"github.com/bricks-cloud/bricksllm/internal/manager"
1818
"github.com/bricks-cloud/bricksllm/internal/message"
@@ -182,12 +182,6 @@ func main() {
182182
DB: cfg.RedisDBStartIndex + dbIndex,
183183
}
184184

185-
if cfg.RedisEnableTLS {
186-
options.TLSConfig = &tls.Config{
187-
InsecureSkipVerify: cfg.RedisInsecureSkipVerify,
188-
}
189-
}
190-
191185
return options
192186
}
193187

@@ -292,9 +286,11 @@ func main() {
292286
psCache := redisStorage.NewProviderSettingsCache(providerSettingsRedisCache, cfg.RedisWriteTimeout, cfg.RedisReadTimeout)
293287
keysCache := redisStorage.NewKeysCache(keysRedisCache, cfg.RedisWriteTimeout, cfg.RedisReadTimeout)
294288

289+
encryptor := encryptor.NewEncryptor(cfg.DecryptionEndpoint, cfg.EncryptionEndpoint, cfg.EnableEncrytion, cfg.EncryptionTimeout)
290+
295291
m := manager.NewManager(store, costLimitCache, rateLimitCache, accessCache, keysCache)
296292
krm := manager.NewReportingManager(costStorage, store, store)
297-
psm := manager.NewProviderSettingsManager(store, psCache)
293+
psm := manager.NewProviderSettingsManager(store, psCache, encryptor)
298294
cpm := manager.NewCustomProvidersManager(store, cpMemStore)
299295
rm := manager.NewRouteManager(store, store, rMemStore, psm)
300296
pm := manager.NewPolicyManager(store, rMemStore)
@@ -332,7 +328,7 @@ func main() {
332328

333329
rec := recorder.NewRecorder(costStorage, userCostStorage, costLimitCache, userCostLimitCache, ce, store)
334330
rlm := manager.NewRateLimitManager(rateLimitCache, userRateLimitCache)
335-
a := auth.NewAuthenticator(psm, m, rm, store)
331+
a := auth.NewAuthenticator(psm, m, rm, store, encryptor)
336332

337333
c := cache.NewCache(apiCache)
338334

internal/authenticator/authenticator.go

Lines changed: 41 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55
"fmt"
66
"math/rand"
77
"net/http"
8+
"strconv"
89
"strings"
910

1011
internal_errors "github.com/bricks-cloud/bricksllm/internal/errors"
@@ -34,19 +35,26 @@ type keyStorage interface {
3435
GetKeyByHash(hash string) (*key.ResponseKey, error)
3536
}
3637

38+
type Decryptor interface {
39+
Decrypt(input string, headers map[string]string) (string, error)
40+
Enabled() bool
41+
}
42+
3743
type Authenticator struct {
38-
psm providerSettingsManager
39-
kc keysCache
40-
rm routesManager
41-
ks keyStorage
44+
psm providerSettingsManager
45+
kc keysCache
46+
rm routesManager
47+
ks keyStorage
48+
decryptor Decryptor
4249
}
4350

44-
func NewAuthenticator(psm providerSettingsManager, kc keysCache, rm routesManager, ks keyStorage) *Authenticator {
51+
func NewAuthenticator(psm providerSettingsManager, kc keysCache, rm routesManager, ks keyStorage, decryptor Decryptor) *Authenticator {
4552
return &Authenticator{
46-
psm: psm,
47-
kc: kc,
48-
rm: rm,
49-
ks: ks,
53+
psm: psm,
54+
kc: kc,
55+
rm: rm,
56+
ks: ks,
57+
decryptor: decryptor,
5058
}
5159
}
5260

@@ -268,6 +276,30 @@ func (a *Authenticator) AuthenticateHttpRequest(req *http.Request) (*key.Respons
268276
used = selected[rand.Intn(len(selected))]
269277
}
270278

279+
if a.decryptor.Enabled() {
280+
encryptedParam := ""
281+
if used.Provider == "amazon" {
282+
encryptedParam = used.Setting["awsSecretAccessKey"]
283+
} else if len(used.Setting["apikey"]) != 0 {
284+
encryptedParam = used.Setting["apikey"]
285+
}
286+
287+
if len(encryptedParam) != 0 {
288+
decryptedSecret, err := a.decryptor.Decrypt(encryptedParam, map[string]string{"X-UPDATED-AT": strconv.FormatInt(used.UpdatedAt, 10)})
289+
if err == nil {
290+
if used.Provider == "amazon" {
291+
used.Setting["awsSecretAccessKey"] = decryptedSecret
292+
} else {
293+
used.Setting["apikey"] = decryptedSecret
294+
}
295+
}
296+
297+
if err != nil {
298+
fmt.Println(fmt.Printf("error when encrypting %v", err))
299+
}
300+
}
301+
}
302+
271303
err := rewriteHttpAuthHeader(req, used)
272304
if err != nil {
273305
return nil, nil, err

internal/config/config.go

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package config
22

33
import (
4+
"errors"
45
"os"
56
"path/filepath"
67
"time"
@@ -25,8 +26,6 @@ type Config struct {
2526
RedisPort string `koanf:"redis_port" env:"REDIS_PORT" envDefault:"6379"`
2627
RedisUsername string `koanf:"redis_username" env:"REDIS_USERNAME"`
2728
RedisPassword string `koanf:"redis_password" env:"REDIS_PASSWORD"`
28-
RedisEnableTLS bool `koanf:"redis_enable_tls" env:"REDIS_ENABLE_TLS" envDefault:"false"`
29-
RedisInsecureSkipVerify bool `koanf:"redis_insecure_skip_verify" env:"REDIS_INSECURE_SKIP_VERIFY" envDefault:"false"`
3029
RedisDBStartIndex int `koanf:"redis_db_start_index" env:"REDIS_DB_START_INDEX" envDefault:"0"`
3130
RedisReadTimeout time.Duration `koanf:"redis_read_time_out" env:"REDIS_READ_TIME_OUT" envDefault:"1s"`
3231
RedisWriteTimeout time.Duration `koanf:"redis_write_time_out" env:"REDIS_WRITE_TIME_OUT" envDefault:"500ms"`
@@ -47,6 +46,10 @@ type Config struct {
4746
AmazonRequestTimeout time.Duration `koanf:"amazon_request_timeout" env:"AMAZON_REQUEST_TIMEOUT" envDefault:"5s"`
4847
AmazonConnectionTimeout time.Duration `koanf:"amazon_connection_timeout" env:"AMAZON_CONNECTION_TIMEOUT" envDefault:"10s"`
4948
RemoveUserAgent bool `koanf:"remove_user_agent" env:"REMOVE_USER_AGENT" envDefault:"false"`
49+
EnableEncrytion bool `koanf:"enable_encryption" env:"ENABLE_ENCRYPTION" envDefault:"false"`
50+
EncryptionEndpoint string `koanf:"encryption_endpoint" env:"ENCRYPTION_ENDPOINT"`
51+
DecryptionEndpoint string `koanf:"decryption_endpoint" env:"DECRYPTION_ENDPOINT"`
52+
EncryptionTimeout time.Duration `koanf:"encryption_timeout" env:"ENCRYPTION_TIMEOUT" envDefault:"5s"`
5053
}
5154

5255
func prepareDotEnv(envFilePath string) error {
@@ -82,6 +85,10 @@ func LoadConfig(log *zap.Logger) (*Config, error) {
8285
return nil, err
8386
}
8487

88+
if cfg.EnableEncrytion && len(cfg.EncryptionEndpoint) == 0 {
89+
return nil, errors.New("encryption endpoint cannot be empty")
90+
}
91+
8592
err = prepareDotEnv(".env")
8693
if err != nil {
8794
log.Sugar().Infof("error loading config from .env file: %v", err)

internal/encryptor/encryptor.go

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
package encryptor
2+
3+
import (
4+
"bytes"
5+
"context"
6+
"encoding/json"
7+
"io"
8+
"net/http"
9+
"time"
10+
)
11+
12+
type Encryptor struct {
13+
decryptionURL string
14+
encryptionURL string
15+
enabled bool
16+
client http.Client
17+
timeout time.Duration
18+
}
19+
20+
type Secret struct {
21+
Secret string `json:"secret"`
22+
}
23+
24+
type EncryptionResponse struct {
25+
EncryptedSecret string `json:"encryptedSecret"`
26+
}
27+
28+
type DecryptionResponse struct {
29+
DecryptedSecret string `json:"decryptedSecret"`
30+
}
31+
32+
func NewEncryptor(decryptionURL string, encryptionURL string, enabled bool, timeout time.Duration) Encryptor {
33+
return Encryptor{
34+
decryptionURL: decryptionURL,
35+
encryptionURL: encryptionURL,
36+
client: http.Client{},
37+
enabled: enabled,
38+
timeout: timeout,
39+
}
40+
}
41+
42+
func (e Encryptor) Encrypt(input string, headers map[string]string) (string, error) {
43+
data, err := json.Marshal(Secret{
44+
Secret: input,
45+
})
46+
if err != nil {
47+
return "", err
48+
}
49+
50+
ctx, cancel := context.WithTimeout(context.Background(), e.timeout)
51+
defer cancel()
52+
req, err := http.NewRequestWithContext(ctx, http.MethodPost, e.encryptionURL, bytes.NewBuffer(data))
53+
if err != nil {
54+
return "", err
55+
}
56+
57+
for header, value := range headers {
58+
req.Header.Add(header, value)
59+
}
60+
61+
res, err := e.client.Do(req)
62+
if err != nil {
63+
return "", err
64+
}
65+
66+
bytes, err := io.ReadAll(res.Body)
67+
if err != nil {
68+
return "", err
69+
}
70+
71+
encryptionResponse := EncryptionResponse{}
72+
err = json.Unmarshal(bytes, &encryptionResponse)
73+
if err != nil {
74+
return "", err
75+
}
76+
77+
return encryptionResponse.EncryptedSecret, nil
78+
}
79+
80+
func (e Encryptor) Enabled() bool {
81+
return e.enabled && len(e.decryptionURL) != 0 && len(e.encryptionURL) != 0
82+
}
83+
84+
func (e Encryptor) Decrypt(input string, headers map[string]string) (string, error) {
85+
data, err := json.Marshal(Secret{
86+
Secret: input,
87+
})
88+
if err != nil {
89+
return "", err
90+
}
91+
92+
ctx, cancel := context.WithTimeout(context.Background(), e.timeout)
93+
defer cancel()
94+
req, err := http.NewRequestWithContext(ctx, http.MethodPost, e.decryptionURL, bytes.NewBuffer(data))
95+
if err != nil {
96+
return "", err
97+
}
98+
99+
for header, value := range headers {
100+
req.Header.Add(header, value)
101+
}
102+
103+
res, err := e.client.Do(req)
104+
if err != nil {
105+
return "", err
106+
}
107+
108+
bytes, err := io.ReadAll(res.Body)
109+
if err != nil {
110+
return "", err
111+
}
112+
113+
decryptionSecret := DecryptionResponse{}
114+
err = json.Unmarshal(bytes, &decryptionSecret)
115+
if err != nil {
116+
return "", err
117+
}
118+
119+
return decryptionSecret.DecryptedSecret, nil
120+
}

0 commit comments

Comments
 (0)