HOWTO · Java
Beheben Sie die Java.Net.SocketException: Permission Denied in Java
Dieses Tutorial demonstriert den Fehler java.net.SocketException: Permission denied in Java.
Auf dieser Seite
java.net.SocketException: Permission denied bedeutet, dass ein Socket-Vorgang von einer Betriebssystem- oder Sicherheitsschicht abgewiesen wurde. Die Meldung allein zeigt weder, welche Schicht ihn abgewiesen hat, noch warum. Beginnen Sie mit dem Stacktrace und dem Vorgang, den Ihr Programm auszuführen versuchte, und prüfen Sie dann die dafür geltenden Richtlinien. Für einen fehlgeschlagenen Server-Bind-Vorgang kommen andere Ursachen und Abhilfen infrage als für eine fehlgeschlagene Client-Verbindung.
Was java.net.SocketException: Permission denied bedeutet
Ein Java-Socket-Aufruf ist letztlich darauf angewiesen, dass die Hostumgebung den angeforderten Vorgang zulässt. Die Ablehnung kann durch Betriebssystemberechtigungen, eine Firewall- oder Endgerätesicherheitsrichtlinie, eine Container- oder Cluster-Richtlinie oder eine Laufzeit- beziehungsweise Bibliotheksbeschränkung verursacht werden. Das sind Möglichkeiten, die untersucht werden sollten, keine Ursachen, die sich allein aus dem Ausnahmetext ableiten lassen.
Die Meldung unterscheidet sich auch von einem Fehler, der besagt, dass ein Port bereits belegt ist. Bei einem Portkonflikt erscheint normalerweise eine eigene Meldung, dass die Adresse bereits verwendet wird. Eine Berechtigungsverweigerung besagt, dass der Vorgang abgewiesen wurde; sie beweist weder, dass ein Port reserviert ist, noch dass das Netzwerk eine Verbindung abgelehnt hat oder Java zu alt ist. Ändern Sie nicht mehrere Einstellungen gleichzeitig, denn sonst lässt sich die tatsächlich wirksame Einschränkung schwerer erkennen.
Ermitteln, an welcher Stelle der Socket-Vorgang fehlschlägt
Stellen Sie zunächst fest, ob das Programm einen Listening-Socket öffnet oder einen anderen Host erreichen soll. Ein Server ruft bind auf, um eine lokale Adresse und einen Port zu belegen. Ein Client ruft normalerweise connect auf, um einen entfernten Endpunkt zu erreichen. Der Stacktrace kann dabei helfen: Frames wie Net.bind, ServerSocketChannel.bind oder der Startcode eines Server-Connectors deuten auf einen lokalen Bind-Versuch hin. Sie geben Hinweise auf den Vorgang, beweisen aber keine bestimmte Richtlinie oder Ursache.
Ein Tomcat-Start-Stacktrace mit Net.bind und NioEndpoint.bind zeigt beispielsweise, dass beim Start ein lokaler Connector gebunden werden sollte. Prüfen Sie die in Tomcat konfigurierte Adresse und den Port und gleichen Sie den Fehlerzeitpunkt mit den Betriebssystem- oder Containerprotokollen ab. Deutet der Trace stattdessen auf eine Client-Verbindung hin, notieren Sie Zielhost und Zielport und untersuchen Sie die Richtlinien für ausgehenden Datenverkehr, das Routing und eine etwaige Proxy-Konfiguration. Ein Fehler beim TLS-Aufbau bedeutet für sich genommen nicht, dass das Zertifikat diese Socket-Ablehnung verursacht hat. Ermitteln Sie zuerst den fehlschlagenden Socket-Aufruf, bevor Sie Zertifikatseinstellungen ändern.
Berechtigungsfehler beim Binden eines Server-Sockets beheben
Ein eingeschränkter Port mit niedriger Nummer ist eine mögliche Ursache dafür, dass ein Server nicht binden kann. Schwellenwerte und Regeln hängen vom Betriebssystem und dessen Konfiguration ab. Dass ein Port kleiner als 1024 ist, beweist daher nicht, dass dies auf jedem Host die Ursache ist. Prüfen Sie den tatsächlich verwendeten Connector-Port und die Berechtigungs- oder Capability-Richtlinien des Hosts. Für einen Entwicklungs- oder internen Dienst ist die Wahl eines Ports ohne besondere Berechtigungsanforderungen oft die engste Abhilfe. Passen Sie die Client-URL oder die Zuordnung des Reverse-Proxys entsprechend an. Verwenden Sie in der Produktion gegebenenfalls die vom jeweiligen System unterstützte Dienst- oder Portweiterleitungskonfiguration.
Starten Sie Tomcat oder den gesamten Java-Prozess nicht routinemäßig als Root oder Administrator. Erhöhte Rechte gewähren weitreichenden Zugriff und können dazu führen, dass ein Fehler in der Anwendung den Host gefährdet. Ist ein privilegierter Listener erforderlich, bevorzugen Sie einen eng begrenzten, vom Betriebssystem unterstützten Dienstmanager, eine passende Capability oder einen vorgeschalteten Proxy. Gewähren Sie nur die Zugriffsrechte, die der Dienst benötigt.
In OpenShift oder einer anderen Containerplattform kann ein Port, der auf einem Entwicklungsrechner funktioniert, durch den Sicherheitskontext oder den zugewiesenen Benutzer des Workloads gesperrt sein. Prüfen Sie den konfigurierten Container-Port, den effektiven Benutzer und Sicherheitskontext sowie die Plattformereignisse und Pod-Protokolle zum Startzeitpunkt. Verwenden Sie den Port und die Identität, die laut Plattformrichtlinie zulässig sind. Schwächen Sie die Workload-Richtlinie nicht ab und nehmen Sie ohne Belege nicht an, dass die Cluster-Firewall verantwortlich ist.
Firewall- und Laufzeitbeschränkungen prüfen
Ist der fehlschlagende Aufruf connect, prüfen Sie die Regeln der Firewall oder der Endgerätesicherheit für ausgehende Verbindungen des Java-Prozesses und für das konkrete Ziel. Bei einem Bind-Vorgang prüfen Sie die lokalen Bind-Richtlinien und alle Anwendungssteuerungsregeln, die Listening-Sockets einschränken. Sehen Sie sich die relevanten Ablehnungsprotokolle an und beantragen oder setzen Sie anschließend die kleinstmögliche Richtlinienänderung für die benötigte Adresse, den Port, die Richtung und den Prozess um. Eine Firewall oder ein Antivirenprogramm auszuschalten ist keine sichere Diagnosemaßnahme. Nutzen Sie stattdessen die Protokolle oder eine eng begrenzte temporäre Regel, die nach den üblichen Kontrollen Ihrer Umgebung verwaltet wird.
Prüfen Sie außerdem, ob die Anwendung in einem Container, einer Sandbox, einem verwalteten Hostingdienst oder unter einer Sicherheitsrichtlinie mit Netzwerkbeschränkungen läuft. Vergleichen Sie den effektiven Laufzeitbenutzer, die Capabilities, die Umgebung und die Richtlinien mit einer funktionierenden Bereitstellung. Eine Bibliothek eines Drittanbieters kann eigene Konfigurationsanforderungen haben; prüfen Sie jedoch deren Dokumentation und den fehlschlagenden Aufruf, bevor Sie Abhängigkeiten ändern. Ein Java-Update kann aus Gründen der Unterstützung oder Sicherheit angebracht sein, doch der Ausnahmetext allein belegt nicht, dass eine ältere Laufzeit die Ablehnung verursacht hat.
Die Systemeigenschaft -Djava.net.preferIPv4Stack=true ist eine ältere Diagnoseoption aus der Java-7-Zeit für Fälle, in denen die Auswahl des IPv4- oder IPv6-Stacks eine Rolle spielt. Sie erteilt keine Berechtigung zum Binden oder Verbinden und sollte nicht als allgemeine Abhilfe dienen. Verwenden Sie sie nur, wenn Laufzeit und beobachtetes Verhalten der Adressfamilie sie relevant machen. Bevorzugen Sie eine unterstützte Java-Laufzeit, statt eine veraltete Version zu verwenden, um diese Option einzusetzen.
Die Abhilfe überprüfen
Notieren Sie vor jeder Änderung den Vorgang, die lokale oder entfernte Adresse, den Port, die Laufzeitidentität und die relevante Richtlinie. Nehmen Sie eine einzelne, auf die vermutete Ursache zugeschnittene Änderung vor, wiederholen Sie dann denselben Vorgang und prüfen Sie dieselben Protokolle. Bei einem Server bestätigen Sie, dass die vorgesehene Adresse und der Port auf Verbindungen warten und der Dienst normal startet. Bei einem Client bestätigen Sie, dass er das vorgesehene Ziel erreicht und der entfernte Dienst die Verbindung annimmt. Bleibt die Ablehnung bestehen, war die erste vermutete Ursache nicht die einzige oder nicht ausreichend. Führen Sie die Diagnose anhand des neuen Stacktraces und der Ablehnungsprotokolle fort, statt umfassendere Berechtigungen zu vergeben.
Führen Sie dieses Beispiel aus:
Der Code und die angezeigten Bezeichnungen oder Ausgaben in diesem Abschnitt bleiben auf Englisch.
public class SocketBindCheck {
public static void main(String[] args) {
try (java.net.ServerSocket socket = new java.net.ServerSocket(0)) {
int assignedPort = socket.getLocalPort();
if (assignedPort > 0 && assignedPort <= 65535) {
System.out.println("Ephemeral port assigned");
} else {
System.out.println("Unexpected port assignment");
}
} catch (java.io.IOException problem) {
System.err.println("Socket could not open: " + problem);
System.exit(1);
}
}
}
Ephemeral port assigned
Der angeforderte Port 0 weist das Betriebssystem an, einen verfügbaren dynamischen Port auszuwählen. Die Prüfung bestätigt, dass der zugewiesene Port im gültigen TCP/UDP-Portbereich liegt; der Try-with-resources-Block schließt den Listening-Socket. Bei erfolgreicher Ausführung gibt das Beispiel Ephemeral port assigned aus und endet mit Status 0. Diese unveränderte englische Ausgabe bedeutet „Dynamischer Port zugewiesen“. Schlägt die Socket-Einrichtung fehl, gibt das Programm die IOException aus und endet mit einem von null verschiedenen Status. So kann eine Automatisierung zwischen einem Einrichtungsfehler und Erfolg unterscheiden. Prüfen Sie die Protokolle und Sicherheitsrichtlinien des Hosts, um die Ursache zu ermitteln. Dieser Test prüft weder einen bestimmten konfigurierten Port noch eine Verbindung zu einem entfernten Ziel, Tomcat oder eine OpenShift-Richtlinie.
Ein Port außerhalb des gültigen Bereichs ist ein anderer Grenzfall: Java weist ihn zurück, bevor es das Betriebssystem um einen Bind-Vorgang bittet. Damit lässt sich also keine Berechtigungsrichtlinie diagnostizieren. Diese kleine Prüfung fängt den Fehler auf Argumentebene ab und wird regulär beendet:
public class SocketPortBoundaryCheck {
public static void main(String[] args) {
try {
new java.net.ServerSocket(65536).close();
System.out.println("Unexpectedly accepted port");
} catch (IllegalArgumentException problem) {
System.out.println("Port rejected before bind");
} catch (java.io.IOException problem) {
System.out.println("Socket setup failed");
}
}
}