Publish artifacts in GitHub actions

GitHub action is perfect to automate simple build workflow and can also be used to publish “releases” of our software. While we can do actions to publish on cloud or elsewhere, what I appreciate from a tool is: allow me to make simple things with simple workflow.

While I appreciate being able to obtain complex result and indeed, sometimes we evaluate products on the ability to fulfill complex scenario, often we forgot about the simple things. Is it true that, if a product allows me to solve complex scenarios, it will surely allow me to solve simple scenario, but I wonder about the complexity.

To automate a complex build and release workflow, currently I’d prefer Azure DevOps pipelines, but if I have a simple tool or library opensource on GitHub, quickly creating an action is the simplest way to go.

GitHub actions is still in its infancy and while it probably lack the ability to orchestrate complex workflows, it makes extremely simple to publish something, especially if the source uses command line tooling lile .NET core.

Here it is a small piece of GH action where I publish two distinct .NET core projects and then upload as artifact attachment to action run result.

    - name: dotnet publish server
      run: dotnet publish src/StupidFirewallManager/StupidFirewallManager.csproj --configuration release

    - name: dotnet publish client
      run: dotnet publish src/StupidFirewallManager.Client/StupidFirewallManager.Client.csproj --configuration release

    - uses: actions/upload-artifact@v1
      with:
        name: Publish server
        path: src/StupidFirewallManager/bin/release/netcoreapp3.1/publish

    - uses: actions/upload-artifact@v1
      with:
        name: Publish client
        path: src/StupidFirewallManager.Client/bin/release/netcoreapp3.1/publish

As you can see I can use dotnet publish commanline to simple publish software written in .NET core, then using actions/upload-artifact@v1 I can publish artifact to the action result.

SNAGHTML4b1e49

Figure 1: Artifacts attached to execution actions, simply downloadable from the UI.

With a simple task I can simply upload folders with build/publish result as execution artifacts, and I can simply download the result from the web interface. Artifacts are downloadable also from action run summary page.

image

Figure 2: Artifacts of the build in the summary page of action run.

Few lines of YAML, push on GH and you have each build to allow download of artifacts. Simple thing made simple.

Gian Maria

Home Made Zero trust Security step 2

If you read my old post about how to create a simple program that can manage Windows Firewall to open ports with a simple udp request you surely got disappointed by the complete lack of security in the request. That program was no more than a mere proof of concept to understand if I can manage windows firewall programmatically in .NET Core.

The absolute critical problem in that program is that, UDP request to open a Tcp port is sent in clear text.

Basically the protocol is, a client C send to the server S a UDP packet in a specific port with a secret key, the server S check if the secret is correct and opens a corresponding TCP port, associated by UDP port in configuration, for requesting IP only and for a predetermined period of time.

You can easily spot the problem: the UDP packet was sent in clear text, everyone that intercept the communication will be able to open port because the secret is sent in clear text.

We have obvious solution to the problem, the most simple one is using the shared secret password to derive a symmetric cryptographic key to encrypt the message. This is far from being perfect, but it is a further step towards a more secure solution.

Since reusing the very same cryptographic key multiple time is not encouraged (even if using a different Initialization Vector solves the problem), a special class called PassdowrdDeriveBytes can be used to derive a sequence of bytes from a password, using a salt and it is cryptographically secure.

public static ICryptoTransform GetEncryptorFromPassword(
    this Aes aes,
    string password,
    byte[] salt)
{
    using (var pdb = new PasswordDeriveBytes(password, salt))
    {
        var key = pdb.GetBytes(32);
        var IV = pdb.GetBytes(16);
        return aes.CreateEncryptor(key, IV);
    }
}

The salt is a sequence of bytes to be used only once, to avoid generating the very same key each time you sent a message. You can use another approach, where you use the very same key and each time you change the Initialization vector, but using the salt to generate a unique Key and IV is probably a better method.

Given this brief introduction we can create a function to generate a random salt to be used for each message.

public static byte[] GenerateRandomSalt()
{
    using (var csp = new RNGCryptoServiceProvider())
    {
        byte[] salt = new byte[saltSize];
        csp.GetBytes(salt);
        return salt;
    }
}

Even for this simple method, it is important to use a random number generator that is cryptographically secure, such as RNGCryptoServiceProvider.  Armed with these two functions we can create a method to encrypt a generic stream of bytes.

public static Byte[] SimmetricEncrypt(string password, byte[] salt, byte[] data)
{
    using (var aes = Aes.Create())
    using (var encryptor = aes.GetEncryptorFromPassword(password, salt))
    using (MemoryStream msEncrypt = new MemoryStream())
    {
        using (CryptoStream csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write))
        {
            csEncrypt.Write(data, 0, data.Length);
        }

        // important, dispose CryptoStream before accessing the array
        return msEncrypt.ToArray();
    }
}

As you can see the shared password and the salt are needed and clearly the sequence of byte to encrypt. Encrypted message can be decrypted by a corresponding method that basically accepts the very same set of parameters.

public static Byte[] SimmetricDecrypt(string password, byte[] salt, byte[] data)
{
    try
    {
        using (var aes = Aes.Create())
        using (var encryptor = aes.GetDecryptorFromPassword(password, salt))
        using (MemoryStream msDecrypt = new MemoryStream())
        {
            using (MemoryStream msOriginalData = new MemoryStream(data))
            using (CryptoStream csDecrypt = new CryptoStream(msOriginalData, encryptor, CryptoStreamMode.Read))
            {
                csDecrypt.CopyTo(msDecrypt);
            }

            // important, dispose CryptoStream before accessing the array
            return msDecrypt.ToArray();
        }
    }
    catch (CryptographicException cex)
    {
        Log.Error(cex, "Error decrypting message");
        //Do not disclose anything to the caller.
        throw new SecurityException("Error in decrypting");
    }
}

Code is really simple to read, the only special care is I’ve intercepted each CryptographicException that the code can raise (such as bad password) and I rethrow a generic Security Exception with no any information. The aim is avoiding to give to the caller any possible clue on what went wrong.

Armed with these two simple functions, we can change communication protocol between client and server, using the shared key to encrypt the request message, so anyone that intercepts the message cannot understand what is contained inside.

To avoid reply attack, were an attacker simple retransmit the very same intercepted UDP packet, content of encrypted packet is a simple class that contains three properties

        /// <summary>
        /// This is the port we want to open
        /// </summary>
        public Int32 PortToOpen { get; private set; }

        /// <summary>
        /// This is the end of opening Date, remember that the
        /// server could leave the port opened for a lesser time
        /// if needed.
        /// </summary>
        public DateTime EndOpeningDate { get; private set; }

        /// <summary>
        /// Ip address to scope port opening to.
        /// </summary>
        public String IpAddress { get; private set; }

Now an attacker can reply an intercepted request, but the net result is to reapply the very same request, opening a port for a required IpAddress. Since the message is encrypted he/she cannot read what is inside the message, nor they can alter it.

This solution is more secure and starts to be almost production ready.

As usual the code is on GitHub (as today encryption is still on a feature branch) https://github.com/alkampfergit/StupidFirewallManager 

Gian Maria.

.NET core configuration array with Bind

New configuration system of .NET core is really nice, but it does not works very well with arrays, I have a configuration object that has an array of FirewallRules

        public FirewallRule[] Rules { get; set; }

This rule is simply composed by four simple properties.

public class FirewallRule
{
    internal FirewallRule()
    {
    }

    public FirewallRule(string name, int udpPort, int tcpPort, string secret)
    {
        Name = name;
        UdpPort = udpPort;
        TcpPort = tcpPort;
        Secret = secret;
    }

    public String Name { get; set; }

    public Int32 UdpPort { get; set; }

    public Int32 TcpPort { get; set; }

    public String Secret { get; set; }
}

Ok, nothing complex, now I’m expecting to being able to write this json configuration file to configure a single rule.

{
  "Rules" : [
    {
      "Name": "Rdp",
      "UdpPort": 23456,
      "TcpPort": 3389,
      "Secret": "this_is_a_secret"
    }
  ]
}

And being able to configure using standard Bind utilities of .NET configuration extensions.

            const string testValue = @"
{
  ""Rules"" : [
    {
      ""Name"": ""Rdp"",
      ""UdpPort"": 23456,
      ""TcpPort"": 3389,
      ""Secret"": ""this_is_a_secret""
    }
  ]
}";
            using var ms = new MemoryStream(Encoding.UTF8.GetBytes(testValue));
            IConfiguration configuration = new ConfigurationBuilder()
                .SetBasePath(AppDomain.CurrentDomain.BaseDirectory)
                .AddJsonStream(ms)
                .Build();

            var config = new Configuration();
            configuration.Bind(config);

Now I’m disappointed because the test fails because in Rules array I have only a single null element. It seems that Bind does not work perfectly with arrays or am I doing something wrong.

The solution was quite simple, I created a method inside the Configuration object that will allows binding from a standard .NET Core IConfiguration object

public void Bind(IConfiguration configuration)
{
    configuration.Bind(this);

    List<FirewallRule> rulesList = new List<FirewallRule>();
    var rules = configuration.GetSection("Rules").GetChildren().ToList();
    foreach (var rule in rules)
    {
        var firewallRule = new FirewallRule();
        rule.Bind(firewallRule);
        rulesList.Add(firewallRule);
    }

    Rules = rulesList.ToArray();
}

As you can see I explicitly get the section Rules, and for each child I explicitly created a FirewallRule and populate properties with standard Bind() method. And everything worked perfectly.

I’am a little bit puzzled, because I suspect that I’m doing something wrong because base Bind() method should work out of the box, but at least with this fix all tests are still green.

Gian Maria.

GitHub actions improvements

GitHub actions is really new kid on the block and even if I still prefer Azure DevOps pipelines, because they are really more production ready, GitHub actions is rapidly evolving.

SNAGHTML3961c2

Figure 1: GitHub actions now has a dedicated editor for actions to quickly include actions

As you can see in Figure 1, when you edit workflow file in GitHub online editor you can simply browse all available actions. Choosing a specific action reveal the snippet of text you should enter to use that action without the need to search around.

image

Figure 2: Detail of the action with a nice button to copy action text in the clipboard.

This feature suggests that it is better to use GitHub online editor to create and edit your workflow files, even if they are simply text files that can be edited from your favorite code editor.

If you need to author a GitHub action file, always prefer online editor than a simple offline editor.

If you edit your workflow directly in GitHub you have also syntax checking to avoid errors, as you can see in Figure 3.

image 

Figure 3: Syntax checking during editing online

Syntax checking is not only available to check some classic YAML errors, like in Figure 3 where the editor spotted a basic error in indentation, but it can also check semantic errors based on action schema, so you can guide the user during editing.

image

Figure 4: Syntax highlighting during editing that can suggests syntax to author action

You have also diff online, so you can check what you really changed during editing session.

image

Figure 5: Diff on actions to verify what you changed in action file during editing session.

As you can see online editor is quite powerful and allows you to quick edit action definition directly on web editor.

Gian Maria.

Azure DevOps YAML pipeline authorization problem

It could happen, sometimes, that when you create a pipeline in Azure Devops at first run you got the following error.

##[error]Pipeline does not have permissions to use the referenced pool(s) Default. For authorization details, refer to https://aka.ms/yamlauthz.

There are more than one kind of this error, the most common one is the build using some external resource that requires authorization, but in this specific error message, pipeline has no permission to run on default pool.

If you look at documentation, you can verify that you are expected to be presented with a nice button that allows you to authorize the pipeline with current pool, but sadly enough, sometime you have no button.

The reason is probably the inability of the system to determine the pool used by the build during authorization phase, this happens as an example when the poll is passed as variable to the pipeline. Look at the following definition

image

Figure 1: Pool specified as variable in the definition.

As you can see in Figure 1 pool name is specified through a standard variable, this allows the user to choose the pool with manual queue, while leaving standard continuous integration to target a pool chosen in the definition.

This kind of flexibility is necessary, as an example, to avoid priority problems. We usually have a dedicated pool with fast machines and left one pipeline license free so we can schedule a build in pool fast to have it being executed immediately.

How can you solve the problem? The simplest solution is going to the administration page of the project, select the pool you want to use for the build and finally grant permission to every pipeline.

image

Figure 2: Giving access to all pipelines to a pool solve authorization problems

If you prefer being able to give permission to single pipelines, the only available approach is editing the build, pushing yaml definition with a specific pool (not using a variable) and wait for the build to fail. If the pool is static you should have a nice button to authorize the build. Once authorized, you can edit the definition again with variable pool and the game is done. Remember to do the game for each pool that the build should use.

Gian MAria.