OpenVPN dengan otentikasi dan otorisasi tingkat lanjut

Artikel ini membahas konfigurasi OpenVPN dengan fitur tambahan:

  • Sertifikat token untuk otentikasi utama (Rutoken sebagai contoh)
  • Backend LDAP untuk otentikasi sekunder (menggunakan ActiveDirectory sebagai contoh)
  • memfilter sumber daya internal yang tersedia untuk pengguna (melalui iptables)

Ini juga menjelaskan cara mengkonfigurasi klien untuk Linux, Windows dan MacOS.

Pengaturan server


Instal OpenVPN


Ambil script Nyr / openvpn-install , jalankan dari root.

git clone https://github.com/Nyr/openvpn-install.git cd openvpn-install 

Proses startup akan mengajukan beberapa pertanyaan.

  • protokol udp
  • port 1194
  • Server DNS - Lokal
  • alamat ip-gateway eksternal di Internet di mana server vpn akan tersedia

Ada juga versi skrip asli yang ditingkatkan keamanannya - github.com/Angristan/OpenVPN-install . Ini memiliki lebih banyak pengaturan enkripsi dengan penjelasan mengapa.

Manajemen pengguna


Menambahkan
Jika token tidak digunakan, pengguna ditambahkan melalui skrip yang sama. Script pada dasarnya menghasilkan konfigurasi ovpn khusus dan menyisipkan sertifikat yang ditandatangani oleh sertifikat root di sana.

Jika token digunakan (lihat bagian token di bawah), maka sertifikat tersebut ditulis secara manual berdasarkan permintaan untuk sertifikat yang dihasilkan pada token. Konfigurasi pengguna harus dilakukan secara manual dari templat yang ada (dari yang sama dari mana skrip konfigurasi dibuat). Templatnya ada di sini /etc/openvpn/client-common.txt . Itu tidak termasuk dalam paket openvpn dan dihasilkan oleh skrip selama proses konfigurasi.

Hapus
Menghapus pengguna dilakukan melalui skrip instalasi yang sama. Sertifikat ditambahkan ke CRL , CRL baru didorong ke server vpn. Server menganggap semua sertifikat yang ada di CRL tidak valid dan menolak untuk menerima.

Cara mencabut sertifikat secara manual:

 cd /etc/openvpn/easyrsa #   ./easyrsa revoke $CLIENT #   crl ./easyrsa gen-crl #   crl rm -rf /etc/openvpn/crl.pem #    cp /etc/openvpn/easy-rsa/pki/crl.pem /etc/openvpn/crl.pem # openvpn    crl,       nobody chown nobody:nobody /etc/openvpn/crl.pem 

Memfilter Host yang Tersedia untuk Klien


Klien harus dibatasi oleh host yang dapat mereka akses di jaringan ketika mereka terhubung ke openvpn.

Secara manual

Idenya adalah untuk menangkap paket bahkan pada antarmuka tun0 , di mana mereka berasal dari klien dan menyaringnya sebelum mereka masuk ke NAT. Setelah NAT, tidak akan ada alasan untuk memfilter mereka - mereka semua akan memiliki alamat ip server openvpn di jaringan internal. Sebelum masuk ke NAT, paket untuk setiap pengguna memiliki alamat ip unik mereka sendiri (untuk korespondensi alamat-ip dan pengguna, lihat file /etc/openvpn/ipp.txt ).

Paket yang melewati sistem (tidak datang langsung dari itu dan tidak masuk, yaitu, sebenarnya, dialihkan oleh sistem) diproses oleh tabel FORWARD. Tabel di iptables diproses dari atas ke bawah, jika tidak ada aturan dalam tabel yang mengarah pada keputusan tentang nasib paket, maka aturan default dipicu.

Mempersiapkan tabel FORWARD:

 #   iptables -F FORWARD #     FORWARD -    iptables -P FORWARD DROP #     iptables -I FORWARD -m state --state ESTABLISHED,RELATED -j ACCEPT 

Contoh aturan untuk klien tertentu. Karena aturan default untuk tabel ini adalah DROP, aturan ini hanya memperbolehkan pasangan host + port di mana Anda bisa. Izinkan akses ke port di host + ping host itu sendiri:

 iptables -I FORWARD -s 10.8.0.3 -i tun0 -d 10.0.2.3 -p tcp --dport 443 -j ACCEPT iptables -I FORWARD -s 10.8.0.3 -i tun0 -d 10.0.2.3 -p icmp --icmp-type echo-request -j ACCEPT 

Pada contoh di atas, host 10.8.0.3 diizinkan mengakses port 443 dari host 10.0.2.3.

Cara menutup akses:

 #         iptables -L FORWARD --line-numbers #     iptables -D FORWARD { } 

Maka Anda perlu menemukan semua aturan untuk klien tertentu dan menghapusnya.

Selama debugging, nyaman untuk melihat aturan apa yang berfungsi. Setiap aturan memiliki penghitung paket yang diproses.

 #  ,      watch iptables -nvL FORWARD #     iptables -Z FORWARD 

Secara otomatis

Server openvpn memiliki kemampuan untuk mengeksekusi skrip untuk tindakan tertentu. Khususnya, saat menghubungkan dan memutuskan klien. Skrip dapat ditulis pada apa saja jika hanya itu yang dapat dieksekusi. Di dalam skrip, variabel lingkungan meneruskan segala macam parameter ke koneksi saat ini. Kami tertarik pada variabel:

  • common_name (nama pemilik sertifikat; apa yang mendorong ke bidang nama umum saat membuat sertifikat)
  • ifconfig_pool_remote_ip (alamat ip klien pada tun0)
  • script_type (peristiwa mana yang terjadi - sambungkan atau putuskan).

Anda membutuhkan hak akses root untuk mengelola iptables. Openvpn setelah menghubungkan hak reset ke siapa pun dan menjalankan skrip darinya. Sangat buruk untuk tidak mengizinkan siapa pun untuk melakukan sesuatu dari bawah sudo, dan lebih baik tidak menggunakan tanda bintang dalam aturan, tapi entah bagaimana Anda perlu mengizinkan pengguna untuk mengontrol iptables.

 # /etc/sudoers.d/50_openvpn # #    nobody ALL = NOPASSWD: /sbin/iptables -A FORWARD* #     nobody ALL = NOPASSWD: /sbin/iptables -L FORWARD* #    nobody ALL = NOPASSWD: /sbin/iptables -D FORWARD* 

Di konfigurasi server, Anda perlu menambahkan izin untuk mengeksekusi file pihak ketiga dan mengaktifkan dua kait yang bertanggung jawab untuk menghubungkan dan memutuskan sambungan pengguna.

 script-security 2 client-connect /etc/openvpn/bin/hosts.rb client-disconnect /etc/openvpn/bin/hosts.rb 

Script itu sendiri, yang membaca konfigurasi dan menerapkan aturan untuk iptables. Skrip bekerja dengan prinsip yang sama seperti yang dijelaskan di bagian sebelumnya.

/openvpn/bin/hosts.rb
 #!/usr/bin/ruby # -*- coding: utf-8 -*- require 'pp' def log(string) puts 'hosts.rb: ' + string end def parse_config_file(name) config_path = "hosts/#{name}" unless File.exist?(config_path) puts "There is no specific configuration for #{name}." p name exit 0 end config_source = IO.read(config_path).split("\n") config = config_source.inject([]) do |result,line| ip, port, protocol = line.split(/\s+/) result << { ip: ip, port: port, protocol: protocol || 'tcp' } end end def get_config(name) user_config = parse_config_file(name) if user_config everybody_config = parse_config_file('everybody') end everybody_config + user_config end def apply_rule(rule) command = "sudo iptables #{rule}" log(command) system(command) end def remove_rule(number) command = "sudo iptables -D FORWARD #{number}" log(command) system(command) end def allow_target(source_ip, options) #         . apply_rule("-A FORWARD -s #{source_ip} -i tun0 -d #{options[:ip]} -p #{options[:protocol]} --dport #{options[:port]} -j ACCEPT") #       apply_rule("-A FORWARD -s #{source_ip} -i tun0 -d #{options[:ip]} -p icmp --icmp-type echo-request -j ACCEPT") end def clear_targets(source_ip) #      FORWARD,  source_ip. rules_exist = true while rules_exist table = `sudo iptables -L FORWARD --line-number`.split("\n") the_line = table.find do |line| fields = line.split(/\s+/) ip = fields[4] ip == source_ip end if the_line number = the_line.split(/\s+/)[0] remove_rule(number) else rules_exist = false end end end ################################################################################ script_type = ENV['script_type'] log(script_type) name = ENV['common_name'] source_ip = ENV['ifconfig_pool_remote_ip'] case script_type when 'client-connect' config = get_config(name) config.each{|target| allow_target(source_ip, target)} when 'client-disconnect' clear_targets(source_ip) else puts "Unknown script type #{script_type}." end 

Aturan disimpan dalam file yang sesuai dengan nama umum sertifikat di folder /etc/openvpn/hosts . Mereka menentukan dengan tepat alamat IP mana yang tersedia untuk klien tertentu. Separator - jumlah ruang yang berubah-ubah. Alamat IP, port, dan protokol (tcp atau udp) ditulis melalui pemisah.

 10.0.0.24 53 udp 10.0.0.25 53 udp 10.0.2.3 443 tcp 

Sebagai hasilnya, struktur berikut ini akan /etc/openvpn di folder /etc/openvpn

β”œβ”€β”€ bin
β”‚ └── hosts.rb
β”œβ”€β”€ tuan rumah
β”‚ β”œβ”€β”€ pengguna1
β”‚ β”œβ”€β”€ pengguna2
β”‚ └── semuanya
β”œβ”€β”€ server.conf
└── ...

User1 dan user2 adalah file dalam format di atas. Mereka menggambarkan host mana yang pengguna dengan nama umum terkait memiliki akses.

Ada file everybody lain tambahan, ini berisi aturan yang berlaku untuk semua klien, asalkan ada file konfigurasi terpisah untuk klien ini. Yaitu, jika pengguna memiliki daftar host di mana ia dapat pergi, maka daftar ini dan host yang terdaftar di everybody diterapkan. Jika tidak, maka everybody tidak berlaku. Misalnya, server DNS nyaman ditempatkan di file ini.

Penebangan

Skrip instalasi hanya menyertakan logging koneksi saat ini (parameter status) . Agar log reguler muncul, Anda perlu menambahkan baris ke konfigurasi server ( /etc/openvpn/server.conf ):
 log-append /var/log/openvpn.log 


LDAP

Ada plugin openvpn-auth-ldap yang memungkinkan Anda untuk mengautentikasi pengguna lagi melalui LDAP.

Paket pengiriman:

 sudo yum install openvpn-auth-ldap 

Tambahkan ke server.conf:

 plugin /usr/lib64/openvpn/plugin/lib/openvpn-auth-ldap.so "/etc/openvpn/ldap.conf" 

Buat config untuk ldap di /etc/openvpn/ldap.conf :
 <LDAP> URL ldaps://{LDAP_DOMAIN_HERE} Timeout 15 TLSEnable no FollowReferrals yes BindDN "BIND_DN_HERE" Password "BIND_PASSWORD_HERE" </LDAP> <Authorization> BaseDN "{BASE_DN_HERE}" SearchFilter "(&(sAMAccountName=%u)(objectClass=organizationalPerson)(objectCategory=person)(!(userAccountControl:1.2.840.113556.1.4.803:=2)))" RequireGroup false </Authorization> 

Tambahkan baris ke konfigurasi ovpn khusus:

 auth-user-pass 

Dengan demikian, pengguna pertama-tama akan diminta nama pengguna dan kata sandi dari domain, kemudian PIN dari token. Jika salah satu dari langkah-langkah ini gagal, koneksi tidak akan dibuat.

Deskripsi opsi untuk ldap.conf ada di repositori plugin . Ini mendukung otentikasi berdasarkan keanggotaan grup, tetapi saya belum mengujinya.

Kecepatan


Peningkatan kecepatan terbesar memberikan masuknya mode udp. Ini disarankan di semua manual. Intinya adalah tidak masuk akal untuk memulai koneksi klien tcp di saluran tcp. Satu tcp di klien sudah cukup untuk membuat pengiriman paket yang benar. Jika paket hilang di saluran udp, koneksi tcp klien akan mengontrol penyesuaian pengiriman.

Kecepatan akan meningkat setidaknya karena tidak perlu menunggu konfirmasi pengiriman setiap paket di saluran. Ada masalah kedua dengan tcp - satu paket klien tcp kemungkinan besar tidak masuk ke dalam satu paket saluran vpn. MTU adalah sama, tetapi header perlu ditambahkan ke paket klien. Akibatnya, Anda harus mengirim dua paket dalam saluran vpn per paket pengguna.

TCP masuk akal untuk digunakan ketika tidak mungkin dengan cara lain. Misalnya, ketika vpn bekerja melalui saluran ssh.

Contoh konfigurasi server lengkap


