<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Exein</title>
    <description>The latest articles on Forem by Exein (@exein).</description>
    <link>https://forem.com/exein</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F317841%2F8fb8c43a-1747-4144-aadf-efdcce6e6e10.png</url>
      <title>Forem: Exein</title>
      <link>https://forem.com/exein</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/exein"/>
    <language>en</language>
    <item>
      <title>Pulsar preventing vulnerabilities #1 — polkit (CVE-2021–4034)</title>
      <dc:creator>Exein</dc:creator>
      <pubDate>Thu, 29 Aug 2024 12:44:22 +0000</pubDate>
      <link>https://forem.com/exein/pulsar-preventing-vulnerabilities-1-polkit-cve-2021-4034-1g1a</link>
      <guid>https://forem.com/exein/pulsar-preventing-vulnerabilities-1-polkit-cve-2021-4034-1g1a</guid>
      <description>&lt;p&gt;Pulsar is a security framework. Its primal goal is to prevent actual attacks in your IoT environment. One part of achieving that are generic rules which might help with detecting suspicious activity in your system, but that’s only one part of what we are focusing on. The other important part is studying known vulnerabilities in order to prevent them.&lt;/p&gt;

&lt;p&gt;In this series of articles we are going to show Pulsar on the battlefield — how it can be used to prevent actual known vulnerabilities.&lt;/p&gt;

&lt;h2&gt;
  
  
  Understanding CVE-2021–4034
&lt;/h2&gt;

&lt;h2&gt;
  
  
  Introducing PwnKit
&lt;/h2&gt;

&lt;p&gt;CVE-2021–4034, also known as “PwnKit,” is a security vulnerability discovered in polkit, a system service installed by default on many Linux distributions. Polkit (formerly PolicyKit) is used to manage system-wide privileges in Unix-like operating systems.&lt;/p&gt;

&lt;p&gt;To be precise, the vulnerability specifically affects pkexec, a program that is part of the polkit system. pkexec allows an authorized user to execute commands as another user, including the superuser (root). It’s responsible for communicating with polkit and making a verdict whether the user executing the command should be able to do it, even though that user normally cannot execute the wrapped command.&lt;/p&gt;

&lt;p&gt;You can think of pkexec of being somehow similar to sudo, although the difference is that sudo simply allows some groups of users (or some specific users) to authenticate as root just by using their passwords, while polkit has more complex rules which define what and under what circumstance can be done.&lt;/p&gt;

&lt;p&gt;A common rule used in most graphical Linux installations is mounting external drives. Normally, mounting new filesystems with the mount syscall, requires root privileges. But when you are using Linux with a desktop environment, you are not being asked for a password after plugging in an USB stick. That’s because of &lt;a href="https://github.com/storaged-project/udisks/blob/master/data/org.freedesktop.UDisks2.policy.in" rel="noopener noreferrer"&gt;udisks which has this polkit rule!&lt;/a&gt; Without it, using Linux on a desktop would be quite annoying, wouldn’t it?&lt;/p&gt;

&lt;p&gt;That’s why the security of pkexec itself is extremely important. If there is any way of tricking pkexec into making a wrong decision, the game is over. And unfortunately, PwnKit won the game.&lt;/p&gt;

&lt;h2&gt;
  
  
  How PwnKit works
&lt;/h2&gt;

&lt;p&gt;In Linux, all processes have an array of arguments, called &lt;code&gt;argv[]&lt;/code&gt;, which is followed by an array of environment variables, called &lt;code&gt;envp[]&lt;/code&gt;. Both arrays are &lt;code&gt;NULL&lt;/code&gt; terminated. The length of &lt;code&gt;argv[]&lt;/code&gt; is being provided in another variable, called &lt;code&gt;argc&lt;/code&gt;. For a simple &lt;code&gt;pkctl my-command&lt;/code&gt; call, the arrays and their memory layout would look like:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F781vdbmoegdtkgi9totg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F781vdbmoegdtkgi9totg.png" alt="Image description" width="800" height="222"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;There are no requirements with regards to the size of these — they can be empty. In that case, &lt;code&gt;argc&lt;/code&gt; would have value 0.&lt;/p&gt;

&lt;p&gt;The problem is that the vulnerable version of pkctl doesn’t check &lt;code&gt;argc&lt;/code&gt; in a safe manner. Instead, it assumes that &lt;code&gt;argv[1]&lt;/code&gt; is either &lt;code&gt;NULL&lt;/code&gt; or a command to execute, no matter what. But then if &lt;code&gt;argv[]&lt;/code&gt; is empty, according to pointer arithmetics in C, reading &lt;code&gt;argv[1]&lt;/code&gt; results in reading &lt;code&gt;envp[0]&lt;/code&gt;. C doesn’t check the bounds of the pointers, the language simply computes the memory location by treating the index (1 in &lt;code&gt;argv[1]&lt;/code&gt;) as an offset from the starting point (&lt;code&gt;argv[0]&lt;/code&gt;). This calculation leads directly to the memory location, without any validation of the pointer's limits.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgylgttmn6fyaufcr8pj8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgylgttmn6fyaufcr8pj8.png" alt="Image description" width="800" height="167"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If an attacker manages to make the argument array empty, pkexec will mistakenly use the content from the adjacent environment array as the application to execute. By adjusting these environment variables to include certain values and payloads, the attacker can exploit this to run commands as a privileged user. This execution occurs without needing any authentication.&lt;/p&gt;

&lt;p&gt;If you are curious about details, you can look at &lt;a href="https://github.com/berdav/CVE-2021-4034" rel="noopener noreferrer"&gt;one of the exploits example&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Polkit upstream fix
&lt;/h3&gt;

&lt;p&gt;Polkit fixed this issue by &lt;a href="https://gitlab.freedesktop.org/polkit/polkit/-/commit/a2bf5c9c83b6ae46cbd5c779d3055bff81ded683" rel="noopener noreferrer"&gt;introducing explicit checks for argc. &lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To prevent such issues in general, Linux kernel 5.18 &lt;a href="https://gitlab.freedesktop.org/polkit/polkit/-/commit/a2bf5c9c83b6ae46cbd5c779d3055bff81ded683" rel="noopener noreferrer"&gt;introduced a change which:&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;For userspace processes, when &lt;code&gt;argv[]&lt;/code&gt; is empty, it adds an empty string and sets &lt;code&gt;arcg&lt;/code&gt; to 1.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;For kernel threads, it rejects ones with empty &lt;code&gt;argv[]&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Preventing polkit exploitation in vulnerable environments
&lt;/h2&gt;

&lt;p&gt;To come up with an answer, let’s think about what’s unique about the pwnkit exploit in comparison to all the other processes in the system.&lt;/p&gt;

&lt;p&gt;The answer is simple — it’s being scheduled with an empty array of arguments — &lt;em&gt;argc&lt;/em&gt; of value 0 and with empty &lt;em&gt;argv&lt;/em&gt;. We can already consider this simple fact as a suspicious property of any process. Why would we even have processes launching without &lt;em&gt;argv&lt;/em&gt;? Every command which is being executed correctly, has at least its own name as an argument. Can you think of any legitimate use case of stripping the &lt;em&gt;argv&lt;/em&gt; when executing a binary? Me neither!&lt;/p&gt;

&lt;p&gt;Could we then just disallow any processes with empty argc and argv? Yes!&lt;/p&gt;

&lt;p&gt;When running Linux with kernel older than 5.18, one can mitigate the issue by this simple Pulsar rule:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;- name: CVE-2021-4034, possible envp manipulation with empty argv&lt;br&gt;
  type: Exec&lt;br&gt;
  condition: payload.argc == 0&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;This rule monitors all &lt;code&gt;Exec&lt;/code&gt; events (a new process launched through execution a binary) and is triggered when argc (number of arguments) is 0.&lt;/p&gt;

&lt;p&gt;Pulsar with the rule above is going to issue the following alert whenever we are trying to either use a PwnKit exploit or, in general, launch processes with empty argv[], trying our luck in finding out whether some software can be vulnerable to manipulation with argv[] and envp[].&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgt3okmii5n6nrebglyh6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgt3okmii5n6nrebglyh6.png" alt="Image description" width="800" height="24"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Final remarks&lt;br&gt;
As we mentioned at the beginning, with this post, we are starting a whole series about using Pulsar on the battlefield against CVEs and known attack vectors. Stay tuned for more rules and examples in the coming weeks.&lt;/p&gt;

&lt;p&gt;If you like it, don’t forget to try out Pulsar and give it a star on &lt;a href="https://github.com/Exein-io/pulsar" rel="noopener noreferrer"&gt;github!&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4p14kawlgbumyps4imkv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4p14kawlgbumyps4imkv.png" alt="Image description" width="800" height="517"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>iot</category>
      <category>security</category>
      <category>linux</category>
      <category>github</category>
    </item>
    <item>
      <title>A tale of TimescaleDB, SQLx and testing in Rust</title>
      <dc:creator>Exein</dc:creator>
      <pubDate>Thu, 02 May 2024 13:12:06 +0000</pubDate>
      <link>https://forem.com/exein/a-tale-of-timescaledb-sqlx-and-testing-in-rust-40h5</link>
      <guid>https://forem.com/exein/a-tale-of-timescaledb-sqlx-and-testing-in-rust-40h5</guid>
      <description>&lt;p&gt;At Exein, we use &lt;a href="https://www.timescale.com/?ref=blog.exein.io"&gt;TimescaleDB&lt;/a&gt; to store all kinds data throughout our services and rely on &lt;a href="https://docs.rs/sqlx/latest/sqlx/?ref=blog.exein.io"&gt;SQLx&lt;/a&gt; to interface with it. Throughout this article, we’ll explore how we recently changed our testing approach in relation to the database along with the problems we encountered and the solutions we came up with.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What is TimescaleDB?&lt;/strong&gt;&lt;br&gt;
Timescale is a database platform engineered to deliver speed and scale to resource-intensive workloads, which makes it great for things like time series, event, and analytics data. Timescale is built on PostgreSQL, so you have access to the entire PostgreSQL ecosystem, with a user-friendly interface that simplifies database deployment and management.&lt;/p&gt;

&lt;p&gt;At the core of TimescaleDB sit hypertables, which are tables that automatically partition data into chunks by time along and on which policies such as chunk compression and retention can be set.&lt;/p&gt;

&lt;p&gt;Another interesting feature of TimescaleDB is its continuous aggregates, which can be seen as a specialization of PostgreSQL materialized views, but for hypertables. Unlike materialized views, instead of refreshing the entire dataset every time, continuous aggregates store the aggregate of time based data and update according to their refresh policy or, in the case of real-time continuous aggregates, additionally aggregating recent, non-materialized data, to provide up-to-date results.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;SQLx&lt;/strong&gt;&lt;br&gt;
SQLx is a popular Rust database framework which, at the time of the article, comes bundled with drivers for SQLite, MySQL and PostgreSQL. Since TimescaleDB is based on PostgreSQL, using the &lt;code&gt;sqlx-postgres&lt;/code&gt; driver allows us to seamlessly interact with the database. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Testing&lt;/strong&gt;&lt;br&gt;
In both Continuous Integration (CI) and local development environments, it’s often necessary to test parts of our system using a live database. This process can become complicated when tests are executed concurrently or in parallel, as it may lead to collisions between the database objects being accessed, such as tables that are being read from or written to.&lt;/p&gt;

&lt;p&gt;To avoid these issues we would run tests sequentially against a single live database, wrapping them in &lt;code&gt;setup&lt;/code&gt; and &lt;code&gt;teardown&lt;/code&gt;functions which allowed us to prevent the state from one test from spilling over to the next. While functionally sound, it takes significantly longer to run the entire test suite.&lt;/p&gt;

&lt;p&gt;SQLx provides a neat testing mechanism through the &lt;code&gt;sqlx::test&lt;/code&gt;procedural macro. The macro provides a harness that runs each test on its own database, thus removing collisions between objects since each test is now isolated. It can even run migrations and even apply fixtures to get the database in the desired state before the test starts running.&lt;/p&gt;

&lt;p&gt;Just like the compile-time query checks of SQLx, the test harness connects to the database through the same DATABASE_URL environment variable. This is generally fine, but we use some custom code to create the database connection pool and wanted to reuse that for testing as well, for consistency reasons.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Wrapper proc macros&lt;/strong&gt;&lt;br&gt;
Since &lt;code&gt;sqlx::test&lt;/code&gt; does mostly what we want, I couldn’t help but wonder whether we can slightly adjust the way it works and still leverage it for our testing.&lt;/p&gt;

&lt;p&gt;Rust’s attribute procedural macros have the following signature:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="nd"&gt;#[proc_macro_attribute]&lt;/span&gt;
&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;dummy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;TokenStream&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;TokenStream&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;TokenStream&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;People that have worked with Rust’s procedural macros might know that parsing and processing their arguments is quite involved, and replicating what &lt;code&gt;sqlx::test&lt;/code&gt; does just to slightly change some inner workings would take a lot of boilerplate code and then effort to maintain it.&lt;/p&gt;

&lt;p&gt;When thinking about procedural macros what comes to mind are annotations that take some code and either produce new code or alter the annotated code, or both. But what if among the modifications we make to the annotated code we could involve new procedural macros annotations? I’ll refer to these as wrapper proc macros.&lt;/p&gt;

&lt;p&gt;While I haven’t realized it before, I actually saw quite a few examples of this out in the wild. One example that comes to mind is in &lt;code&gt;serde&lt;/code&gt; derive macros which adds various attributes to the code it generates. The only questions remaining then is how to do this correctly?&lt;/p&gt;

&lt;p&gt;Some information on this can be found on the &lt;code&gt;rust-lang&lt;/code&gt; forum, specifically &lt;a href="https://users.rust-lang.org/t/is-macro-attribute-execution-order-stable/51029?ref=blog.exein.io"&gt;this&lt;/a&gt; post and the issues listed within it. The bottom line is that while there’s no formal documentation on this yet, work has been done towards the general intent of having macro attributes expand in an outside-in fashion, meaning that top attributes can see and even add/remove/modify bottom attributes.&lt;/p&gt;

&lt;p&gt;The plan&lt;br&gt;
Given all the information listed above, we can now envision a plan:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Write a wrapper proc macro that adds the sqlx::test annotation while also passing in all its arguments to it;&lt;/li&gt;
&lt;li&gt;Add our custom database connection code to the generated code.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The first step of writing a wrapper proc macro is relatively straightforward if you have worked with proc macros before.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="nd"&gt;#[proc_macro_attribute]&lt;/span&gt;
&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;TokenStream&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;TokenStream&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;TokenStream&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;    
        &lt;span class="c1"&gt;// We only convert this to `proc_macro2::TokenStream` to allow using    &lt;/span&gt;
        &lt;span class="c1"&gt;// it with the `quote!` macro.    &lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nd"&gt;parse_macro_input!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nn"&gt;proc_macro2&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;TokenStream&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;    
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;input&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nd"&gt;parse_macro_input!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;input&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;ItemFn&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;    
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;ret&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="py"&gt;.sig.output&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;    
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="py"&gt;.sig.ident&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;    
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;inputs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="py"&gt;.sig.inputs&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;    
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;body&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="py"&gt;.block&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;    
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;attrs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="py"&gt;.attrs&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  

        &lt;span class="nd"&gt;quote!&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;        
                #&lt;span class="p"&gt;(&lt;/span&gt;#&lt;span class="n"&gt;attrs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;        
                &lt;span class="nd"&gt;#[::sqlx_test(&lt;/span&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="nd"&gt;args)]&lt;/span&gt;        
                &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; #&lt;span class="nf"&gt;name&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;#&lt;span class="n"&gt;inputs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; #&lt;span class="n"&gt;ret&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;            
                    #&lt;span class="n"&gt;body&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;    
      &lt;span class="p"&gt;}&lt;/span&gt;    
      &lt;span class="nf"&gt;.into&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;All that is required is that we destructure the input function so that we add the &lt;code&gt;sqlx::test&lt;/code&gt; attribute at the end. Since this is a procedural macro, it will have to reside in a different crate in the workspace, say &lt;code&gt;db_macros&lt;/code&gt;. However, this doesn’t cover the initial goal we set off to achieve, which is re-using our custom connection pool setup code in tests. But it does set the ground for that.&lt;/p&gt;

&lt;p&gt;The next step is figuring out how to alter &lt;code&gt;sqlx::test&lt;/code&gt;’s usage of the DATABASE_URL variable to make it work with our custom code. Perhaps expanding a &lt;code&gt;sqlx::test&lt;/code&gt; annotated test could help with this.&lt;/p&gt;

&lt;p&gt;Say we have this test function as an integration test in the &lt;code&gt;db_internal&lt;/code&gt; crate:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="nd"&gt;#[sqlx::test(&lt;/span&gt;    
        &lt;span class="nd"&gt;migrator&lt;/span&gt; &lt;span class="nd"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"db_internal::MIGRATOR"&lt;/span&gt;&lt;span class="nd"&gt;,&lt;/span&gt;    
        &lt;span class="nd"&gt;fixtures(&lt;/span&gt;        
                &lt;span class="nd"&gt;path&lt;/span&gt; &lt;span class="nd"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"../fixtures"&lt;/span&gt;&lt;span class="nd"&gt;,&lt;/span&gt;        
                &lt;span class="nd"&gt;scripts(&lt;/span&gt;&lt;span class="s"&gt;"some_script.sql"&lt;/span&gt;&lt;span class="nd"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"some_other_script.sql"&lt;/span&gt;&lt;span class="nd"&gt;)&lt;/span&gt;    
        &lt;span class="nd"&gt;)&lt;/span&gt;
&lt;span class="nd"&gt;)]&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;dummy_test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pool&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;PgPool&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;    
        &lt;span class="c1"&gt;// do stuff with `pool`&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This would expand to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="nd"&gt;#[::core::prelude::v1::test]&lt;/span&gt;
&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;dummy_test&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;dummy_test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pool&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;PgPool&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="c1"&gt;// do stuff with `pool`&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;sqlx&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;testing&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;TestArgs&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nd"&gt;concat!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nd"&gt;module_path!&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="s"&gt;"::"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nd"&gt;stringify!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dummy_test&lt;/span&gt;&lt;span class="p"&gt;)));&lt;/span&gt;
    &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="nf"&gt;.migrator&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nn"&gt;db_internal&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;MIGRATOR&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="nf"&gt;.fixtures&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;sqlx&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;testing&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;TestFixture&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"../fixtures/some_script.sql"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;contents&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nd"&gt;include_str!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"../fixtures/some_script.sql"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;sqlx&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;testing&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;TestFixture&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"../fixtures/some_other_script.sql"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;contents&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nd"&gt;include_str!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"../fixtures/some_other_script.sql"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;]);&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;dummy&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;sqlx&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;testing&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;TestFn&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;run_test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can see that some &lt;code&gt;TestArgs&lt;/code&gt; structure is constructed, containing migrations (or the provided migrator in our case) and fixtures. This is based on those args to &lt;code&gt;sqlx::test&lt;/code&gt; that we really want to avoid having to parse and process ourselves.&lt;/p&gt;

&lt;p&gt;For our mission, the &lt;code&gt;TestFn&lt;/code&gt; trait is really the interesting part. Per the &lt;code&gt;sqlx::test&lt;/code&gt; &lt;a href="https://docs.rs/sqlx/latest/sqlx/attr.test.html?ref=blog.exein.io"&gt;docs&lt;/a&gt;, the macro can be used on async functions accepting a connection pool, a single connection or connection options and the trait implementation for each of these function signatures drives the test setup.&lt;/p&gt;

&lt;p&gt;Since &lt;code&gt;sqlx::test&lt;/code&gt; relies on DATABASE_URL for tests we can try a naive implementation where we construct and set the environment variable based on our custom connection code before the test is run by injecting a line before the &lt;code&gt;TestFn&lt;/code&gt;trait method call.&lt;/p&gt;

&lt;p&gt;This raises another question, if we use our &lt;code&gt;test&lt;/code&gt; wrapper proc macro to add the &lt;code&gt;sqlx::test&lt;/code&gt; annotation, how are we going to manipulate the code expanded from the &lt;code&gt;sqlx::test&lt;/code&gt; macro? Using other, “private”, procedural macros. We will again depend on the outside-in nature of proc macro expansion and leverage a second macro that we will add after &lt;code&gt;sqlx::test&lt;/code&gt;so it can see the expanded code and manipulate it.&lt;/p&gt;

&lt;p&gt;That macro will look something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="nd"&gt;#[proc_macro_attribute]&lt;/span&gt;
&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;__private_test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_args&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;TokenStream&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;TokenStream&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;TokenStream&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;input&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nd"&gt;parse_macro_input!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;input&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;ItemFn&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// Pop the last statement&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;last&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;input&lt;/span&gt;
        &lt;span class="py"&gt;.block&lt;/span&gt;
        &lt;span class="py"&gt;.stmts&lt;/span&gt;
        &lt;span class="nf"&gt;.pop&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="nf"&gt;.expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"sqlx::test always generates statements"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// Generate custom statement&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;stmt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nd"&gt;quote!&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// custom connection code statement&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="nf"&gt;.into&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="c1"&gt;// Insert statement at the end of the block&lt;/span&gt;
    &lt;span class="n"&gt;input&lt;/span&gt;
        &lt;span class="py"&gt;.block&lt;/span&gt;
        &lt;span class="py"&gt;.stmts&lt;/span&gt;
        &lt;span class="nf"&gt;.push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nd"&gt;parse_macro_input!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;stmt&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;Stmt&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

    &lt;span class="c1"&gt;// Add the last statement back&lt;/span&gt;
    &lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="py"&gt;.block.stmts&lt;/span&gt;&lt;span class="nf"&gt;.push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;last&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="nd"&gt;quote!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;#&lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nf"&gt;.into&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And so the test wrapper proc macro will now look something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="nd"&gt;#[proc_macro_attribute]&lt;/span&gt;
&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;TokenStream&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;TokenStream&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;TokenStream&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nd"&gt;parse_macro_input!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nn"&gt;proc_macro2&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;TokenStream&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;input&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nd"&gt;parse_macro_input!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;input&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;ItemFn&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;ret&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="py"&gt;.sig.output&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="py"&gt;.sig.ident&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;inputs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="py"&gt;.sig.inputs&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;body&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="py"&gt;.block&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;attrs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="py"&gt;.attrs&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="nd"&gt;quote!&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        #&lt;span class="p"&gt;(&lt;/span&gt;#&lt;span class="n"&gt;attrs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;
        &lt;span class="nd"&gt;#[::sqlx_test(&lt;/span&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="nd"&gt;args)]&lt;/span&gt;
        &lt;span class="nd"&gt;#[::db_macros::__private_test]&lt;/span&gt;
        &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; #&lt;span class="nf"&gt;name&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;#&lt;span class="n"&gt;inputs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; #&lt;span class="n"&gt;ret&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            #&lt;span class="n"&gt;body&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="nf"&gt;.into&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note the usage of the &lt;code&gt;db_macros&lt;/code&gt; crate, which is the crate that houses our custom procedural macros.&lt;/p&gt;

&lt;p&gt;Trying it out&lt;br&gt;
With the wrapper proc macro in place, it’s time to try it out. Naturally, the local environment is the place to start. We spin up a Docker container of TimescaleDB and try to run our &lt;code&gt;dummy&lt;/code&gt; test against it. The harness connects to the database and runs fixtures &amp;amp; migrations before the test function body, so it doesn’t matter that the test itself is empty.&lt;/p&gt;

&lt;p&gt;It all seems to work, great! Time to give it a go in the GitHub CI.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;failures:

---- dummy_test stdout ----
thread 'dummy_test' panicked at /home/runner/.cargo/registry/src/index.crates.io-6f17d22bba15001f/sqlx-core-0.7.4/src/testing/mod.rs:248:14:
failed to apply migrations: Execute(Database(PgDatabaseError { severity: Error, code: "40P01", message: "deadlock detected", detail: Some("Process 104 waits for AccessExclusiveLock on advisory lock [21387,1005,0,29749]; blocked by process 188.\\nProcess 188 waits for ShareLock on relation 15242 of database 21387; blocked by process 104."), hint: Some("See server log for query details."), position: None, where: None, schema: None, table: None, column: None, data_type: None, constraint: None, file: Some("deadlock.c"), line: Some(1148), routine: Some("DeadLockReport") }))
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Uh-oh… A deadlock? From what? Trying it numerous times in both the CI and local environment always gave the same output: locally tests run fine but deadlocks occur almost every time in the CI.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Next plan: no plan&lt;/strong&gt;&lt;br&gt;
I have to admit that I was taken off guard by this. I used the &lt;code&gt;sqlx::test&lt;/code&gt; harness with PostgreSQL numerous times without issues and the little trick we pulled off with the wrapper proc macro merely sets the DATABASE_URL environment variable at runtime so common sense heavily prevents me from considering this as the cause. I started exploring whether the issue was simple, silly and easily fixable.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Speculations and observations: Part 1&lt;/strong&gt;&lt;br&gt;
It immediately felt like this is a TimescaleDB related issue. But how different is TimescaleDB from vanilla PostgreSQL and could these differences really throw things off so much?&lt;/p&gt;

&lt;p&gt;The answer lies in how all the neat features of TimescaleDB work: background workers. With each hypertable, continuous aggregate and policy on these objects TimescaleDB makes use of background jobs to do the necessary processing.&lt;/p&gt;

&lt;p&gt;I strongly suspected that the deadlocks occurred between TimescaleDB background workers and the migration scripts. Out of these two, we can’t really directly alter the background workers behavior, but maybe our migrations have some incompatibility with how TimescaleDB works.&lt;/p&gt;

&lt;p&gt;How &lt;code&gt;SQLx&lt;/code&gt; migrations work&lt;br&gt;
Without going into too much detail, analyzing the code between &lt;code&gt;sqlx::test&lt;/code&gt; shows that migration scripts get run one by one, each in its own individual transaction. A deadlock happens between transactions, so it seemed fair to assume that the issue is between one or more migration steps and the background workers.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Getting database logs&lt;/strong&gt;&lt;br&gt;
The interesting part of the testing step in our GitHub CI goes something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Setup database&lt;/span&gt;
  &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;docker compose up -d timescaledb&lt;/span&gt;

&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Run tests&lt;/span&gt;
  &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions-rs/cargo@v1&lt;/span&gt;
  &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;command&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;test&lt;/span&gt;
    &lt;span class="na"&gt;args&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;--locked --workspace&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Instead of getting the database logs post-mortem, I thought it would help to be able to get them closer to the events that generated them, so I combined these two steps into:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Run tests&lt;/span&gt;
  &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
      &lt;span class="s"&gt;docker compose up timescaledb &amp;amp;&lt;/span&gt;
      &lt;span class="s"&gt;cargo test --locked --workspace&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Running the CI now provides the database logs along the running tests, and some interesting bits started showing up:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;2024-04-01 08:10:19.415 UTC [443] ERROR:  deadlock detected
2024-04-01 08:10:19.415 UTC [443] DETAIL:  Process 443 waits for ShareLock on relation 16529 of database 24363; blocked by process 409.
        Process 409 waits for AccessExclusiveLock on advisory lock [24363,1001,0,29749]; blocked by process 443.
        Process 443: CALL _timescaledb_internal.policy_refresh_continuous_aggregate()
        Process 409: [ Migration step SQL here ]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At this point tests were still running and none failed yet. Then:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;2024-04-01 08:22:53.991 UTC [104] ERROR:  deadlock detected
2024-04-01 08:22:53.991 UTC [104] DETAIL:  Process 104 waits for AccessExclusiveLock on advisory lock [21387,1005,0,29749]; blocked by process 188.
        Process 188 waits for ShareLock on relation 15242 of database 21387; blocked by process 104.
        Process 104: [ Migration step SQL here ]
        Process 188: CALL _timescaledb_internal.policy_refresh_continuous_aggregate()
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is when the first test failure could be noticed. Shortly after, when all the remaining tests either finished or were skipped, the entire CI job failed.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Speculations and observations: Part 2&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The logs shed more light on what is happening. In the first log snippet, we see a deadlock taking place between a migration step and a background worker. However, this does not result in the test failure. The second log snippet shows the same thing, but the processes are reversed. This &lt;em&gt;does&lt;/em&gt; result in a test failure.&lt;/p&gt;

&lt;p&gt;PostgreSQL periodically checks for deadlocks and if it finds any it will stop one of the processes involved to allow the other one to continue. It looks like if the background worker gets killed, the migration step continues and the test moves on. But if the migration step process is killed (unlocking the background worker in the process) the test fails with a migration error.&lt;/p&gt;

&lt;p&gt;Up until now all the failures happened at a given migration step. The step served to change the datatype of some columns in a hypertable (let’s use the table name &lt;code&gt;data&lt;/code&gt;), including the column used as the time series. This requires re-creating the &lt;code&gt;data&lt;/code&gt; table, so the step does something along the lines of:&lt;br&gt;
1) Rename the &lt;code&gt;data&lt;/code&gt; table to &lt;code&gt;data_old&lt;/code&gt;;&lt;br&gt;
2) Create a new table named &lt;code&gt;data&lt;/code&gt;;&lt;br&gt;
3) Move data from &lt;code&gt;data_old&lt;/code&gt; to &lt;code&gt;data&lt;/code&gt;;&lt;br&gt;
4) Make &lt;code&gt;data&lt;/code&gt; a hypertable;&lt;br&gt;
5) Create indices, triggers and various TimescaleDB policies;&lt;br&gt;
6) Drop some obsolete continuous aggregates based on the &lt;code&gt;data_old&lt;/code&gt;table;&lt;br&gt;
7) Drop the &lt;code&gt;data_old&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Was there an underlying issue to how we did migrations, combining vanilla PostgreSQL and TimescaleDB specific actions in some of the bigger steps, like the one above? The logs provide a SQL snippet containing the code related to dropping those obsolete continuous aggregates. It seemed like a long-shot, but perhaps if we somehow dropped these before this step we would observe a different behavior?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Intermediary migrations&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Modifying existing migrations is a no-go. SQLx keeps track of the migrations that have already run and does some integrity checks to ensure they have not changed. But could we maybe add a new step somewhere in the middle? It seems like we can!&lt;/p&gt;

&lt;p&gt;Given a migrations folder such as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;migrations/
    20231129000000_first_step.sql
    20231202000000_second_step.sql
    20240105000000_third_step.sql
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can add a migration step in the middle by giving it a timestamp that is situated between steps.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;migrations/
    20231129000000_first_step.sql
    20231202000000_second_step.sql
    20231202000001_intermediary_step.sql
    20240105000000_third_step.sql
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The caveat here is how this behaves on databases that already went through the previous migrations vs test databases that start from scratch. For the former, the intermediary step actually gets run &lt;em&gt;at the end&lt;/em&gt;, because SQLx knows that it already ran all the other migrations. For the latter, migrations will be ran in order. This means that our intermediary migration step has to be idempotent regardless of whether in runs in or out of order.&lt;/p&gt;

&lt;p&gt;So with this in mind I added such an intermediary step that removed the obsolete continuous aggregates if they existed to see whether that impacts the deadlock occurrence. Since these objects were obsolete we have the luxury of dropping them if they exist at any point in the migration process.&lt;/p&gt;

&lt;p&gt;The result was that deadlocks still occurred, but their frequency was reduced. If they previously happened in 80% of the runs, they would now happen in about 60%. So something seemed to have changed.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Circling back to TimescaleDB&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;At this point our CI test suite was running quite slowly, primarily due to the limited resources available in our GitHub runners. We were using the &lt;code&gt;timescale/timescaledb-ha:pg15-latest&lt;/code&gt; Docker image, so I figured I could take another shot in the dark and see if there’s any difference with the latest image, &lt;code&gt;timescale/timescaledb-ha:pg16&lt;/code&gt;. Newer versions come with bug fixes and performance increases so it was worth trying, if only for comparison’s sake.&lt;/p&gt;

&lt;p&gt;The good news is that this further decreased the deadlock frequency to around 40%. The bad news is that the CI was still inconsistent. Furthermore, migrations would fail at other steps too (including the intermediary migration step I added above), so this rules out the issue being related to a single migration script. This in itself conveys some information; it points to a broader issue, unrelated to singular migration steps but rather to a broader issue when applying migrations containing TimescaleDB specific syntax.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Speculations and observations: Part 3&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;It began obvious that this was not going anywhere and I needed to shift approaches. Waiting for the CI to run was slow and time-consuming and I had too little control and access to properly understand what’s going on.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Constrained environment&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Thinking about the slowness of the CI and resource contraints I thought I could simply limit the resources of a TimescaleDB Docker container and see whether I can replicate the issue locally.&lt;/p&gt;

&lt;p&gt;Eventually, after limiting the CPU usage of the container further and further, I started getting the issue locally too when running TimescaleDB like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker run &lt;span class="nt"&gt;--rm&lt;/span&gt; &lt;span class="nt"&gt;--name&lt;/span&gt; ts-container &lt;span class="nt"&gt;--env&lt;/span&gt; &lt;span class="nv"&gt;POSTGRES_USER&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;postgres &lt;span class="nt"&gt;--env&lt;/span&gt; &lt;span class="nv"&gt;POSTGRES_PASSWORD&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;postgres &lt;span class="nt"&gt;--memory&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;512M &lt;span class="nt"&gt;--cpus&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"0.125"&lt;/span&gt; timescale/timescaledb-ha:pg16
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So when resources are low, both migrations and background workers take too long to finish and concurrency issues start occurring in the form of deadlocks. I started looking through TimescaleDB docs and forums and stumbled unpon something interesting: it looks like it was primarily designed to work on a single database per instance.&lt;/p&gt;

&lt;p&gt;Documentation is sparse, but these are a few references:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.timescale.com/migrate/latest/troubleshooting/#only-one-database-per-instance"&gt;https://docs.timescale.com/migrate/latest/troubleshooting/#only-one-database-per-instance&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.timescale.com/forum/t/does-timescale-cloud-support-multiple-databases/583"&gt;https://www.timescale.com/forum/t/does-timescale-cloud-support-multiple-databases/583&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.timescale.com/use-timescale/latest/configuration/advanced-parameters/#multiple-databases"&gt;https://docs.timescale.com/use-timescale/latest/configuration/advanced-parameters/#multiple-databases&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Isolation: Database vs Schema
&lt;/h3&gt;

&lt;p&gt;Since &lt;code&gt;sqlx::test&lt;/code&gt; creates a new database for every test, performance seems to be affected due to the amount of TimescaleDB background jobs that get started. This makes the overall database server slower and more prone to deadlocks. The alternative presented in the timescale forums is isolating data through schemas rather than databases.&lt;/p&gt;

&lt;p&gt;When isolating tests through separate databases, TimescaleDB manages state, settings and configuration through various internal schemas. Background jobs dedicated to one database get created so when creating multiple databases a set of background jobs and internal schemas get created for each of them.&lt;/p&gt;

&lt;p&gt;If we were to isolate tests through schemas instead, we could increase performance because all the TimescaleDB related objects and background processes would be shared across all the tests, lowering the number of concurrent background workers.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Further down the rabbit hole&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;At this point it became clear that I’d have to either revert to the old sequential testing approach or get my hands dirty with some SQLx internals. Being the relentless individual that I am, I decided to give the latter a go. The goal still has not changed - avoiding boilerplate just to replicate the &lt;code&gt;sqlx::test&lt;/code&gt; proc macro. It just looks like we will have to use more custom code than initially anticipated.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Schema based isolation&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Fortunately for me, I was already somewhat familiar with SQLx’s internals due to the &lt;a href="https://docs.rs/sqlx-exasol/latest/sqlx_exasol"&gt;Exasol driver&lt;/a&gt; I wrote as a third party SQLx driver.&lt;/p&gt;

&lt;p&gt;For PostgreSQL, the most relevent part of the code is &lt;a href="https://github.com/launchbadge/sqlx/blob/main/sqlx-postgres/src/testing/mod.rs"&gt;here&lt;/a&gt;. With this in mind I changed some things around to rely on schemas instead of databases and even simplified some parts of the implementation as this was always meant to be for internal use only..&lt;/p&gt;

&lt;p&gt;Additionally, a custom implementation of the &lt;code&gt;TestFn&lt;/code&gt; trait was required and used in the &lt;code&gt;__private_test&lt;/code&gt; proc macro. The custom &lt;code&gt;TestFn&lt;/code&gt; impl looks something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="nd"&gt;#[derive(Debug)]&lt;/span&gt;
&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;CustomTestFn&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Fut&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;PgPool&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Fut&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;where&lt;/span&gt;
    &lt;span class="n"&gt;Fut&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Future&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;impl&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Fut&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;TestFn&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;CustomTestFn&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Fut&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="k"&gt;where&lt;/span&gt;
    &lt;span class="n"&gt;Fut&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Future&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;Output&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Fut&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Output&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;run_test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;TestArgs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;Self&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Output&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nn"&gt;sqlx&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;test_block_on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;move&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;schema&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pool&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;setup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="k"&gt;.await&lt;/span&gt;&lt;span class="nf"&gt;.expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"test setup failed"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

            &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="na"&gt;.0&lt;/span&gt;&lt;span class="p"&gt;)(&lt;/span&gt;&lt;span class="n"&gt;pool&lt;/span&gt;&lt;span class="nf"&gt;.clone&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;&lt;span class="k"&gt;.await&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

            &lt;span class="nf"&gt;teardown&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;schema&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;pool&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="k"&gt;.await&lt;/span&gt;
                &lt;span class="nf"&gt;.expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"test teardown failed"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

            &lt;span class="n"&gt;res&lt;/span&gt;
        &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This makes the &lt;code&gt;__private_test&lt;/code&gt; proc macro look something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="nd"&gt;#[proc_macro_attribute]&lt;/span&gt;
&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;__private_test_fn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_args&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;TokenStream&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;TokenStream&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;TokenStream&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;input&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nd"&gt;parse_macro_input!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;input&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;ItemFn&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;last&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;input&lt;/span&gt;
        &lt;span class="py"&gt;.block&lt;/span&gt;
        &lt;span class="py"&gt;.stmts&lt;/span&gt;
        &lt;span class="nf"&gt;.pop&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="nf"&gt;.expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"sqlx::test always generates statements"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// Generate and store a new statement to override the local variable&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;test_fn_stmt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nd"&gt;quote!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;db_internal&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;CustomTestFn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;);)&lt;/span&gt;&lt;span class="nf"&gt;.into&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="n"&gt;input&lt;/span&gt;
        &lt;span class="py"&gt;.block&lt;/span&gt;
        &lt;span class="py"&gt;.stmts&lt;/span&gt;
        &lt;span class="nf"&gt;.push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nd"&gt;parse_macro_input!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;test_fn_stmt&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;Stmt&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

    &lt;span class="c1"&gt;// Add the last statement back&lt;/span&gt;
    &lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="py"&gt;.block.stmts&lt;/span&gt;&lt;span class="nf"&gt;.push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;last&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="nd"&gt;quote!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;#&lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nf"&gt;.into&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Unfortunately, even with this change in isolation, deadlocks frequency would still be around 25%. It’s much lower compared to what we started off with but still unnacceptably high for an automated process.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Treating the symptoms&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;At this point it’s apparent that we are focusing on the symptoms rather than the cause. We know this is a concurrency issue and it’s happening between TimescaleDB background jobs and some of the migration scripts. We’re missing something. The small and silly kind.&lt;/p&gt;

&lt;p&gt;I kept running test suites locally, looking through logs, trying to find some a new lead. And then I saw it. When it came to deadlock errors, they &lt;strong&gt;all&lt;/strong&gt; had something in common because of schema based isolation and because I ran the test suite locally multiple times on the same Docker container. This meant that all tests would use the same TimescaleDB internal objects and that these objects would not change since the container was the same.&lt;/p&gt;

&lt;p&gt;So let’s take another look at a log snippet:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;2024-04-01 08:22:53.991 UTC [104] ERROR:  deadlock detected
2024-04-01 08:22:53.991 UTC [104] DETAIL:  Process 104 waits for AccessExclusiveLock on advisory lock [20839,1005,0,29749]; blocked by process 188.
        Process 188 waits for ShareLock on relation 17261 of database 20839; blocked by process 104.
        Process 104: [ Migration step SQL here ]
        Process 188: CALL _timescaledb_internal.policy_refresh_continuous_aggregate()
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What is relation 17261? I’m no PostgreSQL guru so finding a way to figure this out required a short search. As it turns out, this actually is very easy to find out:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="mi"&gt;17261&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;regclass&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Which outputs:&lt;/p&gt;

&lt;p&gt;regclass&lt;/p&gt;




&lt;p&gt;_timescaledb_config.bgw_job&lt;/p&gt;

&lt;p&gt;This is always one of the objects being contended, locked by a transaction that then waits on something else and waited by a second transaction that locked the something else the first one wants!&lt;/p&gt;

&lt;h3&gt;
  
  
  Mitigating the cause
&lt;/h3&gt;

&lt;p&gt;I could not find a lot of information about this table specifically, but right away I could tell it’s internal to TimescaleDB. Querying it returns a list of jobs that exist for the database. This led to the following revelation:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Background jobs run periodically, so background workers access this table to check whether or when they should run which may involve updating the table;&lt;/li&gt;
&lt;li&gt;At the same time, migration scripts issue all kinds of TimescaleDB specific statements and some of them definitely interact with this table due to their nature (i.e., adding, removing or modifying background jobs).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A less abstract scenario may look as follows.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A background worker could update the &lt;code&gt;_timescaledb_config.bgw_job&lt;/code&gt; in a transaction and then want to do something with the &lt;code&gt;data&lt;/code&gt; table;&lt;/li&gt;
&lt;li&gt;At the same time, a migration step would alter the &lt;code&gt;data&lt;/code&gt; table, and also re-create some background jobs within the same transaction (which implies updating the &lt;code&gt;_timescaledb_config.bgw_job&lt;/code&gt; table).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When running in a constrained environment and things move slow, neither of these transactions finish in due time and end up running together, locking each other. The scenario above is just an example, but the idea is always the same: deadlocks between two transactions involve two objects. To avoid deadlocks, one has to ensure that there’s never contention on both objects at the same time.&lt;/p&gt;

&lt;p&gt;In our case, &lt;code&gt;_timescaledb_config.bgw_job&lt;/code&gt; is always one of the objects. The other object seems to vary, because there are multiple migration steps that use TimescaleDB related syntax and it only seems to be a matter of processing power where and if a deadlock would occur.&lt;/p&gt;

&lt;p&gt;In conclusion, a permanent solution would be restricting access to the background jobs table, this way allowing migrations to run unbothered. As a result, background workers can then also run as usual without issues since migrations are over.&lt;/p&gt;

&lt;p&gt;This was incorporated as part of the custom impl of &lt;code&gt;TestFn&lt;/code&gt;, with the relevant bit looking like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Start a transaction to lock various TimescaleDB specific resources to &lt;/span&gt;
&lt;span class="c1"&gt;// prevent deadlocks between migrations and TimescaleDB background workers.&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;tx&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="nf"&gt;.begin&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="k"&gt;.await&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;tx&lt;/span&gt;&lt;span class="nf"&gt;.execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"LOCK TABLE _timescaledb_config.bgw_job IN SHARE ROW EXCLUSIVE MODE;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="k"&gt;.await&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;migrator&lt;/span&gt;&lt;span class="nf"&gt;.run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;tx&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="k"&gt;.await&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;tx&lt;/span&gt;&lt;span class="nf"&gt;.commit&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="k"&gt;.await&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The primary objective is to start a transaction that spans across &lt;em&gt;all&lt;/em&gt; migration steps and first and foremost lock the background jobs table. The chosen lock allows concurrent &lt;code&gt;READS&lt;/code&gt; on the table, but only allows one transaction to &lt;code&gt;WRITE&lt;/code&gt; to the table.&lt;/p&gt;

&lt;p&gt;While we could alternatively lock the background jobs table before each migration step and run each of them in its own transaction, it would only increase contention on the table because all tests would try to lock the same object for every migration step. Locking it and running all migrations at once is faster.&lt;/p&gt;

&lt;h2&gt;
  
  
  The long awaited victory
&lt;/h2&gt;

&lt;p&gt;Running the test suite now would still pose problems sometimes, but unlike previous cases deadlocks would now happen when &lt;code&gt;DROP SCHEMA&lt;/code&gt; is invoked. Therefore we have to add the background jobs table lock in our teardown routine as well. Many succesive test suite runs were now succesful. Given that many things here were experiments, I went ahead and removed the the intermediary migration steps and reverting the TimescaleDB Docker image used to &lt;code&gt;timescale/timescaledb-ha:pg15-latest&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;So what about test isolation? Well, I would’ve never seen the same relation ID in logs if not for schema based isolation and that’s what led me to try locking the &lt;code&gt;_timescaledb_config.bgw_job&lt;/code&gt; table. This increased debuggability as well as the reduced number of background jobs were good enough reasons to keep using this approach.&lt;/p&gt;

&lt;p&gt;After a couple of weeks, though, deadlocks between some TimescaleDB internals started happening every now and then when dropping test schemas. The frequency was very low in CI; so low that a subsequent run was pretty much guaranteed to succeed. Also, regardless of how few resources I’d give to a TimescaleDB Docker container it would never happen locally. Therefore, we decided to switch back to database test isolation instead hoping that it bypasses some, if not all, of the TimescaleDB internals involved here.&lt;/p&gt;

&lt;p&gt;The problem seems to be caused by friction on some advisory locks when dropping a schema, which also deletes background jobs, and some of the background jobs are already running. I reported it to the TimescaleDB team &lt;a href="https://github.com/timescale/timescaledb/issues/6848"&gt;here&lt;/a&gt; and not only did they reply quickly but they even shared ideas to further test in our quest to getting consistent test suite runs in CI. We’ll experiment with them and see what comes out of it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Conclusion&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This brings us to the end of our journey. It’s interesting how something so seemingly simple can raise such convoluted problems.&lt;/p&gt;

&lt;p&gt;It all started from not wanting to rely on the &lt;code&gt;DATABASE_URL&lt;/code&gt; environment variable and aiming to make tests concurrent and we ended up writing wrapper proc macros, creating our own &lt;code&gt;TestFn&lt;/code&gt; SQLx test harness implementation and debugging deadlocks on TimescaleDB internals. The result? Dramatically reduced testing time due to running tests concurrently!&lt;/p&gt;

&lt;p&gt;I hope you enjoyed reading this article and that the ideas contained within will come in handy at some point. If instead you wanted to find out more about IoT devices cybersecurity, feel free to check out our project on &lt;a href="https://github.com/exein-io/pulsar"&gt;GITHUB&lt;/a&gt; :) &lt;/p&gt;

</description>
      <category>security</category>
      <category>rust</category>
      <category>sql</category>
      <category>testing</category>
    </item>
    <item>
      <title>Elevating Security with Meta-Exein: Achieving Yocto Project Compatibility</title>
      <dc:creator>Exein</dc:creator>
      <pubDate>Thu, 14 Mar 2024 11:18:43 +0000</pubDate>
      <link>https://forem.com/exein/elevating-security-with-meta-exein-achieving-yocto-project-compatibility-2ik8</link>
      <guid>https://forem.com/exein/elevating-security-with-meta-exein-achieving-yocto-project-compatibility-2ik8</guid>
      <description>&lt;p&gt;In the changing field of cybersecurity, staying ahead of threats requires constant vigilance and the use of current, reliable technology. We’re excited to share that our high-tech software &lt;a href="https://github.com/Exein-io/pulsar?ref=blog.exein.io"&gt;Pulsar&lt;/a&gt; is now available through the meta-exein layer, which has been officially recognized as a Yocto Project Compatible layer. This important step shows our dedication to providing top-level security to the open source community and makes the Yocto Project system even stronger.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Understanding the Yocto Project Compatibility&lt;/strong&gt;&lt;br&gt;
Before we talk about the impact of meta-exein, we need to understand what &lt;a href="https://www.yoctoproject.org/development/yocto-project-compatible-layers/?ref=blog.exein.io"&gt;Yocto&lt;/a&gt; Project Compatibility means. The Yocto Project provides tools and resources to help developers build custom Linux systems for any hardware. Being compatible with this project is a big deal; it means a product or software meets high standards and works well with others, making it reliable and easy to integrate.&lt;/p&gt;

&lt;p&gt;If a product is compatible, it’s met certain standards that help make developing and building more efficient.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Role and Importance of Meta-Exein&lt;/strong&gt;&lt;br&gt;
Meta-exein isn’t just another part of the Yocto Project. It’s a big step forward in making embedded systems safer from cyber threats. With cyber-attacks becoming more common and tricky, we need better security now more than ever. Meta-exein offers a strong, adaptable security system that fits right into any Yocto Project-based development, with target x86_64, aarch64 and RiscV.&lt;/p&gt;

&lt;p&gt;The fact that meta-exein is now an official Yocto Project Compatible layer shows that it’s reliable and effective. It proves that meta-exein offers good, compatible security solutions within the wider Yocto Project ecosystem. This means developers and organizations can use a checked, top-quality security layer in their projects. This layer can boost protection without giving up efficiency or flexibility. Meta-exein is compatible with other systems, facilitating its use by developers.&lt;/p&gt;

&lt;p&gt;Additionally, as part of the Yocto Project, meta-exein receives regular updates and support from a vast community of developers and security experts. This ensures rapid detection and resolution of any issues, maintaining your system security.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Looking Forward&lt;/strong&gt;&lt;br&gt;
Despite this achievement, we’re still focused on improving meta-exein to counter the constantly changing security threats.&lt;/p&gt;

&lt;p&gt;We encourage developers, groups, and enthusiasts to discover how meta-exein can benefit their Yocto Project-based projects. By working together, we can create a stronger and more secure system, ready for current and future challenges.&lt;/p&gt;

&lt;p&gt;In conclusion, meta-exein being an official Yocto Project Compatible layer is not just proof of its quality and dependability; it’s a call to join us in making a safer future for embedded systems worldwide.&lt;/p&gt;

&lt;p&gt;If you’re interested in seeing meta-exein in action and how it can improve your security, we invite you to check out our &lt;a href="https://github.com/Exein-io/pulsar?ref=blog.exein.io"&gt;GitHub page.&lt;/a&gt;&lt;/p&gt;

</description>
      <category>yocto</category>
      <category>yoctoproject</category>
      <category>security</category>
      <category>cybersecurity</category>
    </item>
    <item>
      <title>Container Support in Pulsar</title>
      <dc:creator>Exein</dc:creator>
      <pubDate>Fri, 01 Mar 2024 13:24:50 +0000</pubDate>
      <link>https://forem.com/exein/container-support-in-pulsar-jea</link>
      <guid>https://forem.com/exein/container-support-in-pulsar-jea</guid>
      <description>&lt;p&gt;Our Rust Engineer, Michal Rostecki, recently discussed about how Pulsar supports containers and enhances their security. This blog post is a simplified overview of his talk.&lt;/p&gt;

&lt;p&gt;Pulsar is an open-source project that stands out for people looking for a strong security observability framework. It was first made for IoT devices, but because of its flexible rule language and complete default rule set, it can be used more widely. This flexibility lets users make custom rules based on Pulsar's specific event hooks, making it useful beyond just IoT devices.&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/IPgZDJMQ2E0"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;Michal's presentation introduces Pulsar's features, focusing on its ability to protect container workloads. He explains the importance of Pulsar's rule system and engine, which allow users to set up custom security policies for their specific operations. Through hands-on examples, Michal shows how Pulsar uses the Linux kernel's eBPF technology to track and respond to different system events, from process activities to network operations.&lt;/p&gt;

&lt;p&gt;A key point in the presentation is the new container support in Pulsar, a big step for the project. This feature lets Pulsar monitor processes in containers, improving security in environments using containers. Michal gives information on how Pulsar can track events from containers, adding extra details like image URIs and checksums. This improvement makes it easier to create rules which target container workloads and even specific containers (recognized by their images).&lt;/p&gt;

&lt;p&gt;Additionally, Michal discusses usual threats to containers like escape attempts and unauthorized access to host resources. He shows how Pulsar's advanced rules can identify and stop these threats early, highlighting Pulsar's effectiveness in keeping containerized environments safe.&lt;/p&gt;

&lt;p&gt;Check out Pulsar's GitHub repository and website to learn more. There, you'll find guides and how-tos. It's a great place to share information and contribute.&lt;/p&gt;

&lt;p&gt;If you're interested in cybersecurity, Michal’s talk gives a detailed look at how Pulsar is leading the way in making container environments safer. As containers are now a common part of deploying applications, Pulsar's progress here is an important move towards a safer digital world.)&lt;/p&gt;

&lt;p&gt;Michal's presentation introduces Pulsar's features, focusing on its ability to protect container workloads. He explains the importance of Pulsar's rule system and engine, which allow users to set up custom security policies for their specific operations. Through hands-on examples, Michal shows how Pulsar uses the Linux kernel's eBPF technology to track and respond to different system events, from process activities to network operations.&lt;/p&gt;

&lt;p&gt;A key point in the presentation is the new container support in Pulsar, a big step for the project. This feature lets Pulsar monitor processes in containers, improving security in environments using containers. Michal gives information on how Pulsar can track events from containers, adding extra details like image URIs and checksums. This improvement makes it easier to create rules which target container workloads and even specific containers (recognized by their images).&lt;/p&gt;

&lt;p&gt;Additionally, Michal discusses usual threats to containers like escape attempts and unauthorized access to host resources. He shows how Pulsar's advanced rules can identify and stop these threats early, highlighting Pulsar's effectiveness in keeping containerized environments safe.&lt;/p&gt;

&lt;p&gt;Check out Pulsar's GitHub repository and website to learn more. There, you'll find guides and how-tos. It's a great place to share information and contribute.&lt;/p&gt;

&lt;p&gt;If you're interested in cybersecurity, Michal’s talk gives a detailed look at how Pulsar is leading the way in making container environments safer. As containers are now a common part of deploying applications, Pulsar's progress here is an important move towards a safer digital world.&lt;/p&gt;

</description>
      <category>iot</category>
      <category>security</category>
      <category>workshop</category>
      <category>programming</category>
    </item>
    <item>
      <title>Container Monitoring in Pulsar 0.7.0</title>
      <dc:creator>Exein</dc:creator>
      <pubDate>Thu, 21 Dec 2023 18:27:31 +0000</pubDate>
      <link>https://forem.com/exein/container-monitoring-in-pulsar-070-1bej</link>
      <guid>https://forem.com/exein/container-monitoring-in-pulsar-070-1bej</guid>
      <description>&lt;p&gt;&lt;strong&gt;Pulsar 0.7.0&lt;/strong&gt;&lt;br&gt;
The Exein team has released a new version of &lt;a href="https://github.com/Exein-io/pulsar?ref=blog.exein.io"&gt;Pulsar&lt;/a&gt;, 0.7.0. Pulsar is a runtime security agent for Linux systems, designed with specific focus on performance and low overhead. Powered by eBPF, it traces system activity from the Linux kernel and lets you apply security policies in real time on top of the events traced.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What’s in 0.7.0&lt;/strong&gt;&lt;br&gt;
The latest release of Pulsar marks a significant advancement in its security capabilities, introducing crucial features such as container monitoring support and SMTP integration for threats logging.&lt;/p&gt;

&lt;p&gt;For a comprehensive overview of all the new features implemented, please refer to the detailed &lt;a href="https://github.com/exein-io/pulsar/blob/main/CHANGELOG.md?ref=blog.exein.io"&gt;release notes&lt;/a&gt; for 0.7.0 and the &lt;a href="https://pulsar.sh/docs/?ref=blog.exein.io"&gt;documentation&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Container monitoring support&lt;br&gt;
Pulsar 0.7.0 now supports container monitoring. This latest feature empowers Pulsar to extend its monitoring capabilities to containerized environments, allowing users to monitor the health and status of individual containers and identify potential vulnerabilities and threats.&lt;/p&gt;

&lt;p&gt;How does container detection work&lt;br&gt;
Pulsar employs eBPF to detect system activity. Containers are merely processes isolated by namespaces and cgroups. So, how can we determine if a new process belongs to a new container?&lt;/p&gt;

&lt;p&gt;We can identify new processes at the following tracepoints:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;sched_process_exec&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;sched_process_fork&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Both of these receive the information about a new process in the form of task_struct. When we go deeper in the members of that struct, we can retrieve the numeric IDs of namespaces.&lt;/p&gt;

&lt;p&gt;The mount namespace is always created, whether Docker or Podman is used. Therefore, we can detect if a new process comes with a new mount namespace.&lt;/p&gt;

&lt;p&gt;Catching the process with a new mount namespace doesn’t provide all the needed information, such as the container engine (e.g., Docker, Podman), unique container ID, and container image details. Task_struct and tracepoints don’t offer these answers, but there’s an alternative method.&lt;/p&gt;

&lt;p&gt;To identify the created container, one approach is to send a user-space event for the new process, a functionality consistently performed by Pulsar. Container engines typically assign container IDs to cgroups. By examining /proc//cgroup for any process within a container, the container ID can be extracted.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ cat /proc/18337/cgroup
15:misc:/libpod_parent/libpod-64cabea89c8766c5432d42ee9fe93c7f853bdc
[...]
2:cpuset:/libpod_parent/libpod-64cabea89c8766c5432d42ee9fe93c7f853bdc29055a30abdbe4e26c9001d904
1:name=openrc:/libpod_parent/libpod-64cabea89c8766c5432d42ee9fe93c7f853bdc29055a30abdbe4e26c9001d904
0::/libpod_parent/libpod-64cabea89c8766c5432d42ee9fe93c7f853bdc29055a30abdbe4e26c9001d904
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;p&gt;&lt;code&gt;$ podman ps&lt;br&gt;
CONTAINER ID  IMAGE                           COMMAND CREATED        STATUS        PORTS  NAMES&lt;br&gt;
64cabea89c87  docker.io/library/alpine:latest /bin/sh 55 seconds ago Up 55 seconds        tender_pascal&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;Once obtained, detailed information about the container and its image can be retrieved by referencing /var/lib/docker/containers//config.v2.json for Docker or using podman inspect –type=container  for Podman.&lt;br&gt;
For example, we can retrieve the container image URI with:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;$ podman inspect --type=container 64cabea89c87 --format "{{ .ImageName }}"&lt;br&gt;
docker.io/library/alpine:latest&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8viystwzk6rbtgxqqlhs.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8viystwzk6rbtgxqqlhs.png" alt="Image description" width="800" height="467"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;SMTP Integration for threats logging&lt;/strong&gt;&lt;br&gt;
Pulsar now features a robust SMTP integration, further improving its threat-logging capabilities. With this new feature, Pulsar can seamlessly send threat alerts via email, ensuring timely and effective communication during security incidents.&lt;/p&gt;

&lt;p&gt;Security administrators and IT teams can now configure Pulsar to send detailed threat notifications directly to designated email addresses. This functionality ensures that crucial threat information is promptly delivered to the right personnel, enabling swift responses to potential security breaches.&lt;/p&gt;

&lt;p&gt;To set up the SMTP feature, simply configure the following fields in the Pulsar configuration file:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;&lt;br&gt;
[smtp-notifier]&lt;br&gt;
enabled=true&lt;br&gt;
receivers=&amp;lt;receivers email address&amp;gt;&lt;br&gt;
server=&amp;lt;SMTP server&amp;gt;&lt;br&gt;
username=&amp;lt;username&amp;gt;&lt;br&gt;
password=&amp;lt;password&amp;gt;&lt;br&gt;
sender=&amp;lt;sender email address&amp;gt;&lt;br&gt;
port=587&lt;br&gt;
encryption=starttls&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Full changelog&lt;/strong&gt;&lt;br&gt;
Added support for monitoring containers within the core functionality &lt;a href="https://github.com/exein-io/pulsar/pull/213"&gt;#213&lt;/a&gt;&lt;br&gt;
Introduced a description field in the Threat structure, providing a human-readable description of the threat&lt;a href="https://github.com/exein-io/pulsar/pull/189"&gt; #189&lt;/a&gt;&lt;br&gt;
Added the namespaces field for events related to fork and exec operations. &lt;a href="https://github.com/exein-io/pulsar/pull/207"&gt;#207&lt;/a&gt;&lt;br&gt;
Implemented SMTP integration within the module for logging threats to sent threats also via email &lt;a href="https://github.com/exein-io/pulsar/pull/201"&gt;#201&lt;/a&gt;&lt;br&gt;
Added the ability to modules to display warnings as part of their functionality &lt;a href="https://github.com/exein-io/pulsar/pull/203"&gt;#203&lt;/a&gt;&lt;br&gt;
Added syslog capabilities to the module &lt;a href="https://github.com/exein-io/pulsar/pull/212"&gt;#212&lt;/a&gt;&lt;br&gt;
Introduced the enabled_by_default flag for every module, allowing the definition of default behavior &lt;a href="https://github.com/exein-io/pulsar/pull/220"&gt;#220&lt;/a&gt;&lt;br&gt;
Refactored preemption in the BPF probes &lt;a href="https://github.com/exein-io/pulsar/pull/188"&gt;#188&lt;/a&gt;&lt;br&gt;
Updated the Continuous Integration (CI) process to support OpenSSL. &lt;a href="https://github.com/exein-io/pulsar/pull/202"&gt;#202&lt;/a&gt;&lt;br&gt;
Updated the GitHub workflow in the CI process &lt;a href="https://github.com/exein-io/pulsar/pull/217"&gt;#217&lt;/a&gt;&lt;br&gt;
Resolved the issue introduced by changes in the kernel affecting the layout of the struct iov_iter &lt;a href="https://github.com/exein-io/pulsar/pull/226"&gt;#226&lt;/a&gt;&lt;br&gt;
Corrected the doctest in the validation module &lt;a href="https://github.com/exein-io/pulsar/pull/195"&gt;#195&lt;/a&gt;&lt;br&gt;
Implemented a check to verify the payload before applying the ruleset in the validatron module &lt;a href="https://github.com/exein-io/pulsar/pull/211"&gt;#211&lt;/a&gt;&lt;br&gt;
Disabled the stack protector in the build process&lt;a href="https://github.com/exein-io/pulsar/pull/205"&gt;#205&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Thank you for reading!&lt;br&gt;
If you like it, don't forget to try out Pulsar and give us a &lt;a href="https://github.com/Exein-io/pulsar?ref=blog.exein.io"&gt;star ⭐️&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbl9wtw3ntd9v9d0xhjmd.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbl9wtw3ntd9v9d0xhjmd.PNG" alt="Image description" width="793" height="608"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>rust</category>
      <category>opensource</category>
      <category>security</category>
      <category>github</category>
    </item>
  </channel>
</rss>
