Google Cloud App Engine Standard - Custom Domain Name with Subdomains - google-app-engine

I am struggling with the problem now for a couple of days. I hope someone can point out where I made the mistake.
I have a domain mydomain.com. (not purchased with Google.)
I have one AppEngine Standard Service (default) with my website.
Then I have 30 additional AppEngine Standard Services for my different api's (service1-service30).
When I browse to mydomain.com or www.mydomain.com I should be redirected to the "default" service.
When I browse to e.g., service25.mydomain.com I wanted to be redirected to the corresponding service. In this example service "service25"
What I did:
AppEngine Settings
DomainRegistrar
Dispatch.yaml:
dispatch:
- url: "mydomain.com/*"
service: default
- url: "www.mydomain.com/*"
service: default
Calling mydomain.com and www.mydomain.com are working as expected with an valid SSL certificate.
Calling service25.mydomain.com redirects to the "default" service
Calling service26.mydomain.com returns ERR_CONNECTION_CLOSED
Calling service27.mydomain.com returns DNS_PROBE_FINISHED_NXDOMAIN
Regarding Mapping Custom Domains and How Requests are Routed at least one of my previous methods should work and the default mapping/routing to the corresponding service name should be done.
If I add
- url: "service25.mydomain.com/*"
service: service25
it works, but due to the limitation of max 20 routes in the Dispatch.yaml file this is not working for me.
What did I do wrong?
Thank you very much in advance.

Related

How to set Cloud Tasks dispatch when load balancer and GAE are combined

I'm using Cloud Tasks from GAE now.
Also, by setting GAE as the backend of the load balancer, the following processing is tested.
batch-service is a service I created.
Request to /job/test_cron from local machine
go to Load balancer
go to GAE's service(batch-servise) from Load balancer
Create Cloud Task and request /job/test_task from GAE
go to GAE's service(batch-servise)
process and complete
I made each setting assuming the above flow, but the request when creating a task in GAE does not go to batch-servise, but goes to default service.
Therefore, the actual processing is as follows.
Request to /job/test_cron from local machine
go to Load balancer
go to GAE's servise(batch-servise) from Load balancer
Create Cloud Task and request /job/test_task from GAE
go to GAE's servise(default servise)
process and complete
GAE uses dispatch.yaml to direct all requests like /job/~ to batch-servise.
Therefore, Requesting /job/test_cron directly to GAE works as expected.
When using a load balancer, I think that dispatch.yaml cannot be used because the IP of GAE is not used. Is this correct?
Also, if anyone else knows how to configure GAE dispatch, it would be very helpful if you could tell me.
To override default service you can define AppEngineRouting which defines routing characteristics specific to App Engine - service, version, and instance.
You can refer this sample which routes to the default service's /log_payload endpoint. And update to this:
const task = {
appEngineHttpRequest: {
httpMethod: 'POST',
relativeUri: '/log_payload',
appEngineRouting: {
service: 'batch-servise'
}
},
};
When using a load balancer, I think that dispatch.yaml cannot be used because the IP of GAE is not used. Is this correct?
The load balancer does not interfere or interact with routing rules in your dispatch.yaml file. The dispatch.yaml rules are not evaluated until a serverless NEG directs traffic to App Engine.
Configuring dispatch.yaml:
The root element in the dispatch.yaml file is dispatch: and contains a list of routing definitions that are specified by the following subelements.
Dispatch rules are order dependent, and only the first rule that matches a URL will be applied.
You may have a look at these Examples
For more information, see How Requests are Routed.

How to make a service discovery for Google App Engine? How to get service URL?

I want to get URL's of services for particular project, how can I do this?
I need URL's like .appspot.com
I tried App Engine Admin api, but it can only provide names of the services.
you can get url's of App Engine services's versions through API calls, in 3 steps:
1) authenticate and get an access-token to the App Engine Admin API:
gcloud auth application-default print-access-token
2) with the access token, list all services in App Engine, and get their version ID (in the nested field "allocations"), and service ID:
curl -H "Authorization: Bearer [MY_ACCESS_TOKEN]" https://appengine.googleapis.com/v1/apps/[MY_PROJECT_ID]/services
3) with the version ID and service ID, get the full data on the version:
curl -H "Authorization: Bearer [MY_ACCESS_TOKEN]" https://appengine.googleapis.com/v1/apps/[MY_PROJECT_ID]/services/[SERVICE_ID]/versions/[VERSION_ID]/?view=FULL
The field versionUrl delivers the app URL for this specific version, in the following form:
default service:
https://[VERSION_ID]-dot-[PROJECT_ID].appspot.com
other services:
https://[SERVICE_ID]-dot-[VERSION_ID]-dot-[PROJECT_ID].appspot.com
From there, you can build your own service discovery.
The URL of a service other than default follows this pattern:
service-dot-projectID.appspot.com
So for example if your service's name is helloworld and your project ID is myprojectid then the URL would be helloworld-dot-myprojectid.appspot.com
If you're manually constructing urls to services within the same project, you may find it easier to setup some dispatch rules.
https://cloud.google.com/appengine/docs/standard/python3/reference/dispatch-yaml
The dispatch.yaml allows you to override routing rules. You can use the dispatch.yaml to send incoming requests to a specific service (formerly known as modules) based on the path or hostname in the URL.
Depending on your needs, you can construct your url off of app_identity.get_default_version_hostname() without worrying about the underlying service, since the dispatch rules will route to the service by the url path.

