SAAS yazılımlarında custom domain desteği ve otomatik SSL oluşturma

Bir süredir yan proje olarak bir SAAS yazılımı geliştiriyoruz. Projeyi bitirdikten sonra bir blog serisi olarak baştan sona karşılaştığımız problemleri ve çözümlerini kaynak kodu da paylaşarak anlatmayı düşünüyoruz.

Proje multi-tenant bir SAAS yazılımı olduğu için platformda hesap oluşturan her bir müşteriye benzersiz bir sub-domain tahsis ediliyor. Bir örnek verecek olursak; diyelim ki platformumuz example.com domaini üzerinde host ediliyor. Bir kullanıcı x isminde bir hesap oluşturduğunda kendi platformuna erişim için x.example.com şeklinde bir domaine sahip oluyor. Bu problemin çözümünü bir örnek ile buradaki yazımda anlatmıştım.

Bu problemin üstesinden geldikten sonra karşımızda çözmemiz gereken yeni bir problem vardı: Kullanıcılar platformlarına erişim için kendilerine ait bir custom domaini kullanabilmeliydiler. Birkaç günlük araştırma ve kodlamanın ardından custom domain desteğinin geliştirmesini tamamladık. Bu makalede de çözümü dilim döndüğünce anlatmaya çalışacağım. Umarım faydalı olur.

Önce geliştirmek istediğimiz özelliği daha iyi anlayabilmemiz için problemimizi örnek bir kullanıcı hikayesi ile açıklamaya çalışalım: Platformumuzu kullanan Ali isminde bir kullanıcı var. Ali xyz.com isminde bir domaine sahip. Bizim SAAS blog platformumuzu kullanarak kendisine bir blog oluşturdu. Platformun kendisine tahsis ettiği otomatik oluşturulan domaini kullanmak yerine kendisine ait olan xyz.com domainini kullanmak istiyor. Bunun için platformun sağladığı dashboard paneli üzerinden custom domani girip kaydetti. Ardından domainini satın aldığı platforma giderek, xyz.com domaini için bizim ona verdiğimiz ip adresini işaret eden bir A record oluşturup yönlendirme işlemini tamamladı.

Bu işlemden sonra kullanıcımız xyz.com adresine istek gönderdikten sonra bizim platformumuza yönlenecek ve biz de ona ait olan içeriği domaine göre veritabanından çekip göstereceğiz. Çözmek istediğimiz ilk problem bu. Fakat başka bir problememiz daha var.

Platformumuzdaki trafiğin SSL üzerinden ilerlemesini istiyoruz. Sistem tarafından onlara tahsis edilen benzersiz subdomainler zaten full SSL desteğine sahip ve buradaki yazıda bunun nasıl yapıldığını anlatmıştık. Fakat bu problemimiz biraz daha farklı. Kullanıcılar kendi domainlerini kullanacaklar ve domainleri bizim sunucumuza yönlenecek. Platformumuza yönlenen bu domainler için SSL desteğini nasıl sağlayabiliriz?

Kullanıcılar kendi domainleri üzerinden platformumuza istek gönderdiklerinde gelen ilk istekte otomatik olarak SSL sertifikasını oluşturup, ondan sonra gelecek olan tüm istekler için bu sertifikayı kullanacağız. Böylelikle kullanıcılarımız, girdikleri custom domainler için SSL desteğine kavuşacak.

Çözeceğimiz problemlerin kafamızda netleştiğini düşünüyorum. O yüzden şimdi çözüm kısmına geçelim.

Custom domainlerden gelen isteklerde otomatik olarak SSL sertifikasını oluşturmak istediğimiz için bunu destekleyen bir yapıya ihtiyacımız var. Biz SAAS yazılımımızı linux üzerinde host ediyoruz. Go programlama dili ile yazılan web uygulamasına gelen istekleri ilk olarak nginx karşılıyor ve bir reverse proxy görevi görerek istekleri docker üzerinde çalışan Go uygulamasına yönlendiriyor.

Kullanıcılar hesap oluşturduktan sonra otomatik oluşturulup onlara tahsis edilen subdomainler için Let's Encrypt kullanarak bir wildcard SSL sertifikası oluşturmuştuk ve böylelikle tüm subdomainler için SSL desteği sağlayabiliyorduk. Fakat burada custom domainlere SSL desteği vermek istediğimiz için biraz daha farklı bir yaklaşıma ihtiyacımız var. Bildiğim kadarı ile nginx'in böyle bir desteği yok. O yüzden bu problemi çözmemizi sağlayan çözümleri araştırmamız gerekiyor.

