mirror of
https://github.com/getnora-io/nora.git
synced 2026-04-13 05:00:31 +00:00
Compare commits
23 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| e34032d08f | |||
| 03a3bf9197 | |||
| 6c5f0dda30 | |||
| fb058302c8 | |||
| 79565aec47 | |||
| 58a484d805 | |||
| 45c3e276dc | |||
|
|
f4e53b85dd | ||
| 05d89d5153 | |||
| b149f7ebd4 | |||
| 5254e2a54a | |||
| 8783d1dc4b | |||
|
|
4c05df2359 | ||
| 7f8e3cfe68 | |||
|
|
13f33e8919 | ||
|
|
7454ff2e03 | ||
|
|
5ffb5a9be3 | ||
|
|
c8793a4b60 | ||
|
|
fd4a7b0b0f | ||
|
|
7af1e7462c | ||
|
|
de1a188fa7 | ||
|
|
36d0749bb3 | ||
| fb0f80ac5a |
2
.github/workflows/ci.yml
vendored
2
.github/workflows/ci.yml
vendored
@@ -72,7 +72,7 @@ jobs:
|
||||
# ── CVE scan of source tree and Cargo.lock ──────────────────────────────
|
||||
- name: Trivy — filesystem scan (Cargo.lock + source)
|
||||
if: always()
|
||||
uses: aquasecurity/trivy-action@0.34.1
|
||||
uses: aquasecurity/trivy-action@0.34.2
|
||||
with:
|
||||
scan-type: fs
|
||||
scan-ref: .
|
||||
|
||||
101
.github/workflows/release.yml
vendored
101
.github/workflows/release.yml
vendored
@@ -6,6 +6,7 @@ on:
|
||||
|
||||
env:
|
||||
REGISTRY: ghcr.io
|
||||
NORA: localhost:5000
|
||||
IMAGE_NAME: ${{ github.repository }}
|
||||
|
||||
jobs:
|
||||
@@ -17,7 +18,7 @@ jobs:
|
||||
packages: write
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v6
|
||||
|
||||
- name: Set up Rust
|
||||
run: |
|
||||
@@ -30,29 +31,40 @@ jobs:
|
||||
cargo build --release --target x86_64-unknown-linux-musl --package nora-registry
|
||||
cp target/x86_64-unknown-linux-musl/release/nora ./nora
|
||||
|
||||
- name: Upload binary artifact
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: nora-binary-${{ github.run_id }}
|
||||
path: ./nora
|
||||
retention-days: 1
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
with:
|
||||
driver-opts: network=host
|
||||
|
||||
- name: Log in to Container Registry
|
||||
- name: Log in to GitHub Container Registry
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ${{ env.REGISTRY }}
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
# ── Alpine (standard) ────────────────────────────────────────────────────
|
||||
# ── Alpine ───────────────────────────────────────────────────────────────
|
||||
- name: Extract metadata (alpine)
|
||||
id: meta-alpine
|
||||
uses: docker/metadata-action@v5
|
||||
with:
|
||||
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
|
||||
images: |
|
||||
${{ env.NORA }}/${{ env.IMAGE_NAME }}
|
||||
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
|
||||
tags: |
|
||||
type=semver,pattern={{version}}
|
||||
type=semver,pattern={{major}}.{{minor}}
|
||||
type=raw,value=latest
|
||||
|
||||
- name: Build and push (alpine)
|
||||
uses: docker/build-push-action@v5
|
||||
uses: docker/build-push-action@v6
|
||||
with:
|
||||
context: .
|
||||
file: Dockerfile
|
||||
@@ -60,15 +72,17 @@ jobs:
|
||||
push: true
|
||||
tags: ${{ steps.meta-alpine.outputs.tags }}
|
||||
labels: ${{ steps.meta-alpine.outputs.labels }}
|
||||
cache-from: type=gha,scope=alpine
|
||||
cache-to: type=gha,mode=max,scope=alpine
|
||||
cache-from: type=registry,ref=${{ env.NORA }}/${{ env.IMAGE_NAME }}-cache:alpine
|
||||
cache-to: type=registry,ref=${{ env.NORA }}/${{ env.IMAGE_NAME }}-cache:alpine,mode=max
|
||||
|
||||
# ── RED OS ───────────────────────────────────────────────────────────────
|
||||
- name: Extract metadata (redos)
|
||||
id: meta-redos
|
||||
uses: docker/metadata-action@v5
|
||||
with:
|
||||
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
|
||||
images: |
|
||||
${{ env.NORA }}/${{ env.IMAGE_NAME }}
|
||||
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
|
||||
flavor: suffix=-redos,onlatest=true
|
||||
tags: |
|
||||
type=semver,pattern={{version}}
|
||||
@@ -76,7 +90,7 @@ jobs:
|
||||
type=raw,value=redos
|
||||
|
||||
- name: Build and push (redos)
|
||||
uses: docker/build-push-action@v5
|
||||
uses: docker/build-push-action@v6
|
||||
with:
|
||||
context: .
|
||||
file: Dockerfile.redos
|
||||
@@ -84,15 +98,17 @@ jobs:
|
||||
push: true
|
||||
tags: ${{ steps.meta-redos.outputs.tags }}
|
||||
labels: ${{ steps.meta-redos.outputs.labels }}
|
||||
cache-from: type=gha,scope=redos
|
||||
cache-to: type=gha,mode=max,scope=redos
|
||||
cache-from: type=registry,ref=${{ env.NORA }}/${{ env.IMAGE_NAME }}-cache:redos
|
||||
cache-to: type=registry,ref=${{ env.NORA }}/${{ env.IMAGE_NAME }}-cache:redos,mode=max
|
||||
|
||||
# ── Astra Linux SE ───────────────────────────────────────────────────────
|
||||
- name: Extract metadata (astra)
|
||||
id: meta-astra
|
||||
uses: docker/metadata-action@v5
|
||||
with:
|
||||
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
|
||||
images: |
|
||||
${{ env.NORA }}/${{ env.IMAGE_NAME }}
|
||||
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
|
||||
flavor: suffix=-astra,onlatest=true
|
||||
tags: |
|
||||
type=semver,pattern={{version}}
|
||||
@@ -100,7 +116,7 @@ jobs:
|
||||
type=raw,value=astra
|
||||
|
||||
- name: Build and push (astra)
|
||||
uses: docker/build-push-action@v5
|
||||
uses: docker/build-push-action@v6
|
||||
with:
|
||||
context: .
|
||||
file: Dockerfile.astra
|
||||
@@ -108,12 +124,12 @@ jobs:
|
||||
push: true
|
||||
tags: ${{ steps.meta-astra.outputs.tags }}
|
||||
labels: ${{ steps.meta-astra.outputs.labels }}
|
||||
cache-from: type=gha,scope=astra
|
||||
cache-to: type=gha,mode=max,scope=astra
|
||||
cache-from: type=registry,ref=${{ env.NORA }}/${{ env.IMAGE_NAME }}-cache:astra
|
||||
cache-to: type=registry,ref=${{ env.NORA }}/${{ env.IMAGE_NAME }}-cache:astra,mode=max
|
||||
|
||||
scan:
|
||||
name: Scan (${{ matrix.name }})
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: [self-hosted, nora]
|
||||
needs: build
|
||||
permissions:
|
||||
contents: read
|
||||
@@ -132,28 +148,19 @@ jobs:
|
||||
suffix: "-astra"
|
||||
|
||||
steps:
|
||||
- name: Log in to Container Registry
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ${{ env.REGISTRY }}
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Set version tag (strip leading v)
|
||||
id: ver
|
||||
run: echo "tag=${GITHUB_REF_NAME#v}" >> $GITHUB_OUTPUT
|
||||
|
||||
# ── CVE scan of the pushed image ────────────────────────────────────────
|
||||
# Images are FROM scratch — no OS packages, only binary CVE scan
|
||||
- name: Trivy — image scan (${{ matrix.name }})
|
||||
uses: aquasecurity/trivy-action@0.30.0
|
||||
uses: aquasecurity/trivy-action@0.34.2
|
||||
with:
|
||||
scan-type: image
|
||||
image-ref: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.ver.outputs.tag }}${{ matrix.suffix }}
|
||||
image-ref: ${{ env.NORA }}/${{ env.IMAGE_NAME }}:${{ steps.ver.outputs.tag }}${{ matrix.suffix }}
|
||||
format: sarif
|
||||
output: trivy-image-${{ matrix.name }}.sarif
|
||||
severity: HIGH,CRITICAL
|
||||
exit-code: 1 # block release on HIGH/CRITICAL vulnerabilities
|
||||
exit-code: 1
|
||||
|
||||
- name: Upload Trivy image results to GitHub Security tab
|
||||
uses: github/codeql-action/upload-sarif@v4
|
||||
@@ -164,59 +171,49 @@ jobs:
|
||||
|
||||
release:
|
||||
name: GitHub Release
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: [self-hosted, nora]
|
||||
needs: [build, scan]
|
||||
permissions:
|
||||
contents: write
|
||||
packages: read # to pull image for SBOM generation
|
||||
packages: read
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Log in to Container Registry
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ${{ env.REGISTRY }}
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
- uses: actions/checkout@v6
|
||||
|
||||
- name: Set version tag (strip leading v)
|
||||
id: ver
|
||||
run: echo "tag=${GITHUB_REF_NAME#v}" >> $GITHUB_OUTPUT
|
||||
|
||||
# ── Binary — extract from Docker image ──────────────────────────────────
|
||||
- name: Extract binary from image
|
||||
- name: Download binary artifact
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: nora-binary-${{ github.run_id }}
|
||||
path: ./artifacts
|
||||
|
||||
- name: Prepare binary
|
||||
run: |
|
||||
docker create --name nora-extract \
|
||||
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.ver.outputs.tag }}
|
||||
docker cp nora-extract:/usr/local/bin/nora ./nora-linux-amd64
|
||||
docker rm nora-extract
|
||||
cp ./artifacts/nora ./nora-linux-amd64
|
||||
chmod +x ./nora-linux-amd64
|
||||
sha256sum ./nora-linux-amd64 > nora-linux-amd64.sha256
|
||||
echo "Binary size: $(du -sh nora-linux-amd64 | cut -f1)"
|
||||
cat nora-linux-amd64.sha256
|
||||
|
||||
# ── SBOM — Software Bill of Materials ───────────────────────────────────
|
||||
- name: Generate SBOM (SPDX)
|
||||
uses: anchore/sbom-action@v0
|
||||
with:
|
||||
image: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.ver.outputs.tag }}
|
||||
image: ${{ env.NORA }}/${{ env.IMAGE_NAME }}:${{ steps.ver.outputs.tag }}
|
||||
format: spdx-json
|
||||
output-file: nora-${{ github.ref_name }}.sbom.spdx.json
|
||||
registry-username: ${{ github.actor }}
|
||||
registry-password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Generate SBOM (CycloneDX)
|
||||
uses: anchore/sbom-action@v0
|
||||
with:
|
||||
image: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.ver.outputs.tag }}
|
||||
image: ${{ env.NORA }}/${{ env.IMAGE_NAME }}:${{ steps.ver.outputs.tag }}
|
||||
format: cyclonedx-json
|
||||
output-file: nora-${{ github.ref_name }}.sbom.cdx.json
|
||||
registry-username: ${{ github.actor }}
|
||||
registry-password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Create Release
|
||||
uses: softprops/action-gh-release@v1
|
||||
uses: softprops/action-gh-release@v2
|
||||
with:
|
||||
generate_release_notes: true
|
||||
files: |
|
||||
|
||||
276
Cargo.lock
generated
276
Cargo.lock
generated
@@ -82,6 +82,12 @@ dependencies = [
|
||||
"windows-sys 0.61.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anyhow"
|
||||
version = "1.0.102"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c"
|
||||
|
||||
[[package]]
|
||||
name = "arbitrary"
|
||||
version = "1.4.2"
|
||||
@@ -184,9 +190,9 @@ checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
|
||||
|
||||
[[package]]
|
||||
name = "bcrypt"
|
||||
version = "0.17.1"
|
||||
version = "0.18.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "abaf6da45c74385272ddf00e1ac074c7d8a6c1a1dda376902bd6a427522a8b2c"
|
||||
checksum = "9a0f5948f30df5f43ac29d310b7476793be97c50787e6ef4a63d960a0d0be827"
|
||||
dependencies = [
|
||||
"base64",
|
||||
"blowfish",
|
||||
@@ -286,9 +292,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "4.5.56"
|
||||
version = "4.5.60"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a75ca66430e33a14957acc24c5077b503e7d374151b2b4b3a10c83b4ceb4be0e"
|
||||
checksum = "2797f34da339ce31042b27d23607e051786132987f595b02ba4f6a6dffb7030a"
|
||||
dependencies = [
|
||||
"clap_builder",
|
||||
"clap_derive",
|
||||
@@ -296,9 +302,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "clap_builder"
|
||||
version = "4.5.56"
|
||||
version = "4.5.60"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "793207c7fa6300a0608d1080b858e5fdbe713cdc1c8db9fb17777d8a13e63df0"
|
||||
checksum = "24a241312cea5059b13574bb9b3861cabf758b879c15190b37b6d6fd63ab6876"
|
||||
dependencies = [
|
||||
"anstream",
|
||||
"anstyle",
|
||||
@@ -320,9 +326,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "clap_lex"
|
||||
version = "0.7.7"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c3e64b0cc0439b12df2fa678eae89a1c56a529fd067a9115f7827f1fffd22b32"
|
||||
checksum = "3a822ea5bc7590f9d40f1ba12c0dc3c2760f3482c6984db1573ad11031420831"
|
||||
|
||||
[[package]]
|
||||
name = "colorchoice"
|
||||
@@ -332,15 +338,15 @@ checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75"
|
||||
|
||||
[[package]]
|
||||
name = "console"
|
||||
version = "0.15.11"
|
||||
version = "0.16.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "054ccb5b10f9f2cbf51eb355ca1d05c2d279ce1804688d0db74b4733a5aeafd8"
|
||||
checksum = "03e45a4a8926227e4197636ba97a9fc9b00477e9f4bd711395687c5f0734bec4"
|
||||
dependencies = [
|
||||
"encode_unicode",
|
||||
"libc",
|
||||
"once_cell",
|
||||
"unicode-width",
|
||||
"windows-sys 0.59.0",
|
||||
"windows-sys 0.61.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -510,6 +516,12 @@ version = "1.0.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
|
||||
|
||||
[[package]]
|
||||
name = "foldhash"
|
||||
version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2"
|
||||
|
||||
[[package]]
|
||||
name = "foldhash"
|
||||
version = "0.2.0"
|
||||
@@ -667,6 +679,19 @@ dependencies = [
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "getrandom"
|
||||
version = "0.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "139ef39800118c7683f2fd3c98c1b23c09ae076556b435f8e9064ae108aaeeec"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"r-efi",
|
||||
"wasip2",
|
||||
"wasip3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "governor"
|
||||
version = "0.10.4"
|
||||
@@ -715,6 +740,15 @@ version = "0.14.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1"
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.15.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1"
|
||||
dependencies = [
|
||||
"foldhash 0.1.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.16.1"
|
||||
@@ -723,7 +757,7 @@ checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100"
|
||||
dependencies = [
|
||||
"allocator-api2",
|
||||
"equivalent",
|
||||
"foldhash",
|
||||
"foldhash 0.2.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -980,6 +1014,12 @@ dependencies = [
|
||||
"zerovec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "id-arena"
|
||||
version = "2.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954"
|
||||
|
||||
[[package]]
|
||||
name = "idna"
|
||||
version = "1.1.0"
|
||||
@@ -1015,14 +1055,14 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "indicatif"
|
||||
version = "0.17.11"
|
||||
version = "0.18.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "183b3088984b400f4cfac3620d5e076c84da5364016b4f49473de574b2586235"
|
||||
checksum = "25470f23803092da7d239834776d653104d551bc4d7eacaf31e6837854b8e9eb"
|
||||
dependencies = [
|
||||
"console",
|
||||
"number_prefix",
|
||||
"portable-atomic",
|
||||
"unicode-width",
|
||||
"unit-prefix",
|
||||
"web-time",
|
||||
]
|
||||
|
||||
@@ -1080,10 +1120,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.180"
|
||||
name = "leb128fmt"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bcc35a38544a891a5f7c865aca548a982ccb3b8650a5b06d0fd33a10283c56fc"
|
||||
checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.182"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6800badb6cb2082ffd7b6a67e6125bb39f18782f793520caee8cb8846be06112"
|
||||
|
||||
[[package]]
|
||||
name = "libredox"
|
||||
@@ -1098,9 +1144,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "linux-raw-sys"
|
||||
version = "0.11.0"
|
||||
version = "0.12.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "df1d3c3b53da64cf5760482273a98e575c651a67eec7f77df96b5b642de8f039"
|
||||
checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53"
|
||||
|
||||
[[package]]
|
||||
name = "litemap"
|
||||
@@ -1201,7 +1247,7 @@ checksum = "38bf9645c8b145698bb0b18a4637dcacbc421ea49bef2317e4fd8065a387cf21"
|
||||
|
||||
[[package]]
|
||||
name = "nora-cli"
|
||||
version = "0.2.22"
|
||||
version = "0.2.25"
|
||||
dependencies = [
|
||||
"clap",
|
||||
"flate2",
|
||||
@@ -1215,7 +1261,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nora-registry"
|
||||
version = "0.2.22"
|
||||
version = "0.2.25"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"axum",
|
||||
@@ -1253,7 +1299,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nora-storage"
|
||||
version = "0.2.22"
|
||||
version = "0.2.25"
|
||||
dependencies = [
|
||||
"axum",
|
||||
"base64",
|
||||
@@ -1298,12 +1344,6 @@ dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "number_prefix"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "830b246a0e5f20af87141b25c173cd1b609bd7779a4617d6ec582abaf90870f3"
|
||||
|
||||
[[package]]
|
||||
name = "once_cell"
|
||||
version = "1.21.3"
|
||||
@@ -1401,6 +1441,16 @@ dependencies = [
|
||||
"zerocopy",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "prettyplease"
|
||||
version = "0.2.37"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.106"
|
||||
@@ -1721,9 +1771,9 @@ checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d"
|
||||
|
||||
[[package]]
|
||||
name = "rustix"
|
||||
version = "1.1.3"
|
||||
version = "1.1.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "146c9e247ccc180c1f61615433868c99f3de3ae256a30a43b49f67c2d9171f34"
|
||||
checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"errno",
|
||||
@@ -1794,6 +1844,12 @@ version = "1.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
|
||||
|
||||
[[package]]
|
||||
name = "semver"
|
||||
version = "1.0.27"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2"
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.228"
|
||||
@@ -2004,12 +2060,12 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tempfile"
|
||||
version = "3.24.0"
|
||||
version = "3.26.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "655da9c7eb6305c55742045d5a8d2037996d61d8de95806335c7c86ce0f82e9c"
|
||||
checksum = "82a72c767771b47409d2345987fda8628641887d5466101319899796367354a0"
|
||||
dependencies = [
|
||||
"fastrand",
|
||||
"getrandom 0.3.4",
|
||||
"getrandom 0.4.1",
|
||||
"once_cell",
|
||||
"rustix",
|
||||
"windows-sys 0.61.2",
|
||||
@@ -2390,6 +2446,18 @@ version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b4ac048d71ede7ee76d585517add45da530660ef4390e49b098733c6e897f254"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-xid"
|
||||
version = "0.2.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853"
|
||||
|
||||
[[package]]
|
||||
name = "unit-prefix"
|
||||
version = "0.5.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "81e544489bf3d8ef66c953931f56617f423cd4b5494be343d9b9d3dda037b9a3"
|
||||
|
||||
[[package]]
|
||||
name = "untrusted"
|
||||
version = "0.9.0"
|
||||
@@ -2465,11 +2533,11 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "uuid"
|
||||
version = "1.20.0"
|
||||
version = "1.21.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ee48d38b119b0cd71fe4141b30f5ba9c7c5d9f4e7a3a8b4a674e4b6ef789976f"
|
||||
checksum = "b672338555252d43fd2240c714dc444b8c6fb0a5c5335e65a07bba7742735ddb"
|
||||
dependencies = [
|
||||
"getrandom 0.3.4",
|
||||
"getrandom 0.4.1",
|
||||
"js-sys",
|
||||
"wasm-bindgen",
|
||||
]
|
||||
@@ -2520,6 +2588,15 @@ dependencies = [
|
||||
"wit-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasip3"
|
||||
version = "0.4.0+wasi-0.3.0-rc-2026-01-06"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5428f8bf88ea5ddc08faddef2ac4a67e390b88186c703ce6dbd955e1c145aca5"
|
||||
dependencies = [
|
||||
"wit-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen"
|
||||
version = "0.2.108"
|
||||
@@ -2579,6 +2656,40 @@ dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-encoder"
|
||||
version = "0.244.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "990065f2fe63003fe337b932cfb5e3b80e0b4d0f5ff650e6985b1048f62c8319"
|
||||
dependencies = [
|
||||
"leb128fmt",
|
||||
"wasmparser",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-metadata"
|
||||
version = "0.244.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bb0e353e6a2fbdc176932bbaab493762eb1255a7900fe0fea1a2f96c296cc909"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"indexmap",
|
||||
"wasm-encoder",
|
||||
"wasmparser",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasmparser"
|
||||
version = "0.244.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"hashbrown 0.15.5",
|
||||
"indexmap",
|
||||
"semver",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "web-sys"
|
||||
version = "0.3.85"
|
||||
@@ -2707,15 +2818,6 @@ dependencies = [
|
||||
"windows-targets 0.52.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.59.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
|
||||
dependencies = [
|
||||
"windows-targets 0.52.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.60.2"
|
||||
@@ -2897,6 +2999,88 @@ name = "wit-bindgen"
|
||||
version = "0.51.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5"
|
||||
dependencies = [
|
||||
"wit-bindgen-rust-macro",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wit-bindgen-core"
|
||||
version = "0.51.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ea61de684c3ea68cb082b7a88508a8b27fcc8b797d738bfc99a82facf1d752dc"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"heck",
|
||||
"wit-parser",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wit-bindgen-rust"
|
||||
version = "0.51.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b7c566e0f4b284dd6561c786d9cb0142da491f46a9fbed79ea69cdad5db17f21"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"heck",
|
||||
"indexmap",
|
||||
"prettyplease",
|
||||
"syn",
|
||||
"wasm-metadata",
|
||||
"wit-bindgen-core",
|
||||
"wit-component",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wit-bindgen-rust-macro"
|
||||
version = "0.51.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0c0f9bfd77e6a48eccf51359e3ae77140a7f50b1e2ebfe62422d8afdaffab17a"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"prettyplease",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"wit-bindgen-core",
|
||||
"wit-bindgen-rust",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wit-component"
|
||||
version = "0.244.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bitflags",
|
||||
"indexmap",
|
||||
"log",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
"serde_json",
|
||||
"wasm-encoder",
|
||||
"wasm-metadata",
|
||||
"wasmparser",
|
||||
"wit-parser",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wit-parser"
|
||||
version = "0.244.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ecc8ac4bc1dc3381b7f59c34f00b67e18f910c2c0f50015669dde7def656a736"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"id-arena",
|
||||
"indexmap",
|
||||
"log",
|
||||
"semver",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
"serde_json",
|
||||
"unicode-xid",
|
||||
"wasmparser",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "writeable"
|
||||
|
||||
@@ -7,7 +7,7 @@ members = [
|
||||
]
|
||||
|
||||
[workspace.package]
|
||||
version = "0.2.24"
|
||||
version = "0.2.25"
|
||||
edition = "2021"
|
||||
license = "MIT"
|
||||
authors = ["DevITWay <devitway@gmail.com>"]
|
||||
|
||||
@@ -18,6 +18,6 @@ reqwest.workspace = true
|
||||
serde.workspace = true
|
||||
serde_json.workspace = true
|
||||
clap = { version = "4", features = ["derive"] }
|
||||
indicatif = "0.17"
|
||||
indicatif = "0.18"
|
||||
tar = "0.4"
|
||||
flate2 = "1.1"
|
||||
|
||||
@@ -28,7 +28,7 @@ hmac.workspace = true
|
||||
hex.workspace = true
|
||||
toml = "1.0"
|
||||
uuid = { version = "1", features = ["v4"] }
|
||||
bcrypt = "0.17"
|
||||
bcrypt = "0.18"
|
||||
base64 = "0.22"
|
||||
prometheus = "0.14"
|
||||
lazy_static = "1.5"
|
||||
@@ -38,7 +38,7 @@ utoipa-swagger-ui = { version = "9", features = ["axum", "reqwest"] }
|
||||
clap = { version = "4", features = ["derive"] }
|
||||
tar = "0.4"
|
||||
flate2 = "1.1"
|
||||
indicatif = "0.17"
|
||||
indicatif = "0.18"
|
||||
chrono = { version = "0.4", features = ["serde"] }
|
||||
thiserror = "2"
|
||||
tower_governor = "0.8"
|
||||
|
||||
@@ -249,6 +249,8 @@ impl Default for AuthConfig {
|
||||
/// - `NORA_RATE_LIMIT_GENERAL_BURST` - General burst size
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct RateLimitConfig {
|
||||
#[serde(default = "default_rate_limit_enabled")]
|
||||
pub enabled: bool,
|
||||
#[serde(default = "default_auth_rps")]
|
||||
pub auth_rps: u64,
|
||||
#[serde(default = "default_auth_burst")]
|
||||
@@ -263,6 +265,9 @@ pub struct RateLimitConfig {
|
||||
pub general_burst: u32,
|
||||
}
|
||||
|
||||
fn default_rate_limit_enabled() -> bool {
|
||||
true
|
||||
}
|
||||
fn default_auth_rps() -> u64 {
|
||||
1
|
||||
}
|
||||
@@ -285,6 +290,7 @@ fn default_general_burst() -> u32 {
|
||||
impl Default for RateLimitConfig {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
enabled: default_rate_limit_enabled(),
|
||||
auth_rps: default_auth_rps(),
|
||||
auth_burst: default_auth_burst(),
|
||||
upload_rps: default_upload_rps(),
|
||||
@@ -426,6 +432,9 @@ impl Config {
|
||||
}
|
||||
|
||||
// Rate limit config
|
||||
if let Ok(val) = env::var("NORA_RATE_LIMIT_ENABLED") {
|
||||
self.rate_limit.enabled = val.to_lowercase() == "true" || val == "1";
|
||||
}
|
||||
if let Ok(val) = env::var("NORA_RATE_LIMIT_AUTH_RPS") {
|
||||
if let Ok(v) = val.parse::<u64>() {
|
||||
self.rate_limit.auth_rps = v;
|
||||
|
||||
@@ -210,6 +210,7 @@ async fn run_server(config: Config, storage: Storage) {
|
||||
|
||||
// Log rate limiting configuration
|
||||
info!(
|
||||
enabled = config.rate_limit.enabled,
|
||||
auth_rps = config.rate_limit.auth_rps,
|
||||
auth_burst = config.rate_limit.auth_burst,
|
||||
upload_rps = config.rate_limit.upload_rps,
|
||||
@@ -264,16 +265,49 @@ async fn run_server(config: Config, storage: Storage) {
|
||||
None
|
||||
};
|
||||
|
||||
// Create rate limiters before moving config to state
|
||||
let auth_limiter = rate_limit::auth_rate_limiter(&config.rate_limit);
|
||||
let upload_limiter = rate_limit::upload_rate_limiter(&config.rate_limit);
|
||||
let general_limiter = rate_limit::general_rate_limiter(&config.rate_limit);
|
||||
let rate_limit_enabled = config.rate_limit.enabled;
|
||||
|
||||
// Initialize Docker auth with proxy timeout
|
||||
let docker_auth = registry::DockerAuth::new(config.docker.proxy_timeout);
|
||||
|
||||
let http_client = reqwest::Client::new();
|
||||
|
||||
// Registry routes (shared between rate-limited and non-limited paths)
|
||||
let registry_routes = Router::new()
|
||||
.merge(registry::docker_routes())
|
||||
.merge(registry::maven_routes())
|
||||
.merge(registry::npm_routes())
|
||||
.merge(registry::cargo_routes())
|
||||
.merge(registry::pypi_routes())
|
||||
.merge(registry::raw_routes());
|
||||
|
||||
// Routes WITHOUT rate limiting (health, metrics, UI)
|
||||
let public_routes = Router::new()
|
||||
.merge(health::routes())
|
||||
.merge(metrics::routes())
|
||||
.merge(ui::routes())
|
||||
.merge(openapi::routes());
|
||||
|
||||
let app_routes = if rate_limit_enabled {
|
||||
// Create rate limiters before moving config to state
|
||||
let auth_limiter = rate_limit::auth_rate_limiter(&config.rate_limit);
|
||||
let upload_limiter = rate_limit::upload_rate_limiter(&config.rate_limit);
|
||||
let general_limiter = rate_limit::general_rate_limiter(&config.rate_limit);
|
||||
|
||||
let auth_routes = auth::token_routes().layer(auth_limiter);
|
||||
let limited_registry = registry_routes.layer(upload_limiter);
|
||||
|
||||
Router::new()
|
||||
.merge(auth_routes)
|
||||
.merge(limited_registry)
|
||||
.layer(general_limiter)
|
||||
} else {
|
||||
info!("Rate limiting DISABLED");
|
||||
Router::new()
|
||||
.merge(auth::token_routes())
|
||||
.merge(registry_routes)
|
||||
};
|
||||
|
||||
let state = Arc::new(AppState {
|
||||
storage,
|
||||
config,
|
||||
@@ -287,35 +321,9 @@ async fn run_server(config: Config, storage: Storage) {
|
||||
http_client,
|
||||
});
|
||||
|
||||
// Token routes with strict rate limiting (brute-force protection)
|
||||
let auth_routes = auth::token_routes().layer(auth_limiter);
|
||||
|
||||
// Registry routes with upload rate limiting
|
||||
let registry_routes = Router::new()
|
||||
.merge(registry::docker_routes())
|
||||
.merge(registry::maven_routes())
|
||||
.merge(registry::npm_routes())
|
||||
.merge(registry::cargo_routes())
|
||||
.merge(registry::pypi_routes())
|
||||
.merge(registry::raw_routes())
|
||||
.layer(upload_limiter);
|
||||
|
||||
// Routes WITHOUT rate limiting (health, metrics, UI)
|
||||
let public_routes = Router::new()
|
||||
.merge(health::routes())
|
||||
.merge(metrics::routes())
|
||||
.merge(ui::routes())
|
||||
.merge(openapi::routes());
|
||||
|
||||
// Routes WITH rate limiting
|
||||
let rate_limited_routes = Router::new()
|
||||
.merge(auth_routes)
|
||||
.merge(registry_routes)
|
||||
.layer(general_limiter);
|
||||
|
||||
let app = Router::new()
|
||||
.merge(public_routes)
|
||||
.merge(rate_limited_routes)
|
||||
.merge(app_routes)
|
||||
.layer(DefaultBodyLimit::max(100 * 1024 * 1024)) // 100MB default body limit
|
||||
.layer(middleware::from_fn(request_id::request_id_middleware))
|
||||
.layer(middleware::from_fn(metrics::metrics_middleware))
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
|
||||
use crate::config::RateLimitConfig;
|
||||
use tower_governor::governor::GovernorConfigBuilder;
|
||||
use tower_governor::key_extractor::SmartIpKeyExtractor;
|
||||
|
||||
/// Create rate limiter layer for auth endpoints (strict protection against brute-force)
|
||||
pub fn auth_rate_limiter(
|
||||
@@ -35,11 +36,12 @@ pub fn auth_rate_limiter(
|
||||
pub fn upload_rate_limiter(
|
||||
config: &RateLimitConfig,
|
||||
) -> tower_governor::GovernorLayer<
|
||||
tower_governor::key_extractor::PeerIpKeyExtractor,
|
||||
SmartIpKeyExtractor,
|
||||
governor::middleware::StateInformationMiddleware,
|
||||
axum::body::Body,
|
||||
> {
|
||||
let gov_config = GovernorConfigBuilder::default()
|
||||
.key_extractor(SmartIpKeyExtractor)
|
||||
.per_second(config.upload_rps)
|
||||
.burst_size(config.upload_burst)
|
||||
.use_headers()
|
||||
@@ -53,11 +55,12 @@ pub fn upload_rate_limiter(
|
||||
pub fn general_rate_limiter(
|
||||
config: &RateLimitConfig,
|
||||
) -> tower_governor::GovernorLayer<
|
||||
tower_governor::key_extractor::PeerIpKeyExtractor,
|
||||
SmartIpKeyExtractor,
|
||||
governor::middleware::StateInformationMiddleware,
|
||||
axum::body::Body,
|
||||
> {
|
||||
let gov_config = GovernorConfigBuilder::default()
|
||||
.key_extractor(SmartIpKeyExtractor)
|
||||
.per_second(config.general_rps)
|
||||
.burst_size(config.general_burst)
|
||||
.use_headers()
|
||||
@@ -102,6 +105,7 @@ mod tests {
|
||||
#[test]
|
||||
fn test_custom_config() {
|
||||
let config = RateLimitConfig {
|
||||
enabled: true,
|
||||
auth_rps: 10,
|
||||
auth_burst: 20,
|
||||
upload_rps: 500,
|
||||
|
||||
Reference in New Issue
Block a user