HashiCorp Vault: وحدة التخزين الآمن في نظام Apache APISIX البيئي

API7.ai

January 24, 2022

Ecosystem

مع ظهور بنية الخدمات المصغرة (microservices)، أصبح الحفاظ على الأمان أكثر تحدياً مما كان عليه في السابق. لقد تجاوزنا مرحلة كانت فيها 100 نسخة من خوادم الواجهة الخلفية تصل إلى خادم قاعدة البيانات باستخدام بيانات اعتماد سرية ثابتة واحدة، لأنه في حالة تسريب هذه البيانات، يتم اختراق النظام بأكمله، وإلغاء هذه البيانات يؤدي إلى انقطاع كبير في الخدمة (حيث لا يمكن لأحد الوصول إلى أي شيء إلا بعد إعادة تكوين النسخ). لا يمكننا القضاء على احتمال حدوث خرق أمني لأن الأمور غير المتوقعة تحدث أحياناً. بدلاً من ذلك، الأمر يعود إلينا للتحكم في نطاق الضرر في مثل هذه الحالات. للتعامل مع مثل هذه السيناريوهات، يأتي حل شائع مثل HashiCorp Vault في بيئة الإنتاج ليعمل كنظام إدارة أسرار وتشفير قائم على الهوية. في هذه المقالة، قمت بتوضيح كيفية دمج Vault مع Apache APISIX (بوابة API سحابية) باستخدام مكون jwt-auth plugin للاستفادة بشكل فعال من مزايا كلا العالمين.

ما هو Vault

تم تصميم HashiCorp Vault لمساعدة المؤسسات على إدارة الوصول إلى الأسرار ونقلها بأمان داخل المؤسسة. يتم تعريف الأسرار على أنها أي شكل من أشكال بيانات الاعتماد الحساسة التي تحتاج إلى التحكم والمراقبة الدقيقة ويمكن استخدامها لفتح معلومات حساسة. يمكن أن تكون الأسرار في شكل كلمات مرور أو مفاتيح API أو مفاتيح SSH أو رموز RSA أو OTP. في العالم الحقيقي، حيث يكون من الشائع وجود انتشار للأسرار حيث يتم تخزين الأسرار في ملفات التكوين أو كمتغيرات في الكود الفعلي للبرنامج، مما يؤدي أحياناً إلى وصولها إلى أنظمة التحكم في الإصدار مثل GitHub أو BitBucket أو GitLab، مما يشكل تهديداً أمنياً كبيراً. يحل Vault هذه المشكلة من خلال مركزية الأسرار. يوفر تخزيناً مشفراً للأسرار الثابتة، وإنشاء أسرار ديناميكية مع عقد إيجار TTL، ومصادقة المستخدمين (سواء كانوا آلات أو بشر) للتأكد من أنهم مخولون للوصول إلى سر معين، وأكثر من ذلك. بحيث حتى في حالة حدوث خرق أمني، يكون نطاق الضرر صغيراً ومحدوداً.

يجعل Vault من السهل التحكم في الوصول وإدارته من خلال توفير واجهة أحادية لإدارة كل سر في البنية التحتية الخاصة بك. ليس ذلك فحسب، بل يوفر أيضاً المرونة لإنشاء سجلات تدقيق مفصلة وتتبع من قام بالوصول إلى ماذا.

HashiCorp Vault

حول مكون APISIX jwt-auth Plugin

هو مكون مصادقة يمكن إرفاقه بأي مسار في APISIX لإجراء مصادقة JWT (رمز ويب JSON، اقرأ المزيد) قبل توجيه الطلب إلى URI المنبع. باختصار، هو آلية مصادقة آمنة تؤدي إلى التفويض للوصول إلى الموارد الحرجة. عادةً، يتم استخدام مفتاح خاص أو سر نصي من قبل المُصدر لتوقيع JWT. يقوم المستقبل لـ JWT بالتحقق من التوقيع للتأكد من أن الرمز لم يتم تغييره بعد توقيعه من قبل المُصدر. تعتمد سلامة آلية JWT بأكملها على سر التوقيع (سواء كان سراً نصياً أو أزواج مفاتيح RSA). مما يجعل من الصعب على المصادر غير المصرح بها تخمين مفتاح التوقيع ومحاولة تغيير المطالبات داخل JWT.

