Intel RMM im Internet

Submitted by admin on So, 18.06.2017 - 10:18

Manchmal benötigt man einen Zugang zur Konsole seines Servers im Internet. In diesem Beitrag wird gezeigt, wie man das zumindest halbwegs sicher machen kann, ohne dass eine VPN Lösung benötigt wird.

Sicherheit

Ein Intel RMM direkt in das Internet zu hängen, ist eine ganz schlechte Idee, denn diese Remote Manangementlösungen haben eines gemeinsam: Jede Menge Sicherheitslücken. Im Falle vom Intel RMM sind noch die Ports 5120, 5123, 5124, 5127, 7578 und 7582 zusätzlich zu den üblichen Verdächtigen 80 und 443 immer offen. Über diese High Port kommuniziert das Java Applet für den Konsolenzugang.

Das Intel RMM3, das auf den damals sehr beliebten Serverboards S3420 und S5520 verwendet wurde, funktioniert nur mit Java 6, das wiederum keine sicheren Verschlüsselungsverfahren unterstützt.

Ein VPN hat gegenüber dieser Lösung den großen Vorteil, dass die Stärke der Verschlüsselung nur vom VPN abhängt. Dieses Proof-Of-Concept benötigt nur ein Client Zertifikat.

Funktionsprinzip

Es wird ein kleiner PC, wie z.B. PC-Engines ALIX verwendet, auf dem FreeBSD installiert wird. Am Interface vr0 wurde in diesem Beipiel der Uplink zum Internet angeschlossen, an vr2 hängt das RMM3 Modul. Auf dem Alix Board sind haproxy und Nginx installiert. Die Konfigurationsbeispiele für haproxy und Nginx berücksichtigen den Dual-Stack Zugang, so dass den RMM Modulen so auch IPv6 beigebracht wird.

In den Beispielconfigs hat das RMM3 Modul die IP-Adresse 192.168.224.2 und das Alix Board auf vr2 die 192.168.224.1, wobei im RMM3 Modul als Default Gateway die 192.168.224.1 eingetragen wird.

Im RMM3 Modul müssen in der Remote Config unbedingt bei Enable KVM Encryption und Enable Media Encryption die Häkchen gesetzt sein, sonst erfolgt die Kommunikation  zwischen RMM und Java Applet unverschlüsselt.

FreeBSD

clear_tmp_enable="YES"
syslogd_flags="-ss"
sendmail_enable="NONE"
hostname="alix"
keymap="de"
ifconfig_em0="DHCP"
sshd_enable="YES"

ntpd_enable="YES"
ntpdate_enable="YES"
ntpdate_hosts="193.138.96.1"
ntpdate_flags="-b"

# Set dumpdev to "AUTO" to enable crash dumps, "NO" to disable
dumpdev="NO"

ifconfig_vr0="inet 193.138.96.99 netmask 255.255.255.0"
ifconfig_vr0_ipv6="inet6 2a01:7a0:1:96::99/64"
defaultrouter="193.138.96.1"
ipv6_defaultrouter="2a01:7a0:1:96::1"

ifconfig_vr2="inet 192.168.224.1 netmask 255.255.255.0"

pf_enable="YES"
pf_rules="/etc/pf.conf"
pflog_enable="YES"
pflog_logfile="/var/log/pflog"

nginx_enable="YES"
haproxy_enable="YES"

Damit wird sichergestellt, dass die Firewall bei Systemstart ativiert wird.

FreeBSD Firewall

Die FreeBSD Firewall spielt eine zentrale Rolle in dem Sicherheitskonzept und darf keinesfalls ausgelassen werden. Der Eintrag anchor RMM bekommt dynamisch die Freischaltungen für die authentifizierte IP-Adresse.

# pf.conf for RMM gateway

int_if="vr2"
ext_if="vr0"
icmp_types="{ echoreq, unreach }"
icmp6_types="{ 128, 133, 134, 135, 136, 137 }"

PUBLIC_PORTS="{ 22, 80, 443 }"

scrub in on $ext_if all fragment reassemble

nat on $ext_if from $int_if:network to any -> ($ext_if)

set skip on lo0

pass quick on $int_if from any to any

antispoof for $ext_if

# --- TCP
pass in  quick on $ext_if inet proto tcp from any to $ext_if  port $PUBLIC_PORTS
pass in  quick on $ext_if inet6 proto tcp from any to $ext_if  port $PUBLIC_PORTS

anchor RMM

# --- ICMP
pass in quick on $ext_if inet proto icmp from any to $ext_if icmp-type $icmp_types
pass in quick on $ext_if inet6 proto icmp6 from any to $ext_if icmp6-type $icmp6_types

pass out quick keep state

block log all

Haproxy

Der Haproxy wird als reiner tcp Proxy verwendet, der sich um die Highports für das Java Applet kümmert. Die Datei /usr/local/etc/haproxy.conf hat folgenden Inhalt:

global
	log /var/run/log local0
	user haproxy
	group haproxy
	daemon

