<?php namespace LakeDrops\DockerTraefik; use Symfony\Component\Filesystem\Filesystem; use Symfony\Component\Yaml\Yaml; /** * Class Traefik. * * @package LakeDrops\DockerTraefik */ class Traefik { /** * The name of the project using Traefik. * * @var string */ protected $name; /** * @var string */ protected $domain; /** * @var int */ protected $http_port; /** * @var int */ protected $https_port; /** * @var string */ protected $cert_filename; /** * @var string */ protected $key_filename; /** * @var bool */ protected $addon_portainer = FALSE; /** * Traefik constructor. * * @param string $name * Name of the network to be created which needs to match the container * prefix of your project you would like to handle with Traefik. * @param string $domain * The domain name used for all local projects. * @param int $http_port * Port for non secure requests. * @param int $https_port * Port for secure requests. * @param string $cert_filename * Filename of the SSL certificate. * @param string $key_filename * Filename of the private key for the SSL certificate. */ public function __construct(string $name, $domain = 'docker.localhost', $http_port = 8000, $https_port = 8443, $cert_filename = 'fullchain.pem', $key_filename = 'privkey.pem') { $this->name = $name; $this->domain = $domain; $this->http_port = $http_port; $this->https_port = $https_port; $this->cert_filename = $cert_filename; $this->key_filename = $key_filename; } /** * @param bool $addon_portainer */ public function setAddonPortainer(bool $addon_portainer): void { $this->addon_portainer = $addon_portainer; } /** * Update the Traefik container. */ public function update(): void { // Update host wide traefik container. $traefikPath = $_SERVER['HOME'] . '/.traefik'; $traefikCertPath = $_SERVER['HOME'] . '/.traefik/certs'; $traefikConfigPath = $_SERVER['HOME'] . '/.traefik/configuration'; $traefikFile = $traefikPath . '/docker-compose.yml'; $fs = new Filesystem(); if (!$fs->exists($traefikPath)) { $fs->mkdir($traefikPath); } if (!$fs->exists($traefikCertPath)) { $fs->mkdir($traefikCertPath); } if (!$fs->exists($traefikConfigPath)) { $fs->mkdir($traefikConfigPath); } file_put_contents($traefikConfigPath . '/certificates.toml', $this->defaultCertificatesConfig()); file_put_contents($traefikFile, Yaml::dump($this->defaultDockerCompose(), 9, 2)); $cwd = getcwd(); chdir($traefikPath); exec('docker network create traefik-public 2>/dev/null'); exec('docker compose --project-name traefik up -d'); chdir($cwd); } /** * @return array */ private function defaultDockerCompose(): array { $config = [ 'version' => '3.3', 'services' => [ 'traefik' => [ 'image' => 'traefik:2.6', 'command' => [ '--api=true', '--api.dashboard=true', '--api.insecure=true', '--entrypoints.web.address=:80', '--entrypoints.websecure.address=:443', '--entrypoints.websecure.http.tls.domains[0].main=' . $this->domain, '--entrypoints.websecure.http.tls.domains[0].sans=.' . $this->domain, '--providers.file.directory=/configuration/', '--providers.file.watch=true', '--providers.docker=true', '--providers.docker.exposedbydefault=false', ], 'restart' => 'unless-stopped', 'networks' => [ 'traefik-public', ], 'ports' => [ $this->http_port . ':80', $this->https_port . ':443', ], 'labels' => [ 'traefik.enable=true', 'traefik.network=traefik-public', 'traefik.http.routers.traefik.service=api@internal', 'traefik.http.routers.traefik.rule=Host(`traefik.' . $this->domain . '`)', ], 'volumes' => [ './certs:/certs/:ro', './configuration:/configuration/:ro', '/var/run/docker.sock:/var/run/docker.sock:ro', ], ], ], 'networks' => [ 'traefik-public' => [ 'external' => true, ], ], ]; if ($this->addon_portainer) { $config['services']['portainer'] = [ 'image' => 'portainer/portainer-ce:2.11.1', 'command' => '-H unix:///var/run/docker.sock', 'restart' => 'unless-stopped', 'networks' => [ 'traefik-public', ], 'labels' => [ 'traefik.enable=true', 'traefik.network=traefik-public', 'traefik.http.routers.frontend.rule=Host(`portainer.' . $this->domain . '`)', 'traefik.http.routers.frontend.entrypoints=websecure', 'traefik.http.services.frontend.loadbalancer.server.port=9000', 'traefik.http.routers.frontend.service=frontend', 'traefik.http.routers.edge.rule=Host(`edge.' . $this->domain . '`)', 'traefik.http.routers.edge.entrypoints=websecure', 'traefik.http.services.edge.loadbalancer.server.port=8000', 'traefik.http.routers.edge.service=edge', ], 'volumes' => [ './portainerdata:/data', '/var/run/docker.sock:/var/run/docker.sock:ro', ], ]; } return $config; } /** * @return string */ private function defaultCertificatesConfig(): string { return <<<EOF [[tls.certificates]] certFile = "/certs/$this->cert_filename" keyFile = "/certs/$this->key_filename" EOF; } }