دعونا تشفير شهادات SSL مع مدير الشهادة في Kubernetes



في هذه المقالة سأتحدث عن كيفية أتمتة طلب وتجديد الشهادات من Let's Encrypt (وليس فقط) لـ Ingress في Kubernetes باستخدام الوظيفة الإضافية Cert-manager. ولكن سأبدأ بمقدمة موجزة لجوهر المشكلة.

القليل من البرنامج التعليمي


دخل بروتوكول HTTP ، الذي تم تطويره في أوائل التسعينات من القرن الماضي ، حياتنا اليومية بإحكام شديد بحيث يصعب تخيل يوم واحد على الأقل دون استخدامه. ومع ذلك ، فإنه في حد ذاته لا يوفر حتى الحد الأدنى من الأمان عند تبادل المعلومات بين المستخدم وخادم الويب. يأتي HTTPS ("S" - آمن) لإنقاذ: باستخدام حزم البيانات المرسلة في SSL / TLS ، أثبت هذا البروتوكول نفسه في حماية المعلومات من الاعتراض ويتم الترويج له بنشاط من قبل الصناعة.

على سبيل المثال ، التزمت Google بـ 2014موضع "HTTPS في كل مكان" وحتى يقلل من أولوية المواقع بدونها في نتائج البحث. لا تتجاوز هذه "الدعاية" المستهلكين العاديين أيضًا: تحذر المتصفحات الحديثة مستخدميها من وجود وصحة شهادات SSL للمواقع التي تمت زيارتها.





تبدأ تكلفة شهادة موقع شخصي من عشرات الدولارات. ليس دائما شرائه مبررا ومناسبا. لحسن الحظ ، منذ نهاية عام 2015 ، يتوفر بديل مجاني في شكل شهادات Let's Encrypt (LE). تم إنشاء هذا المشروع غير الربحي من قبل عشاق موزيلا من أجل تغطية معظم مواقع الويب بالتشفير.

يصدر المرجع المصدق شهادات التحقق من صحة المجال(الأبسط بين تلك المتاحة في السوق) مع فترة صلاحية 90 يومًا ، ولسنوات عديدة كان من الممكن إصدار ما يسمى بشهادة البدل لعدة نطاقات فرعية.

للحصول على الشهادة ، يستخدم الموقع الخوارزميات الموضحة في بروتوكول بيئة إدارة الشهادات المؤتمتة (ACME) ، الذي تم إنشاؤه خصيصًا لـ Let's Encrypt. عند استخدامه ، يتم تأكيد ملكية المجال عن طريق الطلبات من خلال وضع رمز HTTP محدد (يسمى HTTP-01 ) أو تثبيت سجلات DNS (DNS-01) - سيتم تقديم المزيد عنها أدناه.

مدير تصديق


Cert-manager هو مشروع خاص لـ Kubernetes ، وهو عبارة عن مجموعة من CustomResourceDefinitions (وبالتالي الحد الأدنى للإصدار المدعوم من K8s - v1.12) لتهيئة المرجع المصدق (المراجع المصدقة) والطلب المباشر للشهادات. يعد تثبيت CRD في كتلة أمرًا تافهًا ويعود إلى استخدام ملف YAML واحد:

kubectl create ns cert-manager
kubectl apply -f https://github.com/jetstack/cert-manager/releases/download/v0.13.0/cert-manager.yaml

( هناك أيضًا إمكانية التثبيت باستخدام Helm.)

لبدء إجراء الطلب ، يجب الإعلان عن موارد سلطات التصديق (CA) في المجموعة: Issuerأو ClusterIssuer، - والتي يتم استخدامها لتوقيع CSR (طلبات إصدار الشهادة). الفرق بين المصدر الأول والثاني في النطاق:

  • Issuer يمكن استخدامها ضمن نفس مساحة الاسم ،
  • ClusterIssuer هو كائن كتلة عمومي.

تدرب مع مدير الشهادة


رقم 1. شهادة موقعة ذاتيا


