Mike Williamson
Mike Williamson

Reputation: 3158

Azure Web App multi-container, hitting against CORS regardless of settings

Overview

I have a frontend using Vue and a backend using FastAPI.

I have made Docker containers of both and a docker-compose.yml to hook it all together. It all works fine locally when I am developing.

When I move it to Azure, I am receiving a CORS error, specifically Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at https://<my-site>.azurewebsites.net:8080/login.

I have looked at several CORS questions (here is another) and Azure multi-container tutorials (another one, yet another and one last one). None of the suggestions solved the problem, but none of them were my exact situation:

Code

Here are the relevant parts of my code:

docker-compose.yml

version: "3.8"
services:
  frontend:
    image: <my-acr>.azurecr.io/<my-fe-image>:1
    networks:
      - fullstack
    ports:
      - "80:80"
  backend:
    image: <my-acr>.azurecr.io/<my-be-image>:1
    networks:
      - fullstack
    ports:
      - "8080:80"
networks:
  fullstack:

Front End Dockerfile

# develop stage
FROM node:14.8-alpine3.12 as develop-stage

ENV CONTAINER_PATH /vue

WORKDIR $CONTAINER_PATH

COPY package*.json ./

RUN yarn install

COPY . .

# build stage
FROM develop-stage as build-stage
RUN ["yarn", "build"]

# production stage
FROM nginx:1.19.2-alpine as production-stage
COPY --from=build-stage /vue/dist /usr/share/nginx/html
EXPOSE 8080 2222 80 443
CMD ["nginx", "-g", "daemon off;"]

Front End Axios Code

import axios from "axios";

export default () => {
  return axios.create({
    baseURL: `https://<my-site>.azurewebsites.net:8080/`,
    headers: { "Access-Control-Allow-Origin": "*" },
  });
};

Back End Dockerfile

FROM tiangolo/uvicorn-gunicorn:python3.8

RUN ["pip", "install", "--upgrade", "pip"]

RUN ["pip", "install", "--upgrade", "--ignore-installed", \
    "--use-feature=2020-resolver", "--no-cache-dir", "jwcrypto", "fastapi", "passlib", \
    "sqlalchemy", "toml", "topicaxis-opengraph", "sqlalchemy_imageattach", \
    "email_validator", "bcrypt"]

EXPOSE 8080 2222 80 443

COPY ./src /app

Back End FastAPI Code

app = FastAPI()

app.add_middleware(
    CORSMiddleware,
    allow_origins=["*"],
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)

I know much of that is too loose... I will fix it once I can get anything to work.


Azure Commands

First, I read that only ports 80 and 8080 are recognized, so I put my FE on 80 and my BE on 8080.

Next, these are the relevant Azure commands, once I pushed the containers up:

  1. az webapp create -g <my-rg> -p <my-plan> -n <app-name> --multicontainer-config-file ./docker-compose.yml --multicontainer-config-type COMPOSE --assign-identity /subscriptions/<my-subscription/resourceGroups/<my-rg>/providers/Microsoft.ManagedIdentity/userAssignedIdentities/<my-userid>
  2. az webapp config appsettings set -g <my-rg> -n <my-app> --settings WEBSITES_PORT=80
  3. az role assignment create --assignee <my-info> --scope /subscriptions/<my-subscription>/resourceGroups/<my-rg>/providers/Microsoft.ContainerRegistry/registries/<my-acr> --role "AcrPull"
  4. az webapp cors add -g <my-rg> -n <app-name> --allowed-origins "*"
  5. az webapp config container set -n <app-name> -g <my-rg> --multicontainer-config-file ./docker-compose.yml --multicontainer-config-type COMPOSE --docker-registry-server-url https://<my-site>.azurecr.io
  6. az webapp restart -n <app-name> -g <my-rg>

The docker-compose.yml mentioned above is the same one that is shown earlier.


I am able to see all of the front end fine, but any time the front-end tries to reach the back end, it hangs for a while and then comes back with the Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at https://<my-site>.azurewebsites.net:8080 error.

I am also unable to hit the back end directly, either just to see the raw API, or using FastAPI's auto-generated docs page.

Upvotes: 1

Views: 1724

Answers (3)

adiesner
adiesner

Reputation: 686

Using multi container in Azure WebApp currently doesn't support CORS. And as others have mentioned, you can only expose one container to the outside world (only port 80/8080 is supported).

From the Azure Documentation:

Preview limitations

Multi-container is currently in preview. The following App Service platform features are not supported:

  • Authentication / Authorization
  • Managed Identities
  • CORS
  • VNET integration is not supported for Docker Compose scenarios
  • Docker Compose on Azure App Services currently has a limit of 4,000 characters at this time.

Upvotes: 0

Mehdi Khlifi
Mehdi Khlifi

Reputation: 415

We face these issues on the front end side when deploying an application using Docker in remote machines, even though the back end works fine, the front end returns CORS issues. To fix this, we allow the following headers in the response

"Content-Type": "application/json"
"Access-Control-Allow-Origin": "*"
"Access-Control-Allow-Methods": 'POST, GET, PUT, DELETE, OPTIONS'
"Access-Control-Allow-Headers": "Access-Control-Allow-Origin,Authorization, "Access-Control-Allow-Methods,"Content-Type"

Upvotes: 1

jccampanero
jccampanero

Reputation: 53381

I must admit that I never ran a multi-container deployment in App Service with frontend and backend, only self-contained apps or single container ones. Having said that...

I think there are several problems here.

The first is why you can't connect to your backend service externally.

If you are using a Linux based Azure App Service, according to the Azure documentation, in a multi-container deployment, only one container is open to access over the internet. Which one? Here are the rules:

Here are the rules for determining which container is accessible - in the order of precedence:

  • Application setting WEBSITES_WEB_CONTAINER_NAME set to the container name
  • The first container to define port 80 or 8080
  • If neither of the above is true, the first container defined in the file will be accessible (exposed)

(Sorry, I was unable to format the list correctly).

This may be the reason why the backend container is not accessible from the externally, and, if both containers should be accessed from the internet, it will be a good reason to change your deployment for two single-container ones instead.

Another problem is why you are getting the CORS error.

There are several things that could cause the problem.

On one hand, it is not necessary to configure CORS for your web app as long as your frontend need not to be consumed in that way. So this configuration is not necessary:

az webapp cors add -g <my-rg> -n <app-name> --allowed-origins "*"

For the same reason, you do not need to configure CORS in Axios, so you can safely remove the following configuration line from your code:

headers: { "Access-Control-Allow-Origin": "*" },

And a question remains: how can you contact your backend service from your frontend?

First of all, it looks like your FastAPI configuration is right.

As proposed in my comment, my best advice is that you configure your Axios client to take advantage of the docker internal networking and configure Axios to use backend as the host to which sent their requests.

If you review the first multi-container example that you indicated (https://learn.microsoft.com/en-us/azure/app-service/tutorial-multi-container-app), when they configure the Wordpress container, they are referencing, in their configuration properties, db as the hostname, being db an exposed service:

WORDPRESS_DB_HOST: db:3306

In short, I think the multi-container setup is best suited for use cases when you have a service and multiple resources (SQL databases, Kafka, Redis) required for that service to function properly.

It might also be appropriate if you don't need to access your backend externally, only through your frontend.

In any other case, a deployment with two single container web applications should be more suitable.

One last thought ... If you necessarily need to implement a multi-container solution, maybe you can include some kind of webserver like nginx, the idea of a reverse proxy as also suggested in @timur's comment, that you can expose at the 80 port, and define rules to access the frontend and backend services. It should be tested, but this could help you greatly simplify your CORS setup and even avoid the need to use it.

Upvotes: 4

Related Questions