mTLS Di Mana Saja – DZone

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
- Tambahkan repositori grafik.
- Instal objek di namespace khusus.
- 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
- CA yang menghasilkan sertifikat luas kluster
- Buat ruang nama untuk demo kami.
- Sertifikat root dengan namespace menggunakan penerbit seluruh cluster. Digunakan hanya untuk membuat penerbit namespace.
- 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:
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
- Sertifikat untuk dll
- Kubernetes
Secret
nama, lihat di bawah - Penggunaan sertifikat ini
- Kubernetes
Service
nama, lihat di bawah - Referensi emitor yang dibuat sebelumnya dengan namespace
- Sertifikat untuk Apache APISIX sebagai klien etcd
- 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
- Sertifikat yang dibuat sebelumnya
- 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 Secret
Misalnya., 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 sendiritls.key
: Kunci pribadica.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
- Tetapkan Otoritas Sertifikat Tepercaya
- Tetapkan sertifikat
- Tetapkan kunci pribadi
- Mengharuskan klien untuk lulus sertifikat mereka, memastikan otentikasi timbal balik
- 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
- Apache APISIX tidak menawarkan konfigurasi melalui variabel lingkungan. Kita harus menggunakan a
ConfigMap
yang mencerminkan kebiasaanconfig.yaml
untuk mengajukan. - Konfigurasi pelanggan otentikasi untuk etcd
- Konfigurasi server otentikasi untuk API administrasi
- Porta HTTPS biasa
- Porta HTTPS administrator
- Sertifikat untuk otentikasi server
- Sertifikat untuk autentikasi klien
- 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 curl
tetapi 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'
--resolve
menghindari polusi/etc/hosts
untuk mengajukan.curl
akan menerjemahkanadmin
Untuklocalhost
tapi permintaan dikirim keadmin
di dalam kluster Kubernetes, sehingga menggunakan yang benarService
.- Dapatkan data yang diperlukan di dalam
Secret
decode 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 Secret
tidak 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
- NGINX tidak mengizinkan konfigurasi melalui variabel lingkungan; kita harus menggunakan
ConfigMap
mendekati. - Gunakan pasangan kunci-sertifikat yang dibuat melalui
Certificate
. - 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
}
}
}"
- Otentikasi klien untuk admin API, seperti di atas
- Gunakan HTTPS untuk upstream
- 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”).