• Czas czytania ~13 min
  • 25.04.2023

Zabezpieczenie aplikacji Laravel certyfikatem SSL ma kluczowe znaczenie dla zapewnienia, że ruch z i do aplikacji jest zawsze szyfrowany. Niektóre certyfikaty SSL są bezpłatne, podczas gdy inne muszą zostać zakupione i są dostarczane przez urzędy certyfikacji, takie jak letsencrypt i Cloudflare

. Ręczne generowanie tych certyfikatów może stać się żmudnym zadaniem, zwłaszcza jeśli nie masz pojęcia, od czego zacząć.

Certbot przychodzi na ratunek, udostępniając bezpłatny i zautomatyzowany sposób generowania i odnawiania tych certyfikatów.

Wymaga to ręcznego generowania certyfikatów za pomocą narzędzia Certbot.It does requires manually generate the certificates through Certbot. Możesz przeczytać więcej na temat dodawania certyfikatów SSL po wdrożeniu aplikacji w środowisku naturalnym.

Czasami możemy chcieć wygenerować te certyfikaty na dużą skalę, szczególnie w aplikacji z wieloma dzierżawcami. Dobrym przykładem jest Slack. Slack zapewnia każdemu zespołowi unikalną subdomenę, taką jak team.slack.com.

Korzystając z tej samej analogii, możemy również udostępniać użytkownikom bezpłatne subdomeny, gdy tylko zostaną dołączeni do naszej aplikacji dla wielu dzierżawców lub gdy tylko udostępnią własne domeny niestandardowe. Będziemy musieli również wygenerować certyfikaty SSL dla tych nazw domen.

Musimy znaleźć sposób, w jaki możemy generować certyfikaty SSL na dużą skalę. Istnieje wiele sposobów rozwiązania tego problemu, takich jak użycie serwera Caddy. Serwer Caddy zapewnia bezpłatne certyfikaty SSL dla każdego hosta, dzięki czemu jest to łatwa technologia do dodania do naszego stosu.

Inną opcją jest użycie Certbota i wygenerowanie tych certyfikatów, jeśli używamy Apache lub Nginx. W tym samouczku pokażę, jak zautomatyzować to zadanie dla podejścia opartego na Nginx.

Jest to kontynuacja tego artykułu, w którym stworzyliśmy prostą usługę skracania adresów URL i dodaliśmy funkcję umożliwiającą użytkownikom dodawanie domen niestandardowych.

Wygenerujemy certyfikaty SSL, gdy tylko użytkownicy dodadzą swoje niestandardowe nazwy domen do systemu i zaktualizują swoje rekordy DNS, aby wskazywały na nasz skrót adresów URL.

Refaktoryzacja kodu

W poprzedniej części tej serii przechowywaliśmy nazwy domen w tabeli użytkownika. Chociaż jest to dobre dla prostego skracacza adresów URL, w większości przypadków klient może mieć wiele nazw domen. Dlatego będziemy potrzebować sposobu obsługi wielu domen dla tego samego użytkownika.

Utworzymy nowy plik migracji, który usunie kolumnę domeny z tabeli users

php artisan make:migration drop_domain_column_from_users_table --table=users
<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

return new class extends Migration
{
    /**
     * Run the migrations.
     */
    public function up(): void
    {
        Schema::table('users', function (Blueprint $table) {
            $table->dropColumn('domain');
        });
    }

    /**
     * Reverse the migrations.
     */
    public function down(): void
    {
        Schema::table('users', function (Blueprint $table) {
            $table->string('domain')->nullable()->after('email');
        });
    }
};

Następnie utworzymy nowy model domeny, który będzie zawierał wszystkie nazwy domen.

php artisan make:model Domains -m
<?php

namespace App\Models;

use App\Models\User;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Factories\HasFactory;

class Domains extends Model
{
    use HasFactory;

    protected $guarded= [];

    public function user()
    {
        return $this->belongsTo(User::class, 'user_id');
    }
}

Model domen

<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

return new class extends Migration
{
    /**
     * Run the migrations.
     */
    public function up(): void
    {
        Schema::create('domains', function (Blueprint $table) {
            $table->id();
            $table->foreignId('user_id')->constrained('users')->cascadeOnDelete();
            $table->string('domain');
            $table->boolean('status')->default(0);
            $table->timestamps();
        });
    }

    /**
     * Reverse the migrations.
     */
    public function down(): void
    {
        Schema::dropIfExists('domains');
    }
};

Plik

migracji domen Użytkownik będzie mógł teraz dodawać wiele domen do systemu.

Zastanówmy się teraz nad stroną DNS.

Praca z systemem DNS

System nazw domen to usługa, która pomaga przekształcić nazwy domen czytelne dla człowieka, takie jak iankumu.com, w adresy

IP W przypadku naszego narzędzia do skracania adresów URL umożliwialiśmy użytkownikom dodawanie domen niestandardowych. Oznacza to, że musimy dostarczyć im niektóre rekordy DNS, które dodadzą do swoich dostawców DNS, aby ukończyć mapowanie do naszej aplikacji

skracającej Istnieją dwa rekordy, których możemy użyć: rekordy A lub rekordy Cname. Rekord mapuje nazwę domeny na adres IP, podczas gdy rekord Cname mapuje jedną nazwę domeny na inną.

Możesz przeczytać ten przewodnik, aby zrozumieć ich różnice i który wybrać. W zależności od potrzeb możesz wybrać jedną lub drugą.

W moim przypadku wybiorę rekordy Cname. Poproszę użytkowników o dodanie rekordu Cname wskazującego na short.app.com

Więc w moich rekordach DNS w Namecheap dodam ten rekord do domeny niestandardowej, której chcę użyć w skracaczu adresów URL.

Type Host Value TTL
CNAME Record go short.app.com Automatic
Namecheap DNS Records

Spowoduje to zmapowanie domeny go.example.com na short.app.com.

Dzięki temu moja domena niestandardowa jest gotowa do użycia.

Instalowanie certbota

W poprzedniej części tego samouczka zainstalowaliśmy Nginx na naszym serwerze. Teraz chcemy zainstalować Certbota.

Możemy wykonać ten samouczek, aby zainstalować Certbot na Nginx.

Generowanie certyfikatów SSL za pomocą laravel i certbot.

Teraz, gdy poziom DNS jest ustawiony i zainstalowany jest Certbot, możemy teraz generować certyfikaty SSL dla użytkowników.

Zanim wygenerujemy jakiekolwiek certyfikaty SSL, musimy najpierw sprawdzić, czy rekordy DNS zostały rozpropagowane poprawnie. Użyjemy funkcji pomocnika dns_get_record, aby uzyskać rekordy DNS nazwy domeny.

//App/Utils/DomainHelper.php

<?php

namespace App\Utils;

class DomainHelper
{
    public $isfound = false;

    public function verifycname($domain)
    {
        $domain = parse_url($domain);

        $host = array_key_exists('host', $domain) ? $domain['host'] : $domain['path'];

        $records = dns_get_record($host, DNS_CNAME);

        if (!empty($records)) {
            foreach ($records as $record) {
                if ($record['target'] == "short.app.com") {
                    $this->isfound = true;
                    break;
                } else {
                    $this->isfound = false;
                }
            }
        } else {
            $this->isfound = false;
        }

        return $this->isfound;
    }
}

Propagacja rekordów DNS może potrwać do 48 godzin, a zatem musimy mieć sposób sprawdzenia, czy rekordy DNS się rozpropagowały.

Można zaplanować zadanie wykonania tych kontroli. W moim przypadku pominę tylko tę część. Jak tylko rekordy się rozpropagują, dodamy domenę do kolejki do generowania SSL.

Generowanie certyfikatów SSL Następnym krokiem jest wygenerowanie certyfikatów

