4

I'm building an Azure Function App v2.x (.NET Core 2, .csproj) and building a docker image (Ubuntu). When running the docker image locally, I would like some way to differentiate my environment as to allow anonymous access whilst running inside the Azure Function Runtime 2.x witin the container.

UPDATE #2: I've not fallen into a pit of success when running a Function within a Docker container using Azure Functions Runtime - I'm not alone. So I've taken to User Voice to suggest exposing an environment variable to allow overriding the function key for the purposes of local or otherwise custom hosting situations.

Longer term, perhaps Key Vault could be leveraged to configure the access key and be referenced regardless of where the function is running. This all depends on what scope Azure functions take on in terms of being a micro-services platform, but it seems clear that the AuthN story is fairly incomplete for the runtime.

UPDATE #1: As Kamil notes, the Azure Functions CLI is implicitly "local" so bypasses authorization regardless. This explains the debug behavior in VS 2017, but I'm still trying to formulate an strategy for handling auth in C# class library-based Azure Functions 2.x running in Docker. Essentially, how do I affect the HttpTrigger config at runtime? Other variations of Azure Functions have the liberty of modifying the binding in function.json upon deployment.

I have a sample on GitHub to illustrate, but I'll continue to explain here as well.

Original Question: When debugging locally in Visual Studio 2017, I assume it's modifying the functions.json on the fly to make everything anonmyous. So even when I have a function like this:

        public static async Task<HttpResponseMessage> Function(
        [HttpTrigger(AuthorizationLevel.Function, "post", Route = null)]HttpRequestMessage req,
        TraceWriter log)

VS2017 is going to allow me to make a call from Postman with no frills. However, once running in Azure, it'll expect either a query string param or HTTP header [x-functions-key] in order to authorize the request.

Implicitly anonymous access (Joey did not use a key)

However, when I build a docker image from my build output folder and then run this same code within the container...

docker build -t azfunc-sample <path_to_output_folder>

docker run -p 2077:80 azfunc-sample

I get an HTTP 401 back from the container on the "AuthorizeLevel.Function", but of course, the "AuthorizeLevel.Anonymous" is still fine.

No anonymous access

This makes sense given the Azure Functions Runtime in Docker has no idea it's running locally or on some Linux VM, but somehow I need a way of telling it to do what VS 2017 does. With compiled Azure Functions I believe the function.json is generated and I don't see an obvious way to parametrize the HTTP binding with an environment variable.

Matthew
  • 706
  • 9
  • 12

1 Answers1

4

You are partially right - in fact, the way why locally even if a function is secured using Function or Admin key, you can access it as it was Anonymous access, is the fact that it uses Azure Functions CLI instead of a real runtime. In CLI, there is no difference between secured and unsecured functions, what you can see in the following code:

private void DisplayHttpFunctionsInfo(WebScriptHostManager hostManager, Uri baseUri)
{
    if (hostManager != null)
    {
        var httpFunctions = hostManager.Instance.Functions.Where(f => f.Metadata.IsHttpFunction());
        if (httpFunctions.Any())
        {
            ColoredConsole
                .WriteLine()
                .WriteLine(Yellow("Http Functions:"))
                .WriteLine();
        }

        foreach (var function in httpFunctions)
        {
            var httpRoute = function.Metadata.Bindings.FirstOrDefault(b => b.Type == "httpTrigger").Raw["route"]?.ToString();
            httpRoute = httpRoute ?? function.Name;
            var extensions = hostManager.Instance.ScriptConfig.HostConfig.GetService<IExtensionRegistry>();
            var httpConfig = extensions.GetExtensions<IExtensionConfigProvider>().OfType<HttpExtensionConfiguration>().Single();
            var hostRoutePrefix = httpConfig.RoutePrefix ?? "api/";
            hostRoutePrefix = string.IsNullOrEmpty(hostRoutePrefix) || hostRoutePrefix.EndsWith("/")
                ? hostRoutePrefix
                : $"{hostRoutePrefix}/";
            var url = $"{baseUri.ToString()}{hostRoutePrefix}{httpRoute}";
            ColoredConsole
                .WriteLine($"\t{Yellow($"{function.Name}:")} {Green(url)}")
                .WriteLine();
        }
    }

It just uses routes without attached keys. WHen running a function within a container, a runtime is also attached, thus it works as hosted in the cloud.

kamil-mrzyglod
  • 4,948
  • 1
  • 20
  • 29
  • 1
    Thank you for the explanation. So if within the Azure Functions Runtime the function expects to behave as it would in a cloud PaaS deployment, is AuthorizationLevel.Function simply the wrong strategy in general for a Docker deployment? It seems the runtime could rely on some environment variable to determine how to filter the route. I can't really derive a new implementation of HttpTriggerAttribute, but suppose I could rely on a pre-compilation codegen, but that doesn't feel like it should be necessary to handle this. – Matthew Sep 05 '18 at 14:13
  • Personally, I find `AuthorizationLevel` problematic in many different use cases, as it is a completely abstracts authorization when using user's context. Basically when running functions from VS, you are running them inside `Azure Functions CLI`, which is a completely different runtime than the one used in Azure(so it lacks e.g. authentication module used in App Services). Personally I leverage a custom binding, which overrides `AuthorizationLevel` and is not environment specific. – kamil-mrzyglod Sep 05 '18 at 18:55