mirror of
https://github.com/mudler/LocalAI.git
synced 2026-07-07 23:07:34 -04:00
Compare commits
1 Commits
dependabot
...
fix/oidc-s
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ff2ce906c8 |
@@ -613,24 +613,6 @@ static void params_parse(server_context& /*ctx_server*/, const backend::ModelOpt
|
||||
// starts with '-'. Applied once after the loop via common_params_parse.
|
||||
std::vector<std::string> extra_argv;
|
||||
|
||||
auto add_device_options = [&](const std::string & devices) {
|
||||
const std::regex regex{ R"([,]+)" };
|
||||
std::sregex_token_iterator it{ devices.begin(), devices.end(), regex, -1 };
|
||||
std::vector<std::string> split_arg{ it, {} };
|
||||
|
||||
for (std::string device : split_arg) {
|
||||
const auto start = device.find_first_not_of(" \t\n\r");
|
||||
if (start == std::string::npos) {
|
||||
continue;
|
||||
}
|
||||
const auto end = device.find_last_not_of(" \t\n\r");
|
||||
device = device.substr(start, end - start + 1);
|
||||
|
||||
extra_argv.push_back("--device");
|
||||
extra_argv.push_back(device);
|
||||
}
|
||||
};
|
||||
|
||||
// decode options. Options are in form optname:optvale, or if booleans only optname.
|
||||
for (int i = 0; i < request->options_size(); i++) {
|
||||
std::string opt = request->options(i);
|
||||
@@ -762,10 +744,6 @@ static void params_parse(server_context& /*ctx_server*/, const backend::ModelOpt
|
||||
} else if (optval_str == "false" || optval_str == "0" || optval_str == "no" || optval_str == "off" || optval_str == "disabled") {
|
||||
params.no_op_offload = false;
|
||||
}
|
||||
} else if (!strcmp(optname, "device") || !strcmp(optname, "devices")) {
|
||||
if (optval != NULL) {
|
||||
add_device_options(optval_str);
|
||||
}
|
||||
} else if (!strcmp(optname, "split_mode") || !strcmp(optname, "sm")) {
|
||||
// Accepts: none | layer | row | tensor (the latter requires a llama.cpp build
|
||||
// that includes ggml-org/llama.cpp#19378, FlashAttention enabled, and KV-cache
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
--extra-index-url https://download.pytorch.org/whl/cpu
|
||||
accelerate
|
||||
torch==2.12.1+xpu
|
||||
torch==2.9.1+cpu
|
||||
torchvision
|
||||
torchaudio
|
||||
transformers
|
||||
|
||||
@@ -1,12 +1,4 @@
|
||||
# vLLM 0.20+ ships an aarch64 manylinux wheel on PyPI whose Requires-Dist pins
|
||||
# torch==2.11.0 / torchvision==0.26.0 / torchaudio==2.11.0, locking an ABI-
|
||||
# consistent set with the cu130 torch wheel installed above.
|
||||
#
|
||||
# Pinned to match the cublas13 build (requirements-cublas13-after.txt) and to
|
||||
# stay deterministic on GB10 / DGX Spark: 0.24.0 carries vllm-project/vllm#45179
|
||||
# ("release cached device memory under pressure on UMA GPUs during weight
|
||||
# loading"), without which cold model loads on the Grace Blackwell unified-
|
||||
# memory architecture crash deterministically with an empty "Engine core init
|
||||
# failed" set (mudler/LocalAI#10722). Leaving this unpinned let the L4T image
|
||||
# drift onto whatever wheel was latest at build time.
|
||||
vllm==0.24.0
|
||||
vllm
|
||||
|
||||
@@ -23,6 +23,27 @@ import (
|
||||
"github.com/mudler/LocalAI/pkg/httpclient"
|
||||
)
|
||||
|
||||
// oidcSupportedSigningAlgs is the set of ID-token signature algorithms the OIDC
|
||||
// verifier accepts. go-oidc defaults to RS256 only, which rejects identity
|
||||
// providers that sign tokens with EC keys (ES*) or other algorithms — e.g.
|
||||
// Authentik configured with an EC signing key fails the callback with
|
||||
// "unexpected signature algorithm ... expected [RS256]" (#10677). All entries
|
||||
// are asymmetric algorithms verified against the provider's published JWKS;
|
||||
// HS256 is intentionally excluded (it is symmetric and would validate against
|
||||
// the client secret, a different and security-sensitive trust model).
|
||||
var oidcSupportedSigningAlgs = []string{
|
||||
oidc.RS256, oidc.RS384, oidc.RS512,
|
||||
oidc.ES256, oidc.ES384, oidc.ES512,
|
||||
oidc.PS256, oidc.PS384, oidc.PS512,
|
||||
oidc.EdDSA,
|
||||
}
|
||||
|
||||
// OIDCSupportedSigningAlgs returns a copy of the ID-token signature algorithms
|
||||
// the OIDC verifier accepts. Exposed for tests.
|
||||
func OIDCSupportedSigningAlgs() []string {
|
||||
return append([]string(nil), oidcSupportedSigningAlgs...)
|
||||
}
|
||||
|
||||
// providerEntry holds the OAuth2/OIDC config for a single provider.
|
||||
type providerEntry struct {
|
||||
oauth2Config oauth2.Config
|
||||
@@ -85,7 +106,10 @@ func NewOAuthManager(baseURL string, params OAuthParams) (*OAuthManager, error)
|
||||
return nil, fmt.Errorf("OIDC discovery failed for %s: %w", params.OIDCIssuer, err)
|
||||
}
|
||||
|
||||
verifier := provider.Verifier(&oidc.Config{ClientID: params.OIDCClientID})
|
||||
verifier := provider.Verifier(&oidc.Config{
|
||||
ClientID: params.OIDCClientID,
|
||||
SupportedSigningAlgs: oidcSupportedSigningAlgs,
|
||||
})
|
||||
|
||||
m.providers[ProviderOIDC] = &providerEntry{
|
||||
name: ProviderOIDC,
|
||||
|
||||
101
core/http/auth/oauth_signing_algs_test.go
Normal file
101
core/http/auth/oauth_signing_algs_test.go
Normal file
@@ -0,0 +1,101 @@
|
||||
//go:build auth
|
||||
|
||||
package auth_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto"
|
||||
"crypto/ecdsa"
|
||||
"crypto/elliptic"
|
||||
"crypto/rand"
|
||||
"crypto/sha256"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"time"
|
||||
|
||||
"github.com/coreos/go-oidc/v3/oidc"
|
||||
"github.com/mudler/LocalAI/core/http/auth"
|
||||
. "github.com/onsi/ginkgo/v2"
|
||||
. "github.com/onsi/gomega"
|
||||
)
|
||||
|
||||
const (
|
||||
testOIDCIssuer = "https://issuer.example.test"
|
||||
testOIDCClientID = "localai-test-client"
|
||||
)
|
||||
|
||||
// signES256IDToken builds a minimal ES256-signed JWT in JOSE compact form. The
|
||||
// signature is the raw R||S encoding (32 bytes each for P-256) that OIDC
|
||||
// verifiers expect, not ASN.1/DER.
|
||||
func signES256IDToken(key *ecdsa.PrivateKey, claims map[string]any) string {
|
||||
b64 := func(b []byte) string { return base64.RawURLEncoding.EncodeToString(b) }
|
||||
header := b64([]byte(`{"alg":"ES256","typ":"JWT"}`))
|
||||
payloadJSON, err := json.Marshal(claims)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
signingInput := header + "." + b64(payloadJSON)
|
||||
|
||||
digest := sha256.Sum256([]byte(signingInput))
|
||||
r, s, err := ecdsa.Sign(rand.Reader, key, digest[:])
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
sig := make([]byte, 64)
|
||||
r.FillBytes(sig[:32])
|
||||
s.FillBytes(sig[32:])
|
||||
|
||||
return signingInput + "." + b64(sig)
|
||||
}
|
||||
|
||||
var _ = Describe("OIDC ID-token signing algorithms", func() {
|
||||
var (
|
||||
ctx context.Context
|
||||
key *ecdsa.PrivateKey
|
||||
idToken string
|
||||
keySet *oidc.StaticKeySet
|
||||
)
|
||||
|
||||
BeforeEach(func() {
|
||||
ctx = context.Background()
|
||||
var err error
|
||||
key, err = ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
idToken = signES256IDToken(key, map[string]any{
|
||||
"iss": testOIDCIssuer,
|
||||
"sub": "user-1",
|
||||
"aud": testOIDCClientID,
|
||||
"email": "user@example.test",
|
||||
"iat": time.Now().Unix(),
|
||||
"exp": time.Now().Add(time.Hour).Unix(),
|
||||
})
|
||||
keySet = &oidc.StaticKeySet{PublicKeys: []crypto.PublicKey{key.Public()}}
|
||||
})
|
||||
|
||||
It("accepts an EC (ES256) signed ID token with the configured algorithms", func() {
|
||||
verifier := oidc.NewVerifier(testOIDCIssuer, keySet, &oidc.Config{
|
||||
ClientID: testOIDCClientID,
|
||||
SupportedSigningAlgs: auth.OIDCSupportedSigningAlgs(),
|
||||
})
|
||||
tok, err := verifier.Verify(ctx, idToken)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(tok.Subject).To(Equal("user-1"))
|
||||
})
|
||||
|
||||
It("rejects the same token under go-oidc's RS256-only default (the pre-fix behavior)", func() {
|
||||
// This reproduces #10677: without a broadened SupportedSigningAlgs the
|
||||
// verifier only accepts RS256 and rejects EC-signed tokens.
|
||||
verifier := oidc.NewVerifier(testOIDCIssuer, keySet, &oidc.Config{
|
||||
ClientID: testOIDCClientID,
|
||||
})
|
||||
_, err := verifier.Verify(ctx, idToken)
|
||||
Expect(err).To(HaveOccurred())
|
||||
Expect(err.Error()).To(ContainSubstring("ES256"))
|
||||
})
|
||||
|
||||
It("advertises the common asymmetric algorithms and excludes the symmetric HS256", func() {
|
||||
algs := auth.OIDCSupportedSigningAlgs()
|
||||
Expect(algs).To(ContainElements(
|
||||
oidc.RS256, oidc.RS384, oidc.RS512,
|
||||
oidc.ES256, oidc.ES384, oidc.ES512,
|
||||
oidc.PS256, oidc.EdDSA,
|
||||
))
|
||||
Expect(algs).NotTo(ContainElement("HS256"))
|
||||
})
|
||||
})
|
||||
@@ -493,7 +493,6 @@ These llama.cpp options are passed through the `options:` array.
|
||||
| `threads_batch` / `n_threads_batch` | int | same as `threads` | Threads used during prompt processing. `<= 0` means `hardware_concurrency()`. |
|
||||
| `direct_io` / `use_direct_io` | bool | `false` | Open the model with `O_DIRECT` (faster cold loads on NVMe; ignored if not supported). |
|
||||
| `verbosity` | int | `3` | llama.cpp internal log verbosity threshold. Higher = more verbose. |
|
||||
| `device` / `devices` | string | all devices | Select the llama.cpp backend devices to use. Repeat the option or pass a comma-separated list; unlisted devices are excluded. Use the names reported by `llama-server --list-devices` / `--list-devices`. |
|
||||
| `override_tensor` / `tensor_buft_overrides` | string | "" | Per-tensor buffer-type overrides for the main model. Format: `<tensor regex>=<buffer type>,<tensor regex>=<buffer type>,...`. Mirrors the existing `draft_override_tensor` syntax for the draft model. |
|
||||
| `cpu_moe` | bool | false | Keep all MoE expert weights of the main model on CPU (upstream `--cpu-moe`). Frees VRAM on large MoE models (DeepSeek, Qwen3 `*-A3B`). |
|
||||
| `n_cpu_moe` | int | 0 | Keep MoE expert weights of the first N main-model layers on CPU (upstream `--n-cpu-moe`). |
|
||||
@@ -515,7 +514,6 @@ options:
|
||||
- "--cpu-moe" # boolean flag
|
||||
- "--n-cpu-moe:4" # flag with a value
|
||||
- "--override-tensor:exps=CPU"
|
||||
- "devices:CUDA1,CUDA2,CUDA3" # skip CUDA0, e.g. a display GPU
|
||||
```
|
||||
|
||||
Notes:
|
||||
|
||||
@@ -512,7 +512,6 @@ The `llama.cpp` backend supports additional configuration options that can be sp
|
||||
| `check_tensors` | boolean | Validate tensor data for invalid values during model loading. Default: `false`. | `check_tensors:true` |
|
||||
| `warmup` | boolean | Enable warmup run after model loading. Default: `true`. | `warmup:false` |
|
||||
| `no_op_offload` | boolean | Disable offloading host tensor operations to device. Default: `false`. | `no_op_offload:true` |
|
||||
| `device` or `devices` | string | Select the llama.cpp backend devices to use. Repeat the option or pass a comma-separated list; unlisted devices are excluded. Use the names reported by `llama-server --list-devices` / `--list-devices`. | `devices:CUDA1,CUDA2,CUDA3` |
|
||||
| `kv_unified` or `unified_kv` | boolean | Use a single unified KV buffer shared across all sequences. Default: `true` (LocalAI override; upstream defaults to `false` but auto-enables it when slot count is auto). **Required for `cache_idle_slots` to work**: without it the server force-disables idle-slot saving at init, and the prompt cache is never written across requests. | `kv_unified:false` |
|
||||
| `cache_idle_slots` or `idle_slots_cache` | boolean | On a new task, save the previous slot's KV state into the prompt cache (and clear the slot) so a later request with the same prefix can warm-load it. Default: `true`. Auto-disabled by the server if `kv_unified=false` or `cache_ram=0`. | `cache_idle_slots:false` |
|
||||
| `n_ctx_checkpoints` or `ctx_checkpoints` | integer | Maximum number of context checkpoints per slot (used for partial-prefix recovery, e.g. SWA). Default: `32`. | `ctx_checkpoints:16` |
|
||||
@@ -531,7 +530,6 @@ options:
|
||||
- context_shift:true
|
||||
- cache_ram:4096
|
||||
- parallel:2
|
||||
- devices:CUDA1,CUDA2,CUDA3
|
||||
- fit_params:true
|
||||
- fit_target:1024
|
||||
- slot_prompt_similarity:0.5
|
||||
|
||||
Reference in New Issue
Block a user