Flazzo memiliki fokus utama untuk menambah nilai bisnis Anda.

Blog

mTLS Di Mana Saja – DZone

16786941-thumb.jpg
Blog

mTLS Di Mana Saja – DZone


Keamanan sistem informasinya selalu menjadi salah satu persyaratan non-fungsional yang paling kritis. Transport Layer Security, alias TLS dan sebelumnya SSL, adalah salah satu dari banyak pilarnya. Pada artikel ini, saya akan menunjukkan cara mengkonfigurasi TLS untuk Apache APISIX API Gateway.

Singkatnya, TLS

TLS menawarkan beberapa fitur:

  • Otentikasi server: Klien yakin bahwa server tempat bertukar data adalah server yang benar. Ini menghindari pengiriman data, yang mungkin bersifat rahasia, ke aktor yang salah.
  • Otentikasi klien opsional: Kebalikannya: server hanya mengizinkan klien yang identitasnya dapat diverifikasi.
  • Pribadi: Tidak ada pihak ketiga yang dapat membaca data yang dipertukarkan antara klien dan server.
  • Integritas: Tidak ada pihak ketiga yang dapat mengubah data.

TLS bekerja melalui sertifikat. Sertifikat mirip dengan dokumen identitas, membuktikan identitas pemegang sertifikat. Sama seperti ID, Anda harus mempercayai siapa yang mengeluarkannya. Kepercayaan dibangun oleh sebuah rantai: jika saya mempercayai Alice, yang mempercayai Bob, yang pada gilirannya mempercayai Charlie, yang mengeluarkan sertifikat, maka saya mempercayai yang terakhir. Dalam skenario ini, Alice dikenal sebagai otoritas sertifikasi akar.

Otentikasi TLS didasarkan pada kriptografi kunci publik. Alice menghasilkan pasangan kunci publik/kunci pribadi dan menerbitkan kunci publik. Jika Anda mengenkripsi data dengan kunci publik, hanya kunci privat yang menghasilkan kunci publik yang dapat mendekripsinya. Kegunaan lainnya adalah untuk mengenkripsi data dengan kunci privat dan setiap orang dengan kunci publik untuk mendekripsinya, sehingga membuktikan identitas mereka.

Terakhir, mutual TLS, alias mTLS, adalah konfigurasi TLS dua arah: autentikasi server-ke-klien, seperti biasa, tetapi juga sebaliknya, autentikasi klien-ke-server.

Kami sekarang memiliki pemahaman yang cukup tentang konsep untuk mengotori tangan kami.

Menghasilkan sertifikat dengan cert-manager

Beberapa akar California diinstal secara default di browser. Inilah cara kami menjelajahi situs web HTTPS dengan aman, mempercayai situs yang diklaimnya. Framework tidak memiliki sertifikat pra-instal, jadi kami harus memulai dari awal.

Kami membutuhkan setidaknya satu sertifikat root. Pada gilirannya, itu akan menghasilkan semua sertifikat lainnya. Meskipun dimungkinkan untuk melakukan semuanya secara manual, saya mengandalkan cert-manager di Kubernetes. Seperti namanya, cert-manager adalah solusi untuk mengelola sertifikat.

Memasangnya dengan Helm itu sederhana:

helm repo add jetstack   #1

helm install \
  cert-manager jetstack/cert-manager \
  --namespace cert-manager \                       #2
  --create-namespace \                             #2
  --version v1.11.0 \
  --set installCRDs=true \
  --set prometheus.enabled=false                   #3

  1. Tambahkan repositori grafik.
  2. Instal objek di namespace khusus.
  3. Jangan menonton; di bawah posisi ini.

Kami dapat memastikan semuanya berfungsi seperti yang diharapkan dengan melihat pod:

kubectl get pods -n cert-manager

cert-manager-cainjector-7f694c4c58-fc9bk  1/1  Running  2  (2d1h ago)  7d
cert-manager-cc4b776cf-8p2t8              1/1  Running  1  (2d1h ago)  7d
cert-manager-webhook-7cd8c769bb-494tl     1/1  Running  1  (2d1h ago)  7d