SSL. Do pracy z Certbotem wykorzystamy nową fasadę procesu . Umożliwi nam to uruchamianie zewnętrznych procesów z naszej aplikacji Laravel.

W pliku config.php/services stworzymy nową konfigurację, która pozwoli nam zmienić środowisko Certbota w oparciu o nasze potrzeby.

//config/services.php
<?php

...

'certbot' => [
        'test' => env('CERTBOT_TEST', true)
    ]

...

Ta konfiguracja pomoże nam określić, które polecenie Certbot uruchomić.

//App/Utils/SSLManager.php
<?php

namespace App\Utils;

use App\Models\User;
use App\Models\Domains;
use Illuminate\Support\Facades\File;
use Illuminate\Support\Facades\Process;

class SSLManager
{
    public function generateSSL($domain, $email = null)
    {
        $command = $this->certbot_command($domain, $email);

        $certbot = Process::path(base_path())->start($command);

        $process = $certbot->wait();

        if ($process->successful()) {
            return $process->output();
        }


    }

    public function certbot_command($domain, $email): string
    {

        if (config('services.certbot.test') == true) {
            $command = "sudo certbot certonly --nginx --agree-tos --no-eff-email -d $domain --email $email --test-cert";
        } else {
            $command = "sudo certbot certonly --nginx --agree-tos --no-eff-email -d $domain --email $email";
        }

        return $command;
    }

Używamy flagi certonly, aby poinstruować certbota, aby wygenerował tylko certyfikat SSL, ale nie aktualizował pliku konfiguracyjnego Nginx. Dzieje się tak, ponieważ chcemy przygotować niestandardowy plik konfiguracyjny Nginx dla wszystkich domen w naszym systemie.

Używamy również flagi –test-cert do korzystania ze środowiska przejściowego dostarczonego przez let's encrypt. Pomoże to zwiększyć limit certyfikatu na domenę do 30 000 tygodniowo i zapewni nam testowe certyfikaty SSL, których możemy użyć do przetestowania naszej logiki.

W środowisku produkcyjnym pojedyncza domena może zażądać tylko 5 certyfikatów tygodniowo.

Przygotujmy teraz prosty plik konfiguracyjny, który będzie pośredniczył w żądaniach do naszego skracacza adresów URL.

//App/Utils/SSLManager.php
<?php

...

public function nginx_config($domain): string
    {
        $host = "short.app.com";

        $content = "
        server {
            if (\$host = $domain) {
               return 301 https://\$host\$request_uri;
            }
            listen 80;
            listen [::]:80;
            server_name $domain;
        }
        server {
            listen 443 ssl http2;
            listen [::]:443 ssl http2;
            server_name $domain;
            return 301 https://$host\$request_uri;
            resolver 8.8.8.8;
            location / {
                include proxy_params;
                proxy_pass https://$host\$request_uri;
                proxy_set_header Host $domain;
                proxy_set_header X-Forwarded-Host \$http_host;
                proxy_set_header X-Real-IP \$remote_addr;
                proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
                proxy_set_header X-Forwarded-Proto \$scheme;
                proxy_redirect off;
                proxy_http_version 1.1;
                proxy_set_header Upgrade \$http_upgrade;
            }
            ssl_certificate /etc/letsencrypt/live/$domain/fullchain.pem;
            ssl_certificate_key /etc/letsencrypt/live/$domain/privkey.pem;
            ssl_trusted_certificate /etc/letsencrypt/live/$domain/chain.pem;
            include /etc/letsencrypt/options-ssl-nginx.conf;
            ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
        }";

        return $content;
    }

...

Nasza aplikacja wstępnie wypełni zmienne $host i $domain poprawnymi parametrami dla każdej nazwy

domeny Utworzymy te pliki konfiguracyjne w folderze dostępnym dla witryn dla Nginx.

Konfiguracje będą tworzone dynamicznie za pomocą naszej aplikacji.

//App/Utils/SSLManager
<?php

namespace App\Utils;

use App\Models\User;
use App\Models\Domains;
use Illuminate\Support\Facades\File;
use Illuminate\Support\Facades\Process;

class SSLManager
{
    public function generateSSL($domain, $email = null): bool
    {
        $command = $this->certbot_command($domain, $email);

        $certbot = Process::path(base_path())->start($command);

        $process = $certbot->wait();


        if ($process->successful()) {
            $file_path = "/etc/nginx/sites-available/$domain";

            if (File::exists($file_path)) {
                $nginx = File::put($file_path, $this->nginx_config($domain));
            } else {
                touch($file_path);
                $nginx = File::put($file_path, $this->nginx_config($domain));
            }

            if ($nginx) {
                $symlink = $this->execute($this->symlink($domain));
                if ($symlink->successful()) {
                    $finally = $this->execute('sudo nginx -t && sudo systemctl reload nginx');
                    if ($finally->successful()) {
                        return true;
                    } else {
                        return false;
                    }
                }
            }
        } else {
            return false;
        }
    }

    public function certbot_command($domain, $email): string
    {

        if (config('services.certbot.test') == true) {
            $command = "sudo certbot certonly --nginx --agree-tos --no-eff-email -d $domain --email $email --test-cert";
        } else {
            $command = "sudo certbot certonly --nginx --agree-tos --no-eff-email -d $domain --email $email";
        }

        return $command;
    }

    public function nginx_config($domain): string
    {
        $host = "short.app.com";

        $content = "
        server {
            if (\$host = $domain) {
               return 301 https://\$host\$request_uri;
            }
            listen 80;
            listen [::]:80;
            server_name $domain;
        }
        server {
            listen 443 ssl http2;
            listen [::]:443 ssl http2;
            server_name $domain;
            return 301 https://$host\$request_uri;
            resolver 8.8.8.8;
            location / {
                include proxy_params;
                proxy_pass https://$host\$request_uri;
                proxy_set_header Host $domain;
                proxy_set_header X-Forwarded-Host \$http_host;
                proxy_set_header X-Real-IP \$remote_addr;
                proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
                proxy_set_header X-Forwarded-Proto \$scheme;
                proxy_redirect off;
                proxy_http_version 1.1;
                proxy_set_header Upgrade \$http_upgrade;
            }
            ssl_certificate /etc/letsencrypt/live/$domain/fullchain.pem;
            ssl_certificate_key /etc/letsencrypt/live/$domain/privkey.pem;
            ssl_trusted_certificate /etc/letsencrypt/live/$domain/chain.pem;
            include /etc/letsencrypt/options-ssl-nginx.conf;
            ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
        }";

        return $content;
    }

    private function symlink($domain): string
    {
        return "sudo ln -s /etc/nginx/sites-available/$domain /etc/nginx/sites-enabled/";
    }

    private function execute($command)
    {
        return Process::path(base_path())->run($command);
    }

Utworzymy teraz zadanie, które będzie obsługiwać generowanie certyfikatów SSL.

php artisan make:job GenerateSSLJob
//App/Jobs/GenerateSSLJob
<?php

namespace App\Jobs;

use App\Utils\SSLManager;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldBeUnique;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Throwable;

class GenerateSSLJob implements ShouldQueue
{
    use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;

    public $domain;

    public $email;

    public function __construct($domain, $email = null)
    {
        $this->domain = $domain;
        $this->email = $email;
    }

    /**
     * Execute the job.
     */
    public function handle(): void
    {
        (new SSLManager())->generateSSL($this->domain, $this->email);
    }
}

Dzięki temu nasz generator certyfikatów SSL jest gotowy.

Możemy teraz użyć domeny niestandardowej w naszym skracaczu adresów URL.

Usuwanie certyfikatów SSL

Ponieważ tworzymy certyfikaty SSL na dużą skalę, potrzebujemy sposobu na ich usunięcie, gdy tylko użytkownik opuści naszą platformę.

Możemy stworzyć inną metodę, która poradzi sobie z tym zadaniem za nas.

//App/Utils/SSLManager.php
<?php


...
  public function deleteSSL(User $user)
    {
        $domains = $this->getUserDomains($user);

        if (!empty($domains)) {
            foreach ($domains as $domain) {
                $nginx_path = "/etc/nginx/sites-available/$domain";
                $this->deleteConfig($nginx_path);
                $this->execute("sudo rm -rf /etc/nginx/sites-enabled/$domain");
                $this->execute("sudo certbot delete --cert-name $domain --non-interactive");
            }

            $finally = $this->execute('sudo nginx -t && sudo systemctl reload nginx');
            if ($finally->successful()) {

                return true;
            } else {
                return false;
            }
        }
    }

    public function getUserDomains(User $user)
    {
        return Domains::where('user_id', $user->id)->pluck('domain')->toArray();
    }

    public function deleteConfig($file_path)
    {
        if (File::exists($file_path)) {
            return File::delete($file_path);
        } else {
            return false;
        }
    }

...

Na tym etapie można również odwołać certyfikaty , aby dodać logikę w tym miejscu.

Udoskonalanie naszego kodu

Możemy dodać kilka funkcji pomocniczych, które pomogą uczynić naszą aplikację jeszcze lepszą.

Sprawdzanie poprawności nazw

domen Ponieważ zezwalamy użytkownikom na dodawanie domen niestandardowych, możemy mieć przypadki, w których użytkownicy "przypadkowo" dodają nieprawidłowe nazwy domen. Dlatego potrzebujemy sposobu na zapewnienie, że tylko prawidłowe nazwy domen będą kwalifikować się do generowania SSL.

Dodajmy inną metodę, która sprawdzi poprawność nazwy domeny

//App/Utils/DomainHelper.php
<?php

...

public function is_valid_domain_name($domain_name)
    {
        return preg_match("/^(?:[-A-Za-z0-9]+\.)+[A-Za-z]{2,6}$/", $domain_name);
    }
...

Używamy prostego wyrażenia regularnego, który "pożyczyłem" z czatu gpt🙂, aby sprawdzić domenę i zwrócić, czy jest prawidłowa, czy nie.

Zautomatyzuj odnawianie SSL Certyfikaty

Letsencrypt SSL wygasają po 90 dniach. Na szczęście Certbort może odnowić dla nas certyfikaty SSL.

Dodajmy metodę, która odnowi dla nas nazwy domen

//App/Utils/SSLManager.php
<?php

...

 public function renewSSL($domain)
    {
        $command = "sudo certbot certonly --force-renew -d $domain";
        return $this->execute($command);
    }

...

Będziemy potrzebować sposobu, aby wiedzieć, kiedy odnowić te certyfikaty SSL. Dodajmy nową kolumnę w tabeli domen, która będzie zawierać datę odnowienia.

php artisan make:migration add_renewal_column_to_domains_table --table=domains
<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

return new class extends Migration
{
    /**
     * Run the migrations.
     */
    public function up(): void
    {
        Schema::table('domains', function (Blueprint $table) {
            $table->dateTime('renewal_date')->nullable()->after('status');
        });
    }

    /**
     * Reverse the migrations.
     */
    public function down(): void
    {
        Schema::table('domains', function (Blueprint $table) {
            $table->dropColumn('renewal_date');
        });
    }
};

Następnie przygotujemy naszą pracę do odnowienia tych domen

php artisan make:job RenewSSLJob
<?php

namespace App\Jobs;

use App\Utils\SSLManager;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldBeUnique;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;

class RenewSSLJob implements ShouldQueue
{
    use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;

    /**
     * Create a new job instance.
     */
    public $domains;
    public function __construct(array $domains)
    {
        $this->domains = $domains;
    }

    /**
     * Execute the job.
     */
    public function handle(): void
    {
        foreach ($this->domains as $domain) {
            (new SSLManager())->renewSSL($domain);
        }
    }
}

Na koniec zaplanujemy codzienne uruchamianie polecenia. Otrzymamy wszystkie domeny, które wygasły za miesiąc i odnowimy je.

Chcemy je odnowić na 30 dni przed ich wygaśnięciem, aby zapobiec wygasłym certyfikatom SSL na naszym serwerze.

//App/Console/Kernel.php

<?php

namespace App\Console;

use App\Jobs\RenewSSLJob;
use Illuminate\Console\Scheduling\Schedule;
use Illuminate\Foundation\Console\Kernel as ConsoleKernel;
use Illuminate\Support\Carbon;
use Illuminate\Support\Facades\DB;

class Kernel extends ConsoleKernel
{
    /**
     * Define the application's command schedule.
     */
    protected function schedule(Schedule $schedule): void
    {
        // $schedule->command('inspire')->hourly();

        $renewalDate = Carbon::now()->addDays(30)->toDateTimeString();

        $domains = DB::table('domains')
            ->where('renewal_date', '<=', $renewalDate)
            ->pluck('domain')
            ->toArray();

        if (!empty($domains)) {
             $schedule->job(new RenewSSLJob($domains))->daily();
        }
    }

    /**
     * Register the commands for the application.
     */
    protected function commands(): void
    {
        $this->load(__DIR__ . '/Commands');

        require base_path('routes/console.php');
    }
}

Ostatnia klasa pomocnicza powinna przypominać tę poniżej

//App/Utils/SSLManager.php
<?php

namespace App\Utils;

use App\Models\User;
use App\Models\Domains;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\File;
use Illuminate\Support\Facades\Process;

class SSLManager
{
    public function generateSSL($domain, $email = null): bool
    {
        $command = $this->certbot_command($domain, $email);

        $certbot = Process::path(base_path())->start($command);

        $process = $certbot->wait();


        if ($process->successful()) {
            $file_path = "/etc/nginx/sites-available/$domain";

            if (File::exists($file_path)) {
                $nginx = File::put($file_path, $this->nginx_config($domain));
            } else {
                touch($file_path);
                $nginx = File::put($file_path, $this->nginx_config($domain));
            }

            if ($nginx) {
                $symlink = $this->execute($this->symlink($domain));
                if ($symlink->successful()) {
                    $finally = $this->execute('sudo nginx -t && sudo systemctl reload nginx');
                    if ($finally->successful()) {
                        DB::table('domains')->where('domain', '=', $domain)->update([
                            'status' => 1,
                            'renewal_date' => now()->toDateTimeString()
                        ]);

                        return true;
                    } else {
                        return false;
                    }
                }
            }
        } else {
            return false;
        }
    }

    public function certbot_command($domain, $email): string
    {

        if (config('services.certbot.test') == true) {
            $command = "sudo certbot certonly --nginx --agree-tos --no-eff-email -d $domain --email $email --test-cert";
        } else {
            $command = "sudo certbot certonly --nginx --agree-tos --no-eff-email -d $domain --email $email";
        }

        return $command;
    }

    public function nginx_config($domain): string
    {
        $host = "short.app.com";

        $content = "
        server {
            if (\$host = $domain) {
               return 301 https://\$host\$request_uri;
            }
            listen 80;
            listen [::]:80;
            server_name $domain;
        }
        server {
            listen 443 ssl http2;
            listen [::]:443 ssl http2;
            server_name $domain;
            return 301 https://$host\$request_uri;
            resolver 8.8.8.8;
            location / {
                include proxy_params;
                proxy_pass https://$host\$request_uri;
                proxy_set_header Host $domain;
                proxy_set_header X-Forwarded-Host \$http_host;
                proxy_set_header X-Real-IP \$remote_addr;
                proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
                proxy_set_header X-Forwarded-Proto \$scheme;
                proxy_redirect off;
                proxy_http_version 1.1;
                proxy_set_header Upgrade \$http_upgrade;
            }
            ssl_certificate /etc/letsencrypt/live/$domain/fullchain.pem;
            ssl_certificate_key /etc/letsencrypt/live/$domain/privkey.pem;
            ssl_trusted_certificate /etc/letsencrypt/live/$domain/chain.pem;
            include /etc/letsencrypt/options-ssl-nginx.conf;
            ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
        }";

        return $content;
    }

    private function symlink($domain): string
    {
        return "sudo ln -s /etc/nginx/sites-available/$domain /etc/nginx/sites-enabled/";
    }

    private function execute($command)
    {
        return Process::path(base_path())->run($command);
    }

    public function renewSSL($domain)
    {
        $command = "sudo certbot certonly --force-renew -d $domain";
        return $this->execute($command);
    }

    public function deleteSSL(User $user)
    {
        $domains = $this->getUserDomains($user);

        if (!empty($domains)) {
            foreach ($domains as $domain) {
                $nginx_path = "/etc/nginx/sites-available/$domain";
                $this->deleteConfig($nginx_path);
                $this->execute("sudo rm -rf /etc/nginx/sites-enabled/$domain");
                $this->execute("sudo certbot delete --cert-name $domain --non-interactive");
            }

            $finally = $this->execute('sudo nginx -t && sudo systemctl reload nginx');
            if ($finally->successful()) {
                return true;
            } else {
                return false;
            }
        }
    }

    public function getUserDomains(User $user)
    {
        return Domains::where('user_id', $user->id)->pluck('domain')->toArray();
    }

    public function deleteConfig($file_path)
    {
        if (File::exists($file_path)) {
            return File::delete($file_path);
        } else {
            return false;
        }
    }
}

Końcowa klasa

SSLManager Wiązanie wszystkiego

Poskładajmy wszystko razem za pomocą kontrolera

php artisan make:controller DomainController
<?php

namespace App\Http\Controllers;

use App\Jobs\GenerateSSLJob;
use App\Models\Domains;
use App\Utils\DomainHelper;
use Illuminate\Http\Request;

class DomainController extends Controller
{

    public function store(Request $request)
    {
        $domain_name = $request->input('domain');
        $email = auth()->user()->email;

        if ((new DomainHelper())->is_valid_domain_name($domain_name)) {
            $domain = Domains::create([
                'user_id' => auth()->id(),
                'domain' => $domain_name,
                'status' => 0
            ]);

            if ((new DomainHelper())->verifycname($domain_name)) {
                $job = (new GenerateSSLJob($domain_name, $email));
                dispatch($job);
            }

            return $domain;
        } else {
            return response()->json([
                'message' => $domain_name . 'is not a valid domain name. Ensure your domain name is valid',
            ], 406);
        }
    }
}

Wniosek

: I to wszystko. Zautomatyzowaliśmy proces generowania i odnawiania certyfikatów SSL za pomocą laravel.

Mam nadzieję, że ten artykuł był wnikliwy i pokazał, jak zautomatyzować generowanie i odnowienie SSL za pomocą laravel.

Możesz dostosować logikę do swoich potrzeb. Dla mnie używałem prostego przykładu skracacza adresów URL i w ten sposób osiągnąłem automatyzację generowania SSL.

Jeśli masz jakieś pytania, możesz je zadać w sekcji komentarzy.

Dziękuję za przeczytanie.

Comments

No comments yet
Yurij Finiv

Yurij Finiv

Full stack

O

Professional Fullstack Developer with extensive experience in website and desktop application development. Proficient in a wide range of tools and technologies, including Bootstrap, Tailwind, HTML5, CSS3, PUG, JavaScript, Alpine.js, jQuery, PHP, MODX, and Node.js. Skilled in website development using Symfony, MODX, and Laravel. Experience: Contributed to the development and translation of MODX3 i...

O autorze CrazyBoy49z
WORK EXPERIENCE
Kontakt
Ukraine, Lutsk
+380979856297