لذلك، فإن تخزين هذه المفاتيح في بيئة آمنة أمر بالغ الأهمية. إذا وقعت في الأيدي الخطأ، فقد يعرض ذلك أمان البنية التحتية بأكملها للخطر. على الرغم من أننا من جانب APISIX نتخذ جميع الوسائل لاتباع ممارسات SecOps القياسية، إلا أنه من الطبيعي في بيئة الإنتاج وجود حل مركزي لإدارة المفاتيح مثل HashiCorp Vault لإنشاء سجلات تدقيق مفصلة، وتدوير المفاتيح الدوري، وإلغاء المفاتيح، وما إلى ذلك. وسيكون من المشكلات المزعجة إذا كان عليك تحديث تكوين Apache APISIX في كل مرة يتم فيها تدوير مفتاح في جميع أنحاء البنية التحتية.

خطوات استخدام Vault مع Apache APISIX

للتكامل مع Vault، يحتاج Apache APISIX إلى تحميل تكوين Vault في config.yaml.

داخلياً، يتواصل APISIX مع خادم Vault باستخدام محرك الأسرار KV v1 عبر واجهات برمجية HTTP. نظراً لأن معظم الحلول المؤسسية تفضل الالتزام بمحرك أسرار KV - الإصدار 1 في بيئة الإنتاج الخاصة بها، فقد اخترنا في المرحلة الأولية من دعم APISIX-Vault الإصدار 1 فقط. في الإصدارات اللاحقة، سنضيف دعم K/V الإصدار 2.

الفكرة الرئيسية لاستخدام Vault بدلاً من خلفية etcd في APISIX هي الاهتمام بالأمان في بيئة ذات ثقة منخفضة. نحن، مطورو APISIX، نأخذ أولوياتكم على محمل الجد. لهذا السبب نوصي باستخدام رموز الوصول إلى Vault التي تكون محدودة النطاق ويمكنها منح خادم APISIX وصولاً محدوداً.

تكوين Vault

إذا كان لديك بالفعل مثيل Vault يعمل مع الصلاحيات اللازمة، فلا تتردد في تخطي هذا القسم. يشارك هذا القسم أفضل الممارسات لاستخدام Vault داخل نظام Apache APISIX. يرجى اتباع الخطوات المذكورة أدناه.

الخطوة 1: تشغيل خادم Vault

لديك هنا خيارات متعددة، فلا تتردد في الاختيار بين Docker أو الملفات الثنائية المسبقة التجميع أو البناء من المصدر. نظراً لأن التواصل مع خادم Vault يتطلب عميل سطر أوامر Vault، فإنني أفضل استخدام الملفات الثنائية المسبقة التجميع بدلاً من استخدام Docker. على أي حال، الأمر يعود إليك (لا تتردد في استشارة وثائق التثبيت الرسمية لـ Vault). لتشغيل خادم تطوير، يرجى تنفيذ الأمر التالي.

$ vault server -dev -dev-root-token-id=root
…
تحذير! تم تمكين وضع التطوير! في هذا الوضع، يعمل Vault بالكامل في الذاكرة
ويبدأ غير مغلق بمفتاح فك قفل واحد. تم بالفعل مصادقة الرمز الجذري
لعميل سطر الأوامر، لذا يمكنك البدء في استخدام Vault على الفور.
قد تحتاج إلى تعيين المتغير البيئي التالي:
export VAULT_ADDR='http://127.0.0.1:8200'
يتم عرض مفتاح فك القفل والرمز الجذري أدناه في حالة رغبتك في
إغلاق/فتح Vault أو إعادة المصادقة.
مفتاح فك القفل: 12hURx2eDPKK1tzK+8TkgH9pPhPNJFpyfc/imCLgJKY=
الرمز الجذري: root
لا ينبغي استخدام وضع التطوير في التثبيتات الإنتاجية!

قم بتعيين المتغيرات البيئية الصحيحة لعميل سطر الأوامر الحالي.

export VAULT_ADDR='http://127.0.0.1:8200'
export VAULT_TOKEN='root'

قم بتمكين محرك أسرار k/v الإصدار 1 في Vault مع بادئة مسار مناسبة. في هذا العرض التوضيحي، سنختار المسار kv حتى لا يكون هناك تصادم مع مسار الأسرار الافتراضي لـ kv الإصدار 2.

$ vault secrets enable -path=kv -version=1 kv
تم التمكين بنجاح! تم تمكين محرك أسرار kv في: kv/

# للتأكيد على الحالة، قم بتنفيذ
$ vault secrets list
المسار          النوع         الواصف              الوصف
---- ---- -------- -----------
cubbyhole/    cubbyhole    cubbyhole_4eeb394c    تخزين أسرار خاصة لكل رمز
identity/     identity     identity_5ca6201e     مخزن الهوية
kv/           kv           kv_92cd6d37           n/a
secret/       kv           kv_6dd46a53           تخزين أسرار مفتاح/قيمة
sys/          system       system_2045ddb1       نقاط نهاية النظام المستخدمة للتحكم والسياسة والتصحيح