cert-manager dapat menandatangani sertifikat dari beberapa sumber: HashiCorp Vault, Let’s Encrypt, dll. Untuk kesederhanaan:

  • Kami akan membuat sertifikat root khusus kami, mis.., Self-Signed.
  • Kami tidak akan mengelola rotasi sertifikat.

Mari kita mulai dengan yang berikut ini:

apiVersion: cert-manager.io/v1
kind: ClusterIssuer                           #1
metadata:
  name: selfsigned-issuer
spec:
  selfSigned: {}
---
apiVersion: v1
kind: Namespace
metadata:
  name: tls                                   #2
---
apiVersion: cert-manager.io/v1
kind: Certificate                             #3
metadata:
  name: selfsigned-ca
  namespace: tls
spec:
  isCA: true
  commonName: selfsigned-ca
  secretName: root-secret
  issuerRef:
    name: selfsigned-issuer
    kind: ClusterIssuer
    group: cert-manager.io
---
apiVersion: cert-manager.io/v1
kind: Issuer                                  #4
metadata:
  name: ca-issuer
  namespace: tls
spec:
  ca:
    secretName: root-secret

  1. CA yang menghasilkan sertifikat luas kluster
  2. Buat ruang nama untuk demo kami.
  3. Sertifikat root dengan namespace menggunakan penerbit seluruh cluster. Digunakan hanya untuk membuat penerbit namespace.
  4. Penerbit dengan namespace: Digunakan untuk membuat semua sertifikat lain dalam publikasi.

Setelah menerapkan manifes sebelumnya, kita seharusnya dapat melihat sertifikat tunggal yang kita buat:

kubectl get certificate -n tls

NAME            READY   SECRET        AGE
selfsigned-ca   True    root-secret   7s

Infrastruktur sertifikat sudah siap; mari kita lihat Apache APISIX.

Tur singkat tentang contoh arsitektur Apache APISIX

Apache APISIX adalah gerbang API. Secara default, ini menyimpan konfigurasinya di etcd, penyimpanan nilai kunci terdistribusi – sama dengan yang digunakan oleh Kubernetes. Perhatikan bahwa dalam skenario nyata, kita perlu mengonfigurasi pengelompokan etcd untuk meningkatkan ketahanan solusi. Untuk artikel ini, kami akan membatasi diri pada satu contoh etcd. Apache APISIX menawarkan API administrasi melalui titik akhir HTTP. Terakhir, gateway meneruskan panggilan pelanggan ke upstream. Berikut ini ikhtisar arsitektur dan sertifikat yang diperlukan:

Presentasi arsitektur dan sertifikat yang diperlukan

Mari kita mulai dengan batu bata fundamental: etcd dan Apache APISIX. Kami memerlukan dua sertifikat: satu untuk etcd, dalam peran server, dan satu untuk Apache APISIX, sebagai klien etcd.

Mari konfigurasikan sertifikat penerbit kita dengan namespace:

apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
  name: etcd-server                         #1
  namespace: tls
spec:
  secretName: etcd-secret                   #2
  isCA: false
  usages:
    - client auth                           #3
    - server auth                           #3
  dnsNames:
    - etcd                                  #4
  issuerRef:
    name: ca-issuer                         #5
    kind: Issuer
---
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
  name: apisix-client                       #6
  namespace: tls
spec:
  secretName: apisix-client-secret
  isCA: false
  usages:
    - client auth
  emailAddresses:
    - apisix@apache.org                     #7
  issuerRef:
    name: ca-issuer                         #5
    kind: Issuer

  1. Sertifikat untuk dll
  2. Kubernetes Secret nama, lihat di bawah
  3. Penggunaan sertifikat ini
  4. Kubernetes Service nama, lihat di bawah
  5. Referensi emitor yang dibuat sebelumnya dengan namespace
  6. Sertifikat untuk Apache APISIX sebagai klien etcd
  7. Atribut wajib untuk pelanggan

Setelah menerapkan manifes di atas, kita dapat membuat daftar sertifikat di tls ruang nama:

kubectl get certificates -n tls

