diff --git a/README.md b/README.md new file mode 100644 index 0000000..e69de29 diff --git a/composer.json b/composer.json new file mode 100644 index 0000000..d9abcc9 --- /dev/null +++ b/composer.json @@ -0,0 +1,13 @@ +{ + "name": "stechstudio/laravel-ssh-tunnel", + "description": "Easy creation & maintenance of an SSH Tunnel for Laravel/Lumen", + "type": "library", + "authors": [ + { + "name": "Bubba Hines", + "email": "rob@stechstudio.com" + } + ], + "minimum-stability": "dev", + "require": {} +} diff --git a/src/Console/TunnelerCommand.php b/src/Console/TunnelerCommand.php new file mode 100644 index 0000000..bcfef54 --- /dev/null +++ b/src/Console/TunnelerCommand.php @@ -0,0 +1,41 @@ +error($e->getMessage()); + return 1; + } + + if ($result === 1 ){ + $this->info('The Tunnel is already Activated.'); + return 0; + } + + if ($result === 2 ){ + $this->info('The Tunnel has been Activated.'); + return 0; + } + + $this->warn('I have no idea how this happened. Let me know if you figure it out.'); + return 1; + } +} \ No newline at end of file diff --git a/src/Jobs/CreateTunnel.php b/src/Jobs/CreateTunnel.php new file mode 100644 index 0000000..d0c374b --- /dev/null +++ b/src/Jobs/CreateTunnel.php @@ -0,0 +1,94 @@ +ncCommand = sprintf('%s -z %s %d > /dev/null 2>&1', + config('tunneler.nc_path'), + config('tunneler.local_address'), + config('tunneler.local_port') + ); + + $this->sshCommand = sprintf('%s -N -i %s -L %d:%s:%d -p %d %s@%s', + config('tunneler.ssh_path'), + config('tunneler.identity_file'), + config('tunneler.local_port'), + config('tunneler.bind_address'), + config('tunneler.bind_port'), + config('tunneler.port'), + config('tunneler.user'), + config('tunneler.hostname') + ); + } + + + public function handle(): int + { + if ($this->verifyTunnel()){ + return 1; + } + + $this->createTunnel(); + + if ($this->verifyTunnel()){ + return 2; + } + + throw new \ErrorException(sprintf("Could Not Create SSH Tunnel with command:\n\t%s\nCheck your configuration.", + $this->sshCommand)); + } + + + /** + * Creates the SSH Tunnel for us. + */ + protected function createTunnel() + { + $this->runCommand(sprintf('%s %s > /dev/null &', config('tunneler.nohup_path'), $this->sshCommand)); + // Ensure we wait long enough for it to actually connect. + usleep(config('tunneler.wait')); + } + + /** + * Verifies whether the tunnel is active or not. + * @return bool + */ + protected function verifyTunnel(): bool + { + return $this->runCommand($this->ncCommand); + } + + /** + * Runs a command and converts the exit code to a boolean + * @param $command + * @return bool + */ + protected function runCommand($command): bool + { + $return_var = 1; + exec($command, $this->output, $return_var); + return (bool)($return_var === 0); + } + + +} \ No newline at end of file diff --git a/src/TunnelerServiceProvider.php b/src/TunnelerServiceProvider.php new file mode 100644 index 0000000..ca9d6ff --- /dev/null +++ b/src/TunnelerServiceProvider.php @@ -0,0 +1,65 @@ +publishes([$this->configPath => $publishPath], 'config'); + + if (config('tunneler.on_boot')){ + dispatch(new CreateTunnel()); + } + } + + public function register() + { + if ( is_a($this->app,'Laravel\Lumen\Application')){ + $this->app->configure('tunneler'); + } + $this->mergeConfigFrom($this->configPath, 'tunneler'); + + $this->app['command.tunneler.activate'] = $this->app->share( + function ($app) { + return new TunnelerCommand(); + } + ); + + $this->commands('command.tunneler.activate'); + } + + /** + * Get the services provided by the provider. + * + * @return array + */ + public function provides() + { + return array('command.tunneler.activate'); + } + +} \ No newline at end of file diff --git a/src/config/tunneler.php b/src/config/tunneler.php new file mode 100644 index 0000000..8a67d0c --- /dev/null +++ b/src/config/tunneler.php @@ -0,0 +1,20 @@ + env('TUNNELER_NC_PATH', 'nc'), + 'ssh_path' => env('TUNNELER_SSH_PATH', 'ssh'), + 'nohup_path' => env('TUNNELER_NOHUP_PATH', 'nohup'), + + 'local_address' => env('TUNNELER_LOCAL_ADDRESS', '127.0.0.1'), + 'local_port' => env('TUNNELER_LOCAL_PORT'), + 'identity_file' => env('TUNNELER_IDENTITY_FILE'), + + 'bind_address' => env('TUNNELER_BIND_ADDRESS', '127.0.0.1'), + 'bind_port' => env('TUNNELER_BIND_PORT'), + + 'user' => env('TUNNELER_USER'), + 'hostname' => env('TUNNELER_HOSTNAME'), + 'port' => env('TUNNELER_PORT'), + 'wait' => env('TUNNELER_CONN_WAIT', '500000'), + + 'on_boot' => filter_var(env('TUNNELER_ON_BOOT', false), FILTER_VALIDATE_BOOLEAN) +]; \ No newline at end of file