Author: Lisa

Corporate Greed?

Corporate greed is an easy, convenient answer to why things are so expensive … but I’ve tracked profits and revenue for the global “big oil” companies for a while now. Other than Armaco, and generally Petrobras, profits are under 10% of revenue. “Record profits” seem, generally, to come from record sales. We’re not going through record high profit percentages.

Now, a company that “only” profited five billion dollars on 380b isn’t doing badly and could probably have gotten by if they only made 2 million – but that makes for an interesting study. Say oil is $100 per barrel. 380 billion dollars is 3.8 billion barrels sold (yes, I know many of these companies have other lines of business. This is a thought experiment, not a regulated financial filing). If they earned three billion dollars less that year, the price per barrel would only drop $0.79. Under 1% change. Now, if Armaco had only profited 3.4 billion last year instead of 93.4b, that would be a $20/barrel drop.

Raw data:

Company 2025 2024 2023 2022 2021 2020 2019 2018 2017 2016 2015 2014 2013 2012 2011 2010 2009 2008 2007 2006
Revenue ($B) Net profit ($B) Revenue ($B) Net profit ($B) Revenue ($B) Net profit ($B) Revenue ($B) Net profit ($B) Revenue ($B) Net profit ($B) Revenue ($B) Net profit ($B) Revenue ($B) Net profit ($B) Revenue ($B) Net profit ($B) Revenue ($B) Net profit ($B) Revenue ($B) Net profit ($B) Revenue ($B) Net profit ($B) Revenue ($B) Net profit ($B) Revenue ($B) Net profit ($B) Revenue ($B) Net profit ($B) Revenue ($B) Net profit ($B) Revenue ($B) Net profit ($B) Revenue ($B) Net profit ($B) Revenue ($B) Net profit ($B) Revenue ($B) Net profit ($B) Revenue ($B) Net profit ($B)
1. Saudi Aramco 446 93.4 481 106.2 495 121.3 604 161.1 400 110.0 230 49.0 330 88.2 356 111.1 n/a n/a n/a n/a n/a n/a n/a n/a n/a n/a n/a n/a n/a n/a n/a n/a n/a n/a n/a n/a n/a n/a n/a n/a
2. PetroChina 399 21.9 409 22.9 426 22.8 481 22.2 405 14.3 280 2.3 364 10.4 356 7.9 298 3.4 244 0.9 277 5.7 371 17.4 365 20.9 348 18.2 298 20.6 216 20.6 149 15.1 155 16.5 110 18.4 86 17.9
3. Sinopec 388 4.4 428 7.0 454 8.5 493 9.9 435 9.5 305 4.8 430 8.3 437 9.2 349 7.6 291 7.0 324 5.3 459 8.8 465 10.3 457 10.1 388 9.8 283 9.3 197 8.6 209 6.8 157 7.0 131 6.4
4. ExxonMobil 334 28.8 350 33.7 345 36.0 414 55.7 286 23.0 182 -22.4 265 14.3 279 20.8 290 19.7 227 7.8 260 16.2 412 32.5 438 32.6 481 44.9 486 41.1 383 30.5 311 19.3 477 45.2 405 40.6 378 39.5
5. Shell 274 17.8 289 16.1 323 19.4 386 42.3 273 20.1 183 -21.7 352 15.8 397 23.4 312 13.0 240 4.6 272 1.9 431 14.9 460 16.4 482 26.7 484 30.8 378 20.1 285 12.5 458 26.3 356 31.3 319 25.4
6. TotalEnergies 201 13.1 214 15.8 237 21.4 284 20.5 206 16.0 140 -7.2 200 11.3 209 11.4 171 8.6 150 6.2 165 5.1 236 4.2 251 11.2 257 14.7 257 17.1 212 14.1 183 11.7 264 20.4 217 18.0 193 15.8
7. BP 189 0.1 189 0.4 210 15.2 241 -2.5 158 7.6 180 -20.3 277 4.0 299 9.4 240 3.4 183 0.1 223 -6.5 354 3.8 379 23.5 387 11.0 376 25.2 297 -3.7 246 16.6 361 21.2 274 20.8 254 22.3
8. Chevron 189 12.5 203 17.7 201 21.4 246 35.5 162 15.6 95 -5.5 147 2.9 166 14.8 142 9.2 114 -0.5 138 4.6 212 19.2 229 21.4 242 26.2 254 26.9 205 19.0 172 10.5 273 23.9 221 18.7 210 17.1
9. Rosneft 101 3.6 109 11.7 107 14.9 n/a n/a n/a n/a n/a n/a n/a n/a n/a n/a n/a n/a n/a n/a n/a n/a n/a n/a n/a n/a n/a n/a 92 n/a 63 n/a n/a n/a n/a 11.1 n/a 6.5 n/a n/a
10. Petrobras 89 19.6 91 7.5 102 24.9 124 36.6 84 19.9 54 -1.1 77 10.2 85 7.2 89 0.1 81 -4.0 97 -10.0 144 -9.0 141 10.9 144 10.9 146 20.0 121 20.0 91 14.0 n/a n/a n/a n/a n/a n/a

Data as available. Publicly traded companies in US and Europe are far more apt to have published , audited, probably somewhat accurate data.

Flatpak – Escaping the Sandbox

We’ve been running Cura from a flatpak because the rpm distributed version was out of date. The big drawback, though, is that this flatpak could not see the files from network mounts. The mount is fine – in fstab, same user account can interact with those files in other applications. Just not this flatpak thing.

Turns out that’s normal – flatpaks operate in a sort of sandbox. You just have to tell it to let an individual flatpak access the location where the network mounts are. In this case, the /mnt path:

flatpack override com.ultimaker.cura --filesystem=/mnt

MiniPlasma Mitigation

Microsoft Windows Cloud Files Mini Filter Driver Elevation of Privilege Vulnerability (alternately referred to as MiniPlasma) is one of those findings that is a little confusing. The vulnerability appears tied to an issue Microsoft originally addressed in 2020, which raises the obvious question: if it was already patched, why is it relevant again now?

The original fix may have closed a specific exploitation path without fully eliminating the underlying bug class. The remediation may have been incomplete. Or a later code change may have reintroduced a previously fixed condition. Without updated vendor detail, it is hard to say exactly which of those happened; but the operational conclusion is the same: a system can still be exposed today even if the original 2020 patch was installed.

For environments looking for a practical mitigation, the good news is that many servers do not need Windows Cloud Files functionality at all. The vulnerable component is the Cloud Files Mini Filter Driver (CldFlt), which supports placeholder and hydration behavior used by features such as OneDrive Files On-Demand and other CfAPI-based integrations.

That makes CldFlt a viable mitigation target. If the filter is loaded but not attached to any volumes, there is a good chance it can be safely unloaded and disabled. This does not remove the driver from disk, but it does remove the active kernel attack surface associated with the running minifilter. Since this is an elevation-of-privilege issue, that distinction matters: the goal is not to claim the file no longer exists, but to prevent the vulnerable driver from being active in the system.

The following process checks whether any volumes are associated with CldFlt, temporarily unloads the filter, verifies that the system continues functioning normally, and then disables the driver persistently.

REM Check instances on CldFlt — if 0, proceed with testing disablement
fltmc filters

REM Example output:
REM Filter Name Num Instances Altitude Frame
REM —————————— ————- ———— —–
REM bindflt 0 409800 0
REM MsSecFlt 9 385600 0
REM CSAgent 9 321410.78870 0
REM storqosflt 0 244000 0
REM wcifs 0 189900 0
REM CldFlt 0 180451 0
REM FileCrypt 0 141100 0
REM UnionFS 0 130850 0
REM npsvctrig 1 46000 0
REM Wof 1 40700 0

REM Unload cldflt
fltmc unload cldflt

REM Verify stopped
sc query cldflt

REM Verify no longer in filters list
fltmc filters

REM Verify applications and expected file operations still work normally
REM If everything looks good, disable persistent startup
sc config cldflt start= disabled

Fedora 43 to 44 upgrade: dnf5 plugin load failure after upgrade

After completing an in-place upgrade from Fedora 43 to Fedora 44, dnf5 failed to run with this error:

[lisa@fedora05 ~]# dnf5 update
Cannot load dnf5 plugin: /usr/lib64/dnf5/plugins/automatic_cmd_plugin.so
Cannot load shared library “/usr/lib64/dnf5/plugins/automatic_cmd_plugin.so”: libdnf5-cli.so.2: cannot open shared object file: No such file or directory

What happened

The Fedora 44 upgrade completed, and the installed dnf5 packages were all current Fedora 44 versions. However, there was a leftover plugin file still sitting in /usr/lib64/dnf5/plugins/automatic_cmd_plugin.so.

That file was not owned by any RPM package and had been built against an older library, libdnf5-cli.so.2.

But Fedora 44 had /usr/lib64/libdnf5-cli.so.3.

dnf5 was trying to load a stale plugin from before the upgrade.

How I verified it

These commands showed the problem:

ls -l /usr/lib64/libdnf5-cli.so*
rpm -qf /usr/lib64/dnf5/plugins/automatic_cmd_plugin.so
ldd /usr/lib64/dnf5/plugins/automatic_cmd_plugin.so

Results:

  • libdnf5-cli.so.3 existed
  • automatic_cmd_plugin.so was not owned by any package
  • ldd showed it was looking for libdnf5-cli.so.2

Fix

Remove the orphaned plugin file:

rm /usr/lib64/dnf5/plugins/automatic_cmd_plugin.so
ldconfig
dnf5 update

After deleting the stale plugin, dnf5 worked normally again.

Root cause

This appears to be a leftover orphaned dnf5 plugin from before the major version upgrade. Even though the main dnf5 and libdnf5 packages were updated correctly, dnf5 still tried to load the old .so file it found in the plugins directory.

Kafbat – OAUTH With RBAC

I encountered a challenge with a Kafka management tool — it supports SSO, and I was able to get an OAUTH connection set up to control what users could see when logging in through the UI, but the API component didn’t extract information from the bearer token and there was nothing in the rbac mapping to allow the bearer-token client ID to access anything.

Updates to allow the /api components to be authenticated by simple bearer tokens and a client ID mapped into a role are at https://github.com/ljr55555/kafka-ui

AuthorizationController.java was updated to properly support non-browser, machine-principal auth.

Added/fixed:

  • avoids null failure when authentication.getName() is missing
  • resolves principal name from alternate attributes such as:
    • client_id
    • sub
    • username
  • updated displayed permissions logic so /api/authorization uses the same effective RBAC matching idea as the backend

Result

/api/authorization now works for bearer-token API callers and shows:

  • username = client ID
  • populated permissions list

AccessControlService.java

Added support for API bearer-token principals

Previously, getUser() only worked when the authenticated principal was a RbacUser, which covered the browser/user flow.

Now it can also derive an AuthenticatedUser from opaque-token authenticated principals by extracting:

  • principal name
  • group-like values from attributes/authorities if present

Updated role matching logic

Previously, role matching was only role name matches one of user.groups(). Now it also supports role name matches user.principal(). That enables RBAC binding directly to the API client ID.

Result

RBAC now works for:

  • normal browser users via groups
  • API bearer-token callers via client principal name

DynamicConfigMapper.java

Fixed a mapper bug.

Before

The method mapping resource server config created a populated OAuth2ResourceServerProperties result object but always returned null.

After

It now returns result.

Result

Dynamic/config mapping for resource-server settings no longer silently discards the mapped object.

Build/package note

To preserve the browser UI, the jar needs to be built with frontend included — which you know if you read the doc … or you take my route, start it all up, test the API successfully, and then get baffled that the user UI throws


        bash./gradlew clean assemble -Pinclude-frontend=true

application.yml

server:
  port: 8443
  ssl:
    enabled: true
    key-store: file:/etc/kafkaui/certs/kafbat.rushworth.us.p12
    key-store-password: ${KEYSTORE_PASSWORD}
    key-store-type: PKCS12
    key-alias: kafbat

auth:
  type: OAUTH2
  oauth2:
    client:
      pingfed:
        client-id: ${OAUTH_CLIENT_ID}
        client-secret: ${OAUTH_CLIENT_SECRET}
        scope:
          - openid
          - profile
          - email
        client-name: oauthclient
        provider: oauthclient
        redirect-uri: https://kafbat.rushworth.us:8443/login/oauth2/code/oauthclient
        authorization-grant-type: authorization_code
        issuer-uri: https://login.example.com
        jwk-set-uri: https://login.example.com/pf/JWKS
        authorization-uri: https://login.example.com/as/authorization.oauth2
        token-uri: https://login.example.com/as/token.oauth2
        user-info-uri: https://login.example.com/idp/userinfo.openid
        user-name-attribute: username
        custom-params:
          type: oauth
          roles-field: memberOf

    resource-server:
      opaque-token:
        client-id: ${OAUTH_CLIENT_ID}
        client-secret: ${OAUTH_CLIENT_SECRET}
        introspection-uri: https://login.example.com/as/introspect.oauth2

kafka:
  clusters:
    - name: test
      bootstrapServers: ${KAFKA_BOOTSTRAP_SERVERS} 

roles.yml

rbac:
  roles:
    - name: "admins"
      clusters:
        - test
      subjects:
        - provider: oauth
          type: role
          value: "CN=KafbatAdmins,OU=SecurityGroups,DC=example,DC=com"
      permissions:
        - resource: applicationconfig
          actions: all

        - resource: clusterconfig
          actions: all

        - resource: topic
          value: ".*"
          actions: all

        - resource: consumer
          value: ".*"
          actions: all

        - resource: schema
          value: ".*"
          actions: all

        - resource: connect
          value: ".*"
          actions: all

        - resource: ksql
          actions: all

        - resource: acl
          actions: [ view ]

    - name: "${OAUTH_CLIENT_ID}"
      clusters:
        - test
      subjects:
        - provider: oauth
          type: user
          value: "${OAUTH_CLIENT_ID}"
      permissions:
        - resource: applicationconfig
          actions: all

        - resource: clusterconfig
          actions: all

        - resource: topic
          value: ".*"
          actions: all

        - resource: consumer
          value: ".*"
          actions: all

        - resource: schema
          value: ".*"
 
        - resource: connect
          value: ".*"
          actions: all

        - resource: ksql
          actions: all

        - resource: acl
          actions: [ view ]
 

docker-compose.yml

services:
  redpanda:
    image: redpandadata/redpanda:v25.1.2
    container_name: redpanda
    command:
      - redpanda
      - start
      - --overprovisioned
      - --smp=1
      - --memory=1G
      - --reserve-memory=0M
      - --node-id=0
      - --check=false
      - --kafka-addr=PLAINTEXT://0.0.0.0:9092
      - --advertise-kafka-addr=PLAINTEXT://redpanda:9092
    ports:
      - "9092:9092"

  kafbat-ui:
    image: ghcr.io/kafbat/kafka-ui:latest
    container_name: kafbat-ui
    restart: unless-stopped
    depends_on:
      - redpanda
    ports:
      - "8443:8443"
    volumes:
      - ./config/application.yml:/etc/kafkaui/application.yml:ro
      - ./config/roles.yml:/etc/kafkaui/roles.yml:ro
      - ./certs:/etc/kafkaui/certs:ro
    environment:
      SPRING_CONFIG_LOCATION: file:/etc/kafkaui/application.yml
      SPRING_CONFIG_ADDITIONAL_LOCATION: file:/etc/kafkaui/roles.yml
      KEYSTORE_PASSWORD: REDACTED
      OAUTH_CLIENT_ID: REDACTED
      OAUTH_CLIENT_SECRET: REDACTED
      KAFKA_BOOTSTRAP_SERVERS: redpanda:9092

Docker for Java Builds

Instead of flipping back and forth between java versions for various builds, you can just use a docker container for the proper Java version to run the build.

[lisa@docker kafka-ui]# docker run --rm -it   --user $(id -u):$(id -g)   -v "$PWD":/workspace   -w /workspace   eclipse-temurin:25   bash -lc './gradlew clean build'
Downloading https://services.gradle.org/distributions/gradle-9.2.0-bin.zip
............10%.............20%.............30%.............40%.............50%.............60%.............70%.............80%.............90%.............100%

Welcome to Gradle 9.2.0!

Here are the highlights of this release:
 - Windows ARM support
 - Improved publishing APIs
 - Better guidance for dependency verification failures

For more details see https://docs.gradle.org/9.2.0/release-notes.html

Starting a Gradle Daemon (subsequent builds will be faster)
<=============> 100% CONFIGURING [1m 46s]
> Resolve dependencies of :api:detachedConfiguration273