الخطوة 2: إنشاء رمز وصول إلى Vault لـ APISIX

هذه المقالة تتعلق باستخدام Vault في سياق مكون jwt-auth. لذلك، بالنسبة لمستهلك APISIX (إذا كنت غير مطلع على مفهوم المستهلكين في نظام APISIX، يرجى قراءة الوثائق حول Apache APISIX Consumer) مع اسم المستخدم jack، يبحث مكون jwt-auth (إذا تم تمكينه مع تكوين Vault) عن الأسرار في المسار <vault.prefix داخل config.yaml>/consumer/<consumer.username>/jwt-auth في تخزين kv في Vault. في هذا السياق، إذا كنت تقوم بتعيين مساحة اسم kv/apisix (مسار Vault) كـ vault.prefix داخل config.yaml لاسترجاع جميع البيانات المتعلقة بـ APISIX، فإننا نقترح إنشاء سياسة للمسار kv/apisix/consumer/. تضمن العلامة النجمية الإضافية () في النهاية أن السياسة تسمح بالقراءة لأي مسار يحتوي على البادئة kv/apisix/consumer.

قم بإنشاء ملف سياسة بلغة تكوين HashiCorp (HCL).

$ tee apisix-policy.hcl << EOF
path "kv/apisix/consumer/*" {
    capabilities = ["read"]
}
EOF

تطبيق السياسة على مثيل Vault.

$ vault policy write apisix-policy apisix-policy.hcl

تم التحميل بنجاح! تم تحميل السياسة: apisix-policy

إنشاء رمز مع السياسة الجديدة التي تم تكوينها مع نطاق وصول محدود.

$ vault token create -policy="apisix-policy"

المفتاح                  القيمة
--- -----
الرمز                s.KUWFVhIXgoRuQbbp3j1eMVGa
الوصول إلى الرمز       nPXT3q0mfZkLmhshfioOyx8L
مدة الرمز               768h
قابل للتجديد      true
سياسات الرمز       ["apisix-policy" "default"]
سياسات الهوية    []
السياسات             ["apisix-policy" "default"]

في هذا العرض التوضيحي، s.KUWFVhIXgoRuQbbp3j1eMVGa هو رمز الوصول الخاص بك.

إضافة تكوين Vault إلى Apache APISIX

كما تمت مناقشته سابقاً، يتواصل Apache APISIX مع مثيل Vault عبر واجهات برمجية HTTP لـ Vault. يجب إضافة التكوين الضروري إلى config.yaml. هنا معلومات موجزة عن الحقول المختلفة التي يمكنك استخدامها:

  • host: عنوان المضيف حيث يعمل خادم Vault.
  • timeout: مهلة HTTP لكل طلب.
  • token: الرمز الذي تم إنشاؤه من مثيل Vault والذي يمكنه منح الوصول لقراءة البيانات من Vault.
  • prefix: تمكين بادئة يسمح لك بفرض سياسات أفضل، وإنشاء رموز محدودة النطاق، والتحكم بشكل صارم في البيانات التي يمكن الوصول إليها من APISIX. البادئات الصالحة هي (kv/apisix, secret إلخ.)
vault:
  host: 'http://0.0.0.0:8200'
  timeout: 10
  token: 's.KUWFVhIXgoRuQbbp3j1eMVGa'
  prefix: 'kv/apisix'

إنشاء مستهلك APISIX

يحتوي APISIX على تجريد مستوى المستهلك الذي يسير جنباً إلى جنب مع سيناريوهات المصادقة. لتمكين المصادقة لأي مسار في APISIX، يلزم وجود مستهلك مع تكوين مناسب لنوع خدمة المصادقة المحددة. عندها فقط يمكن لـ APISIX توجيه الطلب إلى URI المنبع عن طريق إجراء المصادقة بنجاح فيما يتعلق بتكوين المستهلك. يحتوي مستهلك APISIX على حقلين - أحدهما هو username (مطلوب) لتحديد مستهلك من الآخرين، والآخر هو plugins الذي يحمل تكوينات المكونات الخاصة بالمستهلك.

هنا، في هذه المقالة، سنقوم بإنشاء مستهلك مع مكون jwt-auth. يقوم بإجراء مصادقة JWT للمسارات أو الخدمات المعنية.

لتمكين jwt-auth مع تكوين Vault، قم بإرسال طلب إلى:

$ curl http://127.0.0.1:9080/apisix/admin/consumers -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d '
{
    "username": "jack",
    "plugins": {
        "jwt-auth": {
            "key": "test-key",
            "vault": {}
        }
    }
}'

هنا يبحث المكون عن سر المفتاح داخل مسار Vault (<vault.prefix من conf.yaml>/consumer/jack/jwt-auth) للمستهلك jack المذكور في تكوين المستهلك ويستخدمه للتوقيع والتحقق من JWT اللاحق. إذا لم يتم العثور على المفتاح في نفس المسار، يسجل المكون خطأً ويفشل في إجراء مصادقة JWT.

إعداد خادم منبع اختباري

لاختبار السلوك، يمكنك إنشاء مسار لخادم منبع (معالج ping بسيط يعيد pong). يمكنك إعداده باستخدام خادم HTTP بسيط مكتوب بلغة Go.

// خادم منبع بسيط
package main

import "net/http"

func ping(w http.ResponseWriter, req *http.Request) {
    w.Write([]byte("secure/pong\n"))
}

func main() {
    http.HandleFunc("/secure/ping", ping)
    http.ListenAndServe(":9999", nil)
}

إنشاء مسار APISIX مع تمكين المصادقة

قم بإنشاء مسار APISIX مع خادم HTTP الآمن هذا وتمكين مكون jwt-auth للمصادقة.

$ curl http://127.0.0.1:9080/apisix/admin/routes/1 -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d '
{
    "plugins": {
        "jwt-auth": {}
    },
    "upstream": {
        "nodes": {
            "127.0.0.1:9999": 1
        },
        "type": "roundrobin"
    },
    "uri": "/secure/ping"
}'

إنشاء رمز من مكون jwt-auth

