FreeIPA server upgrade CentOS 7 to *EL 9

This is not really a personal project, but I post it here for lack of other options, and I hope it helps someone.

Migrating FreeIPA servers from CentOS 7 to RHEL 9 is working, but not officially supported. There are two failures that reliably occurred and the solutions are described below.

Plan: Remove the original IPA servers and install new VMs with the same IP addresses and host names. Conditions: both OSes are updated to the newest patches as of 2024-01-17.

Procedure outline; without problems:

  1. (Generate SID for all users; this may prevent Problem 2)
  2. Remove ipa2 from topology using WebUI, then shutdown, install RHEL9.
  3. Join newly installed ipa2 to the IPA domain.
  4. Run migration from ipa1 to the new ipa2.
  5. Remove ipa1 from the topology and reinstall OS.
  6. Join ipa1 to domain.
  7. Replicate to ipa1.

Problem 1: Crash during the first replication from Cent7 master to new RHEL9 master (step 3)

The error looks something like this (in the output of ipa-replica-install --setup-ca):

CalledProcessError(Command [\'/usr/bin/openssl\', \'pkcs12\',
\'-in\',
\'/tmp/tmp7jrs5dqp/import.p12\', \'-clcerts\', \'-nokeys\',
\'-out\',
\'/var/lib/ipa/ra-agent.pem\', \'-password\',
\'file:/tmp/tmp7jrs5dqp/passwd\'] returned non-zero exit status 1:
\'Error outputting keys and
certificates\n80EB2D6B5D7F0000:error:0308010C:digital envelope
routines:inner_evp_generic_fetch:unsupported:crypto/evp/evp_fetch.c:346:Global
default library context, Algorithm (RC2-40-CBC : 0),
Properties ()\n\')\n')
[error] FileNotFoundError: [Errno 2] No such file or directory:
'/var/lib/ipa/ra-agent.key'
Your system may be partly configured.
Run /usr/sbin/ipa-server-install --uninstall to clean up.

Reference: https://lists.fedorahosted.org/archives/list/freeipa-users@lists.fedorahosted.org/thread/KNTSO4CTCSWOMJEEXHEDPN2CYZVJUIIO/ and linked bugs.

The solution is presented as updating the source IPA server – but this is not possible on Cent7/EPEL7. Instead the following modification can be made on the target server: Edit /etc/ssl/openssl.cnf and uncomment the following in [providers_sect] to allow the legacy cipher:

##legacy = legacy_sect
...
##[legacy_sect]
##activate = 1

You could re-comment them after the successful migration.

Problem 2: Unable to authenticate with Kerberos on the new IPA master

The problem may be prevented by generating SIDs for all users BEFORE the upgrade – see the ipa config-mod command below, but this was not tested. The following assumes that you didn’t do this, and explains how to fix the issue.

kinit admin works, but kinit doesn’t work for any other user. Actually it could work for some users, and will work for new users created after the migration. The error from kinit is:

kinit: Generic error (see e-text) while getting initial credentials

The error in /var/log/krb5kdc.log is something like this:

May 12 08:41:42 ipa6.example.com krb5kdc[1573](info): AS_REQ (4 etypes
{aes256-cts-hmac-sha1-96(18), aes256-cts-hmac-sha384-192(20), aes128-cts-hmac-sha256-128(19), aes128-cts-hmac-sha1-96(17)}) 192.168.0.6: HANDLE_AUTHDATA: user(a)EXAMPLE.COM for krbtgt/EXAMPLE.COM(a)EXAMPLE.COM, No such file or directory

From this posting: https://lists.fedorahosted.org/archives/list/freeipa-users@lists.fedorahosted.org/thread/RSAXURG6AR3MWONR3CZSOOI5ULDB2UVC/

The cause of these is that the SID is missing for these users. That’s the cause of the “No such file or directory” error. The SID is needed by FreeIPA’s kerberos after an upgrade, because Kerberos wants to genereate a PAC (Privileged Attribute Certificate) by default now. You can check for a SID with

ipa user-show USERNAME --all

There should be an “ipantsecurityidentifier” line. To fix this, sources will typically tell you to run:

ipa config-mod --enable-sid --add-sids

The problem is that this won’t work if there’s not a valid ID range for the user’s POSIX UID. It’s quite likely that the users won’t be in valid ID ranges after a migration – because the new replicas will have new ID ranges. There will be error messages in the relevant log file under /var/log/dirserv to this effect.

To fix this go into the IPA WebUI and create ID ranges for any UID in the domain. Then the --add-sids function will work, and then Kerberos will work. All the user’s IDs need to be within the range defined by Base ID and Range size for one of the listed ranges. The Primary RID base and Secondary RID base needs to be set to some values so the corresponding RID ranges don’t overlap with existing RID ranges. RID defines the last component of the SIDs that will be generated, and is the unique part per user or group within a domain.

Problem 3: Unable to complete the first replication

When performing the change in the production environment I had another issue. I had the following error: https://lists.fedoraproject.org/archives/list/freeipa-users@lists.fedorahosted.org/thread/5PHFG7FLA3JZ3Z527BPUDPMMO67XIBUK/#IHIPPVMMIWV2TL7BNLW55XII3OIQ62HK

There’s a work-around by Oleg Baranov, that probably works. The solution is to change the password storage format. For setting up this if you don’t have the directory manager password available, the following can be used. Create /root/fixing.ldif:

dn: cn=config
changetype: modify
replace passwordStorageScheme
passwordStorageScheme: PBKDF2_SHA256

Enable this config file when installing the server:

# ipa-replica-install --setup-ca --dirsrv-config-file=/root/fixing.ldif

Automatic roller blinds RF 433 MHz

This post describes trying to control electric roller blinds made by Jysk using a simple RF transmitter and Raspberry Pi. It is just some quick notes, because I don’t have time to describe it in detail.
JYSK HUGLO: https://jysk.no/gardiner/rullegardiner/blackout/rullegardin-huglo-142×190-gra-elektrisk
There are some online references on how to get it to work with Homey (I think), but they didn’t give details on the RF signal.

The blinds use a simple on-off-keying protocol at 433 MHz. It is not compatible with common Arduino libraries that can receive such signals, like rc-switch, IRRemote (other libraries were tried, but I unfortunately didn’t ). It is controlled with a 15-channel battery operated remote control, with buttons for Up, Stop and Down.

The signal was captured using an RTL-SDR dongle and Universal Radio Hacker (https://github.com/jopohl/urh).

The signal for up or down is repeated 12 times over almost a second, but the last 6 pulses have a slightly different code.

Each transmission packet lasts about 57.8 ms including a pause between packets. The picture above shows the demodulated view. The packet can be described as a series of 350 μs intervals. Everything is aligned to 350 μs. One pulse highlighted below.

One raw RF data file here; Sorry – I can’t upload this in wordpress.

  • ch2 up
  • ch2 stop
  • ch2 down
  • ch1 up
  • ch1 stop
  • ch1 down
  • other unknown

The signals are encoded, but for the purpose of reproduction, it’s fine to set each 350 μs block as one bit, receiving=1 and off=0. Then the signals for channel 1 are:

up_command=”fffc349b6d36d369369269a49249a49a498″
stop_command=”fffc349b6d36d369369269a49249a69a698″
down_command=”fffc349b6d36d369369269a49249a4da4d8″

This includes a sync pulse at the start of the signal.

The can be sent using a Raspberry Pi (and an RF transmitter module) using the pigpio library. The following python script is custom made only for my purposes, but the make_wave and send_wave functions could be useful for someone trying to do the same. Control code: https://github.com/famake3/controllers/blob/master/mqtt-pi-node/vindu.py#L38. The code uses pigpio in order to better control the timing of the output. pigpio handles GPIO in its own daemon process.

    def make_wave(self, pi, rf_pin, rf_pulse_time, hex_string):
        bin_string = bin(int(hex_string, 16))[2:].zfill(len(hex_string)*4)
        waves = []
        pin_mask = 1<<rf_pin
        for bit in bin_string:
            wave = []
            # Set the pin high or low depending on the bit value
            wave = [
                    pigpio.pulse(
                            pin_mask if bit == '1' else 0,
                            pin_mask if bit == '0' else 0,
                            rf_pulse_time*1e6
                            )
                    ]
            waves.extend(wave)
        # Always return to zero
        waves.append(pigpio.pulse(0, pin_mask, rf_pulse_time*1e6))
        pi.wave_add_generic(waves)
        return pi.wave_create()


    def send_wave(self, wave_id):
        #print("SENDING WAVE", wave_id)
        for _ in range(self.num_repeat_packets):
            self.pi.wave_send_once(wave_id)
            time.sleep(self.packet_unit_time)

To send, wire up a transmitter module to a pin on Raspberry Pi.

“5 V” USB to phone: voltage/current relationship

This is not about charging Li-ion batteries (directly). Rather, I wanted to check the change in the current draw of a mobile phone when supplying less than 5 V. Supplying power via USB is getting a bit complex. There are different protocols like Quick Charge and USB-PD to negotiate the current and voltage. However, there are billions of stupid chargers that just deliver 5 V, and phones have to deal with the case when the chargers can’t quite keep up.

The hope is that it’s possible to vary the power usage by reducing the voltage. I’d like to make a solar to USB charger, and stay on the Maximum Power Point of the solar panel. So it needs to be able to step back and not pull as much current as it can from the solar panel.

Methods

Many phones will draw high current if the USB data pins are shorted. Great news, I’ll do that 🙂 https://electronics.stackexchange.com/questions/123172/what-is-the-ideal-way-to-handle-data-pins-d-and-d-on-a-usb-power-adapter-to-be

I used an INA219 breakout from Adafruit to measure the voltage and current, and logged the data using an Arduino (I used an old Yun — any one will do).

The power was first converted to 12 V by an ordinary AC-DC adapter. Then I used a Velleman variable voltage regulator to adjust the voltage around 5 V. It has a tiny screw that can be used to adjust the voltage.

Here’s a circuit diagram. The variable voltage regulator is represented here by the black box (Hi-Link). The INA219 is connected in series, after the positive output of the voltage regulator. The arduino is powered from the computer via USB, but the ground is connected to the ground of the system under test. Sorry for the crap quality:

Wiring diagram (Fritzing). See photos and code at the bottom of the post.

I ran the program on the Arduino and opened the serial monitor. Then I slowly adjusted the voltage up and down. After having gone through the relevant voltages, I copied the output in the serial monitor into a text file, and then plotted it with a spreadsheet.

Results

  1. Redmi K20 phone – Battery state of charge 59 %
urrent consumption (mA) as function of voltage.

2. Samsung Tab 5 tablet – Battery state of charge 47 %C

Current (mA) vs voltage (V)

Some of the outliers are caused by a delayed response to changes in voltage.

The Redmi device pulled well over 1000 mA, and it seemed to continue to rise with input voltage. The Samsung device reaches a plateau at 5 V and stays there.

There is a point where the voltage is too low, and the current draw is limited to less than 100 mA. For the Redmi phone, it then won’t start drawing more current again until the voltage first goes very low (for example, by disconnecting it).

Conclusion

The current should ideally rise as the voltage approaches 5 V, and then remain at the safe charging current of the battery. Because the Li-ion charger inside the USB device steps down the voltage, the current drawn from the USB device should actually decrease, to maintain a constant current at the battery voltage. But staying constant would be quite okay, as getting 5.5 V or more is not something that should happen.

None of the two devices do this.

The Redmi device may have a charger that doesn’t properly deal with voltages > 5 V, because it keeps increasing the current. The USB supply voltage should be 5 V, so it would be a waste if it only does fast charging for wonky 5.5 V adapters. But on the plus side, it’s perfectly able to draw more than 1000 mA at 5 V, so it can charge pretty fast.

The Samsung device is clearly limited by software / firmware. It has a large battery that can definitely charge at a higher rate. Like many devices, it may require the data pins to be wired in a special way in order to allow the faster charging rate. Sure, it’s safe, but will be slow on most chargers.

The motivation was to see whether it’s possible to modulate the current draw by reducing the voltage. Indeed there’s a range between 4.5 and 5.0 V that can be used to almost linearly vary the current. Good news for the solar charger project (not introduced yet). Of course, I also can’t guarantee that the charging controller inside the phone can make sense of the varying voltage. Maybe something unsafe or damaging can happen when it comes back to 5 V after being lower.

Photos and code

Code is based on the example in the Adafruit INA219 library, just changed the output format.

#include <Wire.h>
#include <Adafruit_INA219.h>

Adafruit_INA219 ina219;
int i = 0;

void setup(void) 
{
  Serial.begin(115200);
  while (!Serial) {
      // will pause Zero, Leonardo, etc until serial console opens
      delay(1);
  }

  uint32_t currentFrequency;
    
  Serial.println("Hello!");
  
  // Initialize the INA219.
  // By default the initialization will use the largest range (32V, 2A).  However
  // you can call a setCalibration function to change this range (see comments).
  if (! ina219.begin()) {
    Serial.println("Failed to find INA219 chip");
    while (1) { delay(10); }
  }
  // To use a slightly lower 32V, 1A range (higher precision on amps):
  //ina219.setCalibration_32V_1A();
  // Or to use a lower 16V, 400mA range (higher precision on volts and amps):
  //ina219.setCalibration_16V_400mA();

  Serial.println("Measuring volta     ge and current with INA219 ...");
}

void loop(void) 
{
  float shuntvoltage = 0;
  float busvoltage = 0;
  float current_mA = 0;
  float loadvoltage = 0;
  float power_mW = 0;

  shuntvoltage = ina219.getShuntVoltage_mV();
  busvoltage = ina219.getBusVoltage_V();
  current_mA = ina219.getCurrent_mA();
  power_mW = ina219.getPower_mW();
  loadvoltage = busvoltage + (shuntvoltage / 1000);
  Serial.print(i);
  Serial.print('\t');
  Serial.print(busvoltage);
  Serial.print('\t');
  Serial.print(current_mA);
  Serial.println("");
 i++;
  delay(2000);
}