NAME              READY   SECRET                 AGE
selfsigned-ca     True    root-secret            8m59s    //1
apisix-client     True    apisix-client-secret   8m22s    //2
etcd-server       True    etcd-secret            8m54s    //2

  1. Sertifikat yang dibuat sebelumnya
  2. Sertifikat yang baru dibuat ditandatangani oleh selfsigned-ca

sertifikat manajer sertifikat

Sejauh ini kami telah membuat Certificate objek, tapi kami belum menjelaskan apa itu. Memang, mereka adalah Kubernet sederhana CRDdisediakan oleh pengelola sertifikat. Di bawah perlindungan, cert-manager membuat Kubernetes Secret dari Certificate. Itu mengelola seluruh siklus hidup, menghapus a Certificate hilangkan batasnya Secret. ITU secretName Atribut dalam manifes di atas mendefinisikan Secret nama.

kubectl get secrets -n tls

NAME                   TYPE                DATA   AGE
apisix-client-secret   kubernetes.io/tls   3      35m
etcd-secret            kubernetes.io/tls   3      35m
root-secret            kubernetes.io/tls   3      35m

mari kita lihat sebuah SecretMisalnya., apisix-client-secret:

kubectl describe apisix-client-secret -n tls

Name:         apisix-client-secret
Namespace:    tls
Labels:       controller.cert-manager.io/fao=true
Annotations:  cert-manager.io/alt-names: 
              cert-manager.io/certificate-name: apisix-client
              cert-manager.io/common-name: 
              cert-manager.io/ip-sans: 
              cert-manager.io/issuer-group: 
              cert-manager.io/issuer-kind: Issuer
              cert-manager.io/issuer-name: ca-issuer
              cert-manager.io/uri-sans: 

Type:  kubernetes.io/tls

Data
====
ca.crt:   1099 bytes
tls.crt:  1115 bytes
tls.key:  1679 bytes

A Secret dibuat oleh a Certificate menyediakan tiga atribut:

  • tls.crt: Sertifikat itu sendiri
  • tls.key: Kunci pribadi
  • ca.crt: Sertifikat penandatanganan dalam rantai sertifikat, yaitu, root-secret/tls.crt

Enkode Kubernetes Secret konten di basis 64. Untuk mendapatkan yang di atas dalam teks biasa, Anda harus mendekodekannya, misalnya:

kubectl get secret etcd-secret -n tls -o jsonpath="{ .data.tls\.crt }" | base64

-----BEGIN CERTIFICATE-----
MIIDBjCCAe6gAwIBAgIQM3JUR8+R0vuUndjGK/aOgzANBgkqhkiG9w0BAQsFADAY
MRYwFAYDVQQDEw1zZWxmc2lnbmVkLWNhMB4XDTIzMDMxNjEwMTYyN1oXDTIzMDYx
NDEwMTYyN1owADCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMQpMj/0
giDVOjOosSRRKUwTzl1Wo2R9YYAeteOW3fuMiAd+XaBGmRO/+GWZQN1tyRQ3pITM
ezBgogYAUUNcuqN/UAsgH/JM58niMjZdjRKn4+it94Nj1e24jFL4ts2snCn7FfKJ
3zRtY9tyS7Agw3tCwtXV68Xpmf3CsfhPmn3rGdWHXyYctzAZhqYfEswN3hxpJZxR
YVeb55WgDoPo5npZo3+yYiMtoOimIprcmZ2Ye8Wai9S4QKDafUWlvU5GQ65VVLzH
PEdOMwbWcwiLqwUv889TiKiC5cyAD6wJOuPRF0KKxxFnG+lHlg9J2S1i5sC3pqoc
i0pEQ+atOOyLMMECAwEAAaNkMGIwHQYDVR0lBBYwFAYIKwYBBQUHAwIGCCsGAQUF
BwMBMAwGA1UdEwEB/wQCMAAwHwYDVR0jBBgwFoAU2ZaAdEficKUWPFRjdsKSEX/l
gbMwEgYDVR0RAQH/BAgwBoIEZXRjZDANBgkqhkiG9w0BAQsFAAOCAQEABcNvYTm8
ZJe3jUq6f872dpNVulb2UvloTpWxQ8jwXgcrhekSKU6pZ4p9IPwfauHLjceMFJLp
t2eDi5fSQ1upeqXOofeyKSYjjyA/aVf1zMI8ReCCQtQuAVYyJWBlNLc3XMMecbcp
JLGtd/OAZnKDeYYkUX7cJ2wN6Wl/wGLM2lxsqDhEHEZwvGL0DmsdHw7hzSjdVmxs
0Qgkh4jVbNUKdBok5U9Ivr3P1xDPaD/FqGFyM0ssVOCHxtPxhOUA/m3DSr6klfEF
McOfudZE958bChOrJgVrUnY3inR0J335bGQ1luEp5tYwPgyD9dG4MQEDD3oLwp+l
+NtTUqz8WVlMxQ==
-----END CERTIFICATE-----