لنبدأ بالحالة الأبسط - طلب شهادة موقعة ذاتيًا. هذا الخيار شائع جدًا ، على سبيل المثال ، لبيئات الاختبار الديناميكية للمطورين أو في حالة استخدام موازن خارجي ينهي حركة مرور SSL. سيبدو

المورد Issuerكما يلي:

apiVersion: cert-manager.io/v1alpha2
kind: Issuer
metadata:
  name: selfsigned
spec:
  selfSigned: {}

ولإصدار شهادة ، من الضروري وصف المورد Certificate، حيث يتم توضيح كيفية إصداره (انظر القسم issuerRefأدناه) وحيث يوجد المفتاح الخاص (الحقل secretName). بعد ذلك ، سيحتاج Ingress إلى الإشارة إلى هذا المفتاح (انظر القسم tlsج spec):

---
apiVersion: cert-manager.io/v1alpha2
kind: Certificate
metadata:
  name: selfsigned-crt
spec:
  secretName: tls-secret
  issuerRef:
    kind: Issuer
    name: selfsigned
  commonName: "yet-another.website"
  dnsNames:
  - "yet-another.website"
---
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
  name: app
spec:
  tls:
  - hosts:
    - "yet-another.website"
    secretName: tls-secret
  rules:
  - host: "yet-another.website"
    http:
      paths:
      - path: /
        backend:
          serviceName: app
          servicePort: 8080

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

kubectl -n app describe  certificate selfsigned-crt
...
  Normal  GeneratedKey  5s    cert-manager  Generated a new private key
  Normal  Requested     5s    cert-manager  Created new CertificateRequest resource "selfsigned-crt-4198958557"
  Normal  Issued        5s    cert-manager  Certificate issued successfully

إذا نظرت إلى المورد السري نفسه ، فإنه يحتوي على:

  • مفتاح خاص tls.key،
  • شهادة الجذر ca.crt،
  • شهادتنا الموقعة ذاتيا tls.crt.

يمكن رؤية محتويات هذه الملفات باستخدام الأداة المساعدة openssl، على سبيل المثال ، مثل:

kubectl -n app get secret tls-secret -ojson | jq -r '.data."tls.crt"' | base64 -d | openssl x509 -dates -noout -issuer
notBefore=Feb 10 21:01:59 2020 GMT
notAfter=May 10 21:01:59 2020 GMT
issuer=O = cert-manager, CN = yet-another.website

تجدر الإشارة إلى أنه في الحالة العامة ، لن يتم الوثوق بالشهادة الصادرة باستخدام هذا من قبلIssuer العملاء المتصلين . والسبب بسيط: ليس لديه مرجع مصدق (انظر الملاحظة على موقع المشروع ) . لتجنب ذلك ، تحتاج إلى تحديد المسار إلى الملف السري حيث يوجد . يمكن أن تكون هذه منظمة CA للشركات - لتوقيع الشهادات الصادرة لـ Ingress بمفتاح مستخدم بالفعل لاحتياجات خدمات الخادم / أنظمة المعلومات الأخرى.Certificateca.crt

رقم 2. دعونا تشفير الشهادة مع التحقق من صحة HTTP


لإصدار شهادات LE ، كما ذكرنا سابقًا ، يتوفر نوعان من تأكيد ملكية المجال: HTTP-01 و DNS-01.

النهج الأول (HTTP-01) هو إطلاق خادم ويب صغير مع نشر منفصل ، والذي سيتم إرساله إلى الإنترنت عبر الرابط <YOUR_DOMAIN > /. معروف / acme- Challenge / <TOKEN> بعض البيانات المطلوبة من خادم الشهادة. لذلك ، فإن هذه الطريقة تعني توفر Ingress من الخارج على المنفذ 80 وحل سجل DNS للمجال إلى عناوين IP عامة.

الخيار الثاني للتحقق من شهادة صادرة (DNS-01) يأتي منمن وجود API إلى خادم DNS الذي يستضيف سجلات المجال. تقوم جهة الإصدار ، باستخدام الرموز المميزة المشار إليها ، بإنشاء سجلات TXT على المجال ، والتي يتلقاها خادم ACME بعد ذلك أثناء التأكيد. من بين مزودي DNS المدعومين رسميًا CloudFlare و AWS Route53 و Google CloudDNS وغيرها ، بما في ذلك التنفيذ الخاص بها ( acme-dns ).

ملاحظة : لنضع تشفيرًا قيودًا صارمة إلى حد ما على الطلبات المقدمة لخوادم ACME. من أجل عدم الدخول في حظر طويل ، فمن المستحسن استخدام نوع شهادة انطلاق التشفير لتصحيح الأخطاء (الفرق فقط في خادم ACME).

لذلك ، نحن نصف الموارد:

apiVersion: cert-manager.io/v1alpha2
kind: Issuer
metadata:
  name: letsencrypt
spec:
  acme:
    server: https://acme-staging-v02.api.letsencrypt.org/directory 
    privateKeySecretRef:
      name: letsencrypt
    solvers:
    - http01:
       ingress:
         class: nginx
---
apiVersion: cert-manager.io/v1alpha2
kind: Certificate
metadata:
  name: le-crt
spec:
  secretName: tls-secret
  issuerRef:
    kind: Issuer
    name: letsencrypt
  commonName: yet-another.website
  dnsNames:
  - yet-another.website

لاحظ أنه تم تحديد عنوان خادم الترحيل كـ servery acme(c Issuer) . استبداله بمعركة سيكون ممكنًا لاحقًا. بتطبيق هذا التكوين ، نتتبع مسار الطلب بأكمله:



  1. خلق الخلق Certificateمورد جديد CertificateRequest:

    kubectl -n app describe certificate le-crt
    ...
    Created new CertificateRequest resource "le-crt-1127528680"
  2. في وصفه - علامة على الخلق Order:

    kubectl -n app describe certificaterequests le-crt-1127528680
    Created Order resource app/le-crt-1127528680-1805948596
  3. فإنه Orderيصف مع ما المعلمات يمر الاختبار وما وضعه الحالي هو. يتم تنفيذ هذا التحقق من قبل المورد Challenge:

    kubectl -n app describe order le-crt-1127528680-1805948596
    Created Challenge resource "le-crt-1127528680-1805948596-1231544594" for domain "yet-another.website"
  4. أخيرًا ، تحتوي تفاصيل هذا المورد على معلومات حول حالة الفحص نفسه:

    kubectl -n app describe challenges le-crt-1127528680-1805948596-1231544594
    ...
      Reason:      Successfully authorized domain                                                                                                                                                                      
    ...
      Normal  Started         2m45s  cert-manager  Challenge scheduled for processing
      Normal  Presented       2m45s  cert-manager  Presented challenge using http-01 challenge mechanism
      Normal  DomainVerified  2m22s  cert-manager  Domain "yet-another.website" verified with "http-01" validation

إذا تم استيفاء جميع الشروط (أي أن النطاق يمكن الوصول إليه من الخارج ، فلا يوجد حظر من LE ...) - في أقل من دقيقة ، سيتم إصدار الشهادة. إذا نجحت ، describe certificate le-crtسيظهر سجل في الإخراج Certificate issued successfully.

