Recipe for a Private Flutter Package

Raveesh Agarwal
Flutter Community
Published in
5 min readJan 11, 2022

When you need private packages for your flutter project

Complimentary Controversial Meme

Real footage of me being yelled at for suggesting private packages for a project

“If every dev supported behavior like this there would not be a community or ecosystem to support all the work we do! You are evil and don’t deserve to use flutter. Get out of the community!!!”
-

Rest of the Blog

An engineering team can have justifiable reasons to create private packages. Some of them are:

  1. The package sucks. It just works for one use case and team has no intention to make it available for more
  2. The package contains licensed assets (like a package for the icon kit you purchased)
  3. The package contains secret sauce (like business logic, encryption algorithms etc)
  4. The infosec team mandates first party dependencies to be secure

This article is not a discussion of whether we need a private package or not. It is a recipe for when we do.

Get a Private Git Repo

For a package to be private, the source should be hosted on a private git repo. I use github for this example, but there is no reason why this should not be possible with a gitlab instance as well.

Connect to github from your machine via SSH. Here is how:

Add git package import as SSH-url

This is how your pubspec should look:

my_private_package:
git:
url: git@github.com:my_org/my_private_package.git
ref: target_tag

Explanation:

(brag: I was the one who got the SSH bits added there :-p )

  • git url needs to be a ssh url
  • taging the ref and using tags is for version control of the package
  • if your machine’s SSH_KEY is added to your github account, flutter pub getshould work
  • if you are on an unknown machine, pub get should fail

End of Story. Yaay! Private Package achieved. A million people subscribed to my blog and now I am rich and fam…

Let’s dive deeper.

CI - Codemagic!

Pub is able to download the git package on your machine because your machine has SSH access to your private git. But the CI does not.

In codemagic workflows, you need to set the SSH_KEY environment variable to get this working. Any variable that you declare ending with _SSH_KEY will get loaded to the SSH Agent.

These SSH keys are private keys, so they need to be stored and handled with extra care (more in conclusion)

More on this here:

For Workflows:

add environment variables to the workflow

For Codemagic.yaml:

Pass it in the environment while triggering builds. This is where:

pass the _SSH_KEY(s) as environment variables while triggering yaml builds

More info on triggering codemagic.yaml builds via API:

At this point your package works on your machine and on the CI! Now the only question left is…

Security?

There are two major issues with what we did so far:

  1. Handling a private key: The SSH key we are sending codemagic is a private key. Sending it over the internet again and again is not only insecure, but also there is no guarantee that if the key is compromised, a bad actor will not be able to use it
  2. The SSH key is at the moment scoped at an account level. So you are effectively giving codemagic read/write access to your entire account everytime you send your SSH Key.

Encryption — Guarantee that only codemagic can use the key.

codemagic’s environment variable tab for teams to store sensitive stuff

When using workflows, you can secure the SSH key when adding it and that is it.

Codemagic is increasingly promoting usage of their environment variable storage, even with codemagic.yaml based workflows. The steps are also simple.

  1. Add multiple _SSH_KEY in the Environment variables tab
  2. Make sure to keep the secure box ticked and press add
  3. Now these will be loaded to the SSH Agent on every build

This however did not procure a secure enough contract for me. For codemagic to be able to keep both the variable and the means to protect it. I opted for an alternative, more involved method(this method was needed for the complexity of our application).

  1. Stored the SSH_KEY in my own server somewhere(🤫🤐)
  2. Called this API to encrypt it with my account: https://docs.codemagic.io/rest-api/applications/#encrypt-an-environment-variable
  3. Stored the encrypted version, deleted the plaintext version

Effectively, I have the encrypted version of the SSH key, and it can only be used in a build within the codemagic account that it was encrypted by. Also, I store the key so codemagic can not make any unauthosised access.

The second method sounds like a better secure contract and also worked with the complexity of our applcation. You should be fine with either approaches.

Github Deploy Keys — Only provide the SSH that has access to the package

Deploy keys can help scope down the access of the SSH Keys
  • The SSH key we’ve been sending so far has access to our github account.
  • In repo settings, there is a tab called deploy keys.
  • Add the public key corresponding to the private key we encrypted in the last step here
  • The private key have access to only this repo and nothing else as long as you dont add the same public key in your github account
  • This way we have a separate key for just this package
  • You can add the same public key to multiple packages, keep it safe
  • You can pick whether the key has write access to the repo

Deploy Keys on Github:

  • flutter pub get
  • flutter pub run build_runner build — delete-conflicting-outputs
  • flutter run!

https://twitter.com/FlutterComm

--

--

Raveesh Agarwal
Flutter Community

Entrepreneur, software craftsman and technology enthusiast, I continue to solve problems and grow with my projects, partnerships and endeavors.