ساخت مخزن داخلی APT با aptly؛ از بستهٔ شخصی تا انتشار کنترلشده
فهرست نوشته
فرض کنید میخواهیم ابزارهای شرکت را با 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 نیست: مجموعهای قابلشناسایی از بستهها، یک ناشر مشخص، مسیر تأیید کلاینت و روشی برای رساندن تغییرات آزمایششده به سرورهاست.