Biraz araştırdıktan sonra karşıma OpenResty isminde bir web platformu çıktı. Daha önce hiç duymamıştım fakat hakkında biraz okuduktan sonra oldukça popüler bir platform olduğunu gördüm. OpenResty içerisinde Nginx'in core modüllerini barındırıyor ve bunlara ek olarak LuaJIT isminde Lua programlama dili için bir Just-In-Time Compiler (JIT) barındırıyor.

Bildiğiniz gibi nginx ile linux üzerinde host ettiğimiz web uygulamalarımızı dış dünyaya servis edebiliyoruz. LuaJIT ile de nginx tarafına gelen isteklerde Lua programlama dili ile yazılmış scriptleri çalıştırıp gelen isteklere göre istediğimiz spesifik işleri yapabiliyoruz. Yani SAAS platformumuza custom domainlerden istek geldiğinde, bu domain için SSL sertifikası üretip üretmeyeceğimizi sorgulayan ve her sorguda dinamik olarak çalışan bir lua scripti yazabiliriz.

Buraya kadar herşey tamam. Fakat netleştirilmesi gereken bir nokta var. Diyelim ki gelen custom domain için script kodumuz SSL üretmemiz gerektiğine karar verdi. Peki bu SSL nasıl üretilecek? Bunun için openresty/nginx ikilisine ek olarak let's encrypt'i kullanan ve otomatik SSL üreten lua-resty-auto-ssl isminde açık kaynak kodlu bir package var. Nginx konfigürasyonumuz bu paketi kullanarak otomatik SSL sertifikaları üretmek için konfigure edilerek, gelen her custom domain için sadece ilk istekte olmak kaydı ile sertifika oluşturulacak.

Biz daha önce web uygulamamızı nginx üzerinden dışarıya acıyorduk. Fakat şimdi OpenResty kullanacağımız için nginx'i kaldırabiliriz. Çünkü OpenResty Nginx ile beraber geliyor. Gerekli konfig dosyalarınızı yedekledikten sonra nginx'i kaldırıp OpenResty'nin kurulumuna geçiyoruz.

OpenResty kurulumu

Benim linuxte çok fazla tecrübem olmadığından dolayı OpenResty kurulumu için DigitalOcean tutorialından yararlanmıştım. Fakat o tutorial güncelliğini yitirdiğinden midir bilinmez kurulum esnasında bir takım hatalarla karşılaştım. O yüzden burada makaleyi uzatmayı göze alarak OpenResty kurulum kodlarını da paylaşmak istiyorum. Eğer siz OpenResty kurulumunu zaten biliyorsanız bu bölümü atlayıp "OpenResty'i servis haline getirmek" veya "OpenResty/Nginx ikilisinin auto-ssl için konfigure edilmesi"  bölümünden devam edebilirsiniz.

Kurulumu tamamlamak için linux sunucunuzda aşağıdaki komutları sırasıyla çalıştırmalısınız. Fazla detaya girmeden ne yaptığımızı bir açıklayalım. OpenResty'nin yayınlanan son version adresini kullanarak tar dosyasını sunucumuza çekiyoruz.

wget https://openresty.org/download/openresty-1.15.8.3.tar.gz

wget https://openresty.org/download/openresty-1.15.8.3.tar.gz.asc

Ardından paket sahibinin public keyini eklememiz gerekiyor. OpenResty'nin download sayfasında gösterildiği gibi public key A0E98066 olmalı. Public key'i eklemek için aşağıdaki komutu çalıştırmalıyız. Burada sunucuya bağlanma işlemleri biraz uzun sürüyor ve bağlantı hataları alabiliyoruz. Ben kurulumda 3-4 kez çalıştırmak zorunda kaldım ve en sonunda key'i eklemeyi başarabildim.

gpg --keyserver pgpkeys.mit.edu --recv-key A0E98066

Key eklendikten sonra terminale basılan çıktıda “Yichun Zhang” ismini görmelisiniz. Bu sayede doğru public key'i eklediğimizi doğrulamış oluyoruz. Public key'i de indirdiğimize göre artık indirdiğimiz paketin imzasını doğrulayabiliriz.

