Menulis reverse proxy Grafana on Go


Saya benar-benar ingin menamai artikel "Proxy-service on Go dalam 3 baris", tapi saya di atasnya.


Bahkan, memang, logika utama bisa masuk dalam tiga baris. Untuk yang tidak sabar dan mereka yang ingin melihat esensi:


proxy := httputil.NewSingleHostReverseProxy(url) r.Header.Set(header, value) proxy.ServeHTTP(w, r) 

Di bawah kucing adalah cerita yang lebih rinci untuk pendatang baru ke bahasa Golang dan mereka yang perlu membuat proxy terbalik dalam waktu sesingkat mungkin.


Mari kita cari tahu mengapa layanan proxy diperlukan, bagaimana menerapkannya dan apa yang ada di bawah kap perpustakaan standar.


Membalikkan proxy


Proksi terbalik adalah jenis server proxy yang menerima permintaan dari klien, mengarahkannya ke satu atau lebih server, dan meneruskan tanggapan kembali.


Fitur khas dari proxy terbalik adalah bahwa itu adalah titik masuk untuk menghubungkan pengguna ke server dengan mana proxy itu sendiri terhubung oleh logika bisnis. Ini menentukan server mana permintaan klien akan dikirim. Logika membangun jaringan di belakang proxy tetap tersembunyi dari pengguna.



Membalikkan Proksi


Sebagai perbandingan, proxy reguler menghubungkan kliennya ke server mana pun yang mereka butuhkan. Dalam hal ini, proksi ada di depan pengguna dan hanyalah perantara dalam pelaksanaan permintaan.



Proxy normal (Teruskan Proxy)


Mengapa menggunakan
Konsep reverse proxy dapat diterapkan dalam berbagai situasi:
- load balancing,
- Pengujian A / B
- caching sumber daya,
- kompresi data permintaan,
- penyaringan lalu lintas,
- otorisasi.


Tentu saja, cakupannya tidak terbatas pada enam poin ini. Fakta dari kemungkinan memproses permintaan baik sebelum dan sesudah proxy memberikan banyak ruang untuk kreativitas. Dalam artikel ini, kami akan membahas penggunaan proxy terbalik untuk otorisasi.


Tantangan


Kami sedang mengembangkan panel kontrol virtualisasi VMmanager 6. Suatu hari, kami memutuskan untuk memberi pengguna lebih banyak kebebasan dalam memantau dan memvisualisasikan kluster ini. Untuk tujuan ini, mereka memilih Grafana .


Agar Grafana dapat bekerja dengan data kami, perlu untuk mengonfigurasi otorisasi. Tidak sulit untuk melakukan ini, jika bukan untuk tiga "tetapi."


  1. Kami sudah memiliki satu titik masuk - layanan otorisasi.
  2. Kami tidak ingin memulai dan mengizinkan pengguna di Grafana.
  3. Kami tidak ingin memberi pengguna akses ke Grafana secara langsung.

Untuk memenuhi persyaratan, kami memutuskan untuk menempatkan Grafana di jaringan internal dan menulis proksi terbalik. Dia akan memeriksa hak dalam layanan otorisasi dan hanya setelah itu mentransfer permintaan ke Grafana.


Ide


Gagasan utamanya adalah mengalihkan tanggung jawab untuk otorisasi di Grafana ke server proxy terbalik ( dokumentasi resmi ). Grafana akan menerima permintaan apa pun yang diizinkan jika berisi tajuk tertentu. Sebelum mengganti judul ini, kami harus memastikan bahwa pengguna saat ini memiliki hak untuk bekerja dengan Grafana.



Rantai panggilan "Grafana-proxy, atau Round-trip"


Implementasi


Fungsi utamanya cukup standar. Kami memulai http-server, yang akan menerima koneksi pada 4000 port dan memproses setiap alamat "/" yang dengannya koneksi tersebut terjadi.


 func main() { http.HandleFunc("/", handlerProxy) if err := http.ListenAndServe(":4000", nil); err != nil { panic(err) } } 

Sebagian besar pekerjaan terjadi di penangan permintaan.


