Google App Engine inter module communication authorization - python

In the Google Docs it says
You can configure any manual or basic scaling module to accept requests from other modules in your app by restricting its handler to only allow administrator accounts, specifying login: admin for the appropriate handler in the module's configuration file. With this restriction in place, any URLFetch from any other module in the app will be automatically authenticated by App Engine, and any request that is not from the application will be rejected.
so i did that, but unfortunately it does not work. I am requesting a url from module A on module B which is protected by the login: admin property
I can fetch that url in the browser which shows me the login page and after i continue as admin i can fetch my route.
How is it supposed to work? As far as i understand it should add a header to the request which includes some kind of authorization token.
If i fetch that same url within a request on module A i get the same redirect. urllib2 follows the 302 status code by default and the result is the login page.
I am running the environment using the gcloud preview app run command. Module A is a default module and module B is a Managed VM Container, might this be the problem here?

I can confirm this is occurring, and I've reproduced the issue. The issue is being tracked over in the App Engine public issue tracker. Follow there for any updates.
For now, I think it's much better to be manually-inspecting the X-Appengine-Inbound-Appid header, as this is managed by the infrastructure and can't be spoofed.
You could also implement OAuth, but that adds overhead you may not want or need on a small app.

Related

How can I authenticate as a user to Azure App Service from Python script?

I have a Python script that should download data from a web resource using link. It so happened that resource is in Azure App Service protected by Active Directory. My user account is allowed to access the link and download data (I can do it from web browser manually, but want to automate this process). The Python script uses requests library. I can't figure out how to authenticate properly, cause when I'm trying to run the script, I get:
Error 403 - Forbidden
The web app you have attempted to reach has blocked your access.
Usual authentication with requests doesn't work (using auth parameter or session.auth or with HttpNtlmAuth).
I know one can use VS Code to authenticate to Azure and then use DefaultAzureCredential, but I can't get where you should use this DefaultAzureCredential object (cause it doesn't work with requests).
I don't need the whole Python app to be registered or somehow else recognizable by Azure resource. It's just a script to download data, that is not supposed to be productionized.
Any ideas how I can scrap the data from Azure?
Note: I'm not an admin or creator of this Azure App, so can't change any restriction settings.
In short, the part of script making request looks like:
params = {"param1": param1,
"param2": param2}
session = requests.Session()
session.auth = HttpNtlmAuth(USERNAME, PASSWORD)
url = "my-app.azurewebsites.net/the-rest-of-the-path"
response = session.get(url, params=params, verify=False)
If you want to access the Azure App Service, you have to authenticate the Azure App Service. If you don't have access for Azure App Service, we cannot access the Azure resources.
Genereally, when a web server stops you from accessing the page you're trying to open in your browser, you'll get a 403 Forbidden Error. There isn't much you can do most of the time. However, occasionally the issue is on your end.
Here are some points that can cause this error.
If you have an open public API and public access is not allowed on Azure App Service.
Your app's IP address, which you're using to call the app service, isn't whitelisted.
If you have a gateway in the middle, it's possible that it's also blocking your calls.
Here are the possible solutions that you can try:
Remove the access restrictions from your web app's Networking page.
Try adding 0.0.0.0/0 to give access to all. You can later add restrictions based on your needs.
The order of the restrictions is important, so double-check it. It may have an impact if you have a blocked call before an approved call.
You can also have restrictions based on http-headers like X-Forwarded-For. Please double-check that. This can also happen in code, depending on how you handle errors.
Protocol support for HTTP headers in Azure Front Door | Microsoft Docs
Chech this, if your API is behind the Gateway Application Gateway integration with service endpoints

How to secure my Azure WebApp with the built-in authentication mechanism

I created a Flask-Webservice with Python that runs independently inside a docker container. I then uploaded the docker image to an Azure Container Registry. From there I can create a WebService (for Containers) with some few clicks in the Azure Portal, that runs this container. So far so good. It behaves just as I want it to.
But of course I don't want anyone to access the service. So I need some kind if authentication. Luckily (or so I thought) there is a built-in authentication-mechanism (I think it is based on OAuth ... I am not that well versed in security issues). Its documentation is a bit sparse on what actually happens and also concentrates on solutions in C#.
I first created a project with Google as described here and then configured the WebApp-Authentication with the Client-Id and Secret. I of course gave Google a java script source and callback-url, too.
When I now log off my Google account and try a GET-Request to my Webservice in the Browser (the GET should just return a "hello world"-String), I am greeted with a Login Screen ... just as I expected.
When I now login to Google again, I am redirected to the callback-url in the browser with some kind of information in the parameters.
a token perhaps? It looks something like this:
https://myapp.azurewebsites.net/.auth/login/google/callback?state=redirxxx&code=xxx&authuser=xxx&session_state=xxx&prompt=xxx).
Here something goes wrong, because an error appears.
An error occurred.
Sorry, the page you are looking for is currently unavailable.
Please try again later.
If you are the system administrator of this resource then you should check the error log for details.
Faithfully yours, nginx.
As far as I now, nginx is a server software that hosts my code. I can imagine that it also should handle the authentication process. It obviously lets all requests through to my code when authentication is turned off, but blocks un-authenticated accesses otherwise and redirects to the google login. Google then checks if your account is authorized for the application and redirects you to the callback with the access token along with it. This then returns a cookie which should grant my browser access to the app. (I am just reproducing the documentation here).
So my question is: What goes wrong. Does my Browser not accept the cookie. Did I something wrong when configuring Google+ or the Authentication in the WebApp. Do I have to use a certain development stack to use the authentication. Is it not supported for any of the technologies I use (Python, Flask...).
EDIT
#miknik:
In Microsofts documentation of the authentication/authorization it says
The authentication and authorization module runs in the same sandbox
as your application code. When it's enabled, every incoming HTTP
request passes through it before being handled by your application
code.
...
The module runs separately from your application code and is
configured using app settings. No SDKs, specific languages, or changes
to your application code are required.
So while you are probably right that the information in the callback-redirect is the authorization grant/code and that after that this code should now be used to get an access token from Google, I don't quite understand how this would work in my situation.
As far as I can see it Microsofts WebApp for Container-Resource on Azure should take care of getting the token automatically and return it as part of the response to the callback-request. The documentation states 4 steps:
Sign user in: Redirects client to /.auth/login/.
Post-authentication: Provider redirects client to /.auth/login//callback.
Establish authenticated session: App Service adds authenticated cookie to response.
Serve authenticated content: Client includes authentication cookie in subsequent requests (automatically handled by browser).
It seems to me that step 2 fails and that that would be exactly what you wrote: that the authorization grant is to be used by the server to get the access token but isn't.
But I also don't have any control over that. Perhaps someone could clear things up by correcting me on some other things:
First I can't quite figure out which parts of my problem represent which role in the OAuth-scheme.
I think I am the Owner, and by adding users to the list in the Google+-Project I authorize them to use my service.
Google is obviously the authorization server
my WebService (or better yet my WebApp for Containers) is the resource server
and finally an application or postman that does the requests is the Client
In the descriptions of OAuth I read the problematic step boils down to: the resource server gets the access token from the authorization server and passes it along to the client. And Azures WebApps Resource is prompted (and enabled) to do so by being called with the callback-url. Am I right somewhere in this?
Alas, I agree that I don't quite understand the whole protocol. But I find most descriptions on the net less than helpful because they are not specific to Azure. If anyone knows a good explanation, general or Azure-specific, please make a comment.
I found a way to make it work and I try to explain what went wrong as good as I can. Please correct me if I go wrong or use the wrong words.
As I suspected the problem wasn't so much that I didn't understand OAuth (or at least how Azure manages it) but the inner workings of the Azure WebApp Service (plus some bad programming on my part). Azure runs an own Server and is not using the built-in server of flask. The actual problem was that my flask-program didn't implement a WSGI-Interface. As I could gather this is another standard for python scripts to interact with any server. So while rudimentary calls from the server (I think Azure uses nginx) were possible, more elaborate calls, like the redirect to the callback url went to dev/null.
I build a new app following this tutorial and then secured it by following the authentication/authorization-tutorial and everything worked fine. The code in the tutorial implements WSGI and is probably more conform to what Azure expects. My docker solution was too simple.
My conclusion: read up on this WSGI-standard that flask always warned me about and I didn't listen and implement it in any code that goes beyond fiddeling around in development.

Does my default app have to be deployed to appspot.com?

I asked a question about the default app as it related to microservices on app engine and got a great response here, but I have another related question.
Does my default app have to be accessible via appspot.com? When I run the deploy command that's where it puts it, but I'd rather have it not accessible like that. I really just want a semi-empty (like hello world sized) app that satisfies the default app requirement.
It does seem like Google is shoehorning multi-app/microservices into an environment that was originally setup to only serve a single web facing app backed by other modules. It seems very ungraceful and hacky.
You can customize your app to perform differently based on the URL that was used.
For example, you can use domain specific routes with webapp2 or you can check the domain in your handler by checking the value of self.request.url and responding accordingly.
You could for example, have myapp.appspot.com return a 404 but have www.mydomain.com provide content to users.
It depends what you mean by "accessible".
Yes, the app will have a presence on appspot.com, in the sense that requests can make it to some instance of some version of some service inside your app, based on the Routing via URL rules, the most generic ones being:
Sends a request to the named service, version, and instance:
https://instance-dot-version-dot-service-dot-app-id.appspot.com
http://instance.version.service.my-custom-domain.com
Also, from Default service:
The default service is defined by explicitly giving a service the name
"default," or by not including the name parameter in the service's
config file. Requests that specify no service or an invalid service
are routed to the default service. You can designate a default version
for a service, when appropriate, in the Google Cloud Platform Console
versions tab.
But what your app code responds to such requests is really up to you. Nothing stops, for example, your default service handler from simply returning a 404 or your "Hello world" page, for example, if you don't want it to do anything else. As if it wouldn't be there. Yet it still serves the role of the default service.

Authenticate into Google App Engine

I'm trying to access my GAE app from outside the browser.
At the moment it's Python script but I'm planning desktop C++ app.
I'm fallowing Using OAuth 2.0 for Installed Applications.
So far I managed to access user info:
https://www.googleapis.com/oauth2/v1/userinfo?alt=json
However every call to my GAE ends up with redirection to login page.
Is there a way to do authenticated calls to GAE from a script?
Please take a look at my test code
My goal:
Use Python script on my local machine to get data (json endpoint, static file, html, whatever) from my GAE app as authenticated user.
I believe this is sort of possible using ClientLogin (deprecated) https://developers.google.com/accounts/docs/AuthForInstalledApps.
However, I have found it much easier to just have an API secret string that I use (in a header, over HTTPS) to say that the request is from an approved script.
Alternatively you can do the oauth login flow (whichever flow you want, using your own oauth app), but you don't want to use any login: tags in app.yaml, just do it entirely in your Python code.

Heroku router logging format

I've recently deployed a Flask app on Heroku. It provides an API on top of an existing API and requires a confidential API key for the original service from the user. The app is really just a few forms, the values of which are passed with ajax to a specific URL on the server. Nothing fancy. I take steps to not store confidential information in the app and want no traces of it anywhere within the app.
Looking at the logs from heroku logs --source heroku, the heroku router process stores all HTTP requests for the app, including those requests that include the confidential information.
Is there a way to specify the log format for the heroku process so as to not store the URL served?
As other commenters mentioned, it is a bad practice to put confidential info in a URL. These could get cached or logged by a number of systems (e.g. routers, proxy servers, caches) on the roundtrip to the server. There are a couple ways to solve this:
Put them in a the Authorization header. This is probably the most common way authentication is handled for REST-based APIs.
Put them in the POST body. This works to get it out of the URL, but is a little weird semantically to say that your are POSTing the credentials to some resource (if this a REST API), unless it is a login call.

Categories

Resources