contoh-server.conf
 port 1194 proto tcp dev tun sndbuf 0 rcvbuf 0 ca ca.crt cert server.crt key server.key dh dh.pem tls-auth ta.key 0 topology subnet server 10.8.0.0 255.255.255.0 ifconfig-pool-persist ipp.txt push "redirect-gateway def1 bypass-dhcp" push "dhcp-option DNS 10.0.0.25" push "dhcp-option DNS 10.0.0.24" keepalive 10 120 cipher AES-256-CBC comp-lzo user nobody group nobody persist-key persist-tun status openvpn-status.log verb 3 crl-verify crl.pem log-append /var/log/openvpn.log script-security 2 client-connect /etc/openvpn/bin/hosts.rb client-disconnect /etc/openvpn/bin/hosts.rb 


Pengaturan token


Perpustakaan PKCS # 11


Untuk bekerja dengan token, Anda memerlukan perpustakaan khusus. Pustaka diperlukan untuk membuat pasangan kunci, dan untuk koneksi yang sebenarnya. Unduh untuk semua platform melalui tautan .

Di mana pun librtpkcs11ecp.so ditemukan nanti - ini adalah perpustakaan yang perlu diunduh dan diletakkan di suatu tempat di tempat yang nyaman.

Membuat sertifikat di token


Buat pasangan kunci pada token. Parameter id di sini adalah nomor seri slot pada token tempat pasangan kunci cocok.

 pkcs11-tool --module /usr/lib64/librtpkcs11ecp.so --keypairgen --key-type rsa:2048 -l --id 01 

Buat permintaan sertifikat untuk kunci publik. Dalam proses membuat permintaan sertifikat, masa berlaku sertifikat dan nama umum ditetapkan, yang digunakan untuk memfilter alamat ip yang tersedia dalam jaringan. Nama umum harus cocok dengan login di ActiveDirectory sehingga tidak ada kebingungan.

 openssl openssl> engine -t dynamic -pre SO_PATH:/usr/lib64/openssl/engines/pkcs11.so -pre ID:pkcs11 -pre LIST_ADD:1 -pre LOAD -pre MODULE_PATH:/usr/lib64/librtpkcs11ecp.so openssl> req -engine pkcs11 -new -key slot_0-id_01 -keyform engine -out /home/john/good.req 

Permintaan yang diterima harus dipindahkan ke /etc/openvpn/easy-rsa/pki/reqs/ . Ekstensi file harus req .
Mengonversi permintaan ke sertifikat:

 cd /etc/openvpn/easy-rsa/ ./easyrsa sign-req client good 

Setelah itu, sertifikat dengan nama yang sama tetapi dengan ekstensi crt akan muncul di folder /etc/openvpn/easy-rsa/pki/issued/ .

Sebelum merekam, sertifikat harus dikonversi ke DER:

 openssl x509 -in /home/user/user-cert.pem -out /home/user/user-cert.crt -outform DER 

Menulis sertifikat untuk token:

 pkcs11-tool --module /usr/lib/librtpkcs11ecp.so -l -y cert -w /home/user/user-cert.crt --id 45 --label TEST 

Ini ditulis berdasarkan artikel "Menggunakan Rutoken EDS dengan OpenSSL (RSA)" .

Menggunakan Token untuk Otentikasi


Temukan id sertifikat yang akan disajikan ke server:

 $ openvpn --show-pkcs11-ids /usr/lib64/librtpkcs11ecp.so The following objects are available for use. Each object shown below may be used as parameter to --pkcs11-id option please remember to use single quote mark. Certificate DN: /CN=User1 Serial: 490B82C4000000000075 Serialized id: aaaa/bbb/41545F5349474E415455524581D2A1A1B23C4AA4CB17FAF7A4600 

Kami tertarik pada serialized id di sini.

Opsi yang harus dimasukkan dalam konfigurasi ovpn agar token mengambil:

 pkcs11-providers /usr/lib64/librtpkcs11ecp.so pkcs11-id 'aaaa/bbb/41545F5349474E415455524581D2A1A1B23C4AA4CB17FAF7A4600' 

Opsi pkcs11-id harus dilampirkan dalam tanda kutip tunggal.

Manual ini masuk akal di semua platform. Anda perlu menentukan jalur ke perpustakaan dan id sertifikat pada token. Perpustakaan dapat disebut sedikit berbeda, menjadi .dll , bukan .so , tetapi artinya sama.

Dalam hal ini, Anda perlu menghapus bagian cert dan key dari file ovpn, karena sertifikat dan kunci pribadi akan diambil dari token.

Konfigurasi klien sepenuhnya (untuk windows) terlihat seperti ini:

client.ovpn
client
dev tun
proto tcp
sndbuf 0
rcvbuf 0
remote 78.47.37.247 22222
resolv-retry infinite
nobind
persist-key
persist-tun
remote-cert-tls server
cipher AES-256-CBC
comp-lzo
setenv opt block-outside-dns
key-direction 1
verb 3

pkcs11-providers "c://Windows//System32//rtPKCS11ECP.dll"
pkcs11-id 'Aktiv\x20Co\x2E/Rutoken\x20ECP/342b871d/Rutoken/01'

-----BEGIN CERTIFICATE-----
{CERT_HERE}
-----END CERTIFICATE-----


<tls-auth>
#
# 2048 bit OpenVPN static key
#
-----BEGIN OpenVPN Static key V1-----
{KEY_HERE}
-----END OpenVPN Static key V1-----
</tls-auth>


Ditulis berdasarkan "Bagaimana cara menambahkan otentikasi dua faktor ke konfigurasi OpenVPN menggunakan kartu pintar sisi klien . "

Pengaturan pelanggan


Linux


Openvpn memiliki bug yang mencegah pengguna memasukkan kode PIN dari token jika paket dibangun dengan dukungan systemd. Karena systemd telah ada di mana-mana akhir-akhir ini, semua paket yang sudah tersedia di repositori dikompilasi dengan dukungannya. Klien di Linux perlu mengumpulkan paket sendiri. Berikut adalah contoh konfigurasi yang berfungsi untuk saya di Arch Linux:

 ./configure \ --prefix=/usr \ --sbindir=/usr/bin \ --enable-iproute2 \ --enable-pkcs11 \ --enable-plugins \ --enable-x509-alt-username 

Anda dapat memverifikasi bahwa openvpn dibangun dengan atau tanpa systemd menggunakan perintah berikut:

 openvpn --version | grep --color enable_systemd 

Mas os


Di bawah Mac OS, hanya ada satu klien gratis - Tunnelblink .

Dia tidak tahu bagaimana memasukkan kode pin dari token dari gui. Bug tersebut dijelaskan misalnya di sini - https://groups.google.com/forum/#!topic/tunnelblick-discuss/f_Rp_2nV-x8 Dicacah dengan meluncurkan openvpn dari konsol. Ini tidak mengherankan, mengingat bahwa klien resmi untuk windows juga tidak mengetahui hal ini.

Juga di bawah Mac OS (tidak seperti windows) skrip tambahan diperlukan untuk mengkonfigurasi jaringan. Jika Anda hanya menjalankan openvpn dari konsol, maka DNS tidak akan berfungsi (mungkin sesuatu yang lain, hanya DNS yang akan muncul).

TunnelBlick memiliki skrip konfigurasi jaringan ini, mereka hanya perlu dipanggil saat membuat dan memutuskan koneksi. Apa yang perlu Anda tambahkan ke konfigurasi ovpn:

 script-security 2 up "/Applications/Tunnelblick.app/Contents/Resources/client.up.tunnelblick.sh -9 -d -f -m -w -ptADGNWradsgnw" down "/Applications/Tunnelblick.app/Contents/Resources/client.down.tunnelblick.sh -9 -d -f -m -w -ptADGNWradsgnw" 

Contoh skrip untuk meluncurkan koneksi openvpn, yang dapat diletakkan di desktop dan disodok dengan mouse:

 #!/bin/bash tunnelblick=/Applications/Tunnelblick.app/Contents/Resources/openvpn/openvpn-2.4.2-openssl-1.0.2k sudo $tunnelblick/openvpn --config $tunnelblick/user.ovpn 

Windows


Di bawah windows, semuanya tampak berfungsi. Klien resmi tidak tahu bagaimana memasukkan kode pin dari token, ia berhasil membuka openvpn dengan tangan dari konsol.

Yang paling penting adalah melakukan semuanya dari bawah administrator. Jalankan penginstal klien sebagai administrator. Luncurkan terminal di mana openvpn memulai juga dengan hak admin, jika tidak, ia tidak akan dapat mengontrol antarmuka jaringan.

Di bawah Windows, jalur ke perpustakaan untuk bekerja dengan token harus direkam melalui garis miring ganda. Ini berlaku untuk konfigurasi ovpn dan --show-pkcs11-ids pada baris perintah.

 pkcs11-providers "c://Windows//System32//rtPKCS11ECP.dll" pkcs11-id 'Aktiv\x20Co\x2E/Rutoken\x20ECP/342b871d/Rutoken/01' 

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


All Articles