Mengonfigurasi mTLS antara etcd dan APISIX

Dengan sertifikat yang tersedia, sekarang kita dapat mengonfigurasi TLS timbal balik antara etcd dan APISIX. Mari kita mulai dengan etcd:

apiVersion: v1
kind: Pod
metadata:
  name: etcd
  namespace: tls
  labels:
    role: config
spec:
  containers:
    - name: etcd
      image: bitnami/etcd:3.5.7
      ports:
        - containerPort: 2379
      env:
        - name: ETCD_TRUSTED_CA_FILE        #1
          value: /etc/ssl/private/ca.crt
        - name: ETCD_CERT_FILE              #2
          value: /etc/ssl/private/tls.crt
        - name: ETCD_KEY_FILE               #3
          value: /etc/ssl/private/tls.key
        - name: ETCD_ROOT_PASSWORD
          value: whatever
        - name: ETCD_CLIENT_CERT_AUTH       #4
          value: "true"
        - name: ETCD_LISTEN_CLIENT_URLS
          value: 
      volumeMounts:
        - name: ssl
          mountPath: /etc/ssl/private       #5
  volumes:
    - name: ssl
      secret:
        secretName: etcd-secret             #5

  1. Tetapkan Otoritas Sertifikat Tepercaya
  2. Tetapkan sertifikat
  3. Tetapkan kunci pribadi
  4. Mengharuskan klien untuk lulus sertifikat mereka, memastikan otentikasi timbal balik
  5. Pasang rahasia yang dibuat sebelumnya ke dalam wadah untuk mengaksesnya

Sekarang giliran Apache APISIX:

apiVersion: v1
kind: ConfigMap                                            #1
metadata:
  name: apisix-config
  namespace: tls
data:
  config.yaml: >-
    apisix:
      ssl:
        ssl_trusted_certificate: /etc/ssl/certs/ca.crt     #2
    deployment:
      etcd:
        host:
          - 
        tls:
          cert: /etc/ssl/certs/tls.crt                     #2
          key: /etc/ssl/certs/tls.key                      #2
      admin:
        allow_admin:
          - 0.0.0.0/0
        https_admin: true                                  #3
        admin_api_mtls:
          admin_ssl_cert: /etc/ssl/private/tls.crt         #3
          admin_ssl_cert_key: /etc/ssl/private/tls.key     #3
          admin_ssl_ca_cert: /etc/ssl/private/ca.crt       #3
---
apiVersion: v1
kind: Pod
metadata:
  name: apisix
  namespace: tls
  labels:
    role: gateway
spec:
  containers:
    - name: apisix
      image: apache/apisix:3.2.0-debian
      ports:
        - containerPort: 9443                              #4
        - containerPort: 9180                              #5
      volumeMounts:
        - name: config                                      #1
          mountPath: /usr/local/apisix/conf/config.yaml
          subPath: config.yaml
        - name: ssl                                        #6
          mountPath: /etc/ssl/private
        - name: etcd-client                                #7
          mountPath: /etc/ssl/certs
  volumes:
    - name: config
      configMap:
        name: apisix-config
    - name: ssl                                            #6,8
      secret:
        secretName: apisix-server-secret
    - name: etcd-client                                    #7,8
      secret:
        secretName: apisix-client-secret

  1. Apache APISIX tidak menawarkan konfigurasi melalui variabel lingkungan. Kita harus menggunakan a ConfigMap yang mencerminkan kebiasaan config.yaml untuk mengajukan.
  2. Konfigurasi pelanggan otentikasi untuk etcd
  3. Konfigurasi server otentikasi untuk API administrasi
  4. Porta HTTPS biasa
  5. Porta HTTPS administrator
  6. Sertifikat untuk otentikasi server
  7. Sertifikat untuk autentikasi klien
  8. Dua set sertifikat digunakan: satu untuk autentikasi server untuk API administrasi dan protokol HTTPS standar, dan satu untuk autentikasi klien untuk etcd.

