PASOE - Setting different environment vars per app

Posted by ssouthwe on 27-Sep-2019 16:12

I understand that with PASOE, one way to set environment variables is to throw a *_setenv.bat or *_setenv.sh script into the instance's bin directory.

I would have thought that if I had two apps installed, lets say foo and bar, then I would use foo_setenv.bat and bar_setenv.bat, and each agent would pick up its own environment to run.

In my testing, every app on the instance picks up both scripts and ends up with the same environment variables, which breaks Webspeed.  Webspeed needs each app to have its own set.

Is there some other way that I'm missing?

In my *_setenv.bat program, I'm redirecting "set" to a file, and it doesn't appear that PASOE sets any environment variable that I could check to see which app is starting up.  If it had, for example ABLAPP=foo or WEBAPP=bar, then I could make my batch file set other required environment variables like WEB_RUN_PATH conditionally.

Posted by Michael Jacobs on 29-Sep-2019 10:08

Hello Steve,

The *_setenv.{sh|bat} are extensions to the Tomcat web server startup, and are used to setup a common process environment for the server and all of the ABL & Java web apps it starts.   The ABL application code (i.e. the MS-Agent processes) just inherit the Tomcat server's environment, which is why you see them in each ABL application.  You would see the same behavior if you deployed multiple Java web apps in the same Tomcat web server - each Java web app sees the same environment.

To get a different run-time environment per ABL application it would require adding environment variable settings when each ABL application starts and passing those additions down to the MS-Agent processes as they are dynamically started.   That is not currently part of an ABL application startup today.

FWIW: My personal workaround was to stop using immutable environment variables and load application properties from the application's conf/ directory that I can change dynamically at run-time.  I didn't use a lot of environment variables to begin with, so it was easy for me to find and replace them with property references.   That may not be your case.  

Don't think I solved your issue, but perhaps understanding what PASOE is suppose to be doing eliminates -some- questions.

All Replies

Posted by Michael Jacobs on 29-Sep-2019 10:08

Hello Steve,

The *_setenv.{sh|bat} are extensions to the Tomcat web server startup, and are used to setup a common process environment for the server and all of the ABL & Java web apps it starts.   The ABL application code (i.e. the MS-Agent processes) just inherit the Tomcat server's environment, which is why you see them in each ABL application.  You would see the same behavior if you deployed multiple Java web apps in the same Tomcat web server - each Java web app sees the same environment.

To get a different run-time environment per ABL application it would require adding environment variable settings when each ABL application starts and passing those additions down to the MS-Agent processes as they are dynamically started.   That is not currently part of an ABL application startup today.

FWIW: My personal workaround was to stop using immutable environment variables and load application properties from the application's conf/ directory that I can change dynamically at run-time.  I didn't use a lot of environment variables to begin with, so it was easy for me to find and replace them with property references.   That may not be your case.  

Don't think I solved your issue, but perhaps understanding what PASOE is suppose to be doing eliminates -some- questions.

Posted by ssouthwe on 30-Sep-2019 14:32

Thanks Michael.  I was afraid that would be the case.

While I wouldn't call that a bug, I'd call it a shortcoming.  Since different ABL apps on the same instance can have different propaths, it's vital that they can have different webrunpaths.  Running the compatibility handler without using that is a security risk. Having different apps share the same one is a security risk for both.

My temporary workaround is going to be creating a session startup procedure to poke the webrunpath variable into the global shared temp-table ttAgentSetting, which is being setup in web-util.p.

There are several other WebSpeed-related environment variables that web-util reads in, most of them having come in from FreeFramework a long time ago.  I'm not sure how many of them still apply in a PASOE world, but it seems like either web-util.p should change, or Progress should add some method of specifying environment variables per app.  

I can't say how common it is for WebSpeed apps to use environment variables outside of what was used for the FFW enhancements, but it probably does happen.

I'll post sample code later, in case someone else runs into this problem.

Posted by Håvard Danielsen on 30-Sep-2019 15:44

There is an issue ADAS-4708 in the feature backlog that addresses the need to be able to set environment variables for individual ABL applications in a single PAS instance.
 
The issue was originally reported by a customer. Support actually provided a workaround in the form of an executable for both windows and unix, but it was targeted at a setting time zone only and was not general. The support case number was  00444439.
 

Posted by Stefan Drissen on 30-Sep-2019 19:32

Two other cases where we need environment variables:

- DSLOGDIR - to shoehorn dataserv.lg into the correct directory

- NLS_LANG for oracle client when using DatasSrver

Although both these cases can be solved by setting them in *_setenv - it is a step backwards from simply setting them on a properties tab.

Posted by ssouthwe on 09-Oct-2019 16:37

I wanted to come back and share my solution/workaround to this, just in case someone else runs into it.

Setting up WebRunPath (WEB_RUN_PATH) for WebSpeed in PASOE

Background

The WebRunPath, which in Classic WebSpeed was configured by setting an environment variable for the broker called WEB_RUN_PATH, is needed for security purposes.  Similar to a propath, this setting tells WebSpeed what directories and/or file patterns are able to be run by request from the web.

If WebRunPath is not set, then a remote user could pass in any filename as part of a WebSpeed request URL, and as long as that filename resolves as something in the application’s propath, WebSpeed will run it.

All WebSpeed brokers should have WebRunPath setup properly as an extra precaution to ensure that access is limited to approved webobjects that are specifically intended to be run within your application.  Failure to set up WebRunPath as part of the security of a WebSpeed application constitutes a security risk.

Problem description

With classic WebSpeed, each broker could have its own Propath and its own WebRunPath, which was set as an environment variable specific to that broker.  With PASOE, environment variables are set at the instance level, and thus cannot be set specifically for each webapp.

Trying to set webrunpath at the instance level by placing a *_setenv.bat file inside the instance’s bin directory will set the same webrunpath for both apps, potentially exposing one app’s webobjects under the security model of the other webapp.

Ideal solution

Since many web applications used the WebSpeed functionality of setting environment variables per broker, Progress should change the behavior of an instance’s multi-session agent launch, so that it reads in a set of environment variables specific to the application.

For versions up to 12.1 (as of 10/9/2019) this is not a feature of PASOE.

Workaround

Internally, the PASOE version of WebSpeed stores what it reads in from WEB_RUN_PATH into a shared temp-table called ttAgentSetting.

We can use the ability to specify sessionStartupProc and sessionStartupProcParam values for the application within openedge.properties to make PASOE put values in that temp-table and use the correct WebRunPath for the given application.

  1. Copy the attached file agentsettings.p into your PASOE project’s WEB-INF/openedge directory. (It can actually go anywhere in the propath.)

  2. Edit agentsettings.p to set your WebRunPath as needed for your project.

    1. WebRunPath is a comma-separated list of patterns that equate to paths and filenames, using the ABL’s matches syntax.

    2. For example, if you wanted to limit things running from the web so that they could only exist within the openedge/websrc directory within your project, you can list the fully qualified path to that directory, followed by the appropriate slash, followed by a wildcard.  

My version for a development instance looks like this:

The last two entries allow for the use of the classic WebSpeed Workshop / Webtools, which are still handy for maintaining classic WebSpeed code.  THESE ENTRIES FOR WEBSPEED WORKSHOP SHOULD NOT BE INCLUDED IN A PRODUCTION DEPLOYMENT.

  1. If you would like to have a single version of agentsettings.p with different settings for development versus production environments, you could have the code use os-getenv(“COMPUTERNAME”) to conditionally set this based on which host is running it.

4. If your application already included a sessionStartupProc, be sure to have agentsettings.p run it, or directly include that procedure’s functionality.

5. Edit openedge.properties to specify your sessionStartupProc and sessionStartupProcParam values

    1. Set sessionStartupProc to agentsettings.p

    2. Set sessionStartupProcParam to contain the ABLApp name, followed by a comma, followed by the WebApp name.  

For example, if your ABLApp is “IngridABL” and your WebApp is “Ingrid” then your openedge.properties file section for the ABLApp would look like this:

    1. If you are using tlr/Merge.template to have openedge.properties tailored upon deployment of your app, make the changes to the appropriate section of that file.  Use the oeprop command’s -f option to apply it to your instance.

  1. Restart your application to allow it to load the startup procedure.

  2. You can test whether it was loaded correctly by running the following script within the WebTools scripting lab for your application:

define new global shared temp-table ttAgentSetting

  field cKey       as character

  field cSub       as character

  field cName      as character 

  field cVal       as character

  index SettingIdx is primary cKey cSub

  index NameIdx cName.

for each ttAgentSetting:

{&out} ttAgentSetting.cName ": " ttAgentSetting.cval "<br>" skip.

end.

My result looks like this:

You can also test this by placing a webobject (or any .p without parameters) somewhere in your app’s propath.  Try to run the webobject by placing it in a URL for your application.

Here’s an example of a test webobject you can try placing in your propath to test:

When you attempt to run it, you should see a message like this in your browser:


agentsettings.p: (with apologies for the poor formatting here)


/*------------------------------------------------------------------------
File : agentsettings.p
Purpose : Load up some configurations that used to be handled by
environment variables.
Since PAS only allows one set of environment variables
per instance,
Syntax :

Description :

Author(s) : S.E. Southwell - Progress
Created : Fri Sep 27 11:55:19 PDT 2019
Notes :
----------------------------------------------------------------------*/

/* *************************** Definitions ************************** */

block-level on error undo, throw.
define input parameter startupData as character no-undo.
define variable hHdl as handle no-undo.

/* Make this available in case anything else in the app needs to know what app this is */
define new global shared variable myABLApp as character no-undo.
define new global shared variable myWebApp as character no-undo.
define variable dlcpath as character no-undo.
define variable catalinabase as character no-undo.
define variable webrunpath as character no-undo.
define variable apppath as character no-undo.

/* Same as defined in web-util.p. Don't change this */
define new global shared temp-table ttAgentSetting
field cKey as character
field cSub as character
field cName as character
field cVal as character
index SettingIdx is primary cKey cSub
index NameIdx cName.

function setAgentSetting returns logical
(cInKey as character,
cInSub as character,
cInName as character,
cInVal as character) forward.

/* ******************** Preprocessor Definitions ******************** */


/* *************************** Main Block *************************** */
myABLApp = entry(1,startupData).
if num-entries(startupData) > 1 then myWebApp = entry(2,startupData).
assign
dlcpath = os-getenv("DLC")
catalinabase = os-getenv("CATALINA_BASE")
apppath = catalinabase + "\webapps\" + myWebApp.

/* Formerly the WEB_RUN_PATH environment variable */
setAgentSetting("path","","webrunpath",
substitute("&1\WEB-INF\openedge\websrc\*,&2\tty\webtools\*.r,&2\tty\workshop.r",
apppath,dlcpath )).

/* */
/*hHDL = this-procedure. */
/*session:add-super-procedure(hHdl).*/

function setAgentSetting returns logical
(cInKey as character,
cInSub as character,
cInName as character,
cInVal as character):
/*------------------------------------------------------------------------------
Purpose: Used to set the value of a Name/Value key to the user-specified
agent setting temp-table.
Inputs: cInKey: key name that the name/value is specified under
cInSub: sub-key, provides for sub-type orginization. Not required
cInName: name of 'variable' that is being requested
cInVal: value that the 'variable' is to be set to
Returns: Logical ERROR-STATUS:ERROR
Notes: Stolen from web-util.p
------------------------------------------------------------------------------*/
define variable retVal as logical no-undo.

SettingBLOCK:
do on error undo SettingBlock, leave SettingBlock:
find ttAgentSetting where
ttAgentSetting.cKey eq cInKey and
ttAgentSetting.cSub eq cInSub and
ttAgentSetting.cName eq cInName exclusive-lock no-error.
if not available ttAgentSetting then do:
create ttAgentSetting.
assign
ttAgentSetting.cKey = cInKey
ttAgentSetting.cSub = cInSub
ttAgentSetting.cName = cInName NO-ERROR.
end. /* name/value not available */

assign ttAgentSetting.cVal = cInVal.
release ttAgentSetting.
assign retVal = error-status:error.
end. /* SettingBlock */

return retVal.

end function. /* setValue */

Posted by Ken McIntosh on 09-Oct-2019 19:29

Hi Steve (and btw "Hi Steve! How are you"),

Correct me if I'm wrong, but isn't this just a much more complicated way of accomplishing what Spring Authorization does?  

See webapps/<webapp>/WEB-INF/oeablSecurity.csv

This allows you to describe which path/file masks can be called by a request, right up front at the WebServer level, before it gets to WebSpeed.

Cheers,

Ken Mc

Posted by Ken McIntosh on 09-Oct-2019 19:29

Hi Steve (and btw "Hi Steve! How are you"),

Correct me if I'm wrong, but isn't this just a much more complicated way of accomplishing what Spring Authorization does?  

See webapps/<webapp>/WEB-INF/oeablSecurity.csv

This allows you to describe which path/file masks can be called by a request, right up front at the WebServer level, before it gets to WebSpeed.

Cheers,

Ken Mc

Posted by ssouthwe on 09-Oct-2019 20:43

Hi Ken, I'm doing fine.

It is similar to what you can do with oeablSecurity.csv, but not exactly the same.

In oeablSecurity.csv, you are locking down certain URL patterns, whereas with WebRunPath, you're locking down the actual disk folders where the WebSpeed code can pick up a program from and run it.

For example, lets say your propath for PASOE includes these folders:

- (your app)/WEB-INF/openedge    (Typical PASOE starting path)

- (your app)WEB-INF/openedge/websrc (Intended to hold all WebObjects that can be run from the web, using web compatibility handler)

- C:\mydangeroustools\really (Contains a bunch of programs that may be called from within your app, and which are not intended to be run from the web.)

And hypothetically in that latter directory, you have a long-running report or some process or procedure that could cause grave issues if run when it shouldn't be.  Maybe as part of your deployment, you use that folder to contain one-time data conversions or something like that.  Maybe you have something there that you use to mask data when you copy your database from prod to dev so you can troubleshoot something.  In any case, just assume that this is stuff you don't want end users running.

So you have C:\mydangeroustools\really\badprogram.p.

Someone navigates to yourserver:port/.../web/badprogram.p in their browser.  

Now, if you have a line in your oeablSecurity.csv like this:

"/web/**","*","hasAnyRole('ROLE_PSCUser')"

then the user at least has to be logged in.  But if they are, they've just run your dangerous program.  As long as it doesn't have parameters, WebSpeed will pick it up and run it like a webobject.

That's where webrunpath comes in.  You would set your webrunpath to the WEB-INF/openedge/websrc/* and from then on, WebSpeed will only run things that it finds there.

An attacker would have to know things to try, but given all the various folders within the stock propaths, and how nasty it can get with huge apps, this gives you the peace of mind that you don't have to audit every single piece of code to ensure that it can't happen.

Posted by Peter Judge on 09-Oct-2019 20:50

If you want to avoid passing in the ABL-Application name ("IngridABL" here) you can query the session's startup parameters in ABL, and look for the value of the -logname parameter (read through the comma-delimited values in session:startup-parameters ).

I don't believe it's possible to get the webapp name (context) in the startup or activate procedures.

It is available in the 'run procedure' via an IWebRequest's WebAppPath property.

-- peter
 
 
 
 

Posted by ssouthwe on 09-Oct-2019 21:01

Good point there.  I have used the logname trick before in other code, but that slipped my mind here.  

Neither of those are absolutely necessary.  I just wanted a clean way to put them in the path.

Posted by ssouthwe on 09-Oct-2019 21:01

Good point there.  I have used the logname trick before in other code, but that slipped my mind here.  

Neither of those are absolutely necessary.  I just wanted a clean way to put them in the path.

This thread is closed