gpg openresty-1.15.8.3.tar.gz.asc

Terminale basılan çıktıda "Good signature" metnini görüyorsanız herşey doğru demektir. Bu işlemin ardından indirdiğimiz tar dosyasını extract edip klasörün içerisine geçiş yapıyoruz.

tar -xvf openresty-1.15.8.3.tar.gz

cd openresty-1.15.8.3

OpenResty'i compile edebilmek için gerekli olan araçları aşağıdaki komut yardımı ile yüklüyoruz.

sudo apt-get install build-essential

Gerekli olan birkaç paket daha var. Onları da yükleyelim.

sudo apt-get install libreadline-dev libncurses5-dev libpcre3 libpcre3-dev zlib1g zlib1g-dev libssl-dev perl

Buraya kadar herşey sorunsuz gitti ise artık OpenResty yüklemesini başlatmak için tüm hazılıklar tamam demektir. Sırasıyla aşağıdaki komutları çalıştırıyoruz.

./configure -j2 --with-pcre-jit --with-ipv6 --with-http_v2_module

make -j2

sudo make install

Ardından http ve https portları için firewall izinlerini veriyoruz.

sudo ufw allow http

sudo ufw allow https

Bu işlemlerden sonra OpenResty'nin çalışıp çalışmadığını test etmeliyiz. Bunun için ilk olarak OpenResty'i başlatıyoruz.

sudo /usr/local/openresty/bin/openresty

Eğer herhangi bir hata yoksa yukarıdaki komutun herhangi bir çıktı vermeden çalışmış olması gerekiyor. Artık http://sunucunuzun_ip_adresi'ne istek gönderdiğinizde Welcome to OpenResty! sayfasını görebiliyor olmalıyız.

Nginx'in OpenResty ile çalışıp çalışmadığını test etmek için aşağıdaki komutu çalıştıralım. (Kısa bir not: Eğer nginx -V komutu not found hatası veriyorsa export PATH=/usr/local/openresty/nginx/sbin:$PATH komutunu kullanarak nginx yolunu tanımladıktan sonra tekrar denemelisiniz.):

nginx -V

Ekranda aşağıdaki çıktıyı görüyorsanız herşey başarılı demektir.

OpenResty'i servis haline getirmek

Sunucumuzun olası reboot durumlarında OpenResty'nin otomatik olarak başlatılması için servis haline getirmek akıllıca bir yaklaşım olur. Bu yüzden gerekli olan systemd dosyasını oluşturuyoruz.

sudo nano /etc/systemd/system/openresty.service

Bu dosyanın içeriğine aşağıdaki komutları kopyalıyoruz.

# Stop dance for OpenResty
# A modification of the Nginx systemd script
# =======================
#
# ExecStop sends SIGSTOP (graceful stop) to the Nginx process.
# If, after 5s (--retry QUIT/5) OpenResty is still running, systemd takes control
# and sends SIGTERM (fast shutdown) to the main process.
# After another 5s (TimeoutStopSec=5), and if OpenResty is alive, systemd sends
# SIGKILL to all the remaining processes in the process group (KillMode=mixed).
#
# Nginx signals reference doc:
# http://nginx.org/en/docs/control.html
#
[Unit]
Description=A dynamic web platform based on Nginx and LuaJIT.
After=network.target

[Service]
Type=forking
PIDFile=/run/openresty.pid
ExecStartPre=/usr/local/openresty/bin/openresty -t -q -g 'daemon on; master_process on;'
ExecStart=/usr/local/openresty/bin/openresty -g 'daemon on; master_process on;'
ExecReload=/usr/local/openresty/bin/openresty -g 'daemon on; master_process on;' -s reload
ExecStop=-/sbin/start-stop-daemon --quiet --stop --retry QUIT/5 --pidfile /run/openresty.pid
TimeoutStopSec=5
KillMode=mixed

[Install]
WantedBy=multi-user.target

Kaydedip çıktıktan sonra sıra log dosyasını oluşturmaya geliyor. Log dosyasını oluşturmak ve ardından openresty'nin yeni log dosyası ile yeniden yüklenmesini sağlıyoruz.

sudo mkdir /var/log/openresty

sudo systemctl daemon-reload

sudo systemctl start openresty

sudo systemctl enable openresty

OpenResty/Nginx ikilisinin Auto-SSL için konfigüre edilmesi

Gerekli konfigürasyonları yapabilmek için lua-resty-auto-ssl paketini yüklememiz gerekiyor. Fakat bunu yükleyebilmek için LuaRocks ismindeki lua package manager'i yüklemeliyiz. Ben kurulum sırasında "unzip notfound" hatası almıştım. O yüzden siz ilk olarak unzip paketini yüklemekle başlayabilirsiniz.

sudo apt-get install unzip

Ardından luarocks package manager kurulumuna geçebiliriz. Sırasıyla aşağıdaki komutları çalıştırıp kurulumu tamamlıyoruz.

wget http://luarocks.org/releases/luarocks-3.3.1.tar.gz

tar -xzvf luarocks-3.3.1.tar.gz

cd luarocks-3.3.1/

./configure --prefix=/usr/local/openresty/luajit --with-lua=/usr/local/openresty/luajit/ --lua-suffix=jit --with-lua-include=/usr/local/openresty/luajit/include/luajit-2.1

make build

sudo make install

Kurulum sırasında luajit yoluna ihtiyaç olduğu için eğer not found hataları ile karşılaşırsanız aşağıdaki komut ile path'i tanımlayabilirsiniz.

export PATH=/usr/local/openresty/luajit/bin:$PATH

LuaRocks package manager kurulumu tamamlandığına göre artık lua-resty-auto-ssl paketini kurabiliriz.

luarocks install lua-resty-auto-ssl

Birazdan nginx dosyasını konfigure ederken lua scriptinde http isteğinde bulunacağımız için lua-resty-http paketine ihtiyacımız var. Onu da yüklüyoruz.

luarocks install lua-resty-http

Bu işlemin ardından SSL ile ilgili gerekli dosyaların saklanacağı ve OpenResty'nin ihtiyaç duyduğu klasörü yaratıp www-data kullanıcısına bu klasör için gerekli yetkileri veriyoruz. (Not: www-data kullanıcısını OpenResty'nin nginx konfigürasyonunu yaparken kullanacağız.)

mkdir /etc/resty-auto-ssl

sudo mkdir /etc/resty-auto-ssl/storage

sudo chown -R www-data /etc/resty-auto-ssl/storage

Biliyorum buraya kadar olan kısım gerekli altyapıyı hazırlamak ile geçtiği için biraz sıkıcı görünüyor. Şimdi asıl can alıcı noktaya geldik. Auto-SSL için nginx konfigürasyon dosyamızı yapılandıracağız ve bu işlemin ardından custom domain desteği ve auto-ssl desteğini kullanıcılarımıza sağlamış olacağız.

Nginx konfigürasyonu

Nginx ayarlarını yapmak için /usr/local/openresty/nginx/conf/nginx.conf yolunda bulunan nginx.conf dosyasını modifiye edeceğiz. Nginx dosyasında bulunan her bir konfigürasyonu tek tek açıklamayacağım. Sadece auto-ssl üretmek için gerekli olan bölümleri ve lua script kısmını açıklayacağım. Dilerseniz lua-resty-auto-ssl github sayfasından diğer ayarların açıklamalarına ulaşabilirsiniz.

Nginx dosyasında domain için SSL yaratıp yaratmayacağımıza karar verdiğimiz kısım init_by_lua_block ile başlayan kısım. Burada resty'nin auto_ssl paketinin allow_domain fonksiyonunu kullanacağız. Bu script parametre olarak isteğin geldiği domaini alan basit bir fonksiyon sağlıyor. Bu scriptte ihtiyacınıza göre SSL yaratmaya karar veren spesifik kodları yazabilirsiniz.

init_by_lua_block {

    auto_ssl = (require "resty.auto-ssl").new()

    auto_ssl:set("allow_domain", function(domain)

    end)

    auto_ssl:set("dir", "/etc/resty-auto-ssl")
    auto_ssl:init()
}

Bizim SAAS platformumuzda kullanıcıların custom domainleri veritabanında turuluyor. Dolayısı ile biz veritabanındaki custom domain bilgisinin varlığını sorgulayarak SSL yaratıp yaratmayacağımıza karar verebiliriz.

