Skip to main content

So You Hired a Contractor

This post originally appeared on LinkedIn on July 1st.

Let's imagine you decided to hire a consultant company to enhance your product, because your team lacks the intricate knowledge of the technology.

The Management is happy to have the product shipped faster, with more features, and on time.

This is not how it works, sorry.

Read more…

OpenAM creates OpenDJ accounts you don't know about

OpenAM 12.0.1 was recently released (for subscribers only), which fixes this issue. See Issue #201505-05.

TL;DR: If you configure OpenDJ using OpenAM configurator (both on the web or configurator tool), or if you ask OpenAM to load the LDAP schema via Data Sources page on Web UI after installation, your OpenDJ installation will get provisioned with two users: cn=openssouser and cn=ldapuser with default hardcoded passwords.

You can find these entries in OpenAM-X.war/WEB-INF/template/ldif/opendj/opendj_userinit.ldif:

dn: cn=openssouser,ou=opensso adminusers,@userStoreRootSuffix@
objectclass: inetuser
objectclass: organizationalperson
objectclass: person
objectclass: top
cn: openssouser
sn: openssouser

dn: cn=ldapuser,ou=opensso adminusers,@userStoreRootSuffix@
objectclass: inetuser
objectclass: organizationalperson
objectclass: person
objectclass: top
cn: ldapuser
sn: ldapuser
userPassword: @LDAP_USER_PASSWD@

While ldapuser has limited access, cn=openssouser has the following ACI:

aci: (target="ldap:///@userStoreRootSuffix@")(targetattr="*")(version 3.0; acl
"OpenSSO datastore configuration bind  user all rights under the root suffix";
allow (all) userdn = "ldap:///cn=openssouser,ou=opensso adminusers,@userStoreRootSuffix@"; )

Which means that it can do whatever it wants (except with its own entry, there are additional ACIs later in that file).

If this does not make you nervous yet, look at the userPassword values. Yes, you are right. The default password for cn=openssouser is @OPENSSO_USER_PASSWD@. The default password for cn=ldapuser is @LDAP_USER_PASSSWD@.

Read more…

MediaFire + Duplicity = Backup

Update (2016-02-23): The backend has been merged and is now part of official duplicity package. See this announcement for how to use it, there are important differences.

Update (2021-09-06): Backend was removed, see my post.


Your backups must always be encrypted. Period. Use the longest passphrase you can remember. Always assume the worst and don't trust anybody saying that your online file storage is perfectly secure. Nothing is perfectly secure.

MediaFire is an evolving project, so my statements may not be applicable, say, in 3 months. However, I suggest you to err on the side of caution.

Here are the things you need to be aware of upfront security-wise:

  • There is no second factor authentication yet for MediaFire web interface.

  • The sessions created from your username and password have a very long lifespan. It is not possible to destroy a session unless you change a password.

  • The web interface browser part can leak your v1 session since the file viewer (e.g. picture preview or video player) is forcing the connection to run over plain HTTP due to mixed content issues.

All python-mediafire-open-sdk calls are made through HTTPS, but if you are using MediaFire account in an untrusted environment of a coffee shop WiFi you will want to use a VPN.

My primary use case for this service is an encrypted off-site backup of my computers, so I found these risks to be acceptable.

Once upon a time

I was involved in Ubuntu One, and when the file synchronization project closed, I was left without a fallback procedure. I continued with local disk backups, searching for something that had:

  • an API which is easy to use,

  • a low price for 100GB of data,

  • an ability to publish files and folders to a wider internet if needed,

  • no requirement for a daemon running on my machine,

  • a first-party Android client.

Having considered quite a few possibilities, I ended up intrigued by MediaFire, partially because they had API, and they seemingly had a Linux client to upload things (which I was never able to download from their website), but there was not much integration with other software on my favorite platform. They had a first year promo price of $25/year, so I started playing with their API, "Coalmine" project was born, initially for Python 3.

When I got to the point of uploading a file through an API, I decided to upgrade to a paid account which does not expire.

Read more…

NIC.UA Under Attack

As of now, my primary hosting for this site is not accessible due to Ukrainian police action in data center.

According to twitter feed, the hosting servers are being seized allegedly due to separatist web sites hosting their domains at

Andrew Khvetkevich writes (translation mine):

The servers are seized because of separatist's domains. But we terminated them regularly! It does not make sense :( #nicua

This means that a lot of web sites will now be in limbo and if you are reading this, then my emergency hosting switch was successful.

Force Ekiga 3.x Network Interface Setting

This post originally appeared here on 2009-07-19.

New Ekiga versions do not allow setting the network interface to send the requests. This is now controlled by to underlying OPAL library and Ekiga developer does not see any problems. The problems that are caused by sending REGISTERs on all the available interfaces hoping that at least one will make its way to the server.

This manifested as the following message during the registration:

Could not register(Timeout)

Read about the workaround …

Gem rebuild gotcha

Production machines should never have anything compiled from source, and they should not have the tools to do that. Keeping that in mind I was packaging ruby gems using the Effing Package Management.

Usually I write rpm spec files manually when no existing ones fit our purposes, making sure the updates won't affect existing installation, however packaging 100+ rubygems was not the thing I would like to spend a day working on.

Enter fpm

$ gem fetch berkshelf
Fetching: berkshelf-3.1.5.gem (100%)
Downloaded berkshelf-3.1.5
$ fpm -s gem -t rpm berkshelf-3.1.5.gem
no value for epoch is set, defaulting to nil {:level=>:warn}
no value for epoch is set, defaulting to nil {:level=>:warn}
Created package {:path=>"rubygem-berkshelf-3.1.5-1.noarch.rpm"}

Great! Except that the thing does not build the dependencies, but it references them in Requires field:

$ rpm -qpR rubygem-berkshelf-3.1.5-1.noarch.rpm
rubygem(octokit) >= 3.0
rubygem(octokit) < 4.0
rubygem(celluloid) >= 0.16.0.pre
rubygem(celluloid) <
rubygem(celluloid-io) >= 0.16.0.pre
rubygem(celluloid-io) <

See that 0.16.0.pre version?

The Version field in the spec is where the maintainer should put the current version of the software being packaged. If the version is non-numeric (contains tags that are not numbers), you may need to include the additional non-numeric characters in the release field. -- Fedora Packaging Naming Guidelines

To make the story short, our berkshelf RPM will not be installable, celluloid RPM with version 0.16.0 will not satisfy 0.16.0.pre requirements.

A quick and dirty way of handling this would be to build celluloid RPM as is, but update berkshelf's gemspec to reference the version we can use.

Rebuilding gem

Should be as easy as gem unpack and gem build:

$ gem unpack berkshelf-3.1.5.gem
Unpacked gem: '/tmp/vendor/cache/berkshelf-3.1.5'
$ sed -i 's/0\.pre/0/' berkshelf-3.1.5/berkshelf.gemspec
$ gem build berkshelf-3.1.5/berkshelf.gemspec
fatal: Not a git repository (or any of the parent directories): .git
WARNING:  description and summary are identical
  Successfully built RubyGem
  Name: berkshelf
  Version: 3.1.5
  File: berkshelf-3.1.5.gem

Notice fatal: Not a git repository and look at the resulting gem:

$ ls -l berkshelf-3.1.5.gem
-rw-r--r-- 1 rye rye 4608 Oct 25 16:56 berkshelf-3.1.5.gem

The resulting gem is almost 5kiB, down from original 103K. Our gem is empty now.

Note: gem unpack --spec would produce yaml-formatted gemspec file which will not be accepted by gem build. fpm --no-gem-prerelease does not affect dependencies.

Enter git

Look at berkshelf.gemspec and notice that it uses git to provide the file listing:

  s.homepage                  = ''
  s.license                   = 'Apache 2.0'
  s.files                     = `git ls-files`.split($\)
  s.executables               = s.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
  s.test_files                = s.files.grep(%r{^(test|spec|features)/})

That's where the 'fatal' message is coming from, and since that appears to be a recommended way of writing the gemfile, that's what makes our resulting gem file empty (error from git ls-files is silently ignored). It is expected that the gem will be always built from git repository, which is not true in our case.

Again, fixing it quick and dirty way - making unpackged gem folder a git repository:

$ git init berkshelf-3.1.5
Initialized empty Git repository in /tmp/vendor/cache/berkshelf-3.1.5/.git/
$ pushd berkshelf-3.1.5
$ git add .
$ git commit -m "Dummy commit"
$ gem build berkshelf.gemspec
WARNING:  description and summary are identical
  Successfully built RubyGem
  Name: berkshelf
  Version: 3.1.5
  File: berkshelf-3.1.5.gem
$ mv berkshelf-3.1.5.gem ../
$ popd
$ ls -l berkshelf-3.1.5.gem
-rw-r--r-- 1 rye rye 105472 Oct 25 17:10 berkshelf-3.1.5.gem

Much better.

Final build

$ fpm -s gem -t rpm berkshelf-3.1.5.gem
no value for epoch is set, defaulting to nil {:level=>:warn}
no value for epoch is set, defaulting to nil {:level=>:warn}
Created package {:path=>"rubygem-berkshelf-3.1.5-1.noarch.rpm"}
$ rpm -qpR rubygem-berkshelf-3.1.5-1.noarch.rpm
rubygem(celluloid) >= 0.16.0
rubygem(celluloid) < 0.17.0
rubygem(celluloid-io) >= 0.16.0
rubygem(celluloid-io) < 0.17.0

While the original issue may be seen as a bug in FPM (will update the post if/when GitHub issue is created for that), the dependency on git for file listing may cause a bit of confusion for an unsuspected developer/release engineer.

Jenkins System Properties

I started doing much more work with jenkins lately and experiencing the void of solutions to some issues I am facing, I decided to start posting them here.

So today we are going to load some system properties on jenkins startup.

Jenkins allows Groovy hook scripts to be set up that are run early during startup or if jenkins experiences boot failure. Since these scripts use the same JVM as jenkins, we can set up a script that set up system properties directly or load from file.

Setup is simple, put to your $JENKINS_HOME and create init.groovy.d there too. Put the following groovy file under init.groovy.d:

load-properties.groovy (Source)

import jenkins.model.Jenkins
import java.util.logging.LogManager

def logger = LogManager.getLogManager().getLogger("")

/* JENKINS_HOME environment variable is not reliable */
def jenkinsHome = Jenkins.instance.getRootDir().absolutePath

def propertiesFile = new File("${jenkinsHome}/")

if (propertiesFile.exists()) {"Loading system properties from ${propertiesFile.absolutePath}")
    propertiesFile.withReader { r ->
        /* Loading java.util.Properties as defaults makes empty Properties object */
        def props = new Properties()
        props.each { key, value ->
            System.setProperty(key, value)

Now restart jenkins and observe the following output:

Sep 26, 2014 9:59:17 PM jenkins.InitReactorRunner$1 onAttained
INFO: Augmented all extensions
Sep 26, 2014 9:59:20 PM jenkins.InitReactorRunner$1 onAttained
INFO: Loaded all jobs
Sep 26, 2014 9:59:20 PM jenkins.util.groovy.GroovyHookScript execute
INFO: Executing /home/rye/.jenkins/init.groovy.d/load-properties.groovy
Sep 26, 2014 9:59:20 PM org.jenkinsci.main.modules.sshd.SSHD start
INFO: Started SSHD at port 48042
Sep 26, 2014 9:59:20 PM java.util.logging.LogManager$RootLogger log
INFO: Loading system properties from /home/rye/.jenkins/
Sep 26, 2014 9:59:20 PM jenkins.InitReactorRunner$1 onAttained
INFO: Completed initialization

Visit $JENKINS_URL/systemInfo (e.g. http://localhost:8080/systemInfo) and see your system property defined.

I needed this because the certificate I got from StartSSL was not trusted by JVM by default, so I had to override trustStore by creating a new keystore ($JENKINS_HOME/.keystore), importing StartSSL Class 1 certificate, and set system property.

Sad Acer A1 status update

This January after 3 years of horrible performance of buggy hardware and software my Acer Liquid E USB port has partially detached from its motherboard which prevented the device from being charged and accessed over USB.

Replacing the motherboard makes no sense since it's equivalent to buying another horrible broken Acer Liquid E device. Replacing 10 pin MiniUSB requires both compatible part CN 10PIN 215+916+2450 ACT and precision tools I don't have.

This brings the end to my attempts of fixing the thing that was not supposed to be broken from the start.

Acer Liquid E idle at 39.2 ℃

I've learned a lot about Android internals and kernel development. It inspired me to dig deeper and even join Samsung R&D Ukraine briefly to study embedded development, which made me realize that supporting a device without manufacturer assistance is an unthankful job.

The devices get released at an increased rate and deprecation of components brings the cost of support for existing devices prohibitively high. That means there are two purchasing options now - either an undertested or obsolete device.

Sad but true.