mirror of
https://github.com/archtechx/tenancy.git
synced 2026-02-04 18:44:04 +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
|
## 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
|
If you use the model where second level domains are used, there are multiple ways you can solve this.
|
||||||
sudo mkdir /var/lib/letsencrypt_tenancy /var/log/letsencrypt_tenancy /etc/letsencrypt_tenancy
|
|
||||||
# replace ubuntu with your username
|
This guide focuses on nginx.
|
||||||
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
|
### 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
|
## 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.
|
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',
|
// '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