Şöyle ki; bir custom domainden gelen ilk istekte allow_domain fonksiyonu çalışacak ve basitçe web uygulamamızdaki bir rest api endpointine gelen domainin veritabanında olup olmadığını soracak. Eğer cevap 200 dönerse domainin sistemimizde kayıtlı olduğunu anlayıp SSL yaratma işlemini başlatacak. Dönen 404 cevapları içinse bu domain için SSL yaratmayıp işlemi sonlandıracağız.

Bunu yapmak için Go uygulamamızda aşağıdaki gibi domainin varlığını sorgulayan basit bir rest api endpointi açıyoruz. Örnek kod şu şekilde:

/*ExistsCustomDomain checks whether provided custom domain from url query exists or not. If it exists returns 200, othwesise 404*/
func ExistsCustomDomain(w http.ResponseWriter, r *http.Request) {

	domain := r.URL.Query().Get("domain")
	if domain == "" {
		w.WriteHeader(http.StatusBadRequest)
		w.Write([]byte("Domain cannot be empty."))
		return
	}
	exists, err := data.ExistsCustomerByDomain(domain)
	if err != nil {
		panic(err)
	}
	if exists == false {
		w.WriteHeader(http.StatusNotFound)
		return
	}
	w.WriteHeader(http.StatusOK)
}

Go kısmı bu kadar. Şimdi de nginx tarafında bu rest api endpointine http isteğini gönderecek olan lua scriptini yazalım. İlk olarak allow_domain fonksiyonunun bize verdiği domain parametresinin root domainimiz (example.com) ve bu root domaininden türeyen bir subdomain (.example.com) olup olmadığına bakacağız. Eğer bunlardan biri ise SSL sertifikası üretmek istemediğimizi belirten false döneceğiz. Bunların haricinde gelen custom domainlerin sistemimizde kayıtlı olup olmadığını anlamak için web uygulamamıza istek göndereceğiz. Dönen cevap 200 ise true dönüp SSL sertifikası yaratma sürecini başlatacağız. Aksi hallerde false dönüp işlemi sonlandıracağız. Bu mantığı işleyen basit lua scriptini aşağıda görebilirsiniz.

init_by_lua_block {

    auto_ssl = (require "resty.auto-ssl").new()

    -- Define a function to determine which SNI domains to automatically handle
    -- and register new certificates for. Defaults to not allowing any domains,
    -- so this must be configured.

    auto_ssl:set("allow_domain", function(domain)

        ngx.log(ngx.INFO, 'allow domain is in progress...')

        -- we don't want to generate SSL for example.com
        
        if domain == "example.com" then
            ngx.log(ngx.INFO, 'skipped auto ssl for example.com')
            return false
        end

        -- we don't want to generate SSL for *.linkwind.co as well

        if string.find(domain, ".example.com") ~= nil then 
            ngx.log(ngx.INFO, 'skipped auto ssl for .example.com')
            return false
        end

        local http = require("resty.http")
        local httpc = http.new()

        httpc:set_timeout(3000)

        local uri = "https://example.com/exists-custom-domain?domain="..domain

        local res, err = httpc:request_uri(uri, {
            ssl_verify = false,
            method = "GET"
        })

        if not res then
            ngx.say("failed to request: ", err)
            return false
        end

        if res.status == 200 then
            ngx.log(ngx.INFO, 'domain exists. ssl will be creating for domain: '..domain)
            return true
        end

        if res.status == 404 then
            ngx.log(ngx.INFO, 'domain does not exists. ssl will not be creating for domain: '..domain)
            return false
        end

    end)
    --auto_ssl:set("ca", "https://acme-staging.api.letsencrypt.org/directory")
    auto_ssl:set("dir", "/etc/resty-auto-ssl")
    auto_ssl:init()
}

Şimdi gelelim nginx dosyasına eklememiz gereken diğer kısımlara. auto-ssl lua scriptini başlatmaktan sorumlu olan kod bloğunu ekliyoruz.

init_worker_by_lua_block {
	auto_ssl:init_worker()
}

Let's encrypt tarafından gerçekleştirilecek domain doğrulamaları için http 80 portu üzerinde çalışan bir endpointe ihtiyac var. O yüzden aşağıdaki server bloğunu nginx konfig dosyamıza eklememiz gerekiyor. Aşağıdaki kodda ilk if bloğu benim domainim için gelen http requestlerini https'e yönlendirdiğim bir regex kuralı. example.com ile ilgili ayarların auto-ssl ile bir ilgisi yok. En altta yer alan location /.well-known/acme-challenge/ bloğu bizim için önemli olan kısım.

server {

    if ($host ~ ^[^www][^.]+\.example\.com$) {
    	return 301 https://$host$request_uri;
    }

    listen        80;
    server_name   *.example.com;

    location /.well-known/acme-challenge/ {
        content_by_lua_block {
        	auto_ssl:challenge_server()
        }
    }
}

Https isteklerinde custom domainler için ssl sertifikalarını dönen veya sertifika yok ise oluşturmayı sağlayan dinamik bir handlera ihtiyacımız var. Bu yüzden aşağıdaki https server bloğunu ekliyoruz. Bu blokta auto-ssl için bizi ilgilendiren kısım ssl_certificate_by_lua_block ile başlayan blok. Diğer ayarlar benim domainim için yapmış olduğum ayarlar. Sizin domain ayarlarınıza göre bu kısımlar değişiklik gösterebilir.

server {
    server_name *.example.com;

    location / {
        proxy_set_header HOST $host;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

        proxy_pass  http://localhost:3000;
    }

    listen 443 ssl default_server;
    ssl_certificate /etc/letsencrypt/live/example.com-0001/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/example.com-0001/privkey.pem;

    ssl_certificate_by_lua_block {
    	auto_ssl:ssl_certificate()
    }
}

Eklememiz gereken son bir server bloğu daha kaldı. SSL sertifikaları ile ilgili internal görevleri yapacak olan 8999 portunda çalışan aşağıdaki server bloğunu ekliyoruz.

server {
    listen 127.0.0.1:8999;

    # Increase the body buffer size, to ensure the internal POSTs can always
    # parse the full POST contents into memory.
    client_body_buffer_size 128k;
    client_max_body_size 128k;

    location / {
        content_by_lua_block {
        	auto_ssl:hook_server()
        }
    }
}

Bu işlemlerden sonra nginx.conf dosyamız tüm ayarları ile birlikte aşağıdaki gibi olmalı:

user www-data;
worker_processes  auto;
pid /run/openresty.pid;

events {
    worker_connections  1024;
}


http {
	include       mime.types;
	default_type  application/octet-stream;

	sendfile        on;
	tcp_nopush      on;
	tcp_nodelay     on;

	keepalive_timeout  65;

	ssl_protocols TLSv1 TLSv1.1 TLSv1.2; # Dropping SSLv3, ref: POODLE
	ssl_prefer_server_ciphers on;

	access_log /var/log/openresty/access.log;
	error_log /var/log/openresty/error.log debug;

	gzip  on;
	gzip_disable "msie6";

	include ../sites/*;

	lua_ssl_verify_depth 2;
    
	lua_ssl_trusted_certificate /etc/ssl/certs/ca-certificates.pem;

	lua_shared_dict auto_ssl 1m;

	lua_shared_dict auto_ssl_settings 64k;

	resolver 8.8.8.8 ipv6=off;

	lua_package_path "/usr/local/openresty/luajit/lib/luarocks/rocks-5.1/lua-resty-http/lib/?.lua;;";

	# Initial setup tasks.
	init_by_lua_block {

		auto_ssl = (require "resty.auto-ssl").new()

		auto_ssl:set("allow_domain", function(domain)

			ngx.log(ngx.INFO, 'allow domain is in progress...')

			-- we don't want to generate SSL for example.com
			if domain == "example.com" then
				ngx.log(ngx.INFO, 'skipped auto ssl for example.com')
				return false
			end

			-- we don't want to generate SSL for *.example.com as well

			if string.find(domain, ".example.com") ~= nil then 
				 ngx.log(ngx.INFO, 'skipped auto ssl for .example.com')
				 return false
			end

			local http = require("resty.http")
			local httpc = http.new()

			httpc:set_timeout(3000)

			local uri = "https://app.example.com/exists-custom-domain?domain="..domain

			local res, err = httpc:request_uri(uri, {
				ssl_verify = false,
				method = "GET"
			})

			if not res then
				ngx.say("failed to request: ", err)
				return false
			end

			if res.status == 200 then
				ngx.log(ngx.INFO, 'domain exists. ssl will be creating for domain: '..domain)
				return true
			end

			if res.status == 404 then
			    ngx.log(ngx.INFO, 'domain does not exists. ssl will not be creating for domain: '..domain)
				return false
			end

	    end)
	   --auto_ssl:set("ca", "https://acme-staging.api.letsencrypt.org/directory")
	   auto_ssl:set("dir", "/etc/resty-auto-ssl")
	   auto_ssl:init()
  	}

	init_worker_by_lua_block {
		auto_ssl:init_worker()
	}

	# HTTPS server
	server {
	  	server_name *.example.com;

	  	location / {
	    	proxy_set_header HOST $host;
	    	proxy_set_header X-Forwarded-Proto $scheme;
	    	proxy_set_header X-Real-IP $remote_addr;
	    	proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

	    	proxy_pass  http://localhost:3000;
	  	}

	    listen 443 ssl default_server;
	    ssl_certificate /etc/letsencrypt/live/example.com-0001/fullchain.pem;
	    ssl_certificate_key /etc/letsencrypt/live/example.com-0001/privkey.pem;

		# Dynamic handler for issuing or returning certs for SNI domains.
		ssl_certificate_by_lua_block {
		  auto_ssl:ssl_certificate()
		}
	}

	# HTTP server
	server {

		if ($host ~ ^[^www][^.]+\.example\.com$) {
	        return 301 https://$host$request_uri;
	  	}

		listen        80;
		server_name   *.example.com;

		# Endpoint used for performing domain verification with Let's Encrypt.

		location /.well-known/acme-challenge/ {
		  content_by_lua_block {
		    auto_ssl:challenge_server()
		  }
		}
	}

	server {
	  server_name   example.com www.example.com;

	  location / {
	    proxy_pass  http://localhost:8080;
	  }

	  listen 443 ssl;
      ssl_certificate /etc/letsencrypt/live/example.com-0002/fullchain.pem;
      ssl_certificate_key /etc/letsencrypt/live/example.com-0002/privkey.pem;
	}

	server {
	    if ($host = www.example.com) {
	        return 301 https://$host$request_uri;
	    } # managed by Certbot


	    if ($host = example.com) {
	        return 301 https://$host$request_uri;
	    } # managed by Certbot

	    listen        80;
	    server_name   example.com www.example.com;
	    return 404; # managed by Certbot

	}

	# Internal server running on port 8999 for handling certificate tasks.
	server {
		listen 127.0.0.1:8999;

		# Increase the body buffer size, to ensure the internal POSTs can always
		# parse the full POST contents into memory.
		client_body_buffer_size 128k;
		client_max_body_size 128k;

		location / {
		  content_by_lua_block {
		    auto_ssl:hook_server()
		  }
		}
  	}	
}

Ayarlarımızın aktif olması için OpenResty'i yeniden başlatalım.

sudo systemctl restart openresty

Eğer konfigürasyonumuzda bir sıkıntı varsa openresty hata verecektir. Olası hata durumunda OpenResty'nin statüsünü ve loglarını kontrol ederek yönlendirmelere göre hatayı fixleyebilirsiniz.

Artık sunucumuz custom domain ve SSL desteğini sağladığı için kullanıcılarınızın tek yapması gereken kendilerine ait olan domainlerini sizin sunucunuzun ip adresine yönlendirmek. Yönlendirmenin ardından custom domainden sizin sunucunuza gelen ilk istekde lua scriptimiz web uygulamamıza ilgili domainin var olup olmadığına dair bir istek gönderecek ve eğer varsa ssl sertifikası yaratılacaktır. Bu yüzden ilk isteklerde 10 saniyelik bir gecikme yaşanabilir. SSL birkez yaratıldıktan sonra artık bu gecikme ortadan kalkacak ve sonraki istekler full SSL desteği ile çalışacaktır.

Yaratılan SSL'ler yine Let's Encrypt tarafından kontrol edildiği için 3 aylık periyotlarda otomatik olarak yenilenecektir. Siz isteğinize göre bu süreyi daha yakın bir tarihe çekebilirsiniz. Şimdilik bu kadar. Uzun bir makale oldu biliyorum ama hiç bir detayı atlamadan eksiksiz anlatmaya çalıştım.

Bir sonraki makalede görüşmek dileğiyle. Kalın sağlıcakla.