Kubernetes and AAD authentication

On configured AKS there is docker container with application that is using AAD authentication.
Based on this article there is also configured ingress. API is working well.
When I add to Azure Active Directory application registration reply URL with https prefix I receive error "The reply url specified in the request does not match the reply urls configured for the application". And I see that in browser address line redirect_uri is starting with http.
When I add reply URL that is starting with http, then I receive "Exception: Correlation failed".
What I have tried: Add to ingress.yaml setting ingress.kubernetes.io/force-ssl-redirect: "true"
May be there is some way to force ingress run https instead of http, or there might be some AAD redirect configuration? Any ideas?
UPDATE 2: Probably http redirect is because of ADAL.
PS: Was able to find similar topic without an answer
UPDATE3:
I have decided not to use nginx as ingress. Instead I am using now Load balancer. Soon it would be possible to use Azure Application Gateway Ingress Controller
Have you tried this?
By default the controller redirects HTTP clients to the HTTPS port 443 using a 308 Permanent Redirect response if TLS is enabled for that Ingress.
This can be disabled globally using ssl-redirect: "false" in the NGINX config map, or per-Ingress with the nginx.ingress.kubernetes.io/ssl-redirect: "false" annotation in the particular resource.
More information on this on the Ingress documentation link.
You have to make a decision whether to use HTTPS or not. If this is just the start of a development cycle, start without it and get auth to work - but implement HTTPS as soon as possible.
AAD supports both http and https, but of course, the reply urls must be added to the application registration respectively.
As #mihail-stancescu says, ssl-redirect must be set to false, if you choose not to use HTTPS. In addition to this, you also have to ensure that your app does not make the redirect from HTTP to HTTPS.
Using curl with -L -k and -v options will give you a lot of information on what is actually happening with your requests.
When the http/https thing is solved, you have to remove any rewrite annotations you have in your ingress. (e.g. ingress.kubernetes.io/rewrite-target: / should be removed).
Now, if your ingress path to the service in question is e.g. /myservice, then the reply-url should also have that part of the path added ([host]/myservice/signin-oidc) - both in the AAD application registration and in the configuration of your app. (The path in the config should not contain the host)
If you are using https, then you must also have a proper certificate. You can use the free LetsEncrypt (https://letsencrypt.org/) in conjunction with KubeLego (https://github.com/jetstack/kube-lego), where you can find some nice examples on how to implement it.

How to properly enable HTTPS on App Engine flex environment and Go?

I am trying to enable HTTPS on my Go App deployed to GAE flex environment. I have my custom domain successfully mapped, and am using Google-managed SSL certificates. I have app.yaml configured to redirect HTTP to HTTPS as follows:
handlers:
- url: /.*
script: _go_app
secure: always
Now there are two problems that I haven't been able to resolve so far.
First, the above configuration is supposed to redirect HTTP traffic to HTTPS, but apparently it is not happening.
Second, when I add https:// in the url box, I see three different behavior on Firefox, Chrome, and Edge. Edge identifies the website as secure, Firefox marks the website as secure connection, but says that it "has blocked parts of this page that are not secure", and surprisingly Chrome marks the website as Not secure (though it says certificate is valid!).
With these symptoms I was wondering if I should take additional steps to make redirecting and SSL work for my website? Specifically, I would like to know with App Engine, and managed SSL enabled:
Should I continue serving pages on HTTP using http.ListenAndServe(..), or need to switch to http.ListenAndServeTLS(..)?
In my Go app should I redirect HTTP to HTTPS? or the above setting is expected to work just fine?
Thanks in advance for your help and advice.
PS:
Trying out with different suggestions, I added Strict-Transport-Security: max-age=31536000; includeSubDomains to handlers' response. Does not seem if this helped with redirection either.
EDIT/PARTIAL ANSWER:
According to this documentation, under Authentication changes, the secure and login handlers are deprecated. The documentation suggests using Strict-Transport-Security or X-Forwarded-Proto instead.
I am using Strict-Transport-Security on the server side to enrich my response header:
func (h *STLHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
w.Header().Add("Strict-Transport-Security", "max-age=63072000; includeSubDomains")
h.nextHandler.ServeHTTP(w, req)
}
I was wondering if I am using this header in the right place?
For the second set of my problems I realized I have mixed content on my page. My mixed content was a http link to a set of fonts. When I fixed the mixed content, i.e. changed http to https, both Chrome and Firefox security warnings disappeared. You may also find this page Avoiding the Not Secure Warning in Chrome useful on this matter.
You need to check your app using:
http://[YOUR_PROJECT_ID].appspot.com
Or if you nedd HTTPS:
https://[YOUR_PROJECT_ID].appspot.com
If you want your own certificate you will need to upload it and then be available to use: https://your-domain.tld
From the docs:
For APIs that will be hosted on App Engine flexible environment, you must use the appspot.com domain, and the service name must be in the following format:
YOUR_PROJECT_ID.appspot.com
When you deploy your API to App Engine, a DNS entry with a name in the format YOUR_PROJECT_ID.appspot.com is created automatically.
For APIs that will be hosted on Compute Engine, Kubernetes Engine, or Kubernetes, you must use the cloud.goog domain, and the service name must be in the following format:
YOUR_API_NAME.endpoints.YOUR_PROJECT_ID.cloud.goog
Or you could just put a CDN in front like Cloudflare which will do all the SSL termination for you and if required redirect all HTTP to HTTPS

App Engine remote_api with OpenID

I've recently tried to switch my app engine app to using openID, but I'm having an issue authenticating with remote_api. The old authentication mechanism for remote_api doesn't seem to work (which makes sense) - I'm getting a 'urllib2.HTTPError: HTTP Error 302: Found', which I assume is appengine redirecting me to the openid login page I've set up.
I guess I'm missing something fairly obvious. Currently my remote_api script has the following in it -
remote_api_stub.ConfigureRemoteDatastore(app_id=app_id, path='/remote_api', auth_func=auth_func, servername=host, secure=secure)
where auth_func is
def auth_func():
return raw_input('Username:'), getpass.getpass('Password:')
Any ideas what I need to supply to remote_api? I guess similar issues would be encountered with bulkloader too. Cheers,
Colin
This was a fun one.
Looking at remote_api, the flow for authentication seems to be something like this:
Prompt the user for Google credentials
Post the credentials to https://www.google.com/accounts/ClientLogin
Parse the auth token out of the response body
Pass the token to https://myapp.appspot.com/_ah/login
Grab ACSID cookie set in the response
Pass the ACSID cookie in subsequent requests that require authorization
I couldn't find a lot of documentation on the new OpenID support, though Nick's blog entry was informative.
Here's the test app I wrote to see how things work:
app.yaml:
handlers:
- url: /remote_api
script: $PYTHON_LIB/google/appengine/ext/remote_api/handler.py
login: admin
- url: /.*
script: test.py
test.py:
class MainPage(webapp.RequestHandler):
def get(self):
user = users.get_current_user()
if user:
self.response.out.write("Hi, %s!<hr>admin is %s" % (user.user_id(),
users.is_current_user_admin()))
else:
self.redirect(users.create_login_url('/', None,
'https://www.google.com/accounts/o8/id'))
Flipping my auth mode between Google Accounts and Federated Login, I noticed a few things:
Admin users are correctly recognized by is_current_user_admin() with OpenID
Mixing modes doesn't work. With authentication set to Google Accounts, calling create_login_url with a federated_identity throws a NotAllowedError
An ACSID cookie is still produced at the end of the login process, only it comes from /_ah/openid_verify instead of /_ah/login
So what's happening with remote_api when using Federated Login? If we're using the default appengine_rpc.HttpRpcServer, it's dutifully following the same Google Account authentication process described at the top, only the app no longer considers the ACSID cookie returned by /_ah/login to be valid, so since you're still unauthenticated, you get a 302 redirect to the OpenID login page, /_ah/login_required.
I dunno what the right solution is here. Seems like it would require an API update. Maybe Nick or one of the other Googlers can weigh in.
For now, here's a hacky workaround:
Turn on Federated Login for your app
Make sure you're passing save_cookies=True when calling remote_api_stub.ConfigureRemoteDatastore for your console script
Attempt console authentication and get the 302 error
Login as an admin via your app's web interface
In your browser cookies, find the ACSID cookie for myapp.appspot.com
Find and edit your local ~/.appcfg_cookies file
Replace the ACSID cookie for myapp.appspot.com with the one from your browser
The next time you try to use remote_api, it should work without prompting for credentials. You'll have to repeat the last 4 steps every time the cookie expires, though. You can bump the expiration from 1 day to as high as 2 weeks in the admin console to minimize the annoyance. Have fun!
This is definitely an issue... mark your interest in getting Google to fix this by starring the ticket at http://code.google.com/p/googleappengine/issues/detail?id=3258 and feel free to add any of your workarounds there.
On a related note, we also recognize that the docs are somewhat sparse, so I'm working on an article which hopefully fills-in some of those holes... stay tuned and keep your eyes open at http://code.google.com/appengine/articles
Here's a workaround you can use until there's a more permanent solution in place.

Resources