defaults
	log	global
	mode	http
	option	httplog
	option	dontlognull
        timeout connect 5000
        timeout client  50000
        timeout server  50000

# 5124
frontend fr_server5124
        bind :::5124 v4v6
        mode tcp
        default_backend bk_server5124
backend bk_server5124
        mode tcp
        server srv1 192.168.224.2:5124 maxconn 2048

# 5127
frontend fr_server5127
        bind :::5127 v4v6
        mode tcp
        default_backend bk_server5127
backend bk_server5127
        mode tcp
        server srv1 192.168.224.2:5127 maxconn 2048

# 7578
frontend fr_server7578
        bind :::7578 v4v6
        mode tcp
        default_backend bk_server7578
backend bk_server7578
        mode tcp
        server srv1 192.168.224.2:7578 maxconn 2048

# 7582
frontend fr_server7582
        bind :::7582 v4v6
        mode tcp
        default_backend bk_server7582
backend bk_server7582
        mode tcp
        server srv1 192.168.224.2:7582 maxconn 2048

 

Nginx

Der Nginx nutzt als offizielles Zertifikat Let's Encrypt. Das LUA Modul wird benötigt, um die Firewall Freischaltung für die High Ports vorzunehmen.

load_module "/usr/local/libexec/nginx/ngx_http_lua_module.so";

#user  nobody;
worker_processes  1;

# This default error log path is compiled-in to make sure configuration parsing
# errors are logged somewhere, especially during unattended boot when stderr
# isn't normally logged anywhere. This path will be touched on every nginx
# start regardless of error log location configured here. See
# https://trac.nginx.org/nginx/ticket/147 for more info. 
#
#error_log  /var/log/nginx/error.log;
#

#pid        logs/nginx.pid;


events {
    worker_connections  1024;
}


http {
    include       mime.types;
    default_type  application/octet-stream;

    #log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
    #                  '$status $body_bytes_sent "$http_referer" '
    #                  '"$http_user_agent" "$http_x_forwarded_for"';

    log_format combined_ssl '$remote_addr - $remote_user [$time_local] '
                        '$ssl_protocol/$ssl_cipher '
                        '"$request" $status $body_bytes_sent '
                        '"$http_referer" "$http_user_agent" "$request_body_file"';


    #access_log  logs/access.log  main;
    access_log  /var/log/nginx/access.log  combined_ssl;

    sendfile        on;
    #tcp_nopush     on;

    #keepalive_timeout  0;
    keepalive_timeout  65;

    gzip  on;
    gzip_types
        application/atom+xml
        application/rss+xml
        application/javascript
        application/json
        application/ld+json
        application/manifest+json
        application/rdf+xml
        application/schema+json
        application/vnd.geo+json
        application/vnd.ms-fontobject
        application/x-font-ttf
        application/x-javascript
        application/x-web-app-manifest+json
        application/xhtml+xml
        application/xm
        application/xml
        font/eot
        font/opentype
        image/bmp
        image/svg+xml
        image/x-icon
        image/vnd.microsoft.icon
        text/cache-manifest
        text/css
        text/javascript
        text/js
        text/plain
        text/vcard
        text/vnd.rim.location.xloc
        text/vtt
        text/x-component
        text/x-cross-domain-policy
        text/xml;

    server_tokens off;


    upstream lua_bmc {
        server 192.168.224.2;
        balancer_by_lua_block {
        }
    }


    server {
        listen       80 default;
        listen       [::]:80 default;

        #charset koi8-r;

        #access_log  logs/host.access.log  main;

        location / {
            return 301 https://$host$request_uri;
        }

        #error_page  404              /404.html;

        # redirect server error pages to the static page /50x.html
        #
        error_page   500 502 503 504  /50x.html;
        location = /50x.html {
            root   /usr/local/www/nginx-dist;
        }

    }


    # HTTPS server
    #
    server {
        listen       443 ssl default;
        listen       [::]:443 ssl default;

        client_body_in_file_only on;

        ssl_certificate      /usr/local/etc/letsencrypt/live/alix.felsing.net/fullchain.pem;
        ssl_certificate_key  /usr/local/etc/letsencrypt/live/alix.felsing.net/privkey.pem;

        ssl_session_cache    shared:SSL:1m;
        ssl_session_timeout  15m;

        ssl_prefer_server_ciphers  on;

	ssl_protocols  TLSv1 TLSv1.1 TLSv1.2;
	ssl_ciphers ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-CHACHA20-POLY1305:RSA-PSK-CHACHA20-POLY1305:DHE-PSK-CHACHA20-POLY1305:ECDHE-PSK-CHACHA20-POLY1305:PSK-CHACHA20-POLY1305:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:ECDHE-RSA-DES-CBC3-SHA:ECDHE-ECDSA-DES-CBC3-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:AES:CAMELLIA:DES-CBC3-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!aECDH:!EDH-DSS-DES-CBC3-SHA:!EDH-RSA-DES-CBC3-SHA:!KRB5-DES-CBC3-SHA;
	ssl_stapling on;
	ssl_stapling_verify on;
	ssl_trusted_certificate /usr/local/etc/letsencrypt/live/alix.felsing.net/chain.pem;
	resolver_timeout 10s;

	add_header Strict-Transport-Security "max-age=31556926; includeSubDomains; preload";

        ssl_client_certificate /usr/local/etc/nginx/client-ca.pem;
        ssl_verify_client on;
        #ssl_verify_client  optional;

        location /page/login.html {
            access_by_lua_block {
                os.execute("/opt/cftools/myscript.off "..ngx.var.SSL_CLIENT_VERIFY.."  "..ngx.var.REMOTE_ADDR.." \""..ngx.var.SSL_CLIENT_S_DN.."\" &")
            }

            include "common.inc";
        }

        location /Java/jviewer.jnlp {
            access_by_lua_block {
                os.execute("/opt/cftools/myscript "..ngx.var.SSL_CLIENT_VERIFY.."  "..ngx.var.REMOTE_ADDR.." \""..ngx.var.SSL_CLIENT_S_DN.."\" &")
            }

            include "common.inc";
        }

        location / {
            include "common.inc";
        }
    }

}

dazu kommt noch die Datei common.inc, die die gemeinsame Revers-Proxy Config enthält.

proxy_http_version 1.1;

proxy_set_header Proxy "";
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_set_header X-NginX-Proxy true;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Forwarded-Server $host;

#proxy_cookie_path / "/; secure";
proxy_cookie_path / "/";

proxy_pass https://192.168.224.2;

Die Nginx Konfiguration erzwingt ein Clientzertifikat. Nur im Erfolgsfall wird die Firewallfreischaltung für die Highports vorgenommen. Die starke TLSv1.2 Verschlüsselung kommt leider nur zum Tragen, so lange die Konsole nicht aufgerufen wird. Die Konsole nutzt leider mit die in Java 6 verfügbare Verschlüsselung.

Skripte

Mit sudo wird ein Skript aufgerufen, das die Firewallfreischaltung vornimmt. Das ist ein zweistufiger Prozess:

myscript

#!/usr/local/bin/bash

SUDO="/usr/local/bin/sudo"
LOG="/usr/bin/logger"

if [ "$1" == "SUCCESS" ]; then
  ip="$2"
  cn="$3"
  $LOG "$ip enabled for $cn"
  $SUDO /opt/cftools/enable-ip "$ip"
fi

enable-ip

#!/usr/local/bin/bash

PATH=/sbin:/usr/sbin:/bin:/usr/bin
export PATH

usage() {
  echo "user:" $(whoami)
  echo "$0 <ip>"
  exit 1
}

cmd=$(basename $0)

case $cmd in
  enable-ip)
    ip="$1"
    if [ -z "$ip" ]; then
        usage
    fi
    logger "enable $ip"
    echo "pass in quick proto tcp from ${ip} to any port { 5120 5123 5124 5127 7578 7582 }" | pfctl -a RMM -f -
    ;;
  disable-ip)
    logger "disable $ip"
    pfctl -a RMM -F rules
    ;;
  *)
    logger "called unknown $cmd"
    ;;
esac

Es wird noch ein Link enable-ip -> disable-ip gesetzt. Ein Cronjob ruft zu bestimmten Zeiten disable-ip auf. Das kann mach sicher noch eleganter machen.

RMM3

Hier noch einige Screenshots. Als Client Plattform wurde Windows XP verwendet, auch wenn es schon lange Out-Of-Support ist. Das gilt jedoch auch für Java 6. Es versteht sich von selbst, dass so eine Installation ausschließlich für den Konsolenzugang verwendet wird.

Leider ist Intel offenbar nicht Willens, Updates für RMM3 zu liefern, die auch mit Java 8 funktionieren. Noch besser wäre natürlich HTML5/Websockets, wie es z.B. Supermicro macht.

Einige der in der haproxy.conf konfigurierten Ports sind nicht in der Intel Dokumentation zu finden, dennoch werden diese Ports für TLS Verbindungen gebraucht.

RMM Login
RMM Loginmaske

Die Verschlüsselung von KVM und Media sind unverzichtbar. Leider unterstützt RMM3 nur TLS1, das ist allerdings besser als nichts.

RMM Config
RMM Config

Noch ist der Host aus.

RMM Systeminfo
RMM Systeminfo

Mit dem Remote Management Modul kann man ihn jedoch einschalten.

RMM remote power menu
RMM remote menu

Hier ein Blick in das BIOS.

RMM BIOS
RMM BIOS

Tests

Hat man sich erfolgreich authentifizert, dann sollte mit

pfctl -a RMM -sr

die eigene IP-Adresse mit den High-Ports erscheinen. Nach /opt/cftools/disable-ip sollte diese Liste leer sein.

Bevor man sich eingeloggt hat, sollte geprüft werden, ob die Firewall aktiv ist: Ein telnet <RMM Gate externe IP> 7578 sollte zu einem Timeout führen. Nach der Authentifizierung mit dem Client Zertifikat sollte der Port offen sein.