[kode lengkap terpotong]
 func handlerProxy(w http.ResponseWriter, r *http.Request) { fmt.Println(r.URL.Host) if strings.HasPrefix(r.URL.String(), "/api") { //     } url, err := url.Parse(fmt.Sprintf("http://%s/", grafanaHost)) if err != nil { SendJSONError(w, err.Error()) return } proxy := httputil.NewSingleHostReverseProxy(url) fmt.Println(r.URL.Host) r.Header.Set(grafanaHeader, grafanaUser) proxy.ServeHTTP(w, r) } 

Mari kita lihat parameternya. Variabel utama dalam contoh ini saya masukkan ke dalam konstanta:


 grafanaUser = "admin" //,      grafanaHost = "grafana:3000" //  grafana grafanaHeader = "X-GRAFANA-AUTH" //Header,      

Sebagai contoh, ini sudah cukup, dalam praktiknya, Anda mungkin perlu mempreset nilai-nilai ini. Anda bisa memberikan mereka proksi sebagai parameter baris perintah, lalu menggunakan flag atau paket yang lebih canggih untuk menguraikannya. Lingkungan kontainer juga sering menggunakan variabel lingkungan untuk mengkonfigurasi layanan, os.Getenv akan membantu Anda di sepanjang jalan.


Berikutnya adalah cek otorisasi:


 if strings.HasPrefix(r.URL.String(), "/api") { err := CheckRights(r.Header) if err != nil { SendJSONError(w, err.Error()) return } } 

Jika permintaan masuk ke grafana.host/api, kami memeriksa hak pengguna saat ini untuk menggunakan Grafana. Diperlukan verifikasi agar setiap permintaan GET dari skrip JS atau ikon PNG tidak mengganggu titik otorisasi. Kami akan proksi konten statis tanpa pemeriksaan tambahan. Untuk melakukan ini, kami meneruskan peta dengan header yang berisi sesi pengguna ke layanan otorisasi. Ini mungkin permintaan GET biasa. Perangkat layanan otorisasi tidak masalah di sini. Jika data otorisasi tidak sesuai, tutup koneksi, kembalikan kesalahan.


Setelah pemeriksaan, kami membentuk objek jalur dasar:


 url, err := url.Parse(fmt.Sprintf("http://%s/", grafanaHost)) 

Menggunakan paket httputil standar, yang memperluas paket http, kami membentuk objek ReverseProxy.


 proxy := httputil.NewSingleHostReverseProxy(url) 

ReverseProxy adalah penangan permintaan yang akan menerima permintaan yang masuk, mengirimkannya ke Grafana dan meneruskan respons kembali ke klien.


Ini akan meneruskan semua permintaan ke alamat "jalur dasar + url yang diminta". Jika pengguna datang ke alamat proxy: 4000 / api / something, permintaannya akan berubah menjadi grafana: 3000 / api / something, di mana grafana: 3000 adalah jalur dasar yang dilewatkan ke NewSingleHostReverseProxy, dan / api / something adalah permintaan yang masuk.


Tambahkan tajuk otorisasi untuk Grafana dan panggil metode ServeHTTP, yang akan melakukan sebagian besar pemrosesan permintaan.


 r.Header.Set(grafanaHeader, grafanaUser) proxy.ServeHTTP(w, r) 

Di bawah tenda, ServeHTTP melakukan sedikit pekerjaan, misalnya, memproses tajuk X-Forwarded-For atau 101 server terhadap perubahan protokol. Pekerjaan utama metode ini adalah mengirim permintaan ke alamat gabungan dan mentransfer jawaban yang diterima ke ResponseWriter.



Hasil


Semua kode tersedia di github .


Periksa


Tiru sistem kami menggunakan Docker. Mari kita buat dua kontainer - proxy dan Grafana dalam satu jaringan. Kami tidak akan membuat titik otorisasi, kami percaya bahwa itu selalu menjawab dengan tegas. Wadah Grafana tidak akan tersedia offline, wadah proxy tersedia di port tertentu.


Buat jaringan:


 docker network create --driver=bridge --subnet=192.168.0.0/16 gnet 

Naikkan wadah Grafana dengan mode otorisasi yang dikonfigurasi melalui header:


 docker run -d --name=grafana --network=gnet -e "GF_AUTH_PROXY_ENABLED=true" -e "GF_AUTH_PROXY_HEADER_NAME=X-GRAFANA-AUTH" grafana/grafana 

Harap dicatat bahwa ini adalah demo dan bukan konfigurasi final. Minimal, Anda harus menetapkan kata sandi administrator dan melarang pendaftaran pengguna otomatis.


Naikkan proxy terbalik:


 docker run -d --name proxy -p 4000:4000 --network=gnet grafana_proxy:latest 

Di browser, buka localhost: 4000.


Hebat, kami memiliki Grafana resmi di depan kami.


Dockerfile untuk membangun wadah dengan proxy dan instruksi yang lebih rinci tentang mengangkat wadah ada di github .

Source: https://habr.com/ru/post/id480446/


All Articles