Symfony 4, .env files and production - php

.env files are very handy with docker, kubernetes, etc
But what if I have simple nginx server without any orchestration and a pack of cron workers and a pack of daemons(systemd/supervisord/etc)?
I can write these env variables to nginx server section, but I have to set up hundreds of env variables to each cron worker or daemon.
I found a quick solution: using symfony/dotenv component in production.
But it seems to me dirty. Who can suggest better solution?

First of all, not all variables need to be specified using environment variables. Keep variables that do not differ per system in a separate yaml file.
When you have just one environment per server you can specify the environment variables globally in /etc/environment. (Might be different depending on your Linux flavour)
Personally I find that using DotEnv poses more difficulties than solutions when you run multiple environments on the same server. Specifying the variables in a global configuration like /etc/environment doesn't work in that case.
Specifying the environment variables in nginx isn't a solution either since, as you mentioned, they won't be picked up by cron, supervisor, the console, etc. For me, this was the reason to completely remove DotEnv and work with the good old parameters.yaml file again. Nothing will stop you from doing that.
Another solution however is to keep using DotEnv in your development environment and to include a separate parameters.yaml in production. You can then define the environment variables as follows:
parameters:
env(APP_ENV): prod
env(APP_SECRET): 3d05afda019ed4e3faaf936e3ce393ba
...
A way to include this file is to put the following in your services.yaml file:
imports:
- { resource: parameters.yaml, ignore_errors: true }
This way, the import will be ignored when no parameters.yaml file exists. Another solution is to add a line to configureContainer() in your Kernel class:
$loader->load($confDir.'/parameters'.self::CONFIG_EXTS, 'glob');

if you want to centralize your environment variables for cli and fpm you can define them once in your system. And then reference them all in your php-fpm.conf:
....
[www]
env[APP_VAR1] = $APP_VAR1
env[APP_VAR2] = $APP_VAR2
...
In that way you can avoid using DotEnv in production which is encouraged by best practices.
Hope this helps.

Related

.env files in Github Actions CI/CD workflows: how to provide these into the workflow

I use Github Actions workflows for my CI/CD processes for Node and PHP projects.
Within a workflow I clone my repository into Github Actions runner virtual machine. Then in order to run tests within a workflow I have to have the .env file in the cloned repository.
The problem is my .env file is not a part of repository (which is the ubuquitous practice).
To solve the problem I use what I consider a workaround: set up MY_PROJECT_ENV Github Action sercret variable, manually put there the content of my .env file and then dynamically create the .env file within my workflow with Linux console echo "${{ secrets.MY_PROJECT_ENV}}" > .env. This works.
But I would like to know are there other approaches for providing .env files to Github Actions workflows?
There are 3 ways to do this I know by now. I put the answer to my own question a year after in the different question. See there.
For the sake of SO rules and findablity I put here a summary.
You keep your .env file in the repository. Use dotenv actions to read your file into the workflow.
You keep the file out of the repository. Then you have 2 ways of getting .env variables:
2.1. as I wrote in my question above manually copy the file content to the GitHub actions secret variable and then in your workflow create the .env file from that variable.
2.2. Use the GitHub Actions API to create/update the secrets: write the NodeJS script on your machine (chances are you anyway use Webpack, Gulp or the like Node thing so you have Node installed).
The script should read the local .env files and write their content to the GH secrets. Of course you can write a custom console utilty to do this with any language you use in your project.
As easy as this :)
As you know .env doesn't mean to push to the remote repository.
You need to somehow add the environment variables to the machine that you're running the program.
In your case, you can add environment variables by using the .yaml file as below
steps:
- name: Hello Program
run: Hello $FIRST_NAME $LAST_NAME!
env:
FIRST_NAME: Akhil
LAST_NAME: Pentamsetti
for more information please visit github official doc about using the environment variables.
I do the following, which is simple and effective:
Add environment variables (either define them in the yaml file or as secrets) as needed
Keep .env.example in the repository, and run the following at the start of the CI job:
# Create the .env file
cp .env.example .env
# Install dependencies so we can run artisan commands
composer install ...
# generate an APP_KEY
php artisan key:generate
An alternative to this is to commit a .env.ci file to the repository with env vars specific to the CI environment, and run cp .env.ci .env when running tests. Sensitive keys should still be set as secrets.
You can technically provide all of your env vars between secrets / env's in the YAML file and have no .env file, but I like having a random APP_KEY set per test run to ensure there's nothing relying on a specific APP_KEY.
Environment Precedence
As an aside, here's how environment precedence works with Laravel in phpunit tests. This is laravel specific and may come at a surprise as it's not exactly how phpunit alone works outside of Laravel:
Env vars set in phpunit.xml always "win" (this is true in Laravel despite what phpunit's docs say about system env vars taking precedence over phpunit.xml file items)
System environment variations (in GitHub actions, these are ones set as an env var when running commands in the yaml file)
.env file items
Source: I created/run Chipper CI, a CI platform for Laravel.

Symfony4: Access DEV environment on production server

In Symfony3, when I want to browse the website in DEV environment on the "live" server, I just enter my ip address in /web/app_dev.php and open http://www.example.com/app_dev.php/ in the browser.
Since in Symfony4, the environment is now set in /.env, how can I see the DEV environment on the production machine?
EDIT: I'm looking for a solution that works in production, so applying any global changes (like e.g. setting APP_ENV=dev in /.env) is not an option.
You can change inside your .env file APP_ENV to dev like this:
APP_ENV=dev
If you set that variable symfony load the system into dev enviroment because inside Kernel.php there is this line that check that variable:
$kernel = new Kernel($_SERVER['APP_ENV'] ?? 'dev', $_SERVER['APP_DEBUG'] ?? false);
If you want to do it without change .env file you can for example set a variable in the Apache vhost or Nginx FastCgi configuration, based on the URL you are visiting from - such as APP_ENV=/home/user/app-name/dev.env or on a live server: APP_ENV=/etc/app-name.prod.env
So in this case you have many .env file but you can use rule based on url
At first this is a bad idea and that's why it wasn't possible by default to access app_dev.php on production server (symfony < 4). You're giving a lot of internal information to public and especially to attackers.
From symfony docs:
After you deploy to production, make sure that you cannot access the app_dev.php or config.php scripts (i.e. http://example.com/app_dev.php and http://example.com/config.php). If you can access these, be sure to remove the DEV section from the above configuration.
You should be able to debug most of the things from logs.
But if you really want to do it, you can just remove public/index.php and create public/app.php and public/app_dev.php like it was in symfony 3 and make it work with env variables. - https://github.com/symfony/symfony-standard/tree/3.4/web
EDIT: To be clear: you can just remove public/index.php, create public/app.php, public/app_dev.php (copies of index.php). And get inspiration from symfony 3 standard edition to adjust it to your needs.
EDIT2: As #Cerad mentioned it's a better idea to have index.php and index_dev.php file names (following Symfony4 decisions).

How do I use one config key in another?

In Laravel 4's cascading configuration files I was able to reference one configuration key from another. For example, I liked to have app.debug set to true or false, and then I could switch error reporting to Sentry to the opposite of that in a different configuration file, by setting the relevant setting to !Config::get('app.debug').
I knew this was a little hairy because presumably whether the config value can be found depends on the order in which files are loaded. But I managed to get it working each time.
I'm having no such luck in Laravel 5. I get a "class not found" error when I try to do Config::get(...) in a configuration file. Using the config(...) helper instead produces no error but the values aren't retrieved.
The convention, of course, is to use environment variables, the .env file and env(...) to grab the values. That way each configuration key can use the value of the environment variable. That's fine and works, but I have some values which are shared between environments, and I do want some of these values to be committed to version control.
Using the .env.example file isn't a good solution for me since I don't want to have to remember to copy values over to the .env file of each environment if they change.
How can I store some configuration values in a way where they are shared between environments, are present in version control, and can be referenced in multiple configuration files?
This is my solution, which works, but I'm interested in other possibilities.
Add a new shared file, checked in to VCS, called .env.shared. Its structure is just like .env. Edit bootstrap/app.php to load in these variables (in addition to .env):
// Load shared environment variables
$app->afterLoadingEnvironment(function () use ($app) {
Dotenv::load($app->basePath(), '.env.shared');
});
I added this right after $app is instantiated.

Symfony2 Where to save Environment Variables

I've seen plenty of tutorials on configuring an Apache2 virtual host with environment variables and using those variables as a parameter within the site. However, the issue I'm having is when I run php app/console cache:clear or reload my data fixtures, I get the following error:
You have requested a non-existent parameter ...
If I first export the parameter prior to reloading my fixtures, the error goes away as the environment variable is set. Which makes sense as that function isn't using Apache to execute and needs the environment variable set. But this doesn't fix my issue when I clear my cache manually.
So my question is, where do I actually want to save those environment variables?
I would like to store site specific information that's dependent on which virtual host is used.
I could store them in my ~/.profile bash file, but that doesn't really make sense and all of the documentation I've read simply says store with the virtual host (for Apache). But none of those docs mention executing scripts outside of a web browser accessing the site such as reloading fixtures, running migrations, or clearing cache, which is only an issue when I deploy my source.
Thanks a lot for any help.
You can put application variables in app/config/config.yml. You can put there:
parameters:
your.parameter.1: value1
your.parameter.2: value2
If you use any kind of version control system you can exclude those variables to separate file and ignore this file in version control repository. You also should include this file in config.yml. For example if you put the above variables in app/config/example.yml then you should add in app/config/config.yml:
imports:
- { resource: example.yml }
EDIT
You can always put parameters as follows:
parameters:
domain1:
param1: value1
param2: value2
domain2:
param1: value1
param2: value2
domain3:
param1: value1
param2: value2
and then read these variables in places where you want to use them according to the current domain or you can also set service that sets common variables according to current domain. For command line you can use default values.
So just because this is situation is a pet peeve of mine I urge you not to put environment specific information in the config file. So what is needed are two files that must stay in sync. One file will be for command line and one for Apache (or HTTP server of your choice) environment variables
CL: /var/www/applications/coolapp/coolapp.sh
Apache: /var/www/applications/coolapp/coolapp.apache.conf
Usage
For the command line, any time I run a Symfony command I source my environment (this example is for bash shell)
$ . ./coolapp.sh
$ app/console cache:clear
In the Apache config for the site I add an include such as
/etc/httpd/conf.d/coolapp.local.conf
<VirtualHost coolapp.local:443>
...
Include /var/www/applications/coolapp/coolapp.apache.conf
...
</VirtualHost>
What is in these files?
coolapp.sh would contain something like
export SYMFONY__APP_USER = foo
export SYMFONY__APP_PASS = foobar01
and coolapp.conf would have
SetEnv SYMFONY__APP_USER fop
SetEnv SYMFONY__APP_PASS foobar01
So each one of my environments (production, qa, stage, test, dev, localdev) would have these two files. And they can be changed whenever without going into the code.
Within Symfony you would access these variable like
%app_user%
%app_pass%

Artisan unable to use $_SERVER variables from database config file

I am currently deploying a Laravel project on my shared hosting account. It is an open project and hosted on GitHub as a public repository. As a result I'm using dynamic variables set by an .htaccess file in my database.php configuration file for my production environment. This allows me to also update my deployment using a git pull command on my host which helps speed up work.
The database.php file has something similar to
$database = $_SERVER['DBNAME'];
$database_user = $_SERVER['DBUSER'];
This is much like what is done when deploying to PagodaBox & works perfectly fine for the application with all things functioning as expected in the browser, no complaints.
The problem I have is that artisan is unable to use these variables and will attempt instead to connect to the database using what I believe to empty variables when processing a migrate instruction. I get an error that artisan tried to connect to the databases with no password. I have been calling artisan using --env=production and have tested this but found that it will only work if the database.php file has the variables specified explicitly instead of as environment variables.
Is there a way of causing artisan to "see" these environment variables?
answers that have proved useful to me so far:
http://forums.laravel.io/viewtopic.php?pid=8455
and
Environment driven database settings in Laravel?
Because Artisan is a CLI PHP request - the request never hits the .htaccess file - and therefore your variables are never set.
As a workaround - you could define the variables inside the artisan file itself on line 3 (just after the <?php)
$_SERVER['DBNAME'] = 'test';
$_SERVER['DBUSER'] = 'something';
edit: I just noticed you said this is public hosted on github - so you wont want to include your username/password in the file? Maybe put the artisan file as part of the .gitignore group - so you dont push/pull that single file?
The capability to set up environment variables is built in to Laravel, so there's no reason to do it in .htaccess. Laravel's built-in way works with artisan without any trouble.
See this part of the docs about environment variables you would like to protect.
http://laravel.com/docs/configuration#protecting-sensitive-configuration
Quoting:
... create a .env.local.php file within the root of your project [...] The .env.local.php should return an array of key-value pairs, much like a typical Laravel configuration file:
<?php
return array(
'TEST_STRIPE_KEY' => 'super-secret-sauce',
);
All of the key-value pairs returned by this file will automatically be available via the $_ENV and $_SERVER PHP "superglobals". You may now reference these globals from within your configuration files:
'key' => $_ENV['TEST_STRIPE_KEY']
Be sure to add the .env.local.php file to your .gitignore file. This will allow other developers on your team to create their own local environment configuration, as well as hide your sensitive configuration items from source control.
Add your private environment variables
<?php
return array(
'MY_SECRET_KEY' => 'super-secret-sauce',
);

Categories