الآن قم بتوقيع سر JWT من APISIX الذي يمكن استخدامه وتمريره لإجراء طلبات إلى المسار الوكيل [http://localhost:9080/secure/ping](http://localhost:9080/secure/ping) إلى خادم APISIX.

$ curl http://127.0.0.1:9080/apisix/plugin/jwt/sign\?key\=test-key -i
HTTP/1.1 200 OK
Date: Tue, 18 Jan 2022 07:50:57 GMT
Content-Type: text/plain; charset=utf-8
Transfer-Encoding: chunked
Connection: keep-alive
Server: APISIX/2.11.0

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJrZXkiOiJ0ZXN0LWtleSIsImV4cCI6MTY0MjU3ODY1N30.nkyev1_KUapVgY_QVYETsSApA6gEkDWS8tsHFV1EpD8

في الخطوة السابقة، إذا رأيت شيئاً مثل رسالة فشل في توقيع JWT، يرجى التأكد من أن لديك مفتاح سر مخزن في مسار Vault kv/apisix/consumers/jack/jwt-auth.

# مثال
$ vault kv put kv/apisix/consumer/jack/jwt-auth secret=$ecr3t-c0d3
تم الكتابة بنجاح! تمت كتابة البيانات إلى: kv/apisix/consumer/jack/jwt-auth

طلب خادم APISIX

الآن، قم بإرسال طلب إلى الوكيل APISIX للمسار /secure/ping. عند التحقق بنجاح، سيتم توجيه الطلب إلى خادم HTTP الخاص بنا.

$ curl http://127.0.0.1:9080/secure/ping -H 'Authorization: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJrZXkiOiJ0ZXN0LWtleSIsImV4cCI6MTY0MjU3ODU5M30.IYudBr7FTgRme70u4rEBoYNtGmGByzgfGlt8hctI__Q' -i
HTTP/1.1 200 OK
Content-Type: text/plain; charset=utf-8
Content-Length: 12
Connection: keep-alive
Date: Tue, 18 Jan 2022 08:00:04 GMT
Server: APISIX/2.11.0

secure/pong

أي طلب بدون JWT صالح سيؤدي إلى ظهور خطأ HTTP 401 Unauthorized.

$ curl http://127.0.0.1:9080/secure/ping -i
HTTP/1.1 401 Unauthorized
Date: Tue, 18 Jan 2022 08:00:33 GMT
Content-Type: text/plain; charset=utf-8
Transfer-Encoding: chunked
Connection: keep-alive
Server: APISIX/2.11.0

{"message":"Missing JWT token in request"}

حالات استخدام مختلفة حيث يمكن دمج Vault مع مكون APISIX jwt-auth

يمكن تكوين مكون Apache APISIX jwt-auth لاسترداد مفاتيح الأسرار النصية البسيطة بالإضافة إلى أزواج المفاتيح العامة والخاصة RS256 من تخزين Vault.

:::ملاحظة بالنسبة للإصدار المبكر من دعم هذا التكامل، يتوقع المكون أن يكون اسم المفتاح للأسرار المخزنة في مسار Vault من بين [ secret, public_key, private_key] لاستخدام المفتاح بنجاح. في الإصدارات المستقبلية، سنضيف دعم الإشارة إلى المفاتيح ذات الأسماء المخصصة. :::

  1. لديك سر توقيع HS256 مخزن داخل Vault وتريد استخدامه لتوقيع والتحقق من JWT.

    $ curl http://127.0.0.1:9080/apisix/admin/consumers -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d '
    {
        "username": "jack",
        "plugins": {
            "jwt-auth": {
                "key": "key-1",
                "vault": {}
            }
        }
    }'
    

    هنا يبحث المكون عن المفتاح secret داخل مسار Vault (<vault.prefix من conf.yaml>/consumer/jack/jwt-auth) للمستهلك jack المذكور في تكوين المستهلك ويستخدمه للتوقيع والتحقق من JWT اللاحق. إذا لم يتم العثور على المفتاح في نفس المسار، يسجل المكون خطأً ويفشل في إجراء مصادقة JWT.

  2. أزواج مفاتيح RSA RS256، كل من المفاتيح العامة والخاصة مخزنة في Vault.

    $ curl http://127.0.0.1:9080/apisix/admin/consumers -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d '
    {
        "username": "jim",
        "plugins": {
            "jwt-auth": {
                "key": "rsa-keypair",
                "algorithm": "RS256",
                "vault": {}
            }
        }
    }'
    

    يبحث المكون عن المفاتيح public_key و private_key داخل مسار kv في Vault (<vault.prefix من conf.yaml>/consumer/jim/jwt-auth) لـ jim المذكور داخل تكوين المكون Vault. إذا لم يتم العثور عليها، تفشل المصادقة.

    إذا كنت غير متأكد من كيفية تخزين المفاتيح العامة والخاصة في تخزين kv في Vault، استخدم هذا الأمر

    # بشرط أن يحتوي الدليل الحالي على الملفات المسماة "public.pem" و "private.pem"
    $ vault kv put kv/apisix/consumer/jim/jwt-auth public_key=@public.pem private_key=@private.pem
    تم الكتابة بنجاح! تمت كتابة البيانات إلى: kv/apisix/consumer/jim/jwt-auth
    
  3. المفتاح العام في تكوين المستهلك، بينما المفتاح الخاص في Vault.

    $ curl http://127.0.0.1:9080/apisix/admin/consumers -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d '
    {
        "username": "john",
        "plugins": {
            "jwt-auth": {
                "key": "user-key",
                "algorithm": "RS256",
                "public_key": "-----BEGIN PUBLIC KEY-----\n……\n-----END PUBLIC KEY-----"
                "vault": {}
            }
        }
    }'
    

    يستخدم هذا المكون المفتاح العام RSA من تكوين المستهلك ويستخدم المفتاح الخاص الذي يتم استرداده مباشرة من Vault.

تعطيل Vault من المكون

الآن، لتعطيل البحث عن Vault من مكون jwt-auth، قم ببساطة بإزالة كائن Vault الفارغ من تكوين مكون المستهلك (في هذه الحالة هو jack). سيؤدي ذلك إلى جعل مكون JWT يبحث عن أسرار التوقيع (سواء كانت HS256/HS512 أو أزواج مفاتيح RS512) في تكوين المكون للطلبات اللاحقة إلى مسار URI حيث تم تمكين تكوين jwt-auth. حتى إذا كان لديك تكوين Vault ممكناً في APISIX config.yaml، لن يتم إرسال أي طلب إلى خادم Vault.

يتم إعادة تحميل مكونات APISIX تلقائياً، لذلك ليست هناك حاجة لإعادة تشغيل APISIX.

$ curl http://127.0.0.1:9080/apisix/admin/consumers -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d '
{
    "username": "jack",
    "plugins": {
        "jwt-auth": {
            "key": "test-key",
            "secret": "my-secret-key"
        }
    }
}'

ملخص

تقدم هذه المقالة الإصدار القادم من تكامل Vault-Apache APISIX والتفاصيل ذات الصلة.

لا تتردد في بدء نقاش في مناقشات GitHub أو التواصل عبر قائمة البريد.

Tags: