mirror of
https://github.com/archtechx/tenancy.git
synced 2026-02-04 14:44:05 +00:00
Remove unfinished features, expand README
This commit is contained in:
parent
97ab962cb7
commit
256e88444c
6 changed files with 29 additions and 214 deletions
35
README.md
35
README.md
|
|
@ -396,15 +396,38 @@ Tenant migrations are located in `database/migrations/tenant`, so you should mov
|
|||
|
||||
## HTTPS certificates
|
||||
|
||||
todo
|
||||
HTTPS certificates are very easy to deal with if you use the `yourclient1.yourapp.com`, `yourclient2.yourapp.com` model. You can use a wildcard HTTPS certificate.
|
||||
|
||||
```sh
|
||||
sudo mkdir /var/lib/letsencrypt_tenancy /var/log/letsencrypt_tenancy /etc/letsencrypt_tenancy
|
||||
# replace ubuntu with your username
|
||||
sudo chown -R ubuntu:www-data /var/lib/letsencrypt_tenancy /var/log/letsencrypt_tenancy /etc/letsencrypt_tenancy
|
||||
sudo chmod -R 775 /var/lib/letsencrypt_tenancy /var/log/letsencrypt_tenancy /etc/letsencrypt_tenancy
|
||||
If you use the model where second level domains are used, there are multiple ways you can solve this.
|
||||
|
||||
This guide focuses on nginx.
|
||||
|
||||
### 1. Use nginx with the lua module
|
||||
|
||||
Specifically, you're interested in the [`ssl_certificate_by_lua_block`](https://github.com/openresty/lua-nginx-module#ssl_certificate_by_lua_block) directive. Nginx doesn't support using variables such as the hostname in the `ssl_certificate` directive, which is why the lua module is needed.
|
||||
|
||||
This approach lets you use one server block for all tenants.
|
||||
|
||||
### 2. Add a simple server block for each tenant
|
||||
|
||||
You can store most of your config in a file, such as `/etc/nginx/includes/tenant`, and include this file into tenant server blocks.
|
||||
|
||||
```nginx
|
||||
server {
|
||||
include includes/tenant;
|
||||
server_name foo.bar;
|
||||
# ssl_certificate /etc/foo/...;
|
||||
}
|
||||
```
|
||||
|
||||
### Generating certificates
|
||||
|
||||
You can generate a certificate using certbot. If you use the `--nginx` flag, you will need to run certbot as root. If you use the `--webroot` flag, you only need the user that runs it to have write access to the webroot directory (or perhaps webroot/.well-known is enough) and some certbot files (you can specify these using --work-dir, --config-dir and --logs-dir).
|
||||
|
||||
Creating this config dynamically from PHP is not easy, but is probably feasible. Giving `www-data` write access to `/etc/nginx/sites-available/tenants.conf` should work.
|
||||
|
||||
However, you still need to reload nginx configuration to apply the changes to configuration. This is problematic and I'm not sure if there is a simple and secure way to do this from PHP.
|
||||
|
||||
## Testing
|
||||
|
||||
If you run the tests of this package, please make sure you don't store anything in Redis @ 127.0.0.1:6379 db#14. The contents of this database are flushed everytime the tests are run.
|
||||
|
|
|
|||
|
|
@ -1,9 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Stancl\Tenancy\Interfaces;
|
||||
|
||||
interface ServerConfigManager
|
||||
{
|
||||
public function addVhost(string $domain, string $file): bool;
|
||||
public function deployCertificate(string $domain): bool;
|
||||
}
|
||||
|
|
@ -1,39 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Stancl\Tenancy\ServerConfigManagers;
|
||||
|
||||
use Symfony\Component\Process\Process;
|
||||
use Stancl\Tenancy\Interfaces\ServerConfigManager;
|
||||
|
||||
class NginxConfigManager implements ServerConfigManager
|
||||
{
|
||||
public function addVhost(string $domain, string $file): bool
|
||||
{
|
||||
$f = fopen($file, 'a');
|
||||
$fw = fwrite($f, $this->getVhostText($domain));
|
||||
$fc = fclose($f);
|
||||
return $fw && $fc;
|
||||
}
|
||||
|
||||
public function getVhostText(string $domain)
|
||||
{
|
||||
return str_replace('%host%', $domain, config('tenancy.server.nginx.vhost'));
|
||||
}
|
||||
|
||||
public function deployCertificate(string $domain): bool
|
||||
{
|
||||
$process = new Process(array_merge([
|
||||
config('tenancy.server.certbot_path'),
|
||||
'-n',
|
||||
'--webroot',
|
||||
'-d', $domain,
|
||||
'--agree-tos',
|
||||
'--preferred-challenges', 'http',
|
||||
'--webroot-path', config('tenancy.server.nginx.webroot'),
|
||||
], config('tenancy.server.nginx.extra_certbot_args')));
|
||||
|
||||
$process->run();
|
||||
|
||||
return $process->isSuccessful();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,38 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Stancl\Tenancy;
|
||||
|
||||
use Stancl\Tenancy\Interfaces\ServerConfigManager;
|
||||
|
||||
class ServerManager
|
||||
{
|
||||
public function __construct(ServerConfigManager $serverConfigManager, TenantManager $tenantManager)
|
||||
{
|
||||
$this->serverConfigManager = $serverConfigManager;
|
||||
$this->tenantManager = $tenantManager;
|
||||
}
|
||||
|
||||
public function getConfigFilePath()
|
||||
{
|
||||
if (config('tenancy.server.file.single')) {
|
||||
return config('tenancy.server.file.path');
|
||||
}
|
||||
|
||||
return config('tenancy.server.file.path.prefix') . $this->tenantManager->tenant['uuid'] . config('tenancy.server.file.path.suffix');
|
||||
}
|
||||
|
||||
public function createVhost(string $domain)
|
||||
{
|
||||
// todo symlink
|
||||
$this->serverConfigManager->addVhost($domain, $this->getConfigFilePath());
|
||||
$this->serverConfigManager->deployCertificate($domain);
|
||||
if (method_exists($this->serverConfigManager, 'postCertDeploymentChanges')) {
|
||||
$this->serverConfigManager->postCertDeploymentChanges();
|
||||
}
|
||||
}
|
||||
|
||||
public function deleteVhost()
|
||||
{
|
||||
// todo
|
||||
}
|
||||
}
|
||||
|
|
@ -30,35 +30,4 @@ return [
|
|||
// 's3',
|
||||
],
|
||||
],
|
||||
'server' => [
|
||||
'manager' => 'Stancl\Tenancy\ServerConfigManagers\NginxConfigManager',
|
||||
'file' => [
|
||||
'single' => true, // single file for all tenant vhosts
|
||||
'path' => '/etc/nginx/sites-available/tenants.conf',
|
||||
/*
|
||||
'single' => false,
|
||||
'path' => [
|
||||
'prefix' => '/etc/nginx/sites-available/tenants/tenant',
|
||||
'suffix' => '.conf',
|
||||
// results in: '/etc/nginx/sites-available/tenants/tenant' . $uuid . '.conf'
|
||||
]
|
||||
*/
|
||||
],
|
||||
'nginx' => [
|
||||
'webroot' => '/var/www/html',
|
||||
'vhost' => "
|
||||
server {
|
||||
include includes/tenancy;
|
||||
server_name %host%;
|
||||
}",
|
||||
'extra_certbot_args' => [
|
||||
'--must-staple',
|
||||
'--config-dir', '/etc/letsencrypt_tenancy',
|
||||
'--work-dir', '/var/lib/letsencrypt_tenancy',
|
||||
'--logs-dir', '/var/log/letsencrypt_tenancy',
|
||||
'--staging', // obtains a fake cert intended for testing certbot
|
||||
// '--email', 'your@email', // if you haven't created an account in certbot yet
|
||||
],
|
||||
],
|
||||
]
|
||||
];
|
||||
|
|
|
|||
|
|
@ -1,91 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Stancl\Tenancy\Tests;
|
||||
|
||||
use Stancl\Tenancy\ServerManager;
|
||||
|
||||
class ServerManagerTest extends TestCase
|
||||
{
|
||||
public function setUp()
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
$this->serverManager = app(ServerManager::class);
|
||||
}
|
||||
|
||||
/** @test */
|
||||
public function getConfigFilePath_works_when_single_file_mode_is_used()
|
||||
{
|
||||
config([
|
||||
'tenancy.server.file.single' => true,
|
||||
'tenancy.server.file.path' => '/foo/bar',
|
||||
]);
|
||||
|
||||
$this->assertSame('/foo/bar', $this->serverManager->getConfigFilePath());
|
||||
}
|
||||
|
||||
/** @test */
|
||||
public function getConfigFilePath_works_when_multiple_files_mode_is_used()
|
||||
{
|
||||
config([
|
||||
'tenancy.server.file.single' => false,
|
||||
'tenancy.server.file.path' => [
|
||||
'prefix' => '/etc/foo',
|
||||
'suffix' => 'bar'
|
||||
],
|
||||
]);
|
||||
|
||||
$uuid = tenant('uuid');
|
||||
|
||||
$this->assertSame("/etc/foo{$uuid}bar", $this->serverManager->getConfigFilePath());
|
||||
}
|
||||
|
||||
/** @test */
|
||||
public function vhost_is_written()
|
||||
{
|
||||
[$tmpfile, $path, $vhost] = $this->setupCreateVhost();
|
||||
|
||||
$this->serverManager->createVhost('localhost');
|
||||
|
||||
$vhost = str_replace('%host%', 'localhost', $vhost);
|
||||
|
||||
$this->assertContains($vhost, fread($tmpfile, filesize($path)));
|
||||
}
|
||||
|
||||
/** @test */
|
||||
public function cert_is_deployed()
|
||||
{
|
||||
[$tmpfile, $path, $vhost] = $this->setupCreateVhost();
|
||||
|
||||
$this->serverManager->createVhost('localhost');
|
||||
|
||||
dump(fread($tmpfile, filesize($path)));
|
||||
// todo
|
||||
}
|
||||
|
||||
public function setupCreateVhost()
|
||||
{
|
||||
$tmpfile = tmpfile();
|
||||
$path = stream_get_meta_data($tmpfile)['uri'];
|
||||
|
||||
$vhost = "server {
|
||||
include includes/tenancy;
|
||||
server_name %host%;
|
||||
}";
|
||||
|
||||
config([
|
||||
'tenancy.server.nginx' => [
|
||||
'vhost' => $vhost,
|
||||
'extra_certbot_args' => [
|
||||
'--staging'
|
||||
],
|
||||
],
|
||||
'tenancy.server.file' => [
|
||||
'single' => true,
|
||||
'path' => $path,
|
||||
],
|
||||
]);
|
||||
|
||||
return [$tmpfile, $path, $vhost];
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue