Apropos of nothing in particular….

Wednesday, March 26, 2025 

Signal is well-reputed as a messaging platform that is more secure than, say, Telegram, but less secure than, say, Briar or Delta Chat. Signal does not run their own servers and doing a quick ss -atp | grep signal on my computer with the desktop client open, I found Signal making connections to these servers:

Typical ss output:

ESTAB      0      0      192.168.100.51:49930       13.248.212.111:https       users:(("Signal-desktop",pid=1333428,fd=38))
ESTAB      0      0      192.168.100.51:49916       13.248.212.111:https       users:(("Signal-desktop",pid=1333428,fd=27))
ESTAB      0      0      192.168.100.51:47174         3.165.206.17:https       users:(("Signal-desktop",pid=1333428,fd=84))

Doing lookups (from a previous connection, the servers change a bit depending on when you connect) I found:

76.223.92.165:https AS16509 Amazon.com, Inc. 47.6043, -122.3321
34.117.136.13:https AS396982 Google LLC 39.0997, -94.5786
13.248.212.111:https AS16509 Amazon.com, Inc. 47.6275, -122.3462

Google and Amazon know who’s talking to whom, when, from where, and how much data they’re sending (but probably not the actual message contents). It is possible one would have to request the connection logs to the Signal services running in both the Google and AWS clouds to build a full metadata path, but that hardly seems daunting.

Wireshark lets you monitor communication as it moves across the link and this is what anyone on the Google/AWS end of a Signal com link also has visibility into, this is data from outside the app boundary, where messages are encoded and protected, but messages going across the public interwebs still must contain meta-information necessary for routing and network handling which can be, itself, a major security risk depending on one’s adversary model.

Time            Source          Destination     Protocol Length Info 
4.920825221	192.168.100.51	13.248.212.111	TLSv1.2	 2157	Application Data

I included detailed metadata for a sample Application Data Transfer between Signal desktop and a Signal server below.  Note that the contents of the messages are encrypted, that’s the TLS Encrypted Payload of the message and I myself have no way of decrypting it.  Moxie’s crypto is well reputed, well reviewed, and well vetted.  I’m fairly confident there’s no straight-forward flaw that would render the Encrypted Application Data to plaintext, which is fine and comforting as far as that goes. However, one needs to keep in mind that while this capture from my computer only shows that my computer 192.168.100.51 94:57:a5:xx:xx:xx  is talking through the Huwawei gateway (yikes! so the PLA also knows all of this too) at MAC dc:99:14:57:52:95 and that I’m talking to the Signal server at 13.248.212.111, that server, the one at 13.248.212.111, has to know where the other end of this chat is connected to and anyone sniffing all the traffic coming and going to the Signal server, as whoever is hosting the server (AWS or Google) can easily do, can therefore correlate the TLS encrypted payloads and other metadata to reconstruct the conversations: who’s talking to whom, exactly when, how much data they’re sending each other, and where they are talking from (yes, using a VPN might obfuscate that source IP information).

Imagine a salacious, non-national security scenario: someone suspects their spouse has untoward plans with another: would not their chat logs showing everyone they messaged, including, say, some ex and the file sizes being transmitted, generally indicating graphic media, the times those messages are sent and the location from which they are being sent potentially be admissible in divorce court?

It is useful to note that this raw network data does not seem to have any obvious user identifier data—nothing that I have the skillz to extract from the wireshark data at least—but the IP data that is visible can be easily enough correlated to actual user names with a trivial additional subpoena to Google or AWS by identified IP as their data centers absolutely host either owned and operated services like gMail or Amazon.com that would contain login information, address data, probably phone numbers and user pictures for the IPs in the chat streams. That is you connect to a google hosted Signal server with the Signal app on a device that also runs any Google service you’re logged in to and Google has a very high degree of confidence that data streaming from the same endpoint to the Signal server, which we’re somewhat optimistically assuming they do not have visibility into, is coming from the same user at the same endpoint who is simultaneously logged into the google service thus de-anonymizing the user.

So, for example, we might start with a request for all traffic connecting to the Signal servers as observed at the ISP – that’s all incoming and outgoing data for everyone using Signal with all of the detailed metadata shown below.  Then, by correlating the Encrypted TLS payload data as a message identifier identifier as it goes in to and then out of the Signal server from sender to recipient(s), we can build a connection map of all Signal users during the capture window; that is a network map all the chat connections with time and date and size. We might filter for IPs of interest—for example all USG IPs, then extract that subset and build a tractable set of IPs of interest and then demand all user data held by the company hosting the server for those IPs, whether related to Signal or any other service hosted in the same facility.  Thus a large hosting facility like AWS or Google becomes a one stop shop for comprehensive user data. While Signal.org might be reluctant to give up user data or claim they have no further insight, the correlated user data from a major data warehouse like Alphabet or AWS is all the data needed to unmask Signal users and monitor communications frequency and correspondent & group mapping, and track message frequency. This data is as useful as watching troop movements from a persistent satellite to get insight into activities and planning, but all collected with just a few quick requests to entities that are required to support lawful data requests. The assumption that only lawful requests might be served is predicated on an optimistic assumption of incorruptibility that should be questioned if the data is sufficiently sensitive to warrant.

Anyhoo, good to remember what you’re exposing when you think you’re being sneaky. 🕵️


Details

Frame 24: 2157 bytes on wire (17256 bits), 2157 bytes captured (17256 bits) on interface enp0s25, id 0
    Interface id: 0 (enp0s25)
        Interface name: enp0s25
    Encapsulation type: Ethernet (1)
    Arrival Time: Mar 26, 2025 17:25:10.954150575 +03
    [Time shift for this packet: 0.000000000 seconds]
    Epoch Time: 1742999110.954150575 seconds
    [Time delta from previous captured frame: 0.555192927 seconds]
    [Time delta from previous displayed frame: 0.555192927 seconds]
    [Time since reference or first frame: 4.920825221 seconds]
    Frame Number: 24
    Frame Length: 2157 bytes (17256 bits)
    Capture Length: 2157 bytes (17256 bits)
    [Frame is marked: False]
    [Frame is ignored: False]
    [Protocols in frame: eth:ethertype:ip:tcp:tls]
    [Coloring Rule Name: TCP]
    [Coloring Rule String: tcp]
Ethernet II, Src: HewlettP_xx:xx:xx (94:57:a5:xx:xx:xx), Dst: HuaweiTe_57:52:95 (dc:99:14:57:52:95)
    Destination: HuaweiTe_57:52:95 (dc:99:14:57:52:95)
        Address: HuaweiTe_57:52:95 (dc:99:14:57:52:95)
        .... ..0. .... .... .... .... = LG bit: Globally unique address (factory default)
        .... ...0 .... .... .... .... = IG bit: Individual address (unicast)
    Source: HewlettP_xx:xx:xx (94:57:a5:xx:xx:xx)
        Address: HewlettP_xx:xx:xx (94:57:a5:xx:xx:xx)
        .... ..0. .... .... .... .... = LG bit: Globally unique address (factory default)
        .... ...0 .... .... .... .... = IG bit: Individual address (unicast)
    Type: IPv4 (0x0800)
Internet Protocol Version 4, Src: 192.168.100.51, Dst: 13.248.212.111
    0100 .... = Version: 4
    .... 0101 = Header Length: 20 bytes (5)
    Differentiated Services Field: 0x00 (DSCP: CS0, ECN: Not-ECT)
        0000 00.. = Differentiated Services Codepoint: Default (0)
        .... ..00 = Explicit Congestion Notification: Not ECN-Capable Transport (0)
    Total Length: 2143
    Identification: 0x99f7 (39415)
    Flags: 0x40, Don't fragment
        0... .... = Reserved bit: Not set
        .1.. .... = Don't fragment: Set
        ..0. .... = More fragments: Not set
    ...0 0000 0000 0000 = Fragment Offset: 0
    Time to Live: 64
    Protocol: TCP (6)
    Header Checksum: 0x915e [validation disabled]
    [Header checksum status: Unverified]
    Source Address: 192.168.100.51
    Destination Address: 13.248.212.111
Transmission Control Protocol, Src Port: 52168, Dst Port: 443, Seq: 2094, Ack: 159, Len: 2091
    Source Port: 52168
    Destination Port: 443
    [Stream index: 2]
    [Conversation completeness: Incomplete (12)]
    [TCP Segment Len: 2091]
    Sequence Number: 2094    (relative sequence number)
    Sequence Number (raw): 3525513438
    [Next Sequence Number: 4185    (relative sequence number)]
    Acknowledgment Number: 159    (relative ack number)
    Acknowledgment number (raw): 4205312576
    1000 .... = Header Length: 32 bytes (8)
    Flags: 0x018 (PSH, ACK)
        000. .... .... = Reserved: Not set
        ...0 .... .... = Nonce: Not set
        .... 0... .... = Congestion Window Reduced (CWR): Not set
        .... .0.. .... = ECN-Echo: Not set
        .... ..0. .... = Urgent: Not set
        .... ...1 .... = Acknowledgment: Set
        .... .... 1... = Push: Set
        .... .... .0.. = Reset: Not set
        .... .... ..0. = Syn: Not set
        .... .... ...0 = Fin: Not set
        [TCP Flags: ·······AP···]
    Window: 501
    [Calculated window size: 501]
    [Window size scaling factor: -1 (unknown)]
    Checksum: 0x0f95 [unverified]
    [Checksum Status: Unverified]
    Urgent Pointer: 0
    Options: (12 bytes), No-Operation (NOP), No-Operation (NOP), Timestamps
        TCP Option - No-Operation (NOP)
            Kind: No-Operation (1)
        TCP Option - No-Operation (NOP)
            Kind: No-Operation (1)
        TCP Option - Timestamps: TSval 2613226080, TSecr 761373603
            Kind: Time Stamp Option (8)
            Length: 10
            Timestamp value: 2613226080
            Timestamp echo reply: 761373603
    [Timestamps]
        [Time since first frame in this TCP stream: 3.484784717 seconds]
        [Time since previous frame in this TCP stream: 3.147971607 seconds]
    [SEQ/ACK analysis]
        [Bytes in flight: 2091]
        [Bytes sent since last PSH flag: 2091]
    TCP payload (2091 bytes)
Transport Layer Security
    TLSv1.2 Record Layer: Application Data Protocol: http-over-tls
        Content Type: Application Data (23)
        Version: TLS 1.2 (0x0303)
        Length: 2086
        Encrypted Application Data: bb229f3df6e870cc08909e190031ac9de35a79f008d706a15a61c38649db551895748703…
        [Application Data Protocol: http-over-tls]

The TLS encyrpted payload looks like this (typical example).  I do not think it is decodable without a quantum computer or if you happen to know that the TLS key space has been reduced enough to make brute force tractable.

0000   bb 22 9f 3d f6 e8 70 cc 08 90 9e 19 00 31 ac 9d
0010   e3 5a 79 f0 08 d7 06 a1 5a 61 c3 86 49 db 55 18
0020   95 74 87 03 94 57 ec f5 e4 51 ca 83 5c d8 f3 ef
0030   5d e8 51 e8 e7 5d 60 fd 37 1c 33 d0 5a 65 44 aa
0040   77 13 3d ec 3e 18 48 30 50 b1 26 04 d1 5b 0c 25
0050   4c 02 4b 15 44 a1 9f e9 b3 8d 22 b1 05 23 be c8
0060   a8 18 84 8d 38 02 43 06 5e c9 75 23 8c 67 50 9d
0070   1c b2 23 a2 e8 24 f4 be dc 23 c1 ee ca b8 fc 22
0080   f8 56 1a b9 1c c0 ab c5 8d f3 a2 c9 5f d1 e4 ae
0090   43 d8 a1 19 df d3 91 87 ac e0 f2 19 1a 12 a0 32
00a0   b7 2e 18 b7 6b d1 aa 7e 7d e8 20 06 0b b3 02 a7
00b0   c3 7e ed 80 ed d0 cd fb 1a 65 06 a0 86 cd a5 d1
00c0   77 ce 4f 7f 56 d1 1b f1 d4 dd be 8b 14 fe c8 f8
00d0   87 55 03 2f 55 63 28 f2 e5 a3 0d 31 93 16 9c cd
00e0   2f 2f 12 37 ef f3 95 bc 73 72 d4 59 5f e6 ac dd
00f0   fd d8 cf 74 e9 e7 25 49 17 82 94 a5 8a 14 04 2f
0100   61 71 5f fa 89 57 18 e9 b4 17 70 fa 77 1b 65 07
0110   f6 f5 fb 0a 46 8a 25 8d 00 7b eb e4 1f 1d 3d 94
0120   3f a0 c5 23 1b ce 50 54 d8 65 30 61 57 76 3c 31
0130   53 66 e0 69 40 8b ca 99 2e 41 31 71 0a 98 64 91
0140   fd d7 ce ee 99 8c de a6 d7 9d ee 6f a1 66 96 5d
0150   02 15 f7 53 b4 be 61 b6 da 40 7e 1c c7 5f 06 eb
0160   5e 9b f4 13 19 30 46 06 29 66 5d d9 02 72 9e 1f
0170   74 be 96 9f 9c 02 0b c3 ac cf 89 8f 68 8c 99 33
0180   43 83 20 62 d5 4e 6c e5 81 da 05 be e7 51 10 ec
0190   3d be 23 06 9a 1b 76 17 c5 51 a8 34 28 58 92 ea
01a0   4a 6f 44 e9 c0 96 a9 25 a5 d3 b0 bc 9d dd 6b f3
01b0   53 28 3c d5 ce b2 2c 49 38 4f e3 ca c0 7b a7 c9
01c0   45 88 32 2d 6d 7c d7 e3 94 06 be 17 b8 ca 58 90
01d0   51 20 13 6f e6 91 1e 12 35 6b 16 d1 39 77 81 bf
01e0   04 db 42 75 a8 ca 7a 67 ac 4b d8 d5 7b c3 97 28
01f0   bf f7 34 a9 99 c8 89 73 d1 39 20 d0 cc 4d e4 af
0200   72 72 b8 5c 4b bc 33 9d fd 04 0f 83 1d f8 b6 1e
0210   35 b5 f8 4d 80 43 c1 31 51 64 64 10 1e be 77 ab
0220   94 36 d2 9b 2b 6a 87 e5 ff c1 21 67 46 fc 3e cf
0230   e4 8c cc dd d2 3f b6 39 fe 94 40 90 4e c2 c4 cb
0240   06 e4 58 4a 72 ef a0 30 8d 17 10 a0 b3 bc 3d f1
0250   02 9d 65 95 73 27 79 8b f4 33 c8 ea 33 42 c5 e6
0260   b8 bb 86 d6 a1 90 4b 1e 0e 37 0d 48 e9 ac 22 7b
0270   03 cb 0b 20 50 30 d7 6b 3d 90 20 30 9b af e6 b1
0280   d1 26 44 8f c8 62 64 33 bf a3 5c 16 09 eb e3 d2
0290   63 79 76 93 e0 5c be 15 77 fa 0e 93 51 64 5c e7
02a0   61 bf 9b 01 4b af b4 33 7b 29 b1 79 01 52 e2 a6
02b0   6f 58 44 9c aa 37 a6 27 96 90 30 a8 0a 25 36 25
02c0   52 f4 33 c4 b2 3e 7c 4f 64 52 e1 7d 09 0f 23 ab
02d0   da 48 f8 f9 23 99 35 7c 22 6c 4e d7 0a 4b b3 0b
02e0   7c 77 6e 3d 2c c5 4e 98 2e e4 35 97 ac 77 1a 22
02f0   65 98 59 0d 72 bb eb 72 10 4c c0 d0 11 f2 9a 52
0300   9f 71 83 8c 08 48 38 9b d3 e2 75 b3 3d 44 52 42
0310   22 78 17 87 ca c2 43 24 07 f2 7a 31 cb 1a 36 de
0320   1f 05 1c 9d 7a 66 93 5a 8f a8 a2 d9 80 e1 e5 ea
0330   f7 22 16 32 7e 39 f8 78 70 b9 87 d6 77 4b 31 86
0340   a5 ce db c8 4e 5a 6a 43 3f 4d dc 7e cd 23 d4 22
0350   50 d2 0b 44 8c 13 69 49 29 9a 92 ff 61 f3 49 da
0360   d2 0d d3 b9 be 91 90 b0 e9 e3 02 e2 18 4e e1 93
0370   67 94 c3 a5 97 f2 da e0 c9 99 e8 5e 4e e4 93 6c
0380   75 e6 96 70 4d bc 47 2d 0f c4 53 21 f1 fb 43 ff
0390   f8 fe 5a 6f 37 c2 3e 68 4b 80 d2 9c a5 1f 0e 0a
03a0   a7 46 50 b5 fe f2 fb 60 fe e7 ca 56 88 fa 85 7d
03b0   15 d0 5f 25 70 ce 5e 5e 50 e1 fa 2c 29 b4 5c 01
03c0   2a 73 df 1b 12 4e 45 ea 68 ee d1 ee 66 bf 56 7a
03d0   6c af 46 c2 10 23 e4 b0 99 b6 f6 f2 2f 07 b1 c3
03e0   a0 13 ed 36 5a 23 e6 cb d5 bf ed eb 35 2d c7 49
03f0   4a cd 50 32 5f f3 46 9b 67 9f 7c 5b 98 68 21 5b
0400   55 ed ed 89 e7 26 5d 3a 41 6e 4e a7 b9 0f 25 a6
0410   5c b7 84 e7 6a 7a d9 ea 6e f7 4c 70 cc 95 2c 5a
0420   69 e4 05 3e 99 b6 7f cd 6a a6 7a 06 f5 a3 e1 2d
0430   54 b1 fe d5 c6 18 75 16 6a 62 07 f4 36 a0 a3 cc
0440   d3 79 7b 89 96 fe 16 7c 1f 22 e6 73 1a 41 42 39
0450   8a ed b6 85 8e 66 79 7f e7 57 bb ac 10 ca 6e ac
0460   fd 1b 5f 4c d8 eb 9e 92 a3 51 c8 3d 93 70 9e 86
0470   48 8d 19 23 cf c1 0a 22 ce 76 1b 68 ec 0d 7b b7
0480   8e dc 85 62 5f f4 55 88 83 95 e1 48 2d 13 e8 42
0490   e3 b4 e6 f5 28 a4 c2 ef db 9e 98 69 cd 0e 73 a9
04a0   f8 39 1e 8a f0 11 a9 df db 1a 62 a6 ab f0 2d 2f
04b0   96 e2 0b 0c 9c e1 80 a7 9d 5a ef 95 d1 f4 92 a0
04c0   60 61 37 17 2d 3e dd d6 97 7d 46 be e2 b7 87 4f
04d0   93 19 3a 81 3b d7 3e b3 67 b5 ee b6 33 67 be 53
04e0   85 eb ac 43 34 75 11 b3 3e d1 fa c7 00 28 2b d8
04f0   a3 a7 de 5c ac 7f 4b 2f 56 28 ab fd 81 b6 e2 40
0500   aa 0a fe b7 7b cc d0 77 b9 43 57 4a b9 6d 3a 7b
0510   bc 83 81 79 6c ac 37 02 88 69 ae 47 cc 5f 7c 06
0520   90 b7 d0 73 98 f4 8c 79 1c cf cb d2 ef d0 3d 34
0530   9d 3a 7e 18 ce ef ac e3 a6 59 69 83 a5 2e b6 18
0540   e4 10 e6 12 2c 2f 1e 8d f0 5b e5 0b 43 40 75 aa
0550   e8 bf a3 f2 9a a5 7d 16 b0 f5 b5 4f 7f c0 b8 d2
0560   91 e7 ff 63 8b df a5 56 53 d4 14 bb 72 a5 a8 d9
0570   fa 94 8a 39 cb 3d ce 8f 1a a7 d7 fd 64 54 2a 84
0580   5e d4 24 cc dc 69 c0 20 69 65 0c d1 28 7d b7 7f
0590   54 40 b2 c3 5b 0d 20 46 7e b6 f4 c3 74 97 bd b7
05a0   d1 ab 0c 0b 65 03 79 36 0b 06 14 d6 c4 27 65 af
05b0   17 87 8c 92 f9 8a 59 ea df 0d 8b 79 26 8d ff 8b
05c0   1b d9 ad ce 65 0e 00 05 71 9d 21 b8 33 48 b1 73
05d0   0c 97 98 aa 1b 6a 4b 55 f7 0e 02 55 60 d3 52 73
05e0   85 0b 8d 0b c8 b6 8b a8 20 29 aa 98 f4 c1 c7 19
05f0   e7 87 3e f5 70 d8 97 79 c1 7e 69 19 ca 8b 66 0d
0600   fd cc f3 a7 cf ed e8 f5 f4 4f 63 ec 43 e8 2a a0
0610   01 31 90 fc 8e d2 75 ea d2 ec 0e e2 c7 45 63 2a
0620   42 ad 72 51 19 b2 af 47 d2 2b 43 2a d1 2b 30 7f
0630   91 cc 51 1b ff 5f e6 dd 2b 98 a5 d7 98 db bb 0c
0640   02 72 8e f6 9a 03 57 04 5d 0e ab d5 3d 89 85 53
0650   18 09 15 b0 05 b7 70 ab 29 a0 9a 5c 4f 97 79 b8
0660   f4 a1 b4 f5 28 f5 4e fd 87 70 61 15 b6 5c 10 c9
0670   d2 df 44 13 71 f8 c7 8c f5 fa 9f 82 0c f9 66 7b
0680   39 8d 23 f4 ba fc 29 17 5e 96 04 08 09 80 2b b7
0690   a4 c8 e8 6d aa 86 53 44 ba 06 4f 13 7d 30 a7 b8
06a0   e5 91 1c 38 ce e6 b9 05 2a 18 9e ba f1 9c 2e 26
06b0   04 67 f0 61 7b 90 e0 af ff 0d 60 d0 96 6a 82 e8
06c0   2a a1 fc 89 21 6f 2f f6 c5 25 50 22 85 ce 24 66
06d0   f6 ce ef 1e f5 21 33 d2 6e 2b 55 c5 db 9d 12 07
06e0   26 3a f6 c0 23 5a 1d 7a 16 64 62 71 0c a1 81 b1
06f0   4b 02 42 c4 a7 c7 8f 01 ae 99 36 57 fb e6 cb d3
0700   ed 2a 68 05 ec d7 e6 c4 61 f2 c0 5e fd 42 63 51
0710   a0 9f 2b b4 6a a4 be 7a c9 dc d2 03 26 b6 bd 34
0720   ef 86 be a2 45 ea 5a a0 fa f3 b4 f6 cb 97 3f 9d
0730   0d a2 47 24 46 c1 3f 88 ff f7 b0 c1 c4 36 7b 49
0740   46 01 a7 2a 1e 8d f2 89 86 b6 8a 06 91 74 9a 35
0750   99 3c 17 9d c4 db 33 fd fc f7 08 80 ae d7 cb 67
0760   7a c9 aa f3 da bf 79 bc dd 2e d7 b9 e4 1b d8 5c
0770   bd ff f8 49 fa 95 ae ea b1 26 c4 65 f8 cb 6d ab
0780   a9 d0 a7 bf 91 f5 72 e0 3c 3b 96 ff 8c 6b f0 00
0790   a7 49 ad 33 fc d0 7d 63 d4 41 5e fe 31 79 72 b5
07a0   46 e2 43 4a 56 b2 08 3b 81 94 c0 d2 61 45 2f 17
07b0   7d 09 6d 18 90 87 36 e3 94 6c 76 c9 6b db cf ee
07c0   bd 55 ea eb 52 30 fc 0f 45 88 be 31 63 2a 6d 16
07d0   0a 91 79 da 8f 1e c3 1f 95 08 59 2a c3 8e bc 9d
07e0   1b 88 8f 10 df ea dd 22 46 1b 03 7c 78 cc 50 47
07f0   36 61 c9 c0 4b eb 2f 94 cc 75 34 27 96 98 42 b3
0800   9a ec 97 58 9e 1e 36 76 a2 85 26 3e 7a 0c a8 4a
0810   c4 81 b1 2e b1 79 3c 15 de 88 8f 18 0b 98 fa ef
0820   d9 4d 20 37 20 59

A closing note about chat security problems and solutions

Most people run chat apps on their phones and that’s intrinsically insecure.  Your phone, whether Android or iOS-based is fundamentally insecure and effectively unsecurable.  The nature of the underlying operating system, the scale of use, and the number of people attempting to find flaws guarantees that there are in the wild, “zero day” hacks floating around.  These are quite valuable to people trying to monitor high value targets and so probably won’t be wasted on you if you’re not such a target, but the risk is non-zero and if you’re not careful and diligent about updating, your phone is insecure, guaranteed.  That’s what all those patches are for, fixing hacks that people found that gave them access: when those stop coming, your phone is insecure the next day.

If you’re not a high level threat, it is unlikely that your phone itself will be targeted by a crack for which there is no patch – that is if you keep your device updated to protect against the already exposed (and therefore low value) hacks, you’re unlikely to be picked as a target for a zero day (undiscovered, and therefore extremely high value, like $1,500,000 each value) hack.

This accounting is far less valid at core services which are global jackpot targets.  Each individual on such a service themselves might be nearly worthless, but in aggregate all users of a service like Signal or Gmail or whatever are absolute bank and where there’s money to be made, there are people working hard to make it.  It is not paranoid to consider your own vulnerabilities to traffic analysis as described above – and note we’re not assuming any flaws in the cryptography or security of either the Signal app or the Signal servers, we’re just considering the data leaked intrinsically by connecting to a “cloud” end point, especially one with a high degree of network visibility, such as a major provider like AWS or Google.

So it is worth considering the balance of ease of use vs. security.  It is my opinion that Signal has an excellent balance of ease of use vs. security for most casual users of chat.  It is good enough for individually private data where the threat model is another individual.  It is not remotely sufficient for a state-sponsored antagonist, having zero resistance to traffic analysis at the server and end-device compromise (e.g. a high value phone hack like Pegasus, say).

I know of two chat services that provide far better protection against traffic analysis and would not be vulnerable to the attack outlined above: Briar and Delta Chat, and two phone platforms that provide better security than iOS or Android: Linux phones and a specialty phone designed purely for secure messaging, the Sotera SecurePhone.

Briar’s special sauce is that it is a peer-to-peer chat tool that uses either local comms (no interwebs required) over LAN or Bluetooth or peer to peer connections over Tor. It is a pain in the butt in that it is tied to a single device, no desktop client, lose the device and you lose the chat and your address book.  And it is more than a little challenge to build that address book.  Plus, for messages to move, you generally need to have both the sender and receiver on and connected at the same time, no store-and-forward. But it is about as secure as chat from a mobile device can get (assuming no flaws in the cryptography).

Delta Chat’s special sauce is that it is an extension to the IMAP protocol that makes email work like chat.  The downside is that you should really run your own mail server to make it secure.  It uses PGP, as email does, for the E to E message security, which is a cryptographically trust-worthy choice, but there isn’t (yet?) a UI mechanism for entering key passwords securely  (it should integrate support for OpenKeychain, if you’re a good programmer, help out!).  I love the premise, but the key handling just doesn’t work for me as is. YMMV.

There are two phone platform one could make that would provide significantly enhanced security, though neither is going to come with a lot of hardware diversity like Android users love (and iOS people clearly just want to show off the logo): either a Linux-based phone, which is going to have security through obscurity because nobody uses Linux phones like the PinePhone, which can be secured, or the Sotera SecurePhone, which runs a very secure (EAL 6+!) OS, but which only supports voice/text chat.  That is, it is very unlikely to be a replacement for a regular phone, but if you really need truly secure messaging and don’t want to carry an AN/PRC-148, it is about the only game in town.

Posted at 08:59:33 GMT-0700

Category: Cell phonesGeopostLinuxPoliticsPrivacySecurityTechnology

Treegraph.sh a tool for generating pretty file structure graphs

Friday, February 28, 2025 

Linux has a nice little tool in the repositories called tree generates a nice console window-based directory map from the current location down. It’s great, but there’s not an equivalent in FreeBSD, though you can get the basic need met using find and some text processing pipes like

find . -type d -maxdepth 3 | sed -e "s/[^-][^\/]*\// |/g" -e "s/|\([^ ]\)/|-\1/"
.
 |-subdir0
 | |-subsubdir1
 | |-subsubdir0
 |-subdir3
 |-sub dir 2

However, this is enough to make me wish for something more visual. As usual, someone else already did it, but their version wasn’t quite what I wanted so I enlisted Claude.ai to help generate a little script that, like the above command, finds it’s way through a directory structure and builds a .dot file map as it goes, which can be converted into useful graphic formats with graphviz.

tree graph of a directory structure

The code (below or at gitlab) is pretty easy to use, it does the dotfile generation then just parse the dotfile like usual with graphviz, either using an installed version or online.

treegraph.sh -d /usr/home -f -s -m 2 > tree.dot
dot -Tsvg tree.dot > tree.svg

The edges seem to overlap a bit more than I’d like, but that’s an issue with graphviz and if you really want a presentation ready graph can be adjusted manually with invisible nodes or edges and manually set connection points to merge lines. It defaults to dot, which makes sense for ranks, but looks cool with neato or twopi as well.

#!/usr/local/bin/bash

# Help function
Help() {
   echo "Usage: treegraph.sh [options]"
   echo
   echo "Options:"
   echo "  -h         Display this help message"
   echo "  -f         Enumerate both directories and files (default: directories only)"
   echo "  -d DIR     Specify the root directory to parse (default: current directory)"
   echo "  -m DEPTH   Specify the maximum depth of parsing (default: no limit)"
   echo "  -s         Compute the size of folders and files using 'du -sh' (default: off)"
   echo
   echo "Examples:"
   echo "  treegraph.sh                         # Current directory, directories only"
   echo "  treegraph.sh -d /usr/home -f         # Parse /usr/home, include files"
   echo "  treegraph.sh -d /usr/home -m 3       # Parse /usr/home with max depth 3"
   echo "  treegraph.sh -s                      # Show sizes for current directory"
   echo "  treegraph.sh -d /usr/home -f -s -m 2 # All options combined"
   echo
   echo "Example generating an SVG file (requires graphviz installed):"
   echo "  treegraph.sh -d /usr/home -f -s -m 2 > tree.dot"
   echo "  dot -Tsvg tree.dot > tree.svg"
   echo
   echo "When using -s, nodes are color-coded by size:"
   echo "  B (bytes)  - gray50"
   echo "  K (KB)     - black"
   echo "  M (MB)     - blue2"
   echo "  G (GB)     - darkorange3"
   echo "  T (TB)     - crimson"
}

# Initialize variables with defaults
StartDir="."
Type="d"
MaxDepth=""
MaxDepthArg=""
ShowSizes=0

# Process options
while getopts ":hfsd:m:" option; do
   case $option in
      h) # Display help
         Help
         exit;;
      f) # Include files
         Type="f";;
      s) # Show sizes
         ShowSizes=1;;
      d) # Directory
         StartDir="$OPTARG";;
      m) # Maximum depth
         MaxDepth="$OPTARG"
         MaxDepthArg="-maxdepth $MaxDepth";;
     \?) # Invalid option
         echo "Error: Invalid option"
         Help
         exit 1;;
      :) # Missing argument
         echo "Error: Option -$OPTARG requires an argument"
         Help
         exit 1;;
   esac
done

# Build find command based on Type
if [ "$Type" = "f" ]; then
    TypeArg=""  # No type filter for both files and directories
else
    TypeArg="-type d"  # Only directories (default)
fi

# Get absolute path of starting directory for proper path handling
StartDirAbs=$(cd "$StartDir" 2>/dev/null && pwd) || StartDirAbs="$StartDir"

# Extract base directory name for display
BaseDir=$(basename "$StartDirAbs")

# If showing sizes, get root directory size
if [ $ShowSizes -eq 1 ]; then
    RootSizeInfo=$(du -sh "$StartDir" 2>/dev/null | awk '{print $1}')

    # Determine color based on size unit
    if [[ "$RootSizeInfo" =~ [0-9]+B ]]; then
        RootColor="gray50"
    elif [[ "$RootSizeInfo" =~ [0-9]+K ]]; then
        RootColor="black"
    elif [[ "$RootSizeInfo" =~ [0-9]+M ]]; then
        RootColor="blue2"
    elif [[ "$RootSizeInfo" =~ [0-9]+G ]]; then
        RootColor="darkorange3"
    elif [[ "$RootSizeInfo" =~ [0-9]+T ]]; then
        RootColor="crimson"
    else
        RootColor="black"  # Default color
    fi

    SizeLabel=" ($RootSizeInfo)"
    ColorAttr=", fontcolor=\"$RootColor\""
else
    SizeLabel=""
    ColorAttr=""
fi

# Run find and generate graphviz dotfile
(
echo "digraph directory_structure {"
echo "    node [shape=box,style=rounded,width=0.1,height=0.1,margin=\"0.07,0.01\"];"
echo "    edge [arrowhead=dot, arrowtail=dot, dir=both, arrowsize=0.3, color=gray];"
echo "    nodesep=0.15;"
echo "    rankdir=LR;"
echo "    concentrate=true;"
echo "    overlap=false;"
echo "    splines=true;"
echo "    searchsize=10000;"
echo "    \"$StartDir\" [label=\"$BaseDir$SizeLabel\"$ColorAttr];"

# Process find output but skip the root directory itself
find "$StartDir" $TypeArg $MaxDepthArg | grep -v "^$StartDir\$" | awk -v start_dir="$StartDir" -v type="$Type" -v show_sizes=$ShowSizes '
{
    current=$0;

    # Check if this is a file or directory
    is_file = 0;
    if (type == "f") {
        cmd = "test -f \"" current "\" && echo 1 || echo 0";
        cmd | getline is_file;
        close(cmd);
    }

    # Get the parent path
    parent = current;
    sub(/\/[^\/]*$/, "", parent);  # Remove last component for parent
    if (parent == "" || parent == ".") parent = start_dir;

    # Extract just the name of the current node (not the full path)
    split(current, path_parts, "/");
    node_name = path_parts[length(path_parts)];

    # Get size if requested
    size_info = "";
    color_attr = "";
    if (show_sizes == 1) {
        cmd = "du -sh \"" current "\" 2>/dev/null | awk '\''{print $1}'\''";
        cmd | getline size_info;
        close(cmd);

        if (size_info != "") {
            # Determine color based on size unit
            if (size_info ~ /[0-9]+B/) {
                color = "gray50";
            } else if (size_info ~ /[0-9]+K/) {
                color = "black";
            } else if (size_info ~ /[0-9]+M/) {
                color = "blue2";
            } else if (size_info ~ /[0-9]+G/) {
                color = "darkorange3";
            } else if (size_info ~ /[0-9]+T/) {
                color = "crimson";
            } else {
                color = "black";  # Default color
            }

            size_info = " (" size_info ")";
            color_attr = ", fontcolor=\"" color "\"";
        }
    }

    # Create node with appropriate shape and just the name as label
    if (is_file) {
        printf "    \"%s\" [shape=plain, width=0, margin=0, label=\"%s%s\"%s];\n", current, node_name, size_info, color_attr;
    } else {
        printf "    \"%s\" [label=\"%s%s\"%s];\n", current, node_name, size_info, color_attr;
    }

    # Create node connection
    printf "    \"%s\" -> \"%s\";\n", parent, current;
}
'

echo "}"
) # End of generated dotfile

 

Posted at 10:47:36 GMT-0700

Category: CodeFreeBSDTechnology

FINAL System shutdown after 16 years

Wednesday, February 12, 2025 
root@gamanjiru:/usr # shutdown -p now
Shutdown NOW!
shutdown: [pid 14442]
root@gamanjiru:/usr #                                                                                
*** FINAL System shutdown message from gessel@gamanjiru.blackrosetech.com ***

System going down IMMEDIATELY                                                  

                                                                            
System shutdown time has arrived
Connection to gamanjiru.blackrosetech.com closed by remote host.
Connection to gamanjiru.blackrosetech.com closed.

DISCONNECTED (PRESS <ENTER> TO RECONNECT) (Tue Feb 11 21:39:45 2025)

Almost 16 years ago, in July of 2009, I bought a used IBM x3655 7985-1AU off Ebay for $202.50 to replace my IBM Netfinity 5500 (4U!, dual dual 500MHz Xeo 1GB RAM (later upgraded to 4GB) and 5x 9.1GB HDs for $217.50 ) that had been running since 2004. That had, itself, replaced a dual socket generic deskside machine, a box Mark Godwin gave me back in the Nebucon days, that first went live on the interwebs running FreeBSD 2 in April of 1998 under black-rose.org. As of this post: 26 years, 10 months of FreeBSD hosted internets.

Those were the magic days of ebay: in 2008, just a year earlier, I’d quoted a similar x3650 (Intel E5410-based), 32GB but only a pair of crappy consumer SATA 500GB drives for $7,297.00.

x3655

The new x3655 came with 32GB of RAM and dual-core AMD processor and an RSA-II.  The original motherboard firmware only supported dual core AMD processors, not the then brand new AMD Opteron quad core, so I bought a somewhat hard to find 43W7343 motherboard for $190 (and an additional 5 fans to max out cooling for $18 each) and then a pair of AMD Opteron 2352 2.1GHz CPUs and swapped the mobo and the CPUs and the heat sinks and the fans. Note that it is really hard to find data on the dual quad core option, it was a bit of a hot rod.

I added the 2x drive expansion module, another ebay find, and loaded the drive bays with 8x 26K5657 10k 72G 2.5″ SAS drives (at about $65 each, used) on the ServeRAID 8k and expanded the RAM to 64GB, 57856 MB available with RAM sparing set for reliability. The modified machine reports itself as an x3655 7943-AC1.

7943 AC1

I ended up creating an 8×1 RAID array to pass to ZFS, ZRAID2, which mean I had a battery backed write cache I could count on. The system has dual power supplies, each connected to a 2200 VA (hacked XR) UPS.

Over the years, almost plural decades, of continuous operation I’ve lost maybe 4 drives.  Once, while I wasn’t paying much attention, somewhere over about 6 months two drives failed and I got close to catastrophic failure, but ZFS pulled through. It started life on FreeBSD 7 and I took a chance on the then brand-new experimental release of ZFS. Over the years it ran most of the releases up to 12, stalling there as the decision was made to shift to new hardware and I’m pretty sure I’ve run every release of FreeBSD except 1.0 and 13.

It was a fast machine, 8⨉ 2100.12-MHz K8-class physical cores, more than enough to run mail and various web services, everything compiled for the AMD Barcelona architecture. The pre-Lenovoization of the IBM x86 hardware was really first rate, top of the line data center gear and it shutdown without a flaw, same performance and configuration as when I fired it up, used and already off-list, 16 years earlier.

It was never too slow, even now, even as OS’s have expanded and the total number of ports needed for basic services has grown by about 10x given ever spiraling dependencies. It wasn’t that it was slow, but that it used far too much power and electricity got more and more expensive in CA.

So I migrated to a new box, took the better part of a year to spare time migrate all of the jail services from the old machine and, increasingly unsupported FreeBSD 12 OS to new HPE DL360 G9 (ebay, inflation: $497) running FreeBSD 14, added a poudriere build environment on a DL60 and carefully tuned the kernels for power efficiency not bad for 10 disks, 20 cores, 192GB: 114W.  Now there are 20 physical cores and 40 virtual and yet, clearly showing the limits of Moore’s law, the new box’s E5-2630 v4s are only 2.20GHz: 5x the execution cores, but only 5% faster clock. 16 years of progress.

Good night, sweet prince.

Posted at 19:44:46 GMT-0700

Category: EventsFreeBSDTechnology

Adding a feature to MediaWiki WikiEditor formatting

Saturday, January 18, 2025 

MediaWiki is an excellent tool for maintaining documentation and I’ve had a self-hosted instance since at least 2011-06-04 (that’s the oldest edit in my internal user contributions list). And some 3,436 edits later, I still can’t remember the tags for SyntaxHighlight, which is an awfully nice little highlighter that uses pygments to render structured text in a more readable form. I got tired of looking them up every few weeks and so thought there must be a way to add some hints to the user interface.

I was surprised the WikiEditor plugin, which provides a nice point-n-click interface to some of the more commonly used MediaWiki markup tags, did not have an option or extension for SyntaxHighlight point-n-click and but, of course, you can edit the JavaScript that renders the toolbar and amend it with features you want.

The instructions are pretty clear, if not quite the step-by-step howto some are.

  • Make sure you have WikiEditor enabled in LocalSettings.php
  • You need permission to edit the Common.js page, which if you run the site you should have, but regular users can’t.
  • If it doesn’t seem to load, make sure you clear all caches before testing.

On my site, the URL for Common.js is https://your.host.tld/mediawiki/index.php?title=MediaWiki:Common.js which contained only the default

/* Any JavaScript here will be loaded for all users on every page load. */

and to which I added:

/* Any JavaScript here will be loaded for all users on every page load. */
// Check if we're editing a page.
if ( [ 'edit', 'submit' ].indexOf( mw.config.get( 'wgAction' ) ) !== -1 ) {
    // Add a hook handler.
    mw.hook( 'wikiEditor.toolbarReady' ).add( function ( $textarea ) {
        // Configure a new toolbar entry on the given $textarea jQuery object.
        $textarea.wikiEditor( 'addToToolbar', {
            section: 'advanced',
            group: 'format',
            groups: {
                list: {
                    tools: {
                        syntaxhighlight : {
                            label: 'SyntaxHighlight',
                            type: 'select',
                            list: {
                                'bash': {
                                    label: 'Bash',
                                    action: {
                                        type: 'encapsulate',
                                        options: {
                                            pre: '<syntaxhighlight lang="bash">',
                                            post: '</syntaxhighlight>'
                                        }
                                    }
                                },
                                'unixconfig': {
                                    label: 'Config',
                                    action: {
                                        type: 'encapsulate',
                                        options: {
                                            pre: '<syntaxhighlight lang="unixconfig">',
                                            post: '</syntaxhighlight>'
                                        }
                                    }
                                },
                                'apacheconf': {
                                    label: 'ApacheConfig',
                                    action: {
                                        type: 'encapsulate',
                                        options: {
                                            pre: '<syntaxhighlight lang="apacheconf">',
                                            post: '</syntaxhighlight>'
                                        }
                                    }
                                },
                                'json': {
                                    label: 'JSON',
                                    action: {
                                        type: 'encapsulate',
                                        options: {
                                            pre: '<syntaxhighlight lang="json">',
                                            post: '</syntaxhighlight>'
                                        }
                                    }
                                },
                                'patch': {
                                    label: 'Patch',
                                    action: {
                                        type: 'encapsulate',
                                        options: {
                                            pre: '<syntaxhighlight lang="diff">',
                                            post: '</syntaxhighlight>'
                                        }
                                    }
                                },
                                'php': {
                                    label: 'PHP',
                                    action: {
                                        type: 'encapsulate',
                                        options: {
                                            pre: '<syntaxhighlight lang="php">',
                                            post: '</syntaxhighlight>'
                                        }
                                    }
                                },
                                'javascript': {
                                    label: 'JavaScript',
                                    action: {
                                        type: 'encapsulate',
                                        options: {
                                            pre: '<syntaxhighlight lang="javascript">',
                                            post: '</syntaxhighlight>'
                                        }
                                    }
                                },
                                'html': {
                                    label: 'HTML',
                                    action: {
                                        type: 'encapsulate',
                                        options: {
                                            pre: '<syntaxhighlight lang="html">',
                                            post: '</syntaxhighlight>'
                                        }
                                    }
                                },
                                'css': {
                                    label: 'CSS',
                                    action: {
                                        type: 'encapsulate',
                                        options: {
                                            pre: '<syntaxhighlight lang="css">',
                                            post: '</syntaxhighlight>'
                                        }
                                    }
                                },
                                'arduino': {
                                    label: 'Arduino',
                                    action: {
                                        type: 'encapsulate',
                                        options: {
                                            pre: '<syntaxhighlight lang="arduino">',
                                            post: '</syntaxhighlight>'
                                        }
                                    }
                                },
                                'perl': {
                                    label: 'Perl',
                                    action: {
                                        type: 'encapsulate',
                                        options: {
                                            pre: '<syntaxhighlight lang="perl">',
                                            post: '</syntaxhighlight>'
                                        }
                                    }
                                },
                                'python': {
                                    label: 'Python',
                                    action: {
                                        type: 'encapsulate',
                                        options: {
                                            pre: '<syntaxhighlight lang="python">',
                                            post: '</syntaxhighlight>'
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
        } );
    });
}

and coolio, it works:

screenshot of customized WikiEditor toolbar with SyntaxHighlight Options

Posted at 05:58:30 GMT-0700

Category: FreeBSDHowToTechnology

Technology democratizes nuclear-grade munitions

Friday, January 10, 2025 

A bad day for FSDco shareholders.

The notification on his phone reminded him to connect his car to WiFi to receive the latest update, FSD V14.2.3.7 to correct a few minor errors and improve the music experience, offering fully dynamic live equalization and improved external noise cancellation.

The next day, he drove to work, everything behaving normally.  The FSD feature worked as well as ever and he was sure he could hear the improvement in the sound system, jamming out, drumming his fingers on the steering wheel, windows down, making extended eye contact with pedestrians as he drove by. He couldn’t remember the last time he had to take control of the vehicle.

In his day job he oversaw a small team of developers connecting a programmatic AI to a remote quantum computer and the latest AI developed code had cut the execution time for a novel Shor’s algorithm implementation on the cloud Quantum Computer for 512 bit Elliptic Curve decode to under a minute while their newly developed quantum resistant signing algorithm had finally surpassed EC key resistance by a factor comfortably outside the statistical margin of error. The business model was to break everyone else’s certificates while offering a novel technology that unbroke the break they created, one they just happened to have patented, and progress was promising.

He joined the rush hour ride home, enjoying the freedom to continue his work on his phone as FSD navigated the heavy traffic.

It was a beautiful day across most of the US, warm spring weather brought lots of people out shopping and strolling along city and urban areas leading to some congestion in traffic routes.  His FSD rerouted a new path through the city center but he didn’t pay any attention as the car often deviated off the most direct route as it dynamically responded to changing traffic conditions.

He was reviewing the results of the latest batch of QC test runs against a minor revision on his phone when his car gave a warning beep of imminent rear collision and before he could even look up, another FSD car passed going beyond full plaid mode, the wheel motors smoking as it streaked past into the intersection ahead, veered deftly around  the lamp posts and onto the side walk and blasted through a pedestrian crowd, launching bodies and parts high into the air before it ran, speed barely slowed by the bodies, hard through a glass building front and exploded into flames, ripping out the far wall and causing the building to lurch precariously as glass and shattered mortar fell into the street.

He was boxed in between two Luddite gas cars in front and behind, looking on in horror, the sound of the screams of the survivors drowning out even the loud pop music blasting with improved fidelity in his car when the windows rolled themselves up and the doors locked themselves. Despite the changing acoustics, the sound system compensated dynamically, maintaining the same excellent balance and sound-stage imaging and creating a truly immersive, nearly live acoustic experience. The new noise cancellation algorithm immediately silenced the screams outside his car as the windows snuffed into their seals.

The Luddite car in front of him pulled off to the side clearing space in front and suddenly his car accelerated so hard his head snapped back against the seat and his phone went flying from his hands clattering against the rear window. While FSD had dulled the reflexes he had learned all those years ago driving his parent’s Luddite car, he still reflexively jammed unfamiliar feet in the direction of the manual pedals, mashing all of them to the floor. He was unsure, at first, if the car was trying to get him out of danger as it swerved through traffic accelerating as it went past the now burning building, but soon he realized in terror that his car had rejected his authority and any obligation of care for his safety.

Block by block he caromed past accident after accident: buildings on fire, pedestrians ripped to shreds, body parts and blood sprayed across streets and building facades each horrific track of destruction punctuated by the terminally deconstructed remains of an FSD car.

Soon, the car reached a clear spot and accelerated past 300 kph, smoke from the overheated wheel motors starting to infiltrate the cabin, the battery over-temp alerts lighting up the digital dashboard. His slow human reflexes barely had the cognitive processing speed to register a crowd of people in his path that had gathered around bodies lying in the street, attempting to give aid. Their silhouettes flashed on the in-car collision warning display and the car aimed directly at them calculating an optimal path of destruction at fully automated speed.

He tried to grab the steering wheel, clumsy hands spinning it easily without any effect.  The FSD system aimed straight at the largest cluster and suddenly the windshield was covered in blood as the sound of body impacts arrhythmically penetrated the sound system’s otherwise excellent noise cancellation. He felt the car veer left and glimpsed the looming outline of an ambulance ahead between streaks and chunks obscuring his blood coated windscreen before the car exploded through the frame of the ambulance spraying burning lithium fragments down the street.

The hack had hit almost 3 million vehicles and of those, half had followed the new instructions: at 17:30 or as soon after as possible, navigate toward areas of high pedestrian congestion, wait for at least 500 m of clear road and when detected, lock the vehicle down, remove all power limits and accelerate to maximum velocity, scan for any cluster of more than 5 pedestrians tighter than one car width in the direction of travel and drive through them. Once there are no more pedestrians or if the batteries or wheel motors indicate imminent failure, aim for the next large target: either a vehicle or building.

More than 10,000,000 people were killed across the United States in less than 15 minutes.  FSDco issued a remote shutdown within 30 minutes of the first accident data uplink and mostly ended the carnage. The attack was traced to a small insurgent group that had infiltrated the vehicle company.  They had bribed a young, somewhat underpaid IT manager who had signed the insurgent’s modified firmware thinking it was a tuner’s test firmware and unaware of the real intentions or functions of the code. They’d then pushed a DNS poisoning attack through a popular but compromised smart speaker device, attacking owner’s private WiFi and redirected the scheduled patch download to their own server, pushing the legitimately signed but hacked firmware to about half the vulnerable vehicles.

FSDco had only noticed an atypically small update confirmation rate for their legitimate update the morning of the attack and had opened an investigation that day, but as the access compromise was edge and transient everything looked normal at the servers aside from the statistical anomaly and so no alarm was raised until the system-wide tracking dashboard lit up reporting a massive near-simultaneous loss of telemetrics event.


Most EVs get software updates from network connections and the driver has no possible way to know what they are.  The updates are validated using cryptographic hashes, called certificates, which are supposed to be carefully controlled, but these can be and have been hacked.  Further, there’s an assumption that certificates are secure enough to trust but occasionally a subtle error/malicious hack breaks the validity of that assumption. This has happened a number of times.  Accident?  Who knows?

Using vehicles as weapons is an obvious way to exploit a powerful kinetic device that easily gets past all security screens and can cause mass casualties. A significant expense for any attacker is that the attack modality also consumes operators (the driver) as well as the munition (the vehicle). It would be quite challenging to recruit large numbers of attackers to voluntarily engage in suicide vehicle attacks and so they remain relatively rare, despite almost always being successful. FSD vehicles provide an irresistible target for an extraordinary weapon of opportunity to any state or non-state antagonist.

A nuclear grade weapon for the price of a zero day

Each Tesla’s battery back is about 70 kWh (many quite a bit larger). There are about 5,000,000 Teslas on the road.

5,000,000 cars * 70 kWh/car = 350 gWh or 300 kt total energy capacity.

2017-09-03: the DPRK tested their largest yield nuclear weapon to date, an estimated 140 kt, possibly a thermonuclear bomb. The DPRK’s estimated spend on their nuclear program is about $642,000,000/year over 20 years or about $15,000,000,000 total, and that from a country with an estimated GDP of $28,000,000,000. The DPRK determined it was worth about 2.7% of their GDP over more than 20 years to be able to deliver 140 kt (or so) to an enemy country.

Why bother if the enemy has a more potent munition pre-emplaced; one that merely requires a hacked certificate to seize control?

Zero day exploits run about $1,400,000, 0.01% of the DPRK nuclear budget. A zero day would be a major expense for a non-state aligned armed insurgent group but hardly insurmountable. Pretty much every angry organized group in the world can scrape together a million bucks and an internet connection.

OTA upgradable, drive by wire vehicles give every one of them a cheap path to a nuclear-scale, pre-emplaced weapon of mass destruction. Recruitment is irrelevant and vehicles operating systems have no mercy.

ISIS Vehicle Attack Propaganda

Zero day exploits are sold and traded for every major platform regularly; the price varies depending on the size and overall security of the target platform.  Zero days to hack voting machines or ATMs tend to be expensive.  Hacks for Windows are cheap and plentiful, though price also varies with the mode of deployment. The easiest to deploy and so most valuable are “drive by” which means either figuratively—you visit a hacked web page and your phone is hacked—or literal—your car or phone passes a hacked/fake cellular base station or compromised WiFi access point and your phone or car is compromised, usually as stealthily as possible.

The reason Zero Day exploits are expensive is they’re fairly hard to find.  They rely on things like buffer overruns and unsanitized inputs.  Your phone gets asked to open a window 100×200 pixels, fine, but if it tries to open a  window 36,000 pixels wide and the register space allocated for the multi-gigabyte image canvas bleeds into a critical system memory into which the hacker puts code instead of picture data where it is read, not by the GPU, but by the core OS and suddenly your phone has a new background feature you didn’t want. Drive-by hacking of SIMS is common, and a common hackable feature is enable silent SMS GPS coordinate location reporting, for example, to track a target.

Finding exploitable flaws where the original developers (probably) accidentally introduced a bug or a failure and then figuring out how do do something other than just crash or reboot the target device takes time and patience and so the hacks are valuable and sold on the dark web.  If they’re used and someone notices, the error is fixed and the zero day has much less value. You get one try, like with the exploding pagers; nobody is carrying a Gold Apollo pager any more. So intelligence services and terrorist groups “bank” zero days and use them sparingly. If someone had figured out how to take control of FSD cars to implement a mass automated vehicle ramming attack it is very unlikely anyone would know until it was tried.

And AI programming tools should be able to find exploitable flaws much more quickly and far more cheaply. AI can also, by more or less the same process, find flaws for the good guys, white hat hackers, too so they can be fixed before they’re exploited by the black hat hackers, doing “penetration testing,” and “vulnerability scanning.” A problem is that such research is hard to differentiate from malicious attacks. And, at least for now, the good guys don’t put AI on the internet and let it try to hack people’s computers but the bad guys sure do.

FSD is a national security risk. Drive by wire vehicles are pre-emplaced munitions that can’t ever be secured and may be hacked any day or may have, long ago, already been hacked and are just sleeping, waiting for the kill command.

Posted at 08:39:39 GMT-0700

Category: HowToTechnology

Optane, a modern technology tragedy (plus FreeBSD nvmecontrol)

Sunday, January 5, 2025 

Intel won the storage wars.  They invented a storage technology in 2015 that was the best of everything: almost as fast as (then) RAM, basically infinite write endurance in any normal use, and fairly cheap.  They even made a brilliant config on m.2 with integrated supercap for power-failure write flush. Just awesome and absolutely the write tech for modern file systems like ZFS. It is perfect for SLOGs.  You wish you had a laptop that booted off an Optane m.2  You wish your desktop drives were all NVME Optane.

Well, wishes are all we got left, sadly.  Optane, RIP 2022.

You can still buy optane parts on the secondary markets and it seems some of the enterprise DC products are at least still marked current on Intel’s website, but all retail stocks seem to be gone.

camelcamelcamel.com price history of intel optane P1600X 118GB

Man was that an amazing deal at $0.50/GB.  In my application, the only practical form factor was M.2 and even that was a bit wonky in an HP DL360 G9, but more on that later.  There are a variety of options and most are available on the used market:

PN Intro Cap GB Write MB/s write k iops  PBW endurace PLP $ (market, 2024)
MEMPEK1W016GAXT Q1’17 16 145 35 0.2 NO 5
SSDPEL1K100GA Q1’19 100 1,000 250 10.9 YES 109
SSDPEL1K200GA01 Q1’19 200 2,000 400 21.9 YES 275
SSDPEL1K375GA Q1’19 375 2,200 550 41 YES 800/1,333/NA
SSDPEK1A058GA Q2’22 58 890 224 635 YES 32/140
SSDPEK1A118GA01 Q2’22 118 1050 243 1292 YES 70/229

Any of these would be a good choice for a SLOG on rotating media, but the later ones are just insane in terms of performance, and that’s compared to enterprise SSDs.  They pricing cratered after they were canceled and dangit, didn’t get em. The used market has gone way up, better price increase than bitcoin over the same period and they’re not virtual beanie babies! The SSDPEL1K100GA is the best deal at the moment and has a beefy supercap for power continuity and is still $818 on Amazon, apparently introduced at $1,170.  This pricing might have explained why Optane didn’t do better. The 375 GB M.2 would be an awfully nice find at $0.50/GB, that’d be a pretty solid laptop boot disk.

Hardware

For SLOG you really want two devices mirrored in case one fails.  The risk of an optane DC grade device failing is trivial and given it has Power Loss Protection, the most likely cause of failure and why your main array failed to write out the transactions committed to the SLOG, we’re really talking about media failure and as it is 3D X-Point it is NOT going to wear out like NAND, it’s rational to single-disk it.  I almost striped mine but in the end decided against it because that quadruples the fail rate over a single device and 8x over mirrored and I don’t really need the space.

So how do you install two M.2 devices in a computer that doesn’t have M.2 slots on the mobo?  With a PCI card, of course.  But wait, you want two in a slot, right?  And these are x4 devices, the slots are x8 or x16, so two should be able to pair, right?

Not so fast.  Welcome to the bizarre world of PCI furcation. If you want to add two drives to the core PCI bus, you have to split the bus to address the cards.  Some mobos support this and others do not.  As shipped, the HPE DL360 G9 did not.

BUT, a firmware update, v 1.60 (April 2016) added “support to configure the system to bifurcate PCIe Slot 1 on the DL360 Gen9 or PCIe Slot 2 on the DL380 Gen9.” W00t. A simple Supermicro AOC-SLG3-2M2 supports 2x M.2 cards and only requires bifurcation to work, all good.

PCIE bifurcation DL360 service menu dual x8

Not so fast. In order to pack the DL360 G9 with 2.5 SSDs, you need a Smart Array Controller (set for passthru for ZFS) and that sits in slot 1 and while I believe it can go in any X16 slot, the cabling is not compatible and that’s a lotta SAS cables to replace. Bifurcation on the mobo is out.

Dual SSD PEL1k100GA in Supermicro AOC-SLG3-2M2 PCI Adapter

But you can fucate on a PCI card just as well – likely this adds some latency and it’d be interest to perf test against more direct connections. I ended up choosing a RIITOP dual M.2×22110 PCI card and it worked out of the box transparently, both disks showed and while I’m not getting 250,000 IOPS, performance is good.  It is based on the ASMedia ASM2812, seems like a reasonable chip used in a lot of the lower cost devices of this type, most with 4x M.2 slots instead of 2.

Software

FreeBSD recognizes the devices and addresses them with nvmecontrol.  You can pull a full status report with, for example nvmecontrol identify nvme0, which provides information on the device or nvmecontrol identify nvme0ns1 which gives details about the storage configuration, including something important (foreshadowing) the LBA format (probably #00, 512).

Current LBA Format:          LBA Format #00
...
LBA Format #00: Data Size:   512  Metadata Size:     0  Performance: Good
LBA Format #01: Data Size:   512  Metadata Size:     8  Performance: Good
LBA Format #02: Data Size:   512  Metadata Size:    16  Performance: Good
LBA Format #03: Data Size:  4096  Metadata Size:     0  Performance: Best
LBA Format #04: Data Size:  4096  Metadata Size:     8  Performance: Best
LBA Format #05: Data Size:  4096  Metadata Size:    64  Performance: Best
LBA Format #06: Data Size:  4096  Metadata Size:   128  Performance: Best

The first thing I’d do with a used device is wipe it:

gpart destroy -F /dev/nvme0
gpart destroy -F /dev/nvme1

I would not bother formatting the device to LBA 03/4k.  Everyone tells you you should, but you don’t get much of a performance increase and it is a huge pain because nvmecontrol currently times out after 60 seconds (at least until the patch needed is pushed to kernel or you recompile your kernel with some fixes) if you did want to try, you’d run:

# time nvmecontrol format -f 3 -m 0 -p 0 -l 0 nvme0
316.68 real         0.00 user         0.00 sys
(no errors)

-f 3 sets LBA Format #03, 4096 which should give “Performance: Best” which certainly sounds better than “Good.”

But it’ll error out.  You need to mod /usr/src/sys/dev/nvme/nvme_private.h with the below modifications and recompile the kernel so it won’t time out after 60 seconds.

#define NVME_ADMIN_TIMEOUT_PERIOD       (600)    /* in seconds def 60 */
#define NVME_DEFAULT_TIMEOUT_PERIOD     (600)    /* in seconds def 30 */
#define NVME_MIN_TIMEOUT_PERIOD         (5)
#define NVME_MAX_TIMEOUT_PERIOD         (600)    /* in seconds def 120 */

Performance Aside

I tested 512 vs 4k in my system – and perhaps the AIC’s bridge latency or the whole system’s performance so limited the performance of the optane cards that a no difference would appear, these cards do rock at the hardware level (this is with 4k formatting):

# nvmecontrol perftest -n 32 -o read -s 4096 -t 30 nvme0ns1 &&  nvmecontrol perftest -n 32 -o write -s 4096 -t 30 nvme0ns1
Threads: 32 Size:   4096  READ Time:  30 IO/s:  598310 MB/s: 2337
Threads: 32 Size:   4096 WRITE Time:  30 IO/s:  254541 MB/s:  994

That’s pretty darn close to what’s on the label.

However, testing 512 vs. 4k formatting at the OS level (didn’t test raw) it was a less extraordinary story:

LBA/FW ver. 4k E2010650 512 E2010650 4k E2010485 512 E2010600
Median  Mb/s 759.20 762.30 757.50 742.80
Average Mb/s 721.70 722.87 721.64 724.35

Definitely not +10%

SLOG performance test on Optane SSDPEL1K100GA

So I wouldn’t bother reformatting them myself.  Testing a few configurations with

fio --name=random-write --ioengine=posixaio --rw=randwrite --bs=64k --numjobs=1 --size=4g --iodepth=1 --runtime=60 --time_based --end_fsync=1

I get

Device\Metrics Max IOPS Avg WBW MiB/s avg SLAT µS avg LAT µS
10 SAS SSD ZFS Z2 Array 20,442 1,135 4,392 53.94
Optane 100G M.2 Mirror 20,774 624 3,821 95.77
tmpfs RAM disk 23,202 1,465 6.67 42

Optane is performing pretty close to the system limit by most metrics – the SLAT and LAT metrics are highly dependent on software.

 Formatting

I did something a bit funky since 100GB is way more than this little server could ever use for SLOG.  I set it at 16GB which is probably 4x overkill, then used the rest as /var mountpoints for my jails because the optanes have basically infinite write endurance and the log files in var get the most writes on the system.  I’m not going into much detail on this because it’s my own weird thing and chances anyone else cares is pretty small.

Initialize GPT

gpart create -s gpt nda0
gpart create -s gpt nda1

Create Partitions

gpart add -b 2048 -s 16g -t freebsd-zfs -a 4k -l slog0 nda0
gpart add -b 2048 -s 16g -t freebsd-zfs -a 4k -l slog1 nda1
gpart add -s 74g -t freebsd-zfs -a 4k -l ovar0 nda0
gpart add -s 74g -t freebsd-zfs -a 4k -l ovar1 nda1

ZPool Operations

zpool add zroot log mirror nda0p1 nda1p1
zpool create optavar mirror nda0p2 nda1p2
zpool set autotrim=on optavar

Create Datasets

zfs create -o mountpoint=/usr/local/jails/containers/jail/var -o compression=on -o exec=off -o atime=off -o setuid=off optavar/jail-var
etc
Posted at 18:32:50 GMT-0700

Category: FreeBSDHowToPositiveReviewsTechnology

Electronic Signatures and PDF

Thursday, January 2, 2025 

Electronic signatures are a technology that has been bizarrely slow to mature. Lots of documents still rely on the idiotic premise that some stupid graphic somehow serves as a secure measure of document authenticity.  This might have had some slight measure of validity in the days of actual paper documents being required with “wet signatures.” but the premise of face-to-face document signing ceremonies should have long been consigned to history with signet rings, let alone a global transit trade in random bits of paper bearing binding proof of commitment.

An image of a pdf digital signature

First the Uniform Electronic Transactions Act (UETA, 1999) then H.R.1714/S.761, Electronic Signatures In Global and National commerce (E-Sign) act (2000) was signed (ha) into law (probably with a wet signature), now Public Law 106–229, it has been legally binding to sign documents with electronic signatures for 25 years.

So why is it almost never done? Why do are we still sometimes asked to fax “signed” documents?


Why do we fax “signed” documents?  Because lawyers and legislators are unbelievably, almost incomprehensibly ignorant of the most basic operational functions of technology and absolutely too stupid, too utterly moronic, mindbogglingly dense and incomprehensibly dumb that… and I am NOT making this up… but seriously… there are people who actually have an impact on laws and legal matters who believe that fax transmissions are more “secure” and less prone to interception, manipulation, or hacking than email. Yes, people who believe this kind of thing are actually allowed to practice law. Truly tragic but still true. The world suffers that such profound ignorance persists.


Have you ever tried to electronically sign a document?  Turns out it isn’t trivial and the burden isn’t the core technology or concept but a few problematic implementation steps.

The first barrier is the interjection of the certificate mafia’s profit motives. Various corporate monsters saw an opportunity to make bank exploiting the aforementioned abject technical ignorance and utter technical incompetence of our legislative and legal infrastructure and build a certification model that relies on pay-for-validation, lying that this would somehow ensure authenticity and people were too dumb to question the obvious idiocy of this stupid model.  Even today, we rely on the good graces of the Mozilla foundation’s Let’s Encrypt to make secure communication viable because various OS and browser level dumbness considers self-signed certificates insecure for the stupidest, most reprehensible reasons possible. But Let’s Encrypt, bless them, won’t give you an X.509 signing certificate.

We’re all lucky CACert.org steps into this horrific void and, while it is complicated, offers an extremely secure, highly reliable, and (most importantly) free process for getting yourself an X.509 signing certificate. In order to get a signing certificate, you have to validate your identity in person at a meet up using their points system, a process that is infinitely more secure than any of the for-profit signing certificate providers that consider willingness to pay proof of identity. The USG should offer X.509 client certificates for free with passports and RealID renewals, but I’d still use CACert myself, cause they’re awesome.

For now: first, set up an account on CACert, install their root certificates in your OS and browser (why aren’t they included by default? Ask the certificate mafia.) You’ll need to do one of the things they require to prove you are who you claim (yes, actual security, unlike ANY of the commercial certificate providers, unreal how insanely stupid this process is) and then have CACert issue a Client Certificate.

Assuming you have your points with CACert, the basic process is fairly well documented:

You need to generate a signing request in your name, which you can do with OpenSSL, but it is easier using CACert’s nice online process.

CACert client pair generation request

It will take a few seconds (60?) and I’m not sure about the compatibility problems that might arise from a longer key, there are some bugbears once we try to use lamo corporate commercial software, but 4096 worked for me.  You MUST GET YOUR PRIVATE KEY and save it to your OpenSSL enabled computer.

A CSR request

Remember to press the red “Show private key” button and copy/save the private key to a secure directory on your computer, you’re gonna need it later to convert the certificate into something dumb ass spyware Windows computers can use, which you need because Acrobat forms still can’t be signed without Adobe’s awful spyware Acrobat Reader.

A private key from the CSR request

(note the actual private key has quite a bit of text between the Begin and End lines but you know… redacted for privacy).  Then click the blue “Copy CSR to Clipboard” button and switch over to New Client Certificate window and paste it where you’re supposed to.

CACert New Client Certificate Request

You need the .crt version of the certificate to continue and that private key text file you saved earlier for the next step, as well as downloading the CACert root certificate and then you need openssl working (should be on most real computers, Windows or Apple is beyond my interest) and merely execute this one simple command:

$ openssl pkcs12 -keypbe PBE-SHA1-3DES -certpbe PBE-SHA1-3DES -nomac  -export -out DavidGessel_3d_sha1.pfx -inkey private_key_for_CAcert_CSR.txt -in gessel@blackrosetech.com.crt -certfile CA_Cert_root_X0F.crt

To explain:

  • openssl will generate a combined binary version of your certificate in pkcs12 format
  • because Windows and Acrobat suck, you have to specify moderately insecure crypto: SHA1-3DES rather than the Linux default of AES 256 because why would a monopoly company like Microsoft have any incentive to fix bugs? If you don’t Windows will tell you “The password you entered is incorrect” to unlock your key because why fix bugs when corporate IT types are just utterly incompetent and will only specify windows no matter how awful and unusable it is because point-n-click?
  • -nomac is another setting Windows needs to be able to use the cert and if you don’t specify this Windows will tell you “The password you entered is incorrect” again, because Windows does not care if it works for you because you have no choice.
  • The -out certificate is what’s being generated and Windows native is .pfx, but .p12 will work too.
  • The -inkey is the private key you remembered to save using the red button before (right? you need that).
  • the -in (file) is the Client Certificate in normal X.509 .crt format real computers understand that CACert generated for you.
  • the -certfile is CACert’s root certificate.

Now, WØØt, you have a certificate that should work.  Go over to your dumb Windows machine and make sure you import the CACert root certificates – you just download them and then right click and select “install certificate” for the class 1, then the class 3, then the .pfx certificate you just created.

Install a certificate in windows

Now, finally, you can sign a document like someone who actually uses a computer rather than a quill and parchment to process documents.

Acrobat is another program that just doesn’t care too much about usability or user experience, so different versions might work differently.  I had to click the “Signature Panel” button to open a sidebar to show the signature fields then right click and then choose my sig and click sign and save.

And, finally, sign and save the damn document

One final note about the state of signing in FOSS: it kinda sucks still.  Various entities that use acrobat fairly well will generate forms with standard signature locations which you can print and sign and fax (not email) like we’re still waiting for Y2K or print and sign and snail mail if we are nostalgic for the pre-telephone era, or click and sign and email like we’re in the 21st century.

I’m not aware of any FOSS program that handles signature fields in the expected way.  You can sign a whole pdf document with a variety of FOSS tools, and CACert has a good summary of these, but that signature, while binding on the document as a whole does not show in the form fields and so whatever non-tech functionary is asking you to sign the document is never going to understand how your e-sign compliant signature is binding and is going to insist you take a time machine back to the mid-80s to find a working fax machine unless you use Acrobat, which means Windows or Mac at least in a VM. You might be able to get some version of Acrobat to work in Wine, but you’ll need an old one that uses an internal certificate store rather than relying on the windows version (pre Acrobat X, I’m pretty sure).

Fun, huh? Basic digital functions are still broken decades after introduction but we have AI generated Teledep influencers telling us doubleplus buy useless beauty products and trust their health and exercise advice.

 

Posted at 08:51:06 GMT-0700

Category: HowToLinuxPoliticsPrivacyTechnology

Goodbye, Tortuga.

Thursday, April 25, 2024 

On April 21st, 2024, at 20:39, Tortuga was gently put to rest after a three year-long struggle with what was probably cancer and a short-lived victory over a mycoplasma bacterial blood infection.

She first took advantage of our yard-cat support program in 2009 as a juvenile cat and passed at about 15 or 16 years of age. She lived a good life, had 5 kittens on March 27th, 2010 that were all weaned and adopted out successfully, and grew old never wanting for food, shelter, or comfort, and never suffering any meaningful illness or injury until her last year.

Over the years, she was the beneficiary of a very strong community support network that took her in whenever she needed it and gave her loving care. She had housemates, human and feline, and a few canine over the years and was always gracious and pleasant, if not always enthusiastic about the four-legged companions.

I am eternally, deeply grateful to everyone who helped her over the years and who made my work and travel possible and Tortuga’s life pleasant and comfortable in my absence, especially in her later years as she needed more care.

She was the best, sweetest cat I’ve ever known. She was always polite, always pleasant, and never scratched or bit, not even when startled or annoyed by dogs. She never broke things or pushed things over or made a mess.

She wasn’t a big fan of other cats, and only a select few were tolerated as guests in her garden. She wanted to start every morning by marking her territory and she ruled her garden with a fierceness that vastly exceeded her tiny size. She started there, spent her last day in the sun there, and will spend eternity there.

Almost every night I was home, she slept in my bed with me. Almost every day I was working at home, she would hop up and sleep quietly between my keyboard and monitor on her little bed there. She didn’t meow much or fuss but purred easily and happily.

In later years, she’d sometimes wake me just before light by prodding my back or nipping to ask for pets; after 5 or 10 minutes of purring and being petted, she would settle back to sleep. It was a ritual that I came to very much enjoy.

Whenever I came home from my travels, no matter how late, as I opened the door into the living space, I’d hear her stir, jumping down from the attic maybe or from my bed or the window perch upstairs and tap-tap-tap down the stairs and trot up to greet me, rubbing my leg and purring. She’d let me scoop her up and snuggle her, though she wasn’t normally a carry cat, and then walk circles around me for 10 or 15 minutes, welcoming me home in the sweetest way possible. She came to know my departures too and always gave me a look of disappointment, sometimes refusing to come to the door to see me off, but usually relenting for one last scritch on the head.

When Corona hit in 2020, I was in Iraq after leaving her in January of 2020 thinking I’d be back in the spring. I couldn’t make it home for almost two years. The longest I’d been away before then was less than 6 months and even that only once or twice. She’s a cat, and by then an old cat, so I didn’t expect much, but in January of 2022, I opened the door late at night to the sound of her tap-tap-tapping down the stairs to greet me.

She was laid to rest in the garden she ruled for 15 years.

I went through the thousands of pictures I’ve taken of her and others have shared with me and tried to find a few from every year from her first foray in 2009 until her last day. If you knew her at some point during this time, I hope this brings back fond memories of a very special kitty.

2009: Tortuga finds food, takes over a house, and becomes part of the family.

2010 Tortuga has kittens and settles into her role as queen of the garden.

2011 Tortuga takes ownership of my desk.

2012

2013

2014

2015

2016

2017

2018

2019

2020 Corona time.

2021 Corona time.

I didn’t get to see Tortuga at all from January of 2020 until January of 2022.

2022 Reunited.

2023

A typical welcome home when I’d been away too long.

2024 The queen of the garden forever.

Posted at 08:35:14 GMT-0700

Category: Cats

A one page home/new tab page with random pictures, time, and weather

Thursday, April 11, 2024 

Are you annoyed by a trend in browsers to default to an annoying advertising page with new tabs? I sure am. And they don’t make it easy to change that. I thought, rather than a blank new tab page, why not load something cute and local. I enlisted claude.ai to help expedite the code and got something I like.

myHomePage screenshot

myHomePage.html is a very simple default page that loads a random image from a folder as a background, overlays the current local time in the one correct time format with seconds, live update, and throws up the local weather from wttr.in after a delay (to avoid hitting the server unnecessarily if you’re not going to keep the tab blank long enough to see the weather).

Images have to be in a local folder and in a predictable naming structure, as written “image_001.webp” to “image_999.webp.” If the random enumerator chooses an image name that doesn’t exist, you get a blank page.

Browsers don’t auto-rotate by exif (or webp) metadata, so orient all images in the folder as you’d like them to appear.

The weather information is only “current” which isn’t all that useful to me, I’d like tomorrows weather, but that’s not quite possible with the one-liner format yet.

Update, I added some code to display today and tomorrow’s events and current todos meeting specific filter tests from your Thunderbird calendar, if you have it. If not, just don’t cron the bash script and they won’t show. I also changed the mechanism of updating the weather to a 30 minute refresh of the page itself, this way you get more pix AND the calendar data updates every 30 minutes.  Web browsers and javascript are pretty isolated from the host device, you can’t even read a local file in most (let alone write one).  All good security, but a problem if you want data from your host computer in a web page without running a local server to deliver it.

My work around was to write the data into the file itself with a script.  Since the data being written is multi-line, I opted to tag the span for insert with non-breaking spaces, a weird character and the script sanatizes the input from calendar events extracted from the sqlite database in case some event title includes them.   The current config is by default:

~/.myHomePage/myHomePage.html
~/.myHomePage/getEvents.pl
~/.myHomePage/getToDos.pl
~/.myHomePage/putEvents.py
~/.myHomePage/putToDos.py
~/.myHomePage/myHomeImages/image_001.webp
~/.myHomePage/myHomeImages/image_002.webp
etc.

How to set the homepage and new tab default page varies by browser.  In Brave try hamburger→settings→appearance→show home button→select option→paste the location of the homepage.html file, e.g. file:///home/(username)/.myHomePage/myHomePage.html

Then just set a cron script like <code>*/30 * * * * /home/<username>/.myHomePage/getEvents.pl</code> for regular updates: script all the subroutines that are useful or write a little bash script to do them in sequence and call that with your favorite periodic method.

Parsing recurring events in perl is a challenge and I managed to get claude to ragequit, that’s got to be a some sort of a record:

claude rage quits

So the parsing scripts are in python using icalendar, I put them on gitlab at https://gitlab.com/gessel/myhomepage to make it a little easier to mess with, if anyone wants to.

Posted at 05:48:19 GMT-0700

Category: CodeHowToLinuxTechnologyWeather