📌 nixCraft beschreibt in einem Blog Beitrag sehr detailiert, was man mit Bash Parameter Substituion so alles anstellen kann. (Quelle)
📌 Kenneth Finnegan hatte die chance, eine 100 Gigabit Fiber Optic zu zerlegen und zeigt mit vielen Bilder, wie so ein Modul von innen aussieht. (Quelle)
📌 nixCraft beschreibt in einem Blog Beitrag, wie man Windows 10/11 OEM Product Keys von Linux aus im EFI BIOS findet und extrahiert. (Quelle)
📌 staticrypt erlaubt es, simple HTML Seiten zu verschlüsseln. Das Tool baut dann in die Seite eine Passwort Abfrage ein, um die Seite im Browser wieder zu entschlüsseln.
📌 Magic Wormhole erlaubt es Ende-zu-Ende Verschlüsselt Dateien zwischen zwei Computern zu übertragen. Es gibt wohl auch eine Go Version, welche ohne Python funktioniert. (Quelle)
]]>Group Policies are normally managed by Active Directoy, which requires one or more Windows Servers. But Policies can also managed with PowerShell and the PolicyFileEditor Module.
My script set-policies.ps1 will apply the following changes to Windows:
If the magic file C:\configs\enable-uninstallpackages.txt
exists, the script will try to
uninstall the following AppX packages:
The strings are used for searching for the full package names and can match multiple packages. Please check the list with the following commands on your system to make sure, that no package you need is uninstalled by mistake.
Get-AppxPackage -AllUsers -Name "*xbox*"
Get-AppXProvisionedPackage -Online | Where-Object { $_.DisplayName -like "*xbox*" }
C:\configs\enable-logoninfo.txt
exists)shutdown /r
and shutdown /s
on the Desktop (If magic file C:\configs\enable-trueshutdown-icon.txt
exists)C:\Temp\GPReport.html
But Kubernetes brings also huge complexity which is not always necessary. Not every project scales to the size of Netflix or Google, so an good old Docker CE can also be an option.
This guide is a collection of custom configurations to improve the security, stability and isolation of Docker CE servers and it’s containers.
Configuration for the Docker CE Daemon and the underlying operating system.
Debian brings it’s own packages for Docker CE, but it is recommended to use the official package sources from Docker.
When Apt is done, docker ps
should output an empty container list.
The Docker CE Engine is controlled with an HTTP RESTful API accessible using the Unix Socket
/var/run/docker.sock
. Whoever has access to that socket can run any action/query on the Docker CE Engine.
Example:
curl --unix-socket /var/run/docker.sock http://localhost/images/json | jq
(Shorted) Output:
[{
"Created": 1699617762,
"Id": "sha256:3f32ac3a3d6f2c6778eb8ddb5924264e89c3673175e057caab233961b6eb3140",
"RepoDigests": [ "homeassistant/home-assistant@sha256:400f20c77f52ac31334c1e73a2f19b2d6e5820757d1d476f01960b1efed31949" ],
"RepoTags": [ "homeassistant/home-assistant:latest" ],
"Size": 1903993749,
}]
Per default root
and all members of the Unix group docker
can read/write to the socket.
As mentioned in the last chapter, any Unix account can get access to Docker CE by being member
of the group docker
. But be aware, that all users which can create containers will have implicit
root access to the whole server system.
There are alot examples how host access can be done via docker. As one example, a combination of a privileged container and chroot:
# mount the hosts root partition into the container
# use the hosts network scope
# --privileged allows kernel access
# chroot into the hosts root partition
# launch a interactive bash
docker run -it --rm -v /:/mnt/host --net=host --privileged debian:latest chroot /mnt/host /bin/bash -i
If this is no/low risk for you, access to Docker CE for a user can be granted like so:
gpasswd -a christian docker
The docker
CLI command supports plugins, the most used one should be docker compose
, which is also available
as dedicated CLI command.
On debian it can be installed like so:
apt install docker-compose-plugin docker-buildx-plugin
In a Dockerfile it can be installed like so:
FROM docker:latest
COPY --from=docker/buildx-bin:latest /buildx /usr/libexec/docker/cli-plugins/docker-buildx
COPY --from=docker/compose-bin:latest /docker-compose /usr/libexec/docker/cli-plugins/docker-compose
The plugins can be used like so:
docker compose --help
docker buildx --help
/dev/sda2
or /dev/disk/by-id/scsi-0QEMU_QEMU_HARDDISK_drive-scsi0-part2
.
To have better control of the disk space consumtion, it makes sense to move the Docker CE data directory to a dedicated partition. If the partition has a XFS filesystem, it’s even possible to use disk quotas in docker volumes and containers.
Install dependencies, format partition, move data:
# preparations
apt install xfsprogs
systemctl stop docker
# format and mount partition
mkfs.xfs /dev/sda2
mkdir /mnt/dockerdata
echo "/dev/disk/by-id/scsi-0QEMU_QEMU_HARDDISK_drive-scsi0-part2 /mnt/dockerdata xfs discard,nofail,pquota,defaults 0 0" >> /etc/fstab
mount /mnt/dockerdata
# check if partition is mounted
mount | grep dockerdata
# move data
mv /var/lib/docker /mnt/dockerdata/docker
Add data-root
setting in /etc/docker/daemon.json
:
{
"data-root": "/mnt/dockerdata/docker"
}
Start docker again and verify if it has started correctly:
systemctl start docker
systemctl status docker
docker info | grep "Docker Root Dir:"
docker ps -a
Depending of the container, a log file can grow very fast. To prevent running out of disk space, the size and number of log files per container should be limited.
Add log driver options to /etc/docker/daemon.json
:
{
"log-driver": "json-file",
"log-opts": {
"max-size": "32m",
"max-file": "5"
}
}
Restart Docker CE:
systemctl restart docker
daemon.json
is just used as a default for each container deployment.
There are also alternative log drivers available in Docker CE. A good option is for example, to use the
journald
driver to log into systemd journald.
Logs can be accessed then like so:
journalctl CONTAINER_NAME=webserver
This also allows it to forward docker logs including all system log messages to applications like Graylog via filebeat.
Details:
All Log Drivers
Journald log driver documentation
Filebeat Documentation
All containers should have quotas set to prevent, that a faulty process is affecting other containers or even let the whole server system crash.
CPU Quotas are supported out of the box.
See Run containers for examples.
Memory quotas are supported per default, for swap quotas a kernel parameter needs to be set:
In /etc/default/grub
:
GRUB_CMDLINE_LINUX_DEFAULT="quiet cgroup_enable=memory swapaccount=1"
Update bootloader and reboot:
update-grub
reboot
See Run containers for examples.
Disk quotas are supported, if the Dedicated Disk for Dockers Data Directory chapter is applied correctly.
See Run containers for examples.
Connecting Docker Containers to each other and to the outside world.
Per default Docker CE is using the subnet 172.17.0.0/16
to assign IP addresses to containers and Docker
networks. Sometimes, this network is already in use by other services. This can result in the unavailability
of these services from the Docker server system and it’s containers.
Add the default-address-pools
option to /etc/docker/daemon.json
:
{
"default-address-pools": [
{ "base": "172.19.0.0/16", "size": 24 }
],
}
Now Docker CE will create subnets with size size /24
from 172.19.0.0/16
for new Docker networks.
The default way to connect docker containers with each other are bridge networks. When connected to a network, the container can communicate with each other with it’s container names.
docker network create -o com.docker.network.bridge.name=somenetwork somenetwork
docker run -d --name backend --network somenetwork debian:latest sleep infinity
docker run -d --rm --name frontend --network somenetwork debian:latest \
/bin/bash -c "apt update && apt install --yes iputils-ping && ping backend"
The option -o com.docker.network.bridge.name
allows it to set the name of the Linux network
interface. Which can be helpful to keep the overview. The name has a maximum length of 15 characters.
Docker CE is able to use IPv6 ULA (Unique Local Address) ranges and will create SNAT rules for the outgoing communication. This is really helpful, if the server only has a single IPv4 Address. IPv4 and IPv6 can be used and configured exactly identical.
{
"ipv6": true,
"experimental": true,
"ip6tables": true,
"fixed-cidr-v6": "fd00::/49",
"default-address-pools": [
{ "base": "fd00:0:0:8000::/49", "size": 64 },
{ "base": "172.19.0.0/16", "size": 24 }
]
}
The IPv6 ranges are not routed and can be used as-is on multiple servers.
docker network create --ipv6 -o com.docker.network.bridge.name=somenetwork somenetwork
docker run -d --name backend --network somenetwork debian:latest \
/bin/bash -c "apt update && apt install --yes iputils-ping && ping6 google.com"
Incoming traffic should be handled with normal port publishings through a reverse proxy like NGINX or Traefik.
It is also possible to assign real LAN IP addresses directly to containers with the macvlan
network driver.
The following network is attached to the server network interface eth0
,
which provides the LAN subnet 192.168.32.0/24
.
Containers connected to the network will have IP addresses from the range 192.168.32.128/25
assigned.
Services inside of this containers, are directly accessible from the server network without any port
publishings (-p
).
docker network create -d macvlan \
--subnet=192.168.32.0/24 \
--ip-range=192.168.32.128/25 \
--gateway=192.168.32.254 \
-o parent=ens18 publicservices
For outgoing communication, all containers in a bridge
driver network using the primary IP address of
the server system. This can be a problem, if every application on the docker host should have invidual
firewall rules to allow communication with other servers and services.
An individual outgoing IP address can be defined, by managing the NAT rules ourselves.
Create a Docker network with disabled masquerading:
docker network create -d bridge \
-o com.docker.network.bridge.name=appnetwork \
-o com.docker.network.bridge.enable_ip_masquerade=false \
appnetwork
Then the following nftables rule needs to be applied:
table ip docker_network_appnetwork
delete table ip docker_network_appnetwork
table ip docker_network_appnetwork {
chain postrouting {
type nat hook postrouting priority srcnat; policy accept;
ip saddr 172.19.4.0/24 oifname != "appnetwork" snat to 192.168.99.226
}
}
172.19.4.0/24
is the subnet which was assigned to the Docker network, appnetwork
the interface name and
192.168.99.226
the IP address used for the outgoing connection.
On macvlan
networks, it is alot easier, as a NAT rule is not necessary:
docker network create -d macvlan \
--subnet=192.168.32.0/24 \
--ip-range=192.168.32.128/25 \
--gateway=192.168.32.254 \
-o parent=ens18 \
-o com.docker.network.bridge.enable_ip_masquerade=false \
-o com.docker.network.bridge.name=appnetwork \
appnetwork
When two applications which both using custom outgoint IP addresses are required to be connected to each other, a transit network must be used. Otherwise one of the applications will use the outgoing IP address of the other application, as it is not possible to control the routes inside of the containers.
docker network create -d bridge --internal transitnetwork
The --internal
option denies outgoing communication with that network. Only containers can communicate with
each other, but there is no LAN or internet access possible.
When an application is processing thousands of connections per second, the default NATing of Docker can become the bottleneck. Multiple connection trying to use the same port number for the NATing, which causes retries. Because of that the initial connect can take multiple seconds.
The problem is described here and here more detailed.
Besides using macvlan
networks, another option is to use the server host network stack:
# create a network with a known IP Subnet
docker network create --driver=bridge --subnet=10.0.0.0/24 containers0
# connect the existing application container with the network
# and assign a static IP address
docker network connect --ip 10.0.0.101 containers0 dingetun
docker network connect --ip 10.0.0.102 containers0 myip
# create the new webserver container
docker run --name webserver -d \
--add-host dingetun:10.0.0.101 \
--add-host myip:10.0.0.102 \
--network host \
nginx:latest
In this example the webserver container is directly attached to the network stack of the server host. Port
80/tcp
and 443/tcp
are directly bound to the public IP of the server system.
Because of that, the container is unable to use Docker networks and the Docker DNS. This needs to workarounded
with assigning static IP addresses to all other containers and also add static /etc/hosts
entries to
the webserver container.
With this, NAT is completly avoided, but other containers can still accessed by it’s name.
Using the caching mechanics of Docker builds more effective.
Docker checks for changes on all used files before (re)building an image layer. If there are no changes, the layer from the previous build is used instead of building a new one.
This can be used to cache a npm install
or a nuget restore
run, even if the code of the app
itself was changed. Just copy the package.json
and package-lock.json
first into the image,
run the npm install
and copy the rest of the application afterwards.
As long as this two files have no changes, this build step is cached and will safe multiple minutes in future CI Pipeline runs.
FROM node:latest as packages
WORKDIR /src
COPY package.json package.json
COPY package-lock.json package-lock.json
RUN npm install
FROM packages as app
WORKDIR /src
COPY . .
RUN npm run-script build
The “new” docker buildx
includes cache metadata into the container image, which allows it to use an previous
image as a cache source. This makes the cache available even when two builds are done on different servers.
docker buildx build \
--cache-from "example.com/myapp/myapp:latest" \
--cache-to type=inline \
-t "example.com/myapp/myapp:latest" .
Also this enables the use of caching in disposable build environments like GitLab runners with Docker-in-Docker.
Labels can be defined at build time and at runtime. Adding informations like build timestamp, version, maintainer or application name helps to keep an overview about the applications which are running on the Docker system.
The Open Containers Initiative has defined a standard to add metadata to a container image at build time. The format description can be found here.
Dockerfile labels:
LABEL org.opencontainers.image.created=$DATE
LABEL org.opencontainers.image.version=1.0.0
LABEL org.opencontainers.image.authors=Christian
LABEL org.opencontainers.image.url=https://serverless.industries
Build command:
docker buildx build \
--label "org.opencontainers.image.created=$(date --iso-8601=seconds)" \
--label "org.opencontainers.image.version=1.0.0" \
--label "org.opencontainers.image.authors=Christian" \
--label "org.opencontainers.image.url=https://serverless.industries" \
-t "example.com/myapp/myapp:latest" .
The following example demonstrates all quota options:
docker volume create -o size=1G persistent-volume
docker run -d --name cpu-demo \
--cpus 1.5 \
--memory 2GB \
--memory-swap 3GB \
--storage-opt size=1G \
-v persistent-volume:/mnt/data \
debian:latest
/mnt/data
In addition to the container name, the container hostname should also be set as the hostname will appear in log files.
docker run -d \
--name webserver \
--hostname webserver \
-p 80:80 \
nginx:latest
Labels can also be set when creating containers. For example to group containers by project or adding a responsible person to the container. Also projects like Traefik, Watchtower or Ofelia using labels for configuration and container discovery.
docker run -d \
--name webserver \
--label "applicationowner=John Doe" \
--label "project=some-multi-container-project" \
-p 80:80 \
nginx:latest
Frontmatter of the German post:
title: Static Binaries
locale: de
ref: static-binaries
Frontmatter of the English post:
title: Static Binaries
locale: en
ref: static-binaries
The ref
Frontmatter field connects identical posts in different languages with each other.
The plugin is placed into the _plugins/
directory. The generate()
function
iterates though all existing pages and posts and modifies the Frontmatter data.
# _plugins/fontpage.rb
module FrontPagePlugin
class FrontPageGenerator < Jekyll::Generator
safe true
def generate(site)
# group by ref (or id if no ref defined)
byref = site.posts.docs.group_by { |p| p.data['ref'] || p.id }
byref.each do |key, posts|
# ensure frontmatter fields
posts.each do |post|
unless post.data['locale']
post.data['locale'] = 'de'
end
end
# get list of locales
locales = posts.map { |post| post.data['locale'] }
# sort posts by locale in reverse order, so that 'en' wins against 'de'
posts.sort_by { |post| post.data['locale'] }.reverse.each_with_index do |post, postindex|
if postindex <= 0
# add frontpage category to first post in every ref group
post.data['categories'].push('frontpage')
end
# add locales list and language category to all posts
post.data['available_locales'] = locales
post.data['categories'].push("language-#{post.data['locale']}")
end
end
end
end
end
In Jekyll Paginate v2 we can now filter the index page by the auto generated categories. Also creating a main index with all postings and an index for each language.
layout: localizedindex
title: Latest Posts
permalink: /index
pagination:
enabled: true
category: frontpage
Just put the following into the boot.py
:
import storage
import usb_cdc
import usb_midi
storage.disable_usb_drive()
usb_cdc.disable()
usb_midi.disable()
The actual logic of your program should be put into the code.py
and will be launched after
boot.py
has completed.
It is also possible to add a physical button to skip disabling the Flash Drive. The following example will not disable it, when the button is hold while resetting the Pico:
import board
import storage
import usb_cdc
import usb_midi
from digitalio import DigitalInOut, Direction, Pull
btn = DigitalInOut(board.GP13)
btn.direction = Direction.INPUT
btn.pull = Pull.DOWN
if not btn.value:
storage.disable_usb_drive()
usb_cdc.disable()
usb_midi.disable()
tl;dr: Die funktionierende Konfiguration mit Kommentaren:
# This NGINX runs behind a reverse proxy, so parse X-Forwarded-For headers
# Directives for setting real_ip/XFF IP address in log files
set_real_ip_from 172.16.0.0/12;
real_ip_header X-Forwarded-For;
server {
listen 80;
server_name _;
root /var/www/public/;
location / {
# https://serverfault.com/a/1092384
# https://www.dimoulis.net/posts/webdav-behind-reverse-proxy/
# capturing group without name is urlencoded, named capturing group is not.
# see https://trac.nginx.org/nginx/ticket/348
set $destination $http_destination;
if ($destination ~ ^https?://(?<myvar>(.+))$) {
set $destination "http://$myvar";
more_set_input_headers "Destination: $destination";
}
dav_methods PUT DELETE MKCOL COPY MOVE;
dav_ext_methods PROPFIND OPTIONS;
dav_access user:rw;
client_max_body_size 0;
create_full_put_path on;
client_body_temp_path /tmp/;
autoindex on;
}
}
Beim Verschieben und Kopieren von Dateien sendet WebDAV einen Destination: https://...
Header mit dem Zielpfad. Der Header enthält eine https://
URL, dadurch das der WebDAV Server
aber hinter einem Reverse Proxy steht, spricht dieser aber kein TLS.
NGINX weist daher das Request ab:
[error] 6#6: *2 client sent invalid "Destination" header: "https://
Mit more_set_input_headers
aus dem Debian Paket libnginx-mod-http-headers-more-filter
und ein wenig Regex Magie lässt sich dies aber umgehen.
Im eingehenden Header wird https://
durch http://
ersetzt, bevor das WebDAV Modul
mit der Verarbeitung beginnt.
Ein regulärer Ausdruck wie $destination ~ ^https?://(.+)$
erzeugt die Match Group
Variable $1
, welche die URL aus $destination
ohne http://
oder https://
enthält.
Leider aber URL encoded. Sprich aus /
wird %2F
.
Dadurch werden dann auch die Dateien im WebDAV Ordner mit URL kodierten Dateinamen erstellt.
Das ist ein 10 Jahre alter, ungelöster Bug in NGINX.
Der Workaround ist die Nutzung von Named Match Groups.
In dem Code Block weiter oben wird daher die Match Group als myvar
benannt.
Ergebnis ist eine $myvar
Variable, welche nicht URL encoded ist.
Mit dem Parameter --parallel 10
kann angegeben werden, wie viele Checks gleichzeitig
ausgeführt werden. Standardmäßig ist dieser Wert auf 10 eingestellt.
Realisiert wird dies über Bash Background Jobs:
curl https://example.com &
curl https://serverless.industries &
wait
echo "Both jobs are done"
Das &
am Ende der Zeilen verschiebt den entsprechenden Befehl in den Hintergrund,
in einen neuen Prozess. Mit wait
kann dann auf das Fertigstellen aller aktiven Jobs
gewartet werden.
Mit dem Parameter --config
können fast alle Einstellungen aus einer JSON Datei
geladen werden.
{
"$schema": "https://files.serverless.industries/schemas/minimon.json",
"checks": [
{ "type": "icmp", "url": "127.0.0.1", "alias": "localhost" },
{ "type": "http", "url": "https://serverless.industries", "alias": "blog" },
{ "type": "script", "url": "./plugins/certexpire serverless.industries:443", "alias": "blog_cert" }
],
"interval": 10,
"parallel": 2,
"timeout": 5,
"connect-timeout": 2,
"short-timestamps": true
}
Geöffnet in einem Editor wie VSCode, ermöglicht der $schema
Parameter Autovervollständigung
und Syntaxprüfung der JSON Datei.
Auch war Debian Stretch der letzte Release, wo das benötigte MongoDB 3.7 noch ohne Frickelleien verfügbar war. Neuere Debian Versionen werden ohne manuelle Arbeit schlicht nicht unterstützt. Bis heute.
Das Hauptproblem beim Unifi Controller ist, dass die Paket Abhängigkeiten
zu restriktiv sind. mongodb-server (<< 1:4.0.0)
sorgt dafür, dass nur MongoDB Versionen
kleiner als 4.0 akzeptiert werden.
Das Projekt unifi-repack von Julien Lecomte ändert dies. Ein Script entpackt das Debian Paket, entfernt diese Einschränkung und packt es erneut.
Von dem Projekt habe ich mir einen Fork erstellt und kurzerhand ein eigenes APT Repo ausgerollt. Das Repo enthält diverse Versionen des Unifi Controllers wo die MongoDB Version “entsperrt” ist und auch passende MongoDB 4.4 Pakete, für den Fall das diese beim EOL von MongoDB 4.4 im Februar nächsten Jahres aus den ofiziellen Repos verschwinden.
root@ubnt:~# apt-cache madison unifi
unifi | 7.4.162-21057-1+unlocked | https://files.serverless.industries/apt/repo hopefullystable/unifi amd64 Packages
unifi | 7.3.83-19645-1+unlocked | https://files.serverless.industries/apt/repo hopefullystable/unifi amd64 Packages
unifi | 7.2.97-18705-1+unlocked | https://files.serverless.industries/apt/repo hopefullystable/unifi amd64 Packages
unifi | 7.1.68-17885-1+unlocked | https://files.serverless.industries/apt/repo hopefullystable/unifi amd64 Packages
unifi | 7.0.25-17292-1+unlocked | https://files.serverless.industries/apt/repo hopefullystable/unifi amd64 Packages
unifi | 6.5.55-16678-1+unlocked | https://files.serverless.industries/apt/repo hopefullystable/unifi amd64 Packages
unifi | 6.5.54-16676-1+unlocked | https://files.serverless.industries/apt/repo hopefullystable/unifi amd64 Packages
unifi | 6.4.54-16067-1+unlocked | https://files.serverless.industries/apt/repo hopefullystable/unifi amd64 Packages
unifi | 5.14.23-13880-1+unlocked | https://files.serverless.industries/apt/repo hopefullystable/unifi amd64 Packages
unifi | 5.6.42-10376-1+unlocked | https://files.serverless.industries/apt/repo hopefullystable/unifi amd64 Packages
unifi | 5.6.40-10370-1+unlocked | https://files.serverless.industries/apt/repo hopefullystable/unifi amd64 Packages
Neuere Versionen wie MongoD 5 oder 6 werden nicht unterstützt und lassen den Unifi Controller abstürzen.
GnuPG key importieren:
curl -fsSL https://files.serverless.industries/apt/serverless-packages.asc | \
sudo gpg -o /usr/share/keyrings/serverless-packages.gpg --dearmor
Paketquelle hinzufügen:
echo "deb [signed-by=/usr/share/keyrings/serverless-packages.gpg] https://files.serverless.industries/apt/repo hopefullystable mongodb44 unifi" | \
sudo tee /etc/apt/sources.list.d/serverless-packages.list
Der Distributionsname hopefullystable
ist hier auch ernst gemeint. Ich nutze die Pakete
selbst, und es funktioniert soweit alles. Nutzung dennoch auf eigene Gefahr, da MongoDB 4.4 nicht
offiziell von unifi unterstützt wird.
Bis jetzt hatte ich die ganze Situation ausgesessen und noch ein Debian Stretch im Einsatz. Jetzt war das Upgrade auf Debian Bookworm dran. Einige Dinge, die dabei passiert sind.
apt-cache madison unifi
prüfen, dass die aktuell eingesetzte Version auch im Repo drin istEs gibt kein mongodb-server
mehr in den Repos von Buster. Daher macht es Sinn, vor dem OS Update
alles was mit unifi und MongoDB zu tun hat mit apt remove --purge mongodb* unifi
zu deinstallieren.
Auch Pfade die von apt nicht gelöscht wurden manuell löschen!
Anschließend das Upgrade von Stretch auf Buster durchführen.
Hier gibt es keine besonderen Vorkommnisse. Ganz normal das Update durchführen.
Entscheidet man sich hier unifi=versionskennung
und mongodb-org-server
wieder zu installieren,
sollte dies ohne Probleme funktionieren. Der Controller begrüßt einen mit der Ersteinrichtung,
wo das Konfigurationsbackup eingespielt werden kann.
Man muss die zum Backup passende Version installieren!
Nach dem Einspielen des Backups bietet es sich an, unifi
auf die aktuellste Version
hochzuziehen. Möchte man das nicht, sollte man mit apt-mark hold unifi
das Upgrade
verhindern, da apt dies sonst bei jedem System Update versuchen wird.
Bullseye wird noch bis Juni 2026 mit Updates versorgt. Man könnte also hier einfach stoppen und Unifi so weiter laufen lassen.
Das Update selbst lief wie bei Debian üblich ohne Probleme, der unifi Controller crashte danach aber an einer neuen OpenJDK Version.
Unifi benötigt OpenJDK 11 aber nutzt trotzdem immer den Debian Default (OpenJDK 17), obwohl auch OpenJDK 11 installiert ist. Daher kurzerhand die unbenutzten OpenJDK Versionen deinstallieren:
apt remove --purge openjdk-8* openjdk-17* openjdk-7*
Nun sollte unifi wieder starten.
<launcher> ERROR ContextLoader - Context initialization failed org.springframework.beans.factory.UnsatisfiedDependencyException:
Error creating bean with name 'alertPushNotificationSender' defined in com.ubnt.service.alert.notification.AlertNotificationSpringContext:
Unsatisfied dependency expressed through method 'alertPushNotificationSender' parameter 0
java.lang.reflect.InaccessibleObjectException: Unable to make private java.time.Instant(long,int)
accessible: module java.base does not "opens java.time" to unnamed module @73a8da0f
Unifi ist kaputt und es nervt einfach nur noch. Es sieht auch nicht so aus, als ob sich daran kurzfristig was ändern wird. Die Kommunikation des Unternehmens ist dabei auch einfach lächerlich.
Sie verlinken ernsthaft zu Scripts und Hilfetexten der Community, statt das Problem selbst zu beheben.
Ich für meinen Teil werde mir einen anderen Hersteller suchen, sobald die Hardware EOL ist und sich ein Wechsel auf Wifi 6/7 lohnt.
]]>Installing tools is often not an option, especially in environments where there is no internet connection for security reasons.
An alternative can be static builds of these software tools. When a program is compiled static, it contains all depencencies in one single program file and just works after copying the program file.
A disadvantage is the file size. A static compiled dig
is around 6 megabytes large, the
version from the package managers just 200 Kilobytes.
The static binaries can be downloaded here: https://files.serverless.industries/bin/
Supported CPU architectures: x86
(i386
), x86_64
(amd64
), ARM32v7
(armv7
), ARM64v8
(aarch64
)
Supported Tools: busybox
, curl
, dig
, htop
, iperf2
, iperf3
, jq
, rsync
, scp
, sftp
,
ssh-keygen
, ssh-keyscan
, ssh
, tcpdump
, vim
More details can be found in the info.txt file.
The builds are done in my personal Gitlab instance with CI pipelines and Docker containers. Other CPU architectures are build with the help of multiarch/qemu-user-static.
The build process creates a base image for each CPU architecture which includes all necessary tools to build the program, builds the programs for each CPU architecture and uploads them.
A mirror of the build code can be found on my GitHub Account.
]]>Dabei habe ich mich für einen Neustart entschieden. Also den Spielstand gelöscht aber Sternis, Meilentickets und ein paar wichtige Ressourcen bei einer Freundin zwischen geparkt und später auf die neue Insel geholt.
Nach dem Neustart bin ich beim Wühlen in den großen Wikis auf ein paar super interessante Fan Projekte gestoßen.
Den Start macht die Meta Platform acnh.directory, welche eine filterbare Liste mit vielen ACNH Fan Projekten liefert.
Wikis gibt es ja mittlerweile für fast alle Spiele, der Detailgrad ist bei Animal Crossing aber wirklich wahnsinn. Man findet Informationen über jeden Aspekt des Spiels.
Das GitHub Projekt Gioman101/FlipperAmiibo bietet eine Sammlung der NFC Tags fast aller Amiibos. Amiibos sind Sammelfiguren bzw Sammelkarten, welche in verschiedenen Nintendo Spielen für Zusatzinhalte verwendet werden können.
In ACNH kann man mit den Amiibos die Lieblingsbewohner einladen und auf die eigene Insel ziehen lassen. Insgesamt gibt es in ACNH 413 unterschiedliche Bewohner, alle mit unterschiedlichen Eigenschaften und Wahrscheinlichkeiten ob diese die Insel besuchen.
Seinen Wunsch-Bewohner ohne Amiibos auf die Insel zu bekommen, ist also extrem unwahrscheinlich.
Nookipedia nutzt im Wiki die Mediawiki Extension Cargo, um die Daten über Bewohner, Häuser, Möbel, uvm in einem strukturierten Format bereit zu stellen. Auf vielen der Wiki Seiten werden diese Daten dann benutzt, um standardisierte Info Karten anzuzeigen.
Unter api.nookipedia.com gibt es außerdem eine REST API, wo man auf eben diese Daten mit eigener Software zugreifen kann.
Den Code der API gibt es auf GitHub.
Das GitHub Projekt alexislours/ACNHAPI stellt eine weitere REST API bereit, welche aber nicht mehr gepflegt wird. Zumindest für Bewohner kann man hier aber einfach an statische JSON Daten und Bilder kommen und damit Dinge tun.
Unter acnhapi.com kann man die API zwar weiterhin nutzen, aber IMHO ist es nur eine Frage der Zeit, bis dieses Projekt offline geht.
Eines der ältesten Projekte ist wohl die ACNH Spreadsheet, welches eine Google Spreadsheet mit ALLEN Ressourcen des Spiels bereit stellt. Die Daten wurden teilweise via Reverse Engineering erzeugt, aber auch mit sehr viel Handarbeit.
Das GitHub Projekt NooksBazaar/google-sheets-to-json erlaubt es die Daten aus dem Google Spreadsheet in JSON umzuwandeln.
Das acnhcdn.com bietet ein Content Delivery Network mit allen Grafiken aus dem Spiel, welches von vielen Fan Projekten benutzt wird. Details zur Funktionsweise gibt es leider nur auf Discord.
Im GitHub Projekt Treeki/CylindricalEarth gibt es haufenweise Informationen wie ACNH unter der Haube funktioniert. Dieser Reverse Engineering Arbeit verdanken wir, soweit ich das verstanden habe, auch einen Großteil der Daten im Spread Sheet und anderen Projekten.
Das GitHub Projekt ZekeSnider/NintendoSwitchRESTAPI schaut sich die APIs der Nintendo Switch App genauer an. Auch die Issues sind dabei interessant, welche den Großteil der Infos zu ACNH enthalten.
nookdb.io stellt eine schöne Website bereit um Bewohner, Insekten, Fische und vieles andere zu durchsuchen und zu filtern.
critterpedia-plus.mutoo.im liefert eine Übersicht, welche Lebewesen wann gefunden werden können.
]]>