الآن يمكنك تغيير عنوان الخادم بأمان لمكافحة ( https://acme-v02.api.letsencrypt.org/directory) وإعادة طلب الشهادات الحقيقية الموقعة بالفعل Fake LE Intermediate X1، ولكن Let's Encrypt Authority X3.

للقيام بذلك ، تحتاج أولاً إلى حذف المورد Certificate: وإلا ، لا يتم تنشيط أي إجراءات طلب ، لأن الشهادة موجودة بالفعل وأنها ذات صلة. ستؤدي إزالة السر إلى عودته الفورية برسالة إلى describe certificate:

  Normal  PrivateKeyLost  44s                   cert-manager  Lost private key for CertificateRequest "le-crt-613810377", deleting old resource

يبقى تطبيق البيان "القتالي" على البيان Issuerالموضح أعلاه Certificate(لم يتغير):

apiVersion: cert-manager.io/v1alpha2
kind: Issuer
metadata:
  name: letsencrypt
spec:
  acme:
    server: https://acme-v02.api.letsencrypt.org/directory
    privateKeySecretRef:
      name: letsencrypt
    solvers:
    - http01:
       ingress:
         class: nginx

بعد استلام الرسالة Certificate issued successfully، describeتحقق منها:

kubectl -n app get secret tls-secret -ojson | jq -r '.data."tls.crt"' | base64 -d | openssl x509 -dates -noout -issuer
notBefore=Feb 10 21:11:48 2020 GMT
notAfter=May 10 21:11:48 2020 GMT
issuer=C = US, O = Let's Encrypt, CN = Let's Encrypt Authority X3

رقم 3. Wildcard LE مع التحقق من صحة DNS


سنعقد المهمة عن طريق كتابة شهادة على الفور إلى جميع النطاقات الفرعية للموقع واستخدام هذا الوقت فحص DNS (عبر CloudFlare).

أولاً ، احصل على الرمز المميز في لوحة تحكم CloudFlare للعمل من خلال واجهة برمجة التطبيقات:

  1. الملف الشخصي → API Tokens → إنشاء رمز.
  2. قم بتعيين الأذونات على النحو التالي:
    • أذونات:
      • المنطقة - DNS - تحرير
      • المنطقة - المنطقة - اقرأ
    • موارد المنطقة:
      • تضمين - جميع المناطق
  3. انسخ الرمز المميز الذي تم تلقيه بعد الحفظ (على سبيل المثال :) y_JNkgQwkroIsflbbYqYmBooyspN6BskXZpsiH4M.

قم بإنشاء سر يتم فيه تخزين هذا الرمز المميز ، وارجع إليه في جهة الإصدار:

apiVersion: v1
kind: Secret
metadata:
  name: cloudflare-api-token
type: Opaque
stringData:
  api-token: y_JNkgQwkroIsflbbYqYmBooyspN6BskXZpsiH4M
---
apiVersion: cert-manager.io/v1alpha2
kind: Issuer
metadata:
  name: letsencrypt
spec:
  acme:
    server: https://acme-v02.api.letsencrypt.org/directory 
    privateKeySecretRef:
      name: letsencrypt
    solvers:
    - dns01:
        cloudflare:
          email: my-cloudflare-acc@example.com
          apiTokenSecretRef:
            name: cloudflare-api-token
            key: api-token

---
apiVersion: cert-manager.io/v1alpha2
kind: Certificate
metadata:
  name: le-crt
spec:
  secretName: tls-secret
  issuerRef:
    kind: Issuer
    name: letsencrypt
  commonName: yet-another.website
  dnsNames:
  - "yet-another.website"
  - "*.yet-another.website"

(لا تنس استخدام التدريج إذا كنت تقوم بالتجربة!)

فلننتقل إلى عملية تأكيد ملكية النطاق:

kubectl -n app describe challenges.acme.cert-manager.io le-crt-613810377-1285319347-3806582233
...
Status:
  Presented:   true
  Processing:  true
  Reason:      Waiting for dns-01 challenge propagation: DNS record for "yet-another.website" not yet propagated
  State:       pending
Events:
  Type    Reason     Age   From          Message
  ----    ------     ----  ----          -------
  Normal  Started    54s   cert-manager  Challenge scheduled for processing
  Normal  Presented  53s   cert-manager  Presented challenge using dns-01 challenge mechanism

سيظهر سجل TXT في اللوحة:



... وبعد فترة ستتغير الحالة إلى:

Domain "yet-another.website" verified with "dns-01" validation

تأكد من أن الشهادة صالحة لأي مجال فرعي:

kubectl -n app get secret tls-secret -ojson | jq -r '.data."tls.crt"' | base64 -d | openssl x509 -dates -noout -text |grep DNS:
          DNS:*.yet-another.website, DNS:yet-another.website

لا يتم التحقق من صحة DNS ، كقاعدة عامة ، بسرعة ، نظرًا لأن معظم موفري DNS لديهم فترة تحديث بيانات توضح مقدار الوقت المنقضي منذ لحظة تغيير سجل DNS إلى التحديث الفعلي لجميع خوادم DNS الخاصة بالموفر. ومع ذلك ، يوفر معيار ACME أيضًا مزيجًا من خيارين للتحقق ، والذي يمكن استخدامه لتسريع استلام شهادة للمجال الرئيسي. في هذه الحالة ، Issuerسيكون الوصف كما يلي:

apiVersion: cert-manager.io/v1alpha2
kind: Issuer
metadata:
  name: letsencrypt
spec:
  acme:
    server: https://acme-v02.api.letsencrypt.org/directory
    privateKeySecretRef:
      name: letsencrypt
    solvers:
    - selector:
        dnsNames:
        - "*.yet-another.website"
      dns01:
        cloudflare:
          email: my-cloudflare-acc@example.com
          apiTokenSecretRef:
            name: cloudflare-api-token
            key: api-token            
    - selector:
        dnsNames:
        - "yet-another.website"
      http01:
        ingress:
          class: nginx

إذا قمت بتطبيق هذا التكوين ، سيتم إنشاء مصدرين Challenge:

kubectl -n app describe orders le-crt-613810377-1285319347
  Normal  Created  3m29s  cert-manager  Created Challenge resource "le-crt-613810377-1285319347-3996324737" for domain "yet-another.website"                 
  Normal  Created  3m29s  cert-manager  Created Challenge resource "le-crt-613810377-1285319347-1443470517" for domain "yet-another.website"

عدد 4. استخدام شروح الدخول الخاصة


بالإضافة إلى المسار المباشر لإنشاء الشهادات في Cert-manager ، من الممكن استخدام مكون يسمى ingress-shim وعدم إنشاء الموارد بشكل صريح Certificate. الفكرة هي أنه بمساعدة التعليقات التوضيحية الخاصة بالمؤتمر ، سيتم طلب الشهادة تلقائيًا باستخدام الشهادة المشار إليها فيها Issuer. والنتيجة هي مورد Ingress تقريبًا:

apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
  annotations:
    cert-manager.io/cluster-issuer: letsencrypt
spec:
  tls:
  - hosts:
    - "yet-another.website"
    secretName: tls-secret
  rules:
  - host: "yet-another.website"
    http:
      paths:
      - path: /
        backend:
          serviceName: app
          servicePort: 8080

للتشغيل الصحيح ، يكفي وجود المُصدر فقط هنا ، أي إنشاء كيان واحد أقل.

بالإضافة إلى ذلك ، هناك تعليق توضيحي قديم kube-lego - kubernetes.io/tls-acme: "true"، - والذي يتطلب مهمة Issuerافتراضية عند تثبيت cert-manager باستخدام معلمات Helm (أو في خيارات تشغيل حاوية المدير).

نحن في الشركة لا نستخدم هذه الخيارات ولا يمكننا تقديم المشورة لهم بسبب غموض الأساليب المستخدمة لطلب شهادات SSL (وفي نفس الوقت للمشكلات المختلفة التي تنشأ ) ، لكننا قررنا أن نذكر في المقالة للحصول على صورة أكثر اكتمالًا.

بدلا من الاستنتاج


من خلال التلاعب البسيط باستخدام CRD ، تعلمنا كيفية كتابة شهادات SSL القابلة للتجديد التلقائي والموقعة ذاتيًا والمجانية من مشروع Let's Encrypt لنطاقات مواقع الويب التي تم إطلاقها كجزء من مجموعات Ingresss in Kubernetes.

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

ملاحظة


اقرأ أيضا في مدونتنا:


All Articles