فرض کنید می‌خواهیم ابزارهای شرکت را با apt install نصب کنیم و نسخه‌های دریافت‌شده از اینترنت را ابتدا آزمایش و سپس برای سرورها منتشر کنیم. aptly برای مدیریت این چرخه طراحی شده است: دریافت بسته، ساخت مخزن محلی، ثبت snapshot و تولید مخزن قابل‌مصرف برای APT.

این راهنما یک مخزن کوچکِ امضاشده می‌سازد، آن را با Nginx ارائه می‌کند و سپس دریافت گزینشی از دبیان و جابه‌جایی snapshot را توضیح می‌دهد. فرمان‌ها نمونهٔ اجرایی برای ماشین آزمایش هستند؛ در جریان نگارش، روی سرور واقعی اجرا نشده‌اند.

چهار مفهوم اصلی aptly#

مفهومنقش
mirrorوضعیت دریافت‌شده از یک مخزن خارجی
repoمخزن محلی قابل‌تغییر برای فایل‌های deb
snapshotفهرست ثابت نسخه‌های بسته‌ها در یک لحظه
publishساخت فهرست‌ها، امضا و فایل‌های قابل‌دریافت برای APT
Upstream --> mirror --> snapshot --+
                                  +--> publish --> Nginx --> APT clients
CI .deb ---> local repo --> snapshot+

به‌روزرسانی mirror یا افزودن بسته به repo، snapshot قبلی را عوض نمی‌کند. همین جدایی اجازه می‌دهد دریافت روزانه انجام شود، اما انتشار production فقط پس از آزمایش تغییر کند. معرفی و مدل دادهٔ aptly

۱. محدودهٔ آزمایش و آماده‌سازی سرور#

میزبان این مثال یک ماشین دبیان با معماری amd64 و مخازن رسمی سالم است. دامنهٔ apt.example.org نمونه است؛ آن را با نام داخلی خود عوض کنید. بخش مخزن محلی روی اوبونتو هم قابل‌استفاده است، اما بسته‌های واقعی باید برای انتشار مقصد ساخته و آزمایش شوند.

با حساب مدیر، ابزارها و کاربر اختصاصی را آماده کنید:

sudo apt update
sudo apt install aptly gnupg nginx debian-archive-keyring
sudo useradd --create-home --home-dir /var/lib/aptly --shell /bin/bash aptly
sudo install -d -o aptly -g aptly -m 0755 /srv/aptly

اگر کاربر از قبل وجود دارد، مرحلهٔ useradd را تکرار نکنید. فایل /etc/aptly.conf را با محتوای زیر بسازید:

{
  "rootDir": "/srv/aptly",
  "architectures": ["amd64"],
  "downloadConcurrency": 4,
  "gpgProvider": "gpg",
  "gpgDisableSign": false,
  "gpgDisableVerify": false
}

این تنظیم پایگاه داده را زیر db، بسته‌ها را زیر pool و خروجی انتشار را زیر public قرار می‌دهد. فایل تنظیمات داخل home کاربر بر /etc/aptly.conf تقدم دارد؛ با aptly config show تنظیم مؤثر را بررسی کنید. پیکربندی aptly

وارد حساب ناشر شوید؛ فرمان‌های aptly و GPG تا بخش Nginx در همین نشست اجرا می‌شوند:

sudo -iu aptly
umask 022
aptly version
aptly config show

۲. کلید امضای داخلی بسازیم#

gpg --quick-generate-key 'Company APT Archive <apt@example.org>' rsa3072 sign 1y
gpg --list-secret-keys --keyid-format long --fingerprint

GPG برای حفاظت کلید خصوصی رمز می‌خواهد. اثرانگشت کامل کلید ساخته‌شده را در متغیر زیر جایگزین کنید؛ این متغیر رمز نیست:

APTLY_SIGNING_FPR='REPLACE_WITH_FULL_FINGERPRINT'
mkdir -p /srv/aptly/public
gpg --armor --export "$APTLY_SIGNING_FPR" > /srv/aptly/public/company-archive.asc

کلید عمومی برای کلاینت‌هاست؛ کلید خصوصی در home کاربر ناشر می‌ماند و نباید داخل public قرار گیرد. برای این آزمایش، امضا تعاملی است؛ رمز را داخل دستور، Git یا تنظیمات وب‌سرور ننویسید. ساخت و صدور کلید در GnuPG

۳. یک بستهٔ داخلی کوچک بسازیم#

برای اینکه مثال به خروجی CI وابسته نباشد، یک بستهٔ داده می‌سازیم. این بسته فقط یک فایل متنی نصب می‌کند و اسکریپت نگه‌دارنده ندارد:

mkdir -p ~/build/company-hello/DEBIAN
mkdir -p ~/build/company-hello/usr/share/company-hello
cat > ~/build/company-hello/DEBIAN/control <<'EOF'
Package: company-hello
Version: 1.0-1
Section: misc
Priority: optional
Architecture: all
Maintainer: Example Operations <apt@example.org>
Description: Internal repository demonstration package
EOF
printf '%s\n' 'Hello from the internal APT repository.' \
  > ~/build/company-hello/usr/share/company-hello/message.txt
dpkg-deb --root-owner-group --build ~/build/company-hello ~/company-hello_1.0-1_all.deb

Architecture: all برای این بستهٔ مستقل از معماری مناسب است؛ برنامهٔ باینری واقعی ممکن است به معماری، ABI و کتابخانه‌های انتشار مقصد وابسته باشد. گزینهٔ --root-owner-group مالکیت فایل‌های داخل بسته را برای نصب سیستمی تنظیم می‌کند، بدون اینکه ساخت بسته به root نیاز داشته باشد. راهنمای dpkg-deb

اکنون مخزن محلی و snapshot را بسازید:

aptly repo create -distribution=internal -component=main company-tools
aptly repo add company-tools ~/company-hello_1.0-1_all.deb
aptly snapshot create company-tools-v1 from repo company-tools

در فرایند واقعی، فایل .deb تأییدشدهٔ CI جای بستهٔ آزمایش را می‌گیرد. repo add بسته را وارد مخزن می‌کند؛ به معنی نصب آن روی سرور aptly نیست. snapshot هم نمای ثابتی از محتویات repo می‌سازد. ساخت repo ، افزودن بسته ، ساخت snapshot

۴. snapshot را منتشر کنیم#

aptly publish snapshot \
  -distribution=internal -component=main \
  -origin=Company -label=Company \
  -gpg-key="$APTLY_SIGNING_FPR" \
  company-tools-v1 internal

آخرین internal پیشوند مسیر است؛ -distribution=internal نام انتشار داخل dists است. حاصل، ساختاری شبیه زیر دارد:

/srv/aptly/public/
├── company-archive.asc
└── internal/
    ├── dists/internal/InRelease
    ├── dists/internal/Release
    ├── dists/internal/Release.gpg
    └── pool/...

مقدار Origin برای توصیف ناشر و سیاست‌های کلاینت است؛ خودش هویت رمزنگاری ایجاد نمی‌کند. امضا با کلید انتخاب‌شده انجام می‌شود. کلاینت برای این خروجی باید کلید سازمان را بشناسد. انتشار snapshot در aptly

برای مشاهدهٔ سریع محلی می‌توان از فرمان زیر استفاده کرد؛ با Ctrl+C متوقفش کنید:

aptly serve -listen=127.0.0.1:8080

مستندات aptly وب‌سرور داخلی را برای آزمایش معرفی می‌کنند، نه ارائهٔ production. برای سرویس دائمی، فایل‌های منتشرشده را با وب‌سرور مستقل ارائه می‌کنیم. راهنمای aptly serve

۵. ارائه با Nginx و HTTPS#

از نشست aptly با exit خارج شوید. پیش‌نیاز این بخش، DNS درست و گواهی TLS معتبر برای نام مخزن است. در صورت استفاده از CA داخلی، کلاینت‌ها باید از قبل آن CA را بشناسند. مسیر گواهی‌های زیر نمونه است و باید واقعاً ایجاد شده باشد.

فایل /etc/nginx/sites-available/apt-repository:

server {
    listen 443 ssl;
    server_name apt.example.org;

    ssl_certificate /etc/ssl/apt/fullchain.pem;
    ssl_certificate_key /etc/ssl/apt/privkey.pem;

    root /srv/aptly/public;
    autoindex off;

    location / {
        try_files $uri =404;
    }
}

فعال‌سازی در نصب متعارف Nginx روی دبیان:

sudo ln -s /etc/nginx/sites-available/apt-repository /etc/nginx/sites-enabled/apt-repository
sudo nginx -t
sudo systemctl reload nginx

فقط پس از موفقیت nginx -t reload کنید. وب‌سرور به دسترسی خواندن خروجی و عبور از پوشه‌های والد نیاز دارد؛ نباید مالک کلید خصوصی ناشر یا پایگاه داده باشد. root را دقیقاً روی public بگذارید، نه /srv/aptly یا home کاربر. رفتار root و try_files در Nginx

در این معماری برای پاسخ به درخواست دانلود، لازم نیست خود aptly همیشه در حال اجرا باشد. aptly هنگام تغییر انتشار فایل تولید می‌کند و Nginx آن‌ها را ارائه می‌دهد.

۶. اعتماد کلاینت را راه‌اندازی کنیم#

روی کلاینت آزمایش، کلید عمومی را دریافت کنید:

curl --fail --show-error --location \
  https://apt.example.org/company-archive.asc \
  --output /tmp/company-archive.asc
gpg --show-keys --with-fingerprint /tmp/company-archive.asc

پیش از نصب کلید، اثرانگشت کامل را با مقدار تحویل‌گرفته‌شده از کانال سازمانی مورداعتماد مقایسه کنید. دریافت کلید و اثرانگشت از همان سرور، تأیید مستقل نیست. سپس:

sudo install -d -m 0755 /etc/apt/keyrings
sudo install -m 0644 /tmp/company-archive.asc /etc/apt/keyrings/company-archive.asc

فایل /etc/apt/sources.list.d/company.sources را بسازید:

Types: deb
URIs: https://apt.example.org/internal
Suites: internal
Components: main
Architectures: amd64
Signed-By: /etc/apt/keyrings/company-archive.asc

این ورودی به تنظیمات موجود اضافه می‌شود. مخازن رسمی را برای این آزمایش حذف نکنید؛ مخزن ما جایگزین کامل سیستم‌عامل نیست. Signed-By کلید مجاز این منبع را محدود می‌کند و فرمت .asc برای کلید ASCII-armored مناسب است. اعتماد به مخازن ثالث در اوبونتو

sudo apt update
apt-cache policy company-hello
sudo apt install company-hello
cat /usr/share/company-hello/message.txt

معیار موفقیت: update بدون خطای اعتبارسنجی تمام شود، policy نامزد را از منبع داخلی نشان دهد و فایل متنی نصب شود. معتبر بودن TLS و معتبر بودن امضای APT را دو بررسی جدا در نظر بگیرید.

۷. دریافت گزینشی از دبیان#

دوباره وارد کاربر aptly شوید. این بار ورودی را با کلید عمومی دبیان بررسی می‌کنیم؛ کلید امضای سازمان برای تأیید ورودی رسمی کاربرد ندارد:

sudo -iu aptly
aptly mirror create \
  -architectures=amd64 \
  -keyring=/usr/share/keyrings/debian-archive-keyring.gpg \
  -filter='hello' -filter-with-deps \
  debian-trixie-hello https://deb.debian.org/debian trixie main

aptly mirror update \
  -keyring=/usr/share/keyrings/debian-archive-keyring.gpg \
  debian-trixie-hello

aptly snapshot create debian-hello-v1 from mirror debian-trixie-hello
aptly snapshot verify debian-hello-v1

mirror create تعریف و اطلاعات اولیهٔ میرور را می‌سازد؛ دریافت محتوا با mirror update انجام می‌شود. فیلتر، فایل‌های بستهٔ دانلودشده را محدود می‌کند، اما فهرست‌ها برای اعمال فیلتر همچنان خوانده می‌شوند. -filter-with-deps وابستگی‌ها را هم دنبال می‌کند. ساخت mirror ، به‌روزرسانی mirror

snapshot verify بررسی روابط وابستگی است؛ جای اجرای نصب و آزمون برنامه روی کلاینت مقصد را نمی‌گیرد. خروجی آن را بخوانید و فرض نکنید هر snapshot فیلترشده یک توزیع کامل است. بررسی snapshot

برای انتشار این مجموعه در مسیر مجزا، اثرانگشت سازمان را در نشست جدید دوباره تنظیم کنید:

APTLY_SIGNING_FPR='REPLACE_WITH_FULL_FINGERPRINT'
aptly publish snapshot \
  -distribution=trixie -component=main \
  -origin=Company -label=Company \
  -gpg-key="$APTLY_SIGNING_FPR" \
  debian-hello-v1 approved-debian

URI کلاینت دبیان برای این خروجی https://apt.example.org/approved-debian و Suite آن trixie است؛ Signed-By همچنان کلید سازمان خواهد بود، چون Release خروجی دوباره تولید شده است.

۸. برای اوبونتو و میرور کامل چه تغییر می‌کند؟#

برای اوبونتو باید کلید آرشیو اوبونتو، انتشار مقصد و بالادست مناسب همان معماری را انتخاب کنید. مخزن دبیان را به کلاینت اوبونتو اضافه نکنید. در نمونهٔ noble، مجموعهٔ پایه با noble-updates و noble-security یکی نیست؛ security pocket مسیر دریافت اصلاحیه‌های امنیتی است. ساختار به‌روزرسانی امنیتی اوبونتو

اگر هدف جایگزینی اینترنت برای نصب‌های سازمان است، این موارد را در طراحی پوشش دهید:

  • معماری‌ها و componentهای موردنیاز کلاینت‌ها؛
  • انتشار پایه، updates و security و در صورت نیاز backports؛
  • وابستگی‌های بسته‌های داخلی و بسته‌های نصب اولیه؛
  • سیاست دریافت source packageها و نیازهای بازتوزیع؛
  • ظرفیت دیسک و مدت نگهداری نسخه‌های قبلی.

برای حفظ تفکیک componentها، aptly توصیه می‌کند mirrorهای جدا ساخته شوند و انتشار چندمؤلفه‌ای از snapshotهای آن‌ها انجام شود. آزمایش hello در این مقاله یک میرور کامل یا راهکار آمادهٔ air-gap نیست. انتشار چندمؤلفه‌ای aptly

۹. انتشار نسخهٔ جدید و بازگشت مخزن#

فرض کنید CI فایل واقعی company-hello_1.1-1_all.deb را ساخته و در home ناشر قرار داده است. هر محتوای تازه باید نسخهٔ تازه داشته باشد؛ یک شمارهٔ نسخه را با بایت‌های متفاوت دوباره استفاده نکنید.

aptly repo add company-tools ~/company-hello_1.1-1_all.deb
aptly snapshot create company-tools-v2 from repo company-tools
aptly snapshot diff company-tools-v1 company-tools-v2

aptly publish snapshot \
  -distribution=internal -component=main \
  -origin=Company -label=Company \
  -gpg-key="$APTLY_SIGNING_FPR" \
  company-tools-v2 staging

کلاینت آزمایش را به پیشوند staging متصل کنید و نصب و ارتقا را بسنجید. پس از تأیید، همان snapshot را برای مسیر اصلی انتخاب کنید:

aptly publish switch -gpg-key="$APTLY_SIGNING_FPR" internal internal company-tools-v2

در این دستور، آرگومان اول نام distribution و دوم prefix است. برای بازگرداندن نمای مخزن:

aptly publish switch -gpg-key="$APTLY_SIGNING_FPR" internal internal company-tools-v1

publish switch محتوای انتشار را تغییر می‌دهد و تنظیمات آن را نگه می‌دارد؛ منابع کلاینت نیاز به تغییر URL ندارند. راهنمای publish switch

بازگشت مخزن، rollback ماشین‌های نصب‌شده نیست. کلاینتی که نسخهٔ جدید را گرفته معمولاً خودکار downgrade نمی‌کند. حتی downgrade بسته هم تغییرات پایگاه داده یا داده‌های برنامه را الزاماً برنمی‌گرداند؛ بازیابی برنامه باید طرح جدا داشته باشد. قواعد downgrade در APT

۱۰. نگهداری سرویس داخلی#

این معماری پیشنهادی را به سه کار مستقل تقسیم کنید: دریافت منظم بالادست، آزمایش snapshot و انتشار تأییدشده. موفقیت دریافت نباید به‌تنهایی محرک انتشار production باشد. برای هر چرخه، نام snapshot، زمان دریافت، نتیجهٔ آزمون و انتشار مقصد را ثبت کنید.

snapshot یک فهرست ثابت است و برای بازیابی به فایل‌های بسته نیاز دارد. از پایگاه داده و pool نسخهٔ پشتیبان سازگار بگیرید؛ پشتیبان کلید خصوصی را جدا و محافظت‌شده نگه دارید. پیش از پاک‌سازی، snapshotهایی را که باید برای بازگشت حفظ شوند مشخص کنید. aptly db cleanup بسته‌ها و فایل‌های بدون ارجاع را پاک می‌کند و نباید صرفاً برای خالی‌کردن فوری دیسک بدون بررسی اجرا شود. پاک‌سازی پایگاه دادهٔ aptly

پیشنهادهای عملی برای پایش:

شاخصپرسشی که پاسخ می‌دهد
آخرین sync موفقچقدر از بالادست عقب افتاده‌ایم؟
snapshot منتشرشدهکلاینت اکنون دقیقاً چه مجموعه‌ای می‌بیند؟
اصلاحیه‌های امنیتی معطلکنترل انتشار چقدر تأخیر ساخته است؟
فضای دیسک و خطاهای HTTPآیا فایل‌های موردنیاز واقعاً قابل‌دریافت‌اند؟
انقضای کلید و گواهیآیا اعتماد یا اتصال به‌زودی قطع می‌شود؟

برای تعویض کلید، کلید عمومی جدید را پیش از تغییر امضای خروجی به کلاینت‌ها برسانید. کلید دانلود APT، گواهی TLS و مجوز انتشار CI سه موضوع مستقل‌اند. مستندات اوبونتو بستهٔ keyring اختصاصی را راهی برای رساندن به‌روزرسانی کلید مخزن ثالث معرفی می‌کند. مدیریت کلید مخزن ثالث

محصول نهایی این فرایند فقط یک پوشهٔ deb نیست: مجموعه‌ای قابل‌شناسایی از بسته‌ها، یک ناشر مشخص، مسیر تأیید کلاینت و روشی برای رساندن تغییرات آزمایش‌شده به سرورهاست.