Pada titik ini, kita dapat menerapkan manifes di atas dan melihat kedua pod berkomunikasi. Saat menghubungkan, Apache APISIX mengirimkannya apisix-client sertifikat melalui HTTPS. Karena otoritas menandatangani sertifikat yang dipercaya oleh etcd, ia mengotorisasi koneksi.

Saya menghilangkan Service definisi demi singkatnya, tetapi Anda dapat memeriksanya di repositori GitHub.

NAME     READY   STATUS    RESTARTS   AGE
apisix   1/1     Running   0          179m
etcd     1/1     Running   0          179m

Akses pelanggan

Sekarang kita memiliki infrastruktur dasar, kita perlu menguji akses ke sana dengan klien. Kami akan menggunakan setia kami curltetapi setiap klien yang mengizinkan konfigurasi sertifikat harus berfungsi, misalnya httpie.

Langkah pertama adalah membuat pasangan kunci sertifikat khusus untuk klien:

apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
  name: curl-client
  namespace: tls
spec:
  secretName: curl-secret
  isCA: false
  usages:
    - client auth
  emailAddresses:
    - curl@localhost.dev
  issuerRef:
    name: ca-issuer
    kind: Issuer

curl membutuhkan jalur ke file sertifikat, bukan konten. Keterbatasan ini dapat dielakkan berkat keajaiban zsh: the =( ... ) Sintaks memungkinkan pembuatan file sementara. Jika Anda menggunakan shell lain, Anda perlu menemukan sintaks yang setara atau mengunduh file secara manual.

Mari kita kueri API Admin untuk semua rute yang ada. Perintah sederhana ini memverifikasi bahwa Apache APISIX terhubung ke etcd, dan dapat membaca konfigurasinya dari sana.

curl --resolve 'admin:32180:127.0.0.1'  \                     #1
     --cert =(kubectl get secret curl-secret -n tls -o jsonpath="{ .data.tls\.crt }" | base64 -d) \  #2
     --key =(kubectl get secret curl-secret -n tls -o jsonpath="{ .data.tls\.key }" | base64 -d) \   #2
     --cacert =(kubectl get secret curl-secret -n tls -o jsonpath="{ .data.ca\.crt }" | base64 -d) \ #2
     -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1'

  1. --resolve menghindari polusi /etc/hosts untuk mengajukan. curl akan menerjemahkan admin Untuk localhosttapi permintaan dikirim ke admin di dalam kluster Kubernetes, sehingga menggunakan yang benar Service.
  2. Dapatkan data yang diperlukan di dalam Secretdecode dan menggunakannya sebagai file sementara.

Jika semuanya berfungsi, dan seharusnya, hasilnya akan terlihat seperti ini:

Tidak ada rencana perjalanan yang tersedia saat ini karena kami belum membuatnya.

TLS dengan aliran hulu

Terakhir, kita perlu mengonfigurasi TLS untuk upstream. Berikut ini, saya akan menggunakan yang sederhana nginx contoh yang merespons dengan konten statis. Gunakan itu sebagai ilustrasi untuk hulu yang lebih kompleks.

Langkah pertama, seperti biasa, adalah menghasilkan a Certificate untuk hulu. Saya akan melewatkan cara melakukannya karena kita sudah membuat beberapa. aku memanggilnya upstream-server dan miliknya Secrettidak imajinatif, upstream-secret. Kami sekarang dapat menggunakan yang terakhir untuk mengamankan nginx:

apiVersion: v1
kind: ConfigMap                                           #1
metadata:
  name: nginx-config
  namespace: tls
data:
  nginx.conf: >-
    events {
      worker_connections 1024;
    }
    http {
      server {
        listen              443 ssl;
        server_name         upstream;
        ssl_certificate     /etc/ssl/private/tls.crt;     #2
        ssl_certificate_key /etc/ssl/private/tls.key;     #2

        root /www/data;
        location / {
            index index.json;
        }
      }
    }
---
apiVersion: v1
kind: Pod
metadata:
  name: upstream
  namespace: tls
  labels:
    role: upstream
spec:
  containers:
    - name: upstream
      image: nginx:1.23-alpine
      ports:
        - containerPort: 443
      volumeMounts:
        - name: config
          mountPath: /etc/nginx/nginx.conf                #1
          subPath: nginx.conf
        - name: content
          mountPath: /www/data/index.json                 #3
          subPath: index.json
        - name: ssl                                       #2
          mountPath: /etc/ssl/private
  volumes:
    - name: config
      configMap:
        name: nginx-config
    - name: ssl                                           #2
      secret:
        secretName: upstream-secret
    - name: content                                       #3
      configMap:
        name: nginx-content

  1. NGINX tidak mengizinkan konfigurasi melalui variabel lingkungan; kita harus menggunakan ConfigMap mendekati.
  2. Gunakan pasangan kunci-sertifikat yang dibuat melalui Certificate.
  3. Beberapa konten statis tidak relevan dengan artikel ini

Langkah selanjutnya adalah membuat rute menggunakan Admin API. Kami menyiapkan semuanya di langkah sebelumnya; sekarang kita bisa menggunakan API:

curl --resolve 'admin:32180:127.0.0.1' /1 \
     --cert =(kubectl get secret curl-secret -n tls -o jsonpath="{ .data.tls\.crt }" | base64 -d) \     #1
     --key =(kubectl get secret curl-secret -n tls -o jsonpath="{ .data.tls\.key }" | base64 -d) \      #1
     --cacert =(kubectl get secret curl-secret -n tls -o jsonpath="{ .data.ca\.crt }" | base64 -d) \    #1
     -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -i -d "{
        \"uri\": \"/\",
        \"upstream\": {
          \"scheme\": \"https\",                                                                        #2
          \"nodes\": {
            \"upstream:443\": 1
          },
          \"tls\": {
            \"client_cert\": \"$(kubectl get secret curl-secret -n tls -o jsonpath="{ .data.tls\.crt }" | base64 -d)\", #3
            \"client_key\": \"$(kubectl get secret curl-secret -n tls -o jsonpath="{ .data.tls\.key }" | base64 -d)\"   #3
          }
        }
     }"

  1. Otentikasi klien untuk admin API, seperti di atas
  2. Gunakan HTTPS untuk upstream
  3. Konfigurasi pasangan sertifikat kunci untuk rute. Apache APISIX menyimpan data di etcd dan akan menggunakannya saat Anda memanggil rute. Alternatifnya, Anda dapat menyimpan pasangan sebagai objek khusus dan menggunakan referensi yang baru dibuat (seperti pada upstream). Itu tergantung pada berapa banyak rute yang dibutuhkan sertifikat. Untuk informasi lebih lanjut, lihat titik akhir SSL.

Akhirnya, kami dapat memverifikasi bahwa ini berfungsi seperti yang diharapkan:

curl --resolve 'upstream:32443:127.0.0.1'  \
     --cert =(kubectl get secret curl-secret -n tls -o jsonpath="{ .data.tls\.crt }" | base64 -d) \
     --key =(kubectl get secret curl-secret -n tls -o jsonpath="{ .data.tls\.key }" | base64 -d) \
     --cacert =(kubectl get secret curl-secret -n tls -o jsonpath="{ .data.ca\.crt }" | base64 -d)

Dan itu:

Kesimpulan

Pada artikel ini, saya menjelaskan arsitektur Apache APISIX yang berfungsi dan mengimplementasikan TLS bersama antara semua komponen: etcd dan APISIX, klien dan APISIX, dan terakhir, klien dan upstream. Semoga ini membantu Anda mencapai tujuan yang sama.

Kode sumber lengkap untuk artikel ini tersedia di GitHub (ditautkan di atas di bagian “Mengonfigurasi mTLS antara etcd dan APISIX”).

Untuk selanjutnya: