Categories
Linux

SSH Key-Based Authentication on Linux Servers

I want to login from one Linux server to another Linux server without the need to enter a password.

Create SSH keys

# Login to server 1
ssh-keygen

Output:

Generating public/private rsa key pair.
Enter file in which to save the key (/home/'USERNAME'/.ssh/id_rsa):
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in /home/'USERNAME'/.ssh/id_rsa
Your public key has been saved in /home/'USERNAME'/.ssh/id_rsa.pub
The key fingerprint is:
SHA256:27U2nApZQLSwS1UVh2Lw4aDM/B9/gR0Uum1ppRrqjpg 'USERNAME'@server1
The key\'s randomart image is:
+---[RSA 3072]----+
|    +o+ .o       |
| + . Bo+o .      |
|  = o.*...       |
|   o + o .       |
|    + o S   .    |
|     . X * +     |
| .    * @ O      |
|E . .. B * .     |
| ..+..o o        |
+----[SHA256]-----+

The utility will prompt to select a location for the keys that will be generated. By default, the keys will be stored in the ~/.ssh directory within user’s home directory. The private key will be called id_rsa and the associated public key will be called id_rsa.pub.

Now we have a private and public key that we can use to authenticate.

Copy keys to server 2

We will use the same keys on server 2:

scp ~/.ssh/id_rsa     USERNAME@server2ip:~/.ssh
scp ~/.ssh/id_rsa.pub USERNAME@server2ip:~/.ssh

Create authorized_keys on both servers:

ssh-copy-id USERNAME@localhost
ssh-copy-id USERNAME@server2ip

Connect to server 2

ssh USERNAME@server2ip
# or just
ssh server2ip

Now we SHOULD connect to server2 without the need to enter a password.

Failing - But why?

Unluckily in my case I still have to enter a password. But why?

# Login to server 2
ssh server2ip
Password:

# check auth.log
less /var/log/auth.log
...
rexec line 15: Deprecated option RSAAuthentication
...

# check sshd_config
less /etc/ssh/sshd_config
...
RSAAuthentication no
...

So in my case the RSA-Authentication has been disabled.
As the default is enabled by purpose, this has been done by purpose.
Unfortunatly I am not the admin or manager of the server, so I can not change this settings.

Categories
Development Linux

Docker Logs lesen

Problem

Der Server wird gestartet mit:

sudo docker-compose up --detach

Dadurch werden die Logfiles mit dem User root geschrieben und ich kann sie mit meinem User ingo nicht lesen.

Auf der Console kann ich das leicht mit einem vorangestellten sudo lösen, aber um mal eben schnell in die Logfiles rein zu schauen würde ich gerne mein graphisches Tool WinSCP verwenden

Lösung

Man kann Docker / Docker Compose mit einem User starten und mit dem würden dann vermutlich auch die Logfiles geschrieben werden.
Als ich das mit einem Tomcat Image getestet hatte, ist es daran gescheitert, dass mit meinem User ingo auf bestimmte Verzeichnisse im Container nicht zugegriffen werden konnte.

Gelöst habe ich es dann so, dass ich nicht den User, oder User und Gruppe, gesetzt habe, sondern nur die Gruppe.
So wird mit dem root User gearbeitet, die Dateien gehören dem User root und für die gesetzte Gruppe sind sie lesbar.
Mein User muss natürlich ebenfalls in der Gruppe sein.

Gruppe anlegen:

sudo groupadd -g 1001 logfilegroup

Die Group ID ist relativ willkürlich gesetzt. Eigentlich hatte ich groupadd ohne das -g Flag aufgerufen und dann mit cat /etc/group die Group ID rausgesucht. Hier wollte ich das Statement mit explizitem setzen der Group ID hinschreiben, da ich es auch im Projekt verwendet hatte, um auf jedem Server die selbe Group ID zu haben.

User der Gruppe hinzufügen:

sudo usermod --append --groups logfilegroup ingo

Mit den Befehlen groups oder id kann man die Gruppen seines Users sehen, die neue logfilegroup wird aber erst in einer neuen Shell hinzugefügt. Also entweder die Shell schließen und neu öffnen, oder mit su ingo weiter arbeiten.

ingo$ sudo usermod --append --groups logfilegroup ingo
ingo$ groups
ingo adm
ingo$ su ingo
Password:
ingo$ groups
ingo adm logfilegroup

Docker Compose File:

Im Docker Compose File muss die Group ID gesetzt werden, mit dem Namen der Gruppe geht es nicht.

version: "3.2"
services:
  melba-web:
    image: tomcat:10.1.18-jre21 # https://hub.docker.com/_/tomcat
    restart: always
    container_name: myapp-tomcat
    user: :1001
    ports: 
      - "8080:8080"
    environment:
      - _JAVA_OPTIONS=-Duser.language=de -Duser.country=DE
    volumes:
      - ./log/tomcat:/usr/local/tomcat/logs
sudo docker-compose up --detach

ls -lisah log/tomcat/*
4211764 4.0K drwxr-xr-x 2 ingo ingo  4.0K Feb  8 16:52 .
4211762 4.0K drwxr-xr-x 4 ingo ingo  4.0K Feb  5 16:42 ..
4205212  24K -rw-r----- 1 root logfilegroup  24K Feb  8 16:21 catalina.2024-02-08.log
4211758  28K -rw-r----- 1 root logfilegroup  24K Feb  8 16:58 localhost_access_log.2024-02-08.txt
Categories
Development Java Linux

SAPJCO 2

Problem

We have a very, very old application that needs to be migrated into AWS. So we copied all files into AWS EC2 instance and tried to start the application. After fixing a lot of minor problems we faced a tough challenge with a SAPJCO RFC Call.

The Exception message was something like this:

Exception in thread "main" java.lang.ExceptionInInitializerError: JCO.classInitialize(): Could not load middleware layer 'com.sap.mw.jco.rfc.MiddlewareRFC'
JCO.nativeInit(): Could not initialize dynamic link library sapjcorfc [sapjcorfc (Not found in java.library.path)]. java.library.path [/usr/lib/jvm/java-1.6.0-ibm.x86_64/jre/lib/amd64/default:/usr/lib/jvm/java-1.6.0-ibm.x86_64/jre/lib/amd64:/usr/lib]
        at com.sap.mw.jco.JCO.<clinit>(JCO.java:871)
        at java.lang.J9VMInternals.initializeImpl(Native Method)
        at java.lang.J9VMInternals.initialize(J9VMInternals.java:199)

I guess, with a JCO Version 3 we would not have much trouble, but in this ancient application JCO Version 2 is used and we cannot update to Version 3 without a huge efford. In other projects I had the luck that I could migrate to Version.

The application is running on a Linux system. But belive me: it would have been much harder on a Windows machine.

Analysis

To find the cause of the problem I wrote the simpliest JCO Test Programm I can image:

import com.sap.mw.jco.JCO;

public class TestMain {

    public static void main(String[] args) {
        System.out.println(JCO.getVersion());
    }

}

My analysis programm structure:

app
├─ JCo/
│  ├─ librfccm.so
│  ├─ libsapjcorfc.so
│  ├─ sapjco.jar
├─ TestMain.java

Compile from command line:

javac -cp ".:/app/JCo/sapjco.jar" TestMain.java

Run from command line:

java -cp ".:/app/JCo/sapjco.jar" TestMain

That gave me another error:

Exception in thread "main" java.lang.ExceptionInInitializerError: JCO.classInitialize(): Could not load middleware layer 'com.sap.mw.jco.rfc.MiddlewareRFC'
JCO.nativeInit(): Could not initialize dynamic link library sapjcorfc [/app/JCo/libsapjcorfc.so: librfccm.so: cannot open shared object file: No such file or directory]. java.library.path [/app/JCo]
        at com.sap.mw.jco.JCO.<clinit>(JCO.java:871)
        at TestMain.main(TestMain.java:11)

Need to set an environment property first:

export LD_LIBRARY_PATH=/app/JCo

Run command line to start programm again and got another error:

Exception in thread "main" java.lang.ExceptionInInitializerError: JCO.classInitialize(): Could not load middleware layer 'com.sap.mw.jco.rfc.MiddlewareRFC'
JCO.nativeInit(): Could not initialize dynamic link library sapjcorfc [/app/JCo/libsapjcorfc.so: libstdc++.so.5: cannot open shared object file: No such file or directory]. java.library.path [/app/JCo]
        at com.sap.mw.jco.JCO.<clinit>(JCO.java:871)
        at TestMain.main(TestMain.java:11)

The interesting part of the error message:

Could not initialize dynamic link library sapjcorfc [/app/JCo/libsapjcorfc.so: libstdc++.so.5

Solution

We need the libstdc++.so.5 library, but installed is libstdc++.so.6

To get libstdc++.so.5 we installed package compat-libstdc++-33-3.2.3-66.x86_64:

yum install compat-libstdc++-33-3.2.3-66.x86_64

## to be honest, I am not exactly 100% sure, what I did in my investigations, so the command may be a little differend, ex:
# yum install compat-libstdc++-33-3
# yum install compat-libstdc++-33
# yum install compat-libstdc++-33 libstdc++.so.5

Test

Run from command line:

java -cp ".:/app/JCo/sapjco.jar" TestMain

That gave me no error, but SAPJCo Version number.:

2.1.10 (2011-05-10)

Finally it worked 😎

Anekdotum

Quote from an StackOverflow post from 2010:

libstdc++.so.5 is a very old version of the standard c++ library.

Some Analysis Details

Writing this article is giving me the feeling, that this was all super easy. But in reality it was a real pain in the allerwertesten.

To isolate the source of the problem, I did not only write the small Java (JCO.getVersion) application, I also set up a Docker environment.

One challenge was to find a useful Docker image to start from. I started with an OpenJDK Image that was already deprecated. Deprecated was not the problem, but I could not install libstdc++.so.5.

Next I tried to use the newer, undeprecated Eclipse-Temurin Image. But still could not install libstdc++.so.5

So I finally ended in a Debian Image and self installed Java where I was able to install libstdc++5.

FROM debian:bookworm-slim
 RUN apt-get update && apt-get install -y locales openjdk-11-jdk libstdc++5 \
     && rm -rf /var/lib/apt/lists/* \
     && localedef -i de_DE -c -f UTF-8 -A /usr/share/locale/locale.alias de_DE.UTF-8
 ENV LANG de_DE.utf8

COPY ./JCo/* /app/JCo/
COPY TestMain.java /app
WORKDIR /app
 ENV LD_LIBRARY_PATH=/app/JCo
 
 RUN javac -cp ".:/app/JCo/sapjco.jar" TestMain.java 

 CMD ["java", "-cp", ".:/app/JCo/sapjco.jar", "-Djava.library.path=/app/JCo", "TestMain"]

Note:

The locale might not be neccessary.
The "-Djava.library.path=/app/JCo" Parameter is not neccessary. But I leave it as an example, how one can use it.

Build and start the app:

docker build -t my-java-app .
docker run -it --rm --name my-running-app my-java-app
2.1.10 (2011-05-10)

Work in container

To work in the running container:

docker exec -it my-running-app /bin/sh

But there is one problem: You can only interact with a running container. But the TestMain-Programm is executed and immediately closed.

So I wrote another Test Programm, that keeps running, so I can enter the running container and test stuff (install packages, compile Java programm, etc.):

import java.io.BufferedReader;
import java.io.InputStreamReader;

import com.sap.mw.jco.JCO;

public class TestMain {

    public static void main(String[] args) {
        System.out.println("Hello World");
        //System.out.println("JCO Version: " + JCO.getVersion());

        while (true) {
            BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
            System.out.println("Enter Input : ");
            try {
                String s = br.readLine();
                System.out.println(s);
            }catch(Exception e) {
                System.out.println(e);
            }
        }
        
    }

}