SSD Nodes Learn 🎉 VPS از $5.50/ماه
راهنماها Matt Connorتوسط Matt Connor

امن‌سازی Ollama API و رفع مشکل نبود احراز هویت

سرویس Ollama روی پورت 11434 هیچ احراز هویتی ندارد و دسترسی عمومی به آن خطرناک است. در این مقاله 3 روش عملی برای محدود کردن دسترسی و ایمن‌سازی سرور خود را بررسی می‌کنیم.

رابط برنامه‌نویسی Ollama فاقد رمز عبور است

رابط برنامه‌نویسی (API) Ollama هیچ‌گونه احراز هویتی ندارد. در سروری که Ollama را روی آن اجرا می‌کنید، هیچ کاربر، رمز عبور، بررسی کلید یا لیست مجاز (allowlist) وجود ندارد. هر چیزی که بتواند یک اتصال TCP به پورت 11434 برقرار کند، می‌تواند مدل‌های شما را لیست کند، آن‌ها را اجرا نماید، مدل‌های جدید دانلود کند و مدل‌های موجود را حذف نماید.

مستندات رسمی به‌صراحت بیان می‌کنند: "هنگام دسترسی محلی به API Ollama از طریق http://localhost:11434، هیچ احراز هویتی لازم نیست." کلمه محلی (locally) کل مدل امنیتی را در بر می‌گیرد. Ollama به‌صورت پیش‌فرض روی 127.0.0.1 گوش می‌دهد؛ بنابراین روی یک لپ‌تاپ، رابط loopback نقش کنترل دسترسی را ایفا می‌کند. اگر این listener را به یک آدرس عمومی منتقل کنید، کنترل دسترسی از بین می‌رود، زیرا هیچ جایگزینی برای آن در نظر گرفته نشده است.

به همین دلیل است که این موضوع در یک VPS (سرور مجازی خصوصی) اهمیت پیدا می‌کند. حالت پیش‌فرض امن است. اولین تغییری که اکثر افراد ایجاد می‌کنند، یعنی باز کردن listener برای اینکه دستگاه دومی بتواند از مدل استفاده کند، همان تغییری است که تمام محافظت‌ها را به‌یک‌باره حذف می‌کند.

پورت 11434 باز چه اطلاعاتی را فاش می‌کند

تمام endpointها. هیچ حالت read-only یا پورت مدیریتی مجزایی وجود ندارد. این‌ها درخواست‌های واقعی هستند که به‌جای localhost، به آدرس سرور ارسال می‌شوند:

# List every model on the box
curl http://SERVER_IP:11434/api/tags

# See what is loaded into memory right now
curl http://SERVER_IP:11434/api/ps

# Run a prompt on your hardware
curl http://SERVER_IP:11434/api/generate -d '{"model":"llama3.2","prompt":"Why is the sky blue?"}'

# Write several gigabytes to your disk
curl http://SERVER_IP:11434/api/pull -d '{"model":"llama3.2"}'

# Remove a model
curl -X DELETE http://SERVER_IP:11434/api/delete -d '{"model":"llama3.2"}'

از دیدگاه اپراتور، چهار مشکل اصلی رخ می‌دهد:

  • پردازنده (CPU) یا کارت گرافیک (GPU) شما برای شخص دیگری عملیات استنتاج (inference) انجام می‌دهد. در پلن‌هایی که محدودیت مصرف CPU دارند، بار کاری مداوم به معنای هدر رفتن سهمیه شما توسط یک غریبه است و کنترل هزینه‌های بار کاری هوش مصنوعی روی VPS زمانی که شما تنها کاربر نباشید، بسیار دشوارتر می‌شود.
  • /api/pull روی دیسک شما می‌نویسد. مدل‌ها بین 2 تا 40 گیگابایت حجم دارند. یک حلقه از دستورات pull باعث پر شدن فضای ذخیره‌سازی می‌شود و پر شدن کامل دیسک، نه تنها Ollama، بلکه تمام سرویس‌های دیگر روی سرور را از کار می‌اندازد.
  • درخواست‌ها وارد پردازش شما شده و ثبت (log) می‌شوند. در سطح لاگ پیش‌فرض، Ollama فقط متادیتا را ذخیره می‌کند؛ بنابراین شما endpoint، وضعیت، تأخیر و آدرس کلاینت را دریافت می‌کنید، نه متن prompt را. با این حال، این همچنان سندی از این است که چه کسی و برای چه کاری از سرور شما استفاده کرده است که در journal شما باقی می‌ماند، در حالی که شما انتخاب نکرده‌اید چنین داده‌هایی را جمع‌آوری کنید.
  • /api/delete مدل‌ها را حذف می‌کند. بازگرداندن آن‌ها به معنای دانلود مجددشان با استفاده از پهنای باند خودتان است.

هیچ‌کدام از این موارد نیازی به exploit ندارند. این رفتار مستند API است که دقیقاً مطابق طراحی عمل می‌کند.

کلید Ed25519 یک ابزار کنترل دسترسی نیست

اگر عبارت "Ollama API key" را جستجو کنید، با دو مفهوم متفاوت مواجه می‌شوید. هیچ‌کدام از این دو، رمز عبور سرور شما نیستند و تفکیک آن‌ها از یکدیگر، بخش بزرگی از سردرگمی‌ها را برطرف می‌کند.

مورد اول، جفت‌کلید هویت است. Ollama در اولین اجرا یک جفت‌کلید Ed25519 تولید می‌کند. در لینوکس، اسکریپت نصب یک کاربر سیستمی به نام ollama ایجاد می‌کند که دایرکتوری خانگی آن در /usr/share/ollama قرار دارد، بنابراین این جفت‌کلید در مسیر زیر ذخیره می‌شود:

/usr/share/ollama/.ollama/id_ed25519
/usr/share/ollama/.ollama/id_ed25519.pub

این کلید به سمت بیرون اشاره دارد. ollama signin نیمه عمومی (public) آن را در حساب کاربری ollama.com شما ثبت می‌کند و این همان چیزی است که به شما اجازه می‌دهد مدلی را به رجیستری ارسال (push) کنید یا یک مدل خصوصی را دریافت (pull) نمایید. این کلید، هویت دستگاه شما را برای ollama.com اثبات می‌کند. این کلید هیچ درخواستی از کلاینت‌هایی که به دستگاه شما متصل می‌شوند، ندارد. حذف کردن، تغییر دادن یا هرگز ایجاد نکردن آن، هیچ تغییری در اینکه چه کسی می‌تواند API شما را فراخوانی کند، ایجاد نمی‌کند.

مورد دوم، OLLAMA_API_KEY است. این متغیر حاوی کلیدی است که شما در https://ollama.com/settings/keys ایجاد می‌کنید و کلاینت شما هنگام فراخوانی API میزبانی‌شده در https://ollama.com/api، آن را به عنوان Authorization: Bearer $OLLAMA_API_KEY ارسال می‌کند. این یک اعتبارنامه برای سرویس آن‌هاست که شما به عنوان کلاینت از آن استفاده می‌کنید. ollama serve خودتان هرگز آن را نمی‌خواند. تنظیم OLLAMA_API_KEY روی VPS، هیچ رمز عبوری روی VPS شما قرار نمی‌دهد.

بنابراین هیچ تنظیمی برای فعال‌سازی وجود ندارد. سه روش دفاعی زیر همگی به یک شکل عمل می‌کنند: پورت را غیرقابل دسترس نگه دارید و چیزی را در مقابل آن قرار دهید که احراز هویت را انجام دهد.

بررسی وضعیت گوش دادن سرور در حال حاضر

sudo ss -tlnp | grep 11434

نتیجهٔ ایمن، آدرس loopback را نشان می‌دهد:

LISTEN 0  4096  127.0.0.1:11434  0.0.0.0:*  users:(("ollama",pid=812,fd=3))

نتیجهٔ در معرض خطر، تمام رابط‌های شبکه را نشان می‌دهد:

LISTEN 0  4096  0.0.0.0:11434  0.0.0.0:*  users:(("ollama",pid=812,fd=3))

عبارت 0.0.0.0 به معنای تمام آدرس‌های IPv4 روی دستگاه، از جمله آدرس عمومی است. *:11434 و [::]:11434 نیز همین مفهوم را با احتساب IPv6 بیان می‌کنند.

اکنون از خارج از سرور بررسی کنید. این دستور را روی لپ‌تاپ خود اجرا کنید، نه روی سرور:

curl -m 5 http://YOUR_SERVER_IP:11434/api/version

curl: (28) Connection timed out after 5001 milliseconds پاسخی است که انتظار دارید، و curl: (7) Failed to connect ... Connection refused نیز همین‌طور. یک شیء JSON که شامل فیلد version باشد، به این معناست که کل API برای هر کسی که درخواست بفرستد، در دسترس است. تست کردن با curl روی خودِ سرور چیزی را ثابت نمی‌کند، زیرا loopback همیشه پاسخ می‌دهد.

قرار گرفتن در معرض اینترنت معمولاً به دو روش اتفاق می‌افتد. روش اول، ویرایش عمدی است، زیرا فردی نیاز داشته است که ماشین دومی به مدل دسترسی داشته باشد:

sudo systemctl edit ollama.service
[Service]
Environment="OLLAMA_HOST=0.0.0.0:11434"

همان یک خط، کل دلیل در معرض قرار گرفتن است. روش دوم Docker است، که اصلاً از شما نمی‌خواهد چیزی را ویرایش کنید. این مورد بخش مخصوص به خود را در زیر دارد.

دفاع 1: محدود کردن به localhost و استفاده از تونل

این اولین راهکار پیشنهادی است. این روش به هیچ نرم‌افزار جدیدی نیاز ندارد و هیچ اعتبارنامه‌ای ایجاد نمی‌کند که احتمال نشت داشته باشد. پورت مورد نظر هرگز روی رابط عمومی (public interface) ایجاد نمی‌شود، بنابراین اسکنرها نمی‌توانند آن را پیدا کنند.

آدرس bind را به‌جای تکیه بر پیش‌فرض، به‌صورت صریح تنظیم کنید:

sudo systemctl edit ollama.service
[Service]
Environment="OLLAMA_HOST=127.0.0.1:11434"

این دستور فایل /etc/systemd/system/ollama.service.d/override.conf را می‌نویسد. آن را اعمال کرده و بررسی کنید:

sudo systemctl daemon-reload
sudo systemctl restart ollama
sudo ss -tlnp | grep 11434

ss اکنون باید 127.0.0.1:11434 را نشان دهد. اگر همچنان 0.0.0.0 را نشان می‌دهد، یک فایل drop-in دیگر اولویت دارد. دستور systemctl cat ollama.service را اجرا کنید تا unit و تمام فایل‌های drop-in به همراه مسیرشان فهرست شوند، سپس فایل قدیمی را حذف کنید.

برای استفاده از این مدل از روی لپ‌تاپ خود، پورت را از طریق SSH فوروارد کنید:

ssh -N -L 11434:127.0.0.1:11434 you@your-server

-L 11434:127.0.0.1:11434 پورت 11434 را روی لپ‌تاپ شما باز می‌کند و هر درخواستی که به آن برسد را به 127.0.0.1:11434 از دید سرور هدایت می‌کند. -N به SSH می‌گوید که هیچ دستور راه دوری را اجرا نکند، بنابراین فرآیند فقط تونل را باز نگه می‌دارد. تا زمانی که این دستور اجرا می‌شود، دستور زیر روی لپ‌تاپ شما کار می‌کند:

curl -s http://localhost:11434/api/tags

با دو خطا مواجه خواهید شد. bind [127.0.0.1]:11434: Address already in use به این معنی است که لپ‌تاپ شما در حال اجرای Ollama روی همان پورت است، بنابراین یک پورت محلی متفاوت با -L 11500:127.0.0.1:11434 انتخاب کنید و کلاینت خود را روی 11500 تنظیم کنید. پاسخ خالی از طریق تونلی که به‌درستی متصل شده، به این معنی است که SSH کار می‌کند اما Ollama در سمت سرور گوش نمی‌دهد؛ بنابراین پیش از تغییر دستور SSH، وضعیت ss را در سرور بررسی کنید.

برای چندین دستگاه کلاینت، یک شبکه خصوصی بهتر از ایجاد یک تونل برای هر نفر است. دستگاه‌ها را در WireGuard یا Tailscale قرار دهید، سپس Ollama را به‌جای 0.0.0.0، به آدرس آن در همان شبکه bind کنید:

[Service]
Environment="OLLAMA_HOST=10.8.0.1:11434"

در این حالت، پورت فقط روی رابطی وجود دارد که برای اتصال به آن به کلید نیاز دارید. این روش در برابر اشتباهات فایروال نیز مقاوم است، زیرا قانونی که به‌طور تصادفی دسترسی عمومی را باز می‌گذارد، همچنان نمی‌تواند سرویسی را که روی رابط عمومی گوش نمی‌دهد، در معرض دید قرار دهد.

دفاع 2: استفاده از reverse proxy برای بررسی bearer token

هنگامی که سرویسی در اینترنت عمومی باید مدل را فراخوانی کند، Ollama را روی loopback نگه دارید و یک proxy در مقابل آن قرار دهید. این proxy عملیات TLS (امنیت لایه انتقال) را خاتمه می‌دهد و درخواست‌های فاقد header صحیح را رد می‌کند. Ollama همچنان فقط اتصالات از 127.0.0.1 را می‌پذیرد، بنابراین proxy تنها مسیر ورود است.

ابتدا یک توکن واقعی تولید کنید. آن را به‌صورت دستی نسازید:

openssl rand -base64 36

یک سایت nginx که آن را بررسی می‌کند:

map $http_authorization $ollama_ok {
    default                                   0;
    "Bearer PASTE_YOUR_GENERATED_TOKEN_HERE"  1;
}

server {
    listen 443 ssl;
    server_name llm.example.com;

    ssl_certificate     /etc/letsencrypt/live/llm.example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/llm.example.com/privkey.pem;

    location = /api/pull   { return 403; }
    location = /api/delete { return 403; }
    location = /api/push   { return 403; }

    location / {
        if ($ollama_ok = 0) { return 401; }

        proxy_pass http://127.0.0.1:11434;
        proxy_set_header Host 127.0.0.1:11434;
        proxy_buffering off;
        proxy_read_timeout 600s;
    }
}

پنج خط در اینجا کار اصلی را انجام می‌دهند و هر کدام از یک شکست که در غیر این صورت با آن مواجه می‌شدید، جلوگیری می‌کنند.

استفاده از if داخل یک بلاک location معمولاً در nginx ایده بدی است، اما بدنه دقیقاً return یکی از دو شکلی است که رفتار قابل پیش‌بینی دارد، بنابراین این استفاده ایمن است.

location = /api/pull یک تطابق دقیق است و nginx تطابق‌های دقیق را بالاتر از پیشوند location / رتبه‌بندی می‌کند، بنابراین آن سه endpoint پیش از آنکه توکن حتی بررسی شود، رد می‌شوند. یک توکن معتبر سپس امکان استنتاج (inference) را فراهم می‌کند، نه توانایی پر کردن دیسک شما را.

proxy_set_header Host 127.0.0.1:11434; اهمیت دارد زیرا Ollama هدرهای ورودی Host و Origin را بررسی می‌کند. عبور مستقیم hostname عمومی proxy می‌تواند منجر به تولید یک 403 Forbidden شود که از سمت Ollama آمده است نه از سمت nginx، که اشکال‌زدایی آن گیج‌کننده است. OLLAMA_ORIGINS اهرم دیگر است، برای کلاینت مرورگری که نیاز دارد یک origin خاص مجاز باشد.

proxy_buffering off; اهمیت دارد زیرا Ollama پاسخ خود را توکن به توکن stream می‌کند. با فعال بودن buffering، nginx جریان را نگه می‌دارد و آن را در پایان به صورت یکجا تحویل می‌دهد، بنابراین کلاینت شما در تمام مدت تولید پاسخ، منجمد به نظر می‌رسد.

proxy_read_timeout 600s; اهمیت دارد زیرا مقدار پیش‌فرض nginx برابر با 60 ثانیه است. یک تولید طولانی روی CPU به راحتی از این زمان می‌گذرد، کلاینت 504 Gateway Time-out دریافت می‌کند و /var/log/nginx/error.log مقدار upstream timed out (110: Connection timed out) while reading response header from upstream را ثبت می‌کند. درخواست همچنان در حال پردازش بوده است، اما nginx از آن قطع امید کرده است.

پیکربندی را reload کرده و هر دو مسیر را تست کنید:

sudo nginx -t && sudo systemctl reload nginx
curl -s -o /dev/null -w '%{http_code}\n' https://llm.example.com/api/tags
curl -s -H "Authorization: Bearer YOUR_TOKEN" https://llm.example.com/api/tags

اولی باید 401 را چاپ کند. دومی باید لیست مدل‌های شما را چاپ کند. اگر اولی هم لیست مدل‌ها را برگرداند، بلاک map در scope اشتباهی قرار دارد. این بلاک متعلق به سطح http است، بنابراین آن را در فایلی زیر /etc/nginx/conf.d/ یا بالای بلاک server قرار دهید، هرگز داخل server نگذارید.

Caddy همین کار را با احراز هویت پایه (basic authentication) در چهار خط انجام می‌دهد که برای کلاینت مرورگر مناسب‌تر از bearer token است:

llm.example.com {
	basic_auth {
		apiuser PASTE_BCRYPT_HASH_HERE
	}
	reverse_proxy 127.0.0.1:11434
}

دستور caddy hash-password را اجرا کنید تا hash bcrypt مورد انتظار آن تولید شود. یک تله در نام‌گذاری: این دستور پیش از Caddy v2.8 به صورت basicauth بود و اکنون basic_auth است، بنابراین پیکربندی کپی‌شده از راهنماهای قدیمی بارگذاری نمی‌شود و Caddy دستوری را که تشخیص نداده است، نام می‌برد.

هر proxy که انتخاب کنید، این یک secret مشترک برای همه است. هر کلاینتی که آن را در اختیار دارد دسترسی یکسانی دارد و ابطال آن به معنای ویرایش پیکربندی و به‌روزرسانی همزمان تمام فراخوان‌کننده‌ها است.

دفاع 3: یک درگاه (Gateway) که برای هر کلاینت کلید صادر می‌کند

هنگامی که بیش از یک شخص یا برنامه مدل را فراخوانی می‌کنند، یک توکن مشترک دیگر پاسخگو نیست. در این حالت نمی‌توان تشخیص داد کدام کلاینت باعث ایجاد بار شده است و نمی‌توان دسترسی یکی از آن‌ها را بدون قطع دسترسی همه مسدود کرد. یک درگاه (Gateway) در همان جایگاهی قرار می‌گیرد که پروکسی قرار داشت، از همان API سازگار با OpenAI استفاده می‌کند، برای هر کلاینت یک کلید مجزا صادر کرده و میزان مصرف هر کلید را ثبت می‌کند. یک درگاه LiteLLM خودمیزبان (self-hosted) راهکار معمول برای این مسئله است که علاوه بر کنترل دسترسی، امکان تعیین بودجه برای هر کلید و ثبت لاگ درخواست‌ها را نیز فراهم می‌کند.

قانون ذکر شده در دفاع 1 تغییر نمی‌کند. Ollama باید به 127.0.0.1 متصل باشد، درگاه تنها فرآیندی است که با آن ارتباط برقرار می‌کند و درگاه تنها سرویسی است که شنونده (listener) عمومی دارد. وجود درگاه روی سروری که پورت 11434 آن همچنان برای دسترسی عمومی باز است، بی‌فایده است؛ زیرا فراخوان‌کننده‌ها به‌سادگی می‌توانند آن را دور بزنند.

تله فایروال: پورت‌های منتشرشده کانتینر، UFW را دور می‌زنند

به همین دلیل است که نمونه‌های سرویس‌های در معرض دید، روی سرورهایی وجود دارند که مالکان آن‌ها فایروال را به‌درستی پیکربندی کرده‌اند.

ابزار UFW (فایروال ساده) قوانین خود را در زنجیره INPUT از جدول filter هسته می‌نویسد و INPUT بسته‌هایی را که خطاب به خود میزبان هستند، مدیریت می‌کند. فلگ -p در Docker، یک قانون NAT (ترجمه آدرس شبکه) مقصد را در زنجیره PREROUTING از جدول nat می‌نویسد که هسته پیش از تصمیم‌گیری درباره مقصد بسته، آن را ارزیابی می‌کند. تا زمانی که تصمیم مسیریابی اتخاذ شود، مقصد قبلاً به آدرس کانتینر تغییر یافته است؛ بنابراین بسته به‌جای تحویل محلی، فوروارد می‌شود و به‌جای عبور از INPUT، از FORWARD می‌گذرد. قوانین INPUT در UFW هرگز بررسی نمی‌شوند، بنابراین بسته به‌جای عبور از فایروال، آن را دور می‌زند.

به همین دلیل است که این توالی، پورت 11434 را برای اینترنت باز می‌گذارد:

sudo ufw default deny incoming
sudo ufw enable
docker run -d -v ollama:/root/.ollama -p 11434:11434 --name ollama ollama/ollama

و sudo ufw status همچنان گزارش می‌دهد که فایروال فعال است و تنظیمات پیش‌فرض آن روی deny قرار دارد. هر دو گزارش هم‌زمان درست هستند و دقیقاً به همین دلیل است که کاربران به گزارش اشتباه اعتماد می‌کنند. می‌توانید قانونی که این کار را انجام داده است مشاهده کنید:

sudo iptables -t nat -L DOCKER -n

راه‌حل، تعیین یک آدرس در فلگ publish است:

docker rm -f ollama
docker run -d -v ollama:/root/.ollama -p 127.0.0.1:11434:11434 --name ollama ollama/ollama

عبارت -p 11434:11434 مخفف -p 0.0.0.0:11434:11434 است. تعیین 127.0.0.1 باعث می‌شود سمت میزبانِ نگاشت (mapping) به loopback متصل شود، بنابراین SSH tunnel و reverse proxy شما همچنان به آن دسترسی دارند اما اینترنت نمی‌تواند به آن متصل شود. بازسازی کانتینر در اینجا ایمن است، زیرا مدل‌ها در volume نام‌گذاری‌شده ollama قرار دارند، نه داخل کانتینر.

تأیید کنید که هر دو دیدگاه با هم توافق دارند:

docker port ollama
sudo ss -tlnp | grep 11434

docker port ollama باید 11434/tcp -> 127.0.0.1:11434 را چاپ کند. اگر 0.0.0.0:11434 را چاپ کرد، شما همچنان در معرض خطر هستید. این مکانیزم را یک‌بار بیاموزید تا برای هر کانتینری که منتشر می‌کنید، کاربرد داشته باشد: چرا پورت‌های منتشرشده Docker، فایروال UFW را دور می‌زنند زنجیره DOCKER-USER و قوانینی که پس از راه‌اندازی مجدد Docker باقی می‌مانند را پوشش می‌دهد. اگر همچنان در حال ساخت سیاست‌های اصلی میزبان هستید، قوانین UFW مورد نیاز برای یک VPS جدید پایه‌ای که این تنظیمات روی آن قرار می‌گیرد را توضیح می‌دهد.

این پردازش با چه کاربری اجرا می‌شود

اسکریپت نصب لینوکس یک حساب کاربری اختصاصی ایجاد می‌کند و سرویس را تحت همان حساب اجرا می‌کند:

useradd -r -s /bin/false -U -m -d /usr/share/ollama ollama

فایل unit در مسیر /etc/systemd/system/ollama.service سپس مقادیر User=ollama و Group=ollama را تنظیم می‌کند. این تنظیمات را تغییر ندهید. اجرای سریع ollama serve به‌صورت دستی در ترمینال، با کاربری که با آن لاگین کرده‌اید اجرا می‌شود؛ اگر آن کاربر root باشد، یک API بدون احراز هویت در حال نوشتن فایل‌ها با دسترسی root خواهد بود. بررسی کنید وضعیت چگونه است:

ps -o user= -C ollama

پاسخ باید ollama باشد. هر پاسخ دیگری به این معناست که یک پردازشِ دستی در کنار یا به‌جای unit در حال اجراست. همین منطق برای تمام daemonهایی که بعداً اضافه می‌کنید نیز صادق است و اجرای سرویس‌ها با کاربران دارای حداقل دسترسی این موضوع را به‌درستی پوشش می‌دهد.

نحوه بررسی امنیت endpoint مربوط به Ollama API

هر روشی که انتخاب کرده باشید، یک تست تکلیف را مشخص می‌کند و باید آن را از روی یک ماشین دیگر اجرا کنید:

curl -m 5 http://YOUR_SERVER_IP:11434/api/version
curl -m 5 http://YOUR_SERVER_IP:11434/api/tags

هر دو دستور باید با خطای timeout یا connection refused مواجه شوند. اگر یک proxy راه‌اندازی کرده‌اید، همین دو مسیر روی نام میزبان (hostname) مربوط به proxy باید بدون اعتبارنامه، کد 401 و با اعتبارنامه، JSON واقعی را بازگردانند.

سپس لاگ دسترسی را یک‌بار بررسی کنید، زیرا به شما می‌گوید که آیا در زمانی که پورت باز بوده، کسی آن را پیدا کرده است یا خیر:

journalctl -u ollama --since "-30 days" | grep GIN | grep -v 127.0.0.1

Ollama برای هر درخواست یک خط می‌نویسد و آدرس کلاینت را نیز شامل می‌شود:

[GIN] 2026/08/12 - 14:01:10 | 200 | 103.965898ms | 127.0.0.1 | POST "/api/generate"

هنگامی که Ollama روی loopback محدود شده باشد، هر خط باید مقدار 127.0.0.1 را نشان دهد، زیرا این تنها آدرسی است که اتصال می‌تواند از آن برقرار شود. وجود یک آدرس عمومی در آن ستون به معنای یک درخواست از خارج است و timestamp زمان دقیق آن را به شما می‌گوید. عدم وجود خروجی از این دستور، نتیجه مطلوب شماست. اگر مباحث مربوط به مدل‌ها برای شما جدید است، اجرای Ollama روی VPS شامل نصب، تعیین اندازه مدل و محدودیت‌های حافظه است که مشخص می‌کند چه چیزی واقعاً بارگذاری خواهد شد.

FAQ

آیا Ollama دارای API key یا رمز عبور است؟

خیر. سروری که اجرا می‌کنید هیچ‌گونه احراز هویتی ندارد و مستندات رسمی تأکید می‌کنند که برای دسترسی به API نیازی به احراز هویت نیست. هر دو موردی که با نام "Ollama API key" شناخته می‌شوند، کاربرد متفاوتی دارند. جفت‌کلید Ed25519 در /usr/share/ollama/.ollama/ دستگاه شما را برای ollama.com احراز هویت می‌کند تا بتوانید مدل‌ها را push یا مدل‌های خصوصی را pull کنید. OLLAMA_API_KEY یک اعتبارنامه است که کلاینت شما به API میزبانی‌شده در https://ollama.com/api ارسال می‌کند. نمونه محلی ollama serve شما هیچ‌کدام از این دو را نمی‌خواند، بنابراین کنترل دسترسی باید از طریق شبکه یا یک پروکسی در لایه جلویی اعمال شود.

آیا OLLAMA_HOST=0.0.0.0 در صورت داشتن فایروال امن است؟

تنها تا زمانی که هیچ برنامه دیگری قوانین فایروال را روی آن سیستم تغییر ندهد. 0.0.0.0 به این معنی است که listener واقعاً روی اینترفیس عمومی فعال است و شما صرفاً به فایروال اعتماد کرده‌اید تا آن را غیرقابل دسترس نگه دارد. این اعتماد در لحظه‌ای که Docker یک پورت را منتشر می‌کند از بین می‌رود، زیرا قانون DNAT که Docker به جدول nat اضافه می‌کند، پیش از رسیدن بسته به زنجیره INPUT (جایی که UFW در آن قرار دارد) ارزیابی می‌شود؛ در نتیجه بسته فوروارد شده و UFW هرگز آن را نمی‌بیند. Bind کردن روی 127.0.0.1 یا یک آدرس تونل خصوصی، listener را از اینترفیس عمومی حذف می‌کند تا در صورت بروز اشتباه در فایروال، چیزی برای افشا شدن باقی نماند.

چگونه بررسی کنم که پورت Ollama من برای اینترنت باز است؟

دستور sudo ss -tlnp | grep 11434 را روی سرور و curl -m 5 http://YOUR_SERVER_IP:11434/api/version را از یک دستگاه دیگر اجرا کنید. مشاهده ss به همراه 127.0.0.1:11434 و timeout شدن دستور curl از راه دور، همان نتیجه‌ای است که انتظار دارید. مشاهده ss به همراه 0.0.0.0:11434 یا *:11434 در حالی که دستور curl از راه دور پاسخ JSON برمی‌گرداند، به این معنی است که کل API در دسترس است. هرگز با استفاده از curl روی خودِ سرور تست نکنید، زیرا loopback فارغ از آدرس bind شده، همیشه پاسخ می‌دهد.

آیا می‌توانم پورت را از 11434 به یک پورت تصادفی تغییر دهم؟

خیر، و دلیل آن قابل تأمل است. تغییر پورت هیچ چیزی را کند نمی‌کند مگر اسکن یک پورت واحد. اسکنرها کل محدوده پورت‌ها را بررسی می‌کنند و یک درخواست به /api/tags، نوع سرویس را فارغ از پورتی که روی آن ارسال شده، شناسایی می‌کند. تغییر پورت همچنین تنظیمات پیش‌فرض تمام کلاینت‌ها را مختل کرده و درک پیکربندی شما را در آینده دشوارتر می‌کند. به جای جابه‌جایی پورت، آن را روی loopback bind کنید تا listener به جای تغییر مکان، حذف شود.

شخصی به Ollama باز من دسترسی پیدا کرده است. چه چیزی را باید بررسی کنم؟

ابتدا آن را روی 127.0.0.1 bind کرده و سرویس را restart کنید تا دسترسی غیرمجاز پیش از شروع بررسی متوقف شود. سپس journalctl -u ollama --since "-30 days" | grep GIN | grep -v 127.0.0.1 را اجرا کنید تا ببینید کدام آدرس‌های خارجی، چه endpointهایی را و در چه زمانی فراخوانی کرده‌اند. ollama list را با مدل‌هایی که قصد داشتید داشته باشید مقایسه کنید، زیرا /api/pull بدون احراز هویت است و وجود مدلی که شما آن را pull نکرده‌اید، هم به معنای اشغال فضای دیسک و هم مدرکی از نفوذ است. فضای آزاد را با df -h بررسی کنید. Ollama در سطح لاگ پیش‌فرض، متن promptها را ثبت نمی‌کند؛ بنابراین شما سوابقی از اینکه چه کسی و برای چه مدلی درخواست داده دارید، اما محتوای تولید شده ثبت نمی‌شود.