cannot get environment variables set in Flask application - apache

I tried to set up sensitive information as environment variables in CentOS, and pass them to Flask config file used in main file, i.e. init.py . But it did not work. The Flask application is running under Apache.
I first edit /etc/environment as root user
MAIL_USERNAME="abcde#abc.com"
then logout, login again
Then verify MAIL_USERNAME is set by running
echo $MAIL_USERNAME
This works fine
And in configuration.py, this is how I set MAIL_USERNAME.
MAIL_USERNAME = os.environ.get('MAIL_USERNAME')
for testing purpose,
I print out MAIL_USERNAME
in __init__.py
print(MAIL_USERNAME)
Then from terminal, if I run
python3.4 __init__.py
it print out correct values of MAIL_USERNAME
However, if I tested on web browser, MAIL_USERNAME is just not set. it shows NONE. I verify this by looking Apache log.
Any idea of how this works would be really appreciated.
Thanks

With your CLI, set the environment variable as you want. On Linux and macOS, this is done with export KEY=value.
After that, the environment variable KEY will be available for your Python script or Flask app via os.environ.get('KEY'), like this:
import os
print os.environ.get('key')
>>> value

I had a very similar problem because I used PyCharm terminal to run flask. A similar issue was described and solved here.
My solution was switching to regular cmd (I worked on Windows 10) and just running everything there:
>> set MAIL_USERNAME='bla#example.com'
... (other env variables sets)
>> py manage.py runserver (I run my flask app through a manage script)
I could successfully send an email using my flask app - all the environment variables used in the app were read correctly.
On Linux you can just use export instead of set.
I hope it helps.

Maybe you can use Apache directive PassEnv as mentioned here on Apache's official web documenting how to use environment variables.
There are two kinds of environment variables that affect the Apache HTTP Server.
First, there are the environment variables controlled by the underlying
operating system. These are set before the server starts. They can be used in
expansions in configuration files, and can optionally be passed to CGI scripts
and SSI using the PassEnv directive.
Second, the Apache HTTP Server provides a mechanism for storing information
in named variables that are also called environment variables. This information
can be used to control various operations such as logging or access control.
The variables are also used as a mechanism to communicate with external programs
such as CGI scripts. This document discusses different ways to manipulate and
use these variables.
Although these variables are referred to as environment variables, they are
not the same as the environment variables controlled by the underlying
operating system. Instead, these variables are stored and manipulated in an
internal Apache structure. They only become actual operating system environment
variables when they are provided to CGI scripts and Server Side Include scripts.
If you wish to manipulate the operating system environment under which the server
itself runs, you must use the standard environment manipulation mechanisms
provided by your operating system shell.
I make some of the text cited above bold to make things clearer and maybe easier to explain.
Hope this helps.

Related

SetEnv not updating my environment variable (noob warning)

I am still an Apache noob, and I am trying to set an environment variable that will be used by my Rails application.
I've read https://httpd.apache.org/docs/2.4/mod/mod_env.html#setenv and done some google and SO searches. I have at least determined that the value to be assigned must be in quotes. However, when I run sudo service apache2 restart, the value of SECRET_KEY_BASE is still not correct (viewed via printenv). I don't know what I don't know. Is there some step i'm missing?
In my apache configuration I have:
SetEnv SECRET_KEY_BASE "e10e721..."
# Tell Apache and Passenger where your app's 'public' directory is
DocumentRoot /var/www/some_path
Please let me know what other information I might need to share. Thanks for looking.
There are a few subtle pitfalls here.
First: You can't check it in the terminal you ran that command from. "SetEnv" sets a per-request internal variable that will be copied to CGI-like processes that the server subsequently executes.
Secondly, even if you set a real native environment variable (in e.g. /etc/apache2/envvars) you should not do a restart operation since that will not necessarily reload that particular file. You should do a stop and a start. You still won't see the variable in the command you start the server from, since it was only in the webserver process.
If you want to see the environment of a running process, you can write a basic CGI to dump the environment that was passed down to the CGI script. If you're a PHP user, a basic script with phpinfo() will dump it.
Or, you can determine Apache's process ID with ps and then check /proc/$thepid/env (on Linux).

Is it possible to SetEnv (set environment variable) through cgi?

I am wondering if you call setenv() on a shared web host, in general, is this allowed? Or would such a feature be disabled on most hosts as you are setting a global environment variable. I.e. if you have a cgi program, on most web hosts would setenv actually work?
Wondering if it is a security issue and they have it disabled..
Reason I ask is because for some programs, setting up PATH variables as a setenv call would be very useful, but if this feature is not portable to all servers and some servers disable this then it would not be very portable code if you ever change servers.
And I do not mean a cgi variable or post variable, I mean an actual operating system setenv call that really sets a real environment variable on unix, not just an apache server variable or http variable or such.
Yes, it's allowed but note that it will only have an effect on processes that your CGI script launches.
This is true in general and not specific to CGI. setenv() only affects the current program's environment and (in general) the environment of any child processes. Setting of "systemwide" environment variables is done in various startup scripts, etc.

propagate operating system variable in apache server

I'm trying to read the operating system variable HOMEPATH from apache but the getenv() doesn't work in the browser but works in command line.
I have read several articles and they say it's a permission issue. But is there a way to propagate the operating system variable to be an apache env variable when apache starts?
I'm assuming you are on linux, and if you aren't I'm just posting this here for record (my search was fruitless, I'm on CentOS 6.5)
From what I understand, there's no way to provide apache any direct access to the environment variables including variables from the environment of the user that started the apache process and global environment variables you've specified within /etc/profile.d startup scripts.
Since I'm using bash, I have a variables.bashrc file that I source from my ~/.bashrc. This variables.bashrc declares my user environment variables. Within my apache startup script (/etc/init.d/httpd) I have added a line
. /path/to/variables.bashrc
that sources the same variables as my user has access to. This makes these environment variables available to apache.
Apache may receive the variables but it can also block the variables unless you explicitly say to pass them on to your scripts. This uses mod_env so you must ensure this module is loaded So you'll have to add
PassEnv VARIABLE_NAME
to the directory/virtual host you are configuring. For example...
<Directory "/path/to/cgi/scripts/">
AllowOverride None
Options None
Order allow,deny
Allow from all
PassEnv VARIABLE_NAME
</Directory>
This will make it available to your CGI scripts, or whatever script may be trying to access environment variables.
I'm not sure if this is the most elegant way to solve this, so I would be interested to see what other people have done to solve this issue... perhaps some automated way (mod_something) to make these variables visible.
Thanks.

Allowing a PHP script to ssh, using sudo

I need to allow a PHP script on my local web server, to SSH to another machine to perform a specified task on some files. My httpd runs as _www with low permissions, so setting up direct passwordless SSH is difficult, not to say ill-advised.
The way I do it now is to have a minimal PHP script that sudo-exec's (as me) a shell script which is outside of the document root. The shell script in turn calls (as me) the PHP code that does the actual SSH work, and prints its output. Here's the code.
read_remote_files.php (The script I call from my browser):
exec('sudo -u me -n /home/me/run_php.sh /path/to/my_prog.php', $results);
print $results;
/home/me/run_php.sh (Runs as me, calls whatever it's given):
php $1 2>&1
sudoers:
_www ALL = (me) NOPASSWD: /home/me/run_php.sh
This all works, as my_prog.php is called as me and can SSH as me. It seems it's not too insecure since run_php.sh can't be called directly from a browser (outside document root). The issue I'm having is that my_prog.php isn't called as an HTTP program so doesn't have access to the HTTP environment variables (DOCUMENT_ROOT etc).
Two questions:
Am I making this too complicated?
Is there an easy way for my final script to get the HTTP variables?
Thanks!
Andy
Many systems do stuff like this using a (privileged) cron job that frequently checks for the existence of a file, a database record or some other resource, and then performs actions if there are any.
The huge advantage of this is that there is no direct interaction between the PHP script and the privileged script at all. The PHP script leaves the instructions in a resource, the privileged script fetches it. As long as the instructions can't lead to the system getting compromised or damaged, it's definitely more secure than sudoing.
The disadvantage is that you can't push changes whenever you like; you have to wait until the cron job runs again. But maybe it's an option anyway?
"I need to allow a PHP script on my local web server, to SSH to another machine to perform a specified task on some files."
I think that you are phrasing this in terms of a solution that you have difficulty in getting to work rather than a requirement. Surely what you should be saying is "I want to invoke a task on machine B from a PHP script running under Apache on Machine A." And then research solutions to this -- to which there are many from a simple 'roll-your-own' RPC tunnelled over HTTP(S) to using an XMLRPC or SOA framework.
Two caveats:
Do a phpinfo(); on both machines to check what extensions are available and
Also check your php.ini setting to make sure that your service provider hasn't disabled any functions that you expect to use (or do a Q&D script to echo 'disable_functions = ' . ini_get('disable_functions') . "\n"; ...)
If you browse here and the wider internet you'll find many examples. Here is one that I use for a similar purpose.

Prevent file differences between live and staging websites AND system scripts (PHP)

I'm trying to find a good way to maintain PHP configuration differences between a dev and live environment. I know how to handle differences when scripts are run by Apache (see below), but not when they are run on the command line, e.g. via cron.
Background info:
What do I mean by "configuration differences"? Like most code shops, we run a non-public 'staging' version of our website where we test code before it goes to the live website. We use Subversion and have the live website as 'Trunk' and 'Staging' as a branch. It makes life easier when code goes from staging to live if the repository version of the files have minimal differences. But obviously, some details need to be different, e.g. the DB connection details.
How configuration differences are solved with Apache
In PHP, we set branch specific variables as follows:
switch ($_SERVER['HTTP_HOST']) {
case 'ourstagingurl.com':
$dbPassword = "blahblah";
break;
default:
$dbPassword = "blahblah";
}
or we put the following in the .htaccess file relevant to the specific site:
php_value dbPassword "blahblah"
Why I can't resolve configuration differences using the CLI?
When a script is run on the CLI, there's no super globals such as $_SERVER. I could include a config file using a absolute path but how can I know whether the script is from live or staging? I could pass in a command line argument that specifies the environment but I was hoping there was a better way?
Hey, I work for a company with a similar setup. What I generally do is set up a scenerio where the staging and live servers both modify a common and generic config file. When the site is deployed on both the staging and live the information will be incorrect and will need to be modified once in each location. However, this will ONLY need to happen once because the parts you modify will be noted as modified by subversion and will not be overwritten by subversion in subsequent svn updates.
$host = isset( $_SERVER['HTTP_HOST'] ) ? $SERVER_['HTTP_HOST'] : php_uname('n');
switch ( $host )
{
case 'ourstagingurl.com':
$dbPassword = "blahblah";
break;
default:
$dbPassword = "blahblah";
}
If php version is => 5.3 then gethostname() could be used instead.
If live and staging are the same server (why don' t you use virtualisation?) then you'll probably have to resort to passing arguments to your scripts ie. call php script.php staging for staging and php script.php live for live site. BTW In case you wouldn' t know, the arguments are accessible using $argv in your PHP script -> http://www.php.net/manual/en/reserved.variables.argv.php.
Though maybe you could get part of the path of the file and use that as your "environment". ie. when file layout is /project/staging/script.php for staging and /project/live/script.php for live it should be easy to get the environment from the script itself using dirname(__FILE __) - extra space after FILE needs to be removed - and stripping the /project folder from it. Pretty stupid but it will work :p.
You can do as Jurassic suggested, or you can set an environment variable and read it in through $_ENV.
In some of my applications, I attempt to include a file that only exists to override values on the development server. I use the dreaded # operator, but you could do an is_file() as well.
#include 'includes/debug.php';
debug.php would attempt to set some variables:
define('DEBUG', true);
define('DB_PASSWORD', 'foobar');
I tell svn to ignore this file. It sticks around in my development checkout, and has a minimal impact on production.
More generally I use hostname checking as it works in both CLI and web apps, but you've stated you have both branches on the same box, so that's out.