Skip to content

Commit

Permalink
Implement TLS
Browse files Browse the repository at this point in the history
This also replaces the configuration parser with Blackthorne as it
became too awkward to combine multiple schemas with xml bind.

Fix: #94
  • Loading branch information
io7m committed Oct 21, 2023
1 parent 25e1f4e commit e36cf08
Show file tree
Hide file tree
Showing 124 changed files with 5,649 additions and 1,212 deletions.
3 changes: 3 additions & 0 deletions checkstyle-filter.xml
Original file line number Diff line number Diff line change
Expand Up @@ -199,4 +199,7 @@
<suppress files="IdUVHandlerCoreAuthenticated\.java"
checks="NPathComplexity"/>

<suppress files="IdServer\.java"
checks="ExecutableStatementCount"/>

</suppressions>
Original file line number Diff line number Diff line change
Expand Up @@ -30,42 +30,60 @@
<Verbatim><![CDATA[
<?xml version="1.0" encoding="UTF-8" ?>
<Configuration xmlns="urn:com.io7m.idstore:configuration:1">
<Branding ProductTitle="idstore"/>
<Database Name="idstore"
Kind="POSTGRESQL"
OwnerRoleName="idstore_install"
OwnerRolePassword="mydatabasewill"
WorkerRolePassword="probablybecompromised"
Address="db.example.com"
Port="5432"
Create="true"
Upgrade="true"/>
<HTTPServices>
<HTTPServiceAdminAPI ListenAddress="[::]"
ListenPort="51000"
ExternalURI="http://[::]:51000/"/>
<HTTPServiceUserAPI ListenAddress="[::]"
ListenPort="50000"
ExternalURI="http://[::]:50000/"/>
<HTTPServiceUserView ListenAddress="[::]"
ListenPort="50001"
ExternalURI="http://[::]:50001/"/>
</HTTPServices>
<History UserLoginHistoryLimit="10" AdminLoginHistoryLimit="100"/>
<Mail SenderAddress="[email protected]" VerificationExpiration="PT24H">
<SMTP Host="mail.example.com"
<i:Configuration xmlns:i="urn:com.io7m.idstore:configuration:1"
xmlns:it="urn:com.io7m.idstore.tls:1">
<i:Branding ProductTitle="idstore"/>
<i:Database Name="idstore"
Kind="POSTGRESQL"
OwnerRoleName="idstore_install"
OwnerRolePassword="mydatabasewill"
WorkerRolePassword="probablybecompromised"
Address="db.example.com"
Port="5432"
Create="true"
Upgrade="true"/>
<i:HTTPServices>
<i:HTTPServiceAdminAPI ListenAddress="[::]"
ListenPort="51000"
ExternalURI="https://[::]:51000/">
<it:TLSEnabled>
<it:KeyStore Type="CANONMILL"
Provider="CANONMILL"
Password="changeit"
File="keystore.xml"/>
<it:TrustStore Type="CANONMILL"
Provider="CANONMILL"
Password="changeit"
File="truststore.xml"/>
</it:TLSEnabled>
</i:HTTPServiceAdminAPI>
<i:HTTPServiceUserAPI ListenAddress="[::]"
ListenPort="50000"
ExternalURI="http://[::]:50000/">
<it:TLSDisabled/>
</i:HTTPServiceUserAPI>
<i:HTTPServiceUserView ListenAddress="[::]"
ListenPort="50001"
ExternalURI="http://[::]:50001/">
<it:TLSDisabled/>
</i:HTTPServiceUserView>
</i:HTTPServices>
<i:History UserLoginHistoryLimit="10"
AdminLoginHistoryLimit="100"/>
<i:Mail SenderAddress="[email protected]" VerificationExpiration="PT24H">
<i:SMTP Host="mail.example.com"
Port="25"/>
</Mail>
</i:Mail>
<RateLimiting EmailVerificationRateLimit="PT10M" PasswordResetRateLimit="PT10M" />
<i:RateLimiting EmailVerificationRateLimit="PT10M" PasswordResetRateLimit="PT10M" />
<Sessions UserSessionExpiration="PT30M" AdminSessionExpiration="PT30M"/>
</Configuration>
<i:Sessions UserSessionExpiration="PT30M" AdminSessionExpiration="PT30M"/>
</i:Configuration>
]]></Verbatim>
</FormalItem>
</Subsection>
Expand Down Expand Up @@ -131,11 +149,7 @@
<Paragraph>
The <Term type="expression">ListenAddress</Term>
and <Term type="expression">ListenPort</Term> attributes specify the address and port to which to the HTTP
service will bind. It is recommended that the service be bound to
<Term type="expression">localhost</Term>
and a reverse proxy such as <LinkExternal target="https://www.nginx.org">nginx</LinkExternal> be used to
provide <LinkExternal target="https://en.wikipedia.org/wiki/Transport_Layer_Security">TLS</LinkExternal>
<LinkFootnote target="dffb7f36-a515-46c7-9ae1-f476b2257ce2"/>.
service will bind.
</Paragraph>
<Paragraph>
The <Term type="expression">ExternalAddress</Term> attribute specifies the external address that clients will
Expand All @@ -145,6 +159,15 @@
<Paragraph>
By convention, the Admin API should listen on TCP port <Term type="constant">51000</Term>.
</Paragraph>
<Paragraph>
The <Term type="expression">HTTPServiceAdminAPI</Term> element must contain either a
<Term type="expression">TLSEnabled</Term> or <Term type="expression">TLSDisabled</Term> element specifying
whether TLS should be enabled or disabled, respectively. The <Term type="expression">TLSEnabled</Term>
element describes the key store and trust store. The <Term type="package">idstore</Term> server
automatically reloads certificates periodically in order to work well in environments using the
<LinkExternal target="https://datatracker.ietf.org/doc/html/rfc8555">ACME</LinkExternal> protocol to
issue certificates.
</Paragraph>
</Subsection>
<Subsection title="HTTPServiceUserAPI">
<Paragraph>
Expand All @@ -153,11 +176,7 @@
<Paragraph>
The <Term type="expression">ListenAddress</Term>
and <Term type="expression">ListenPort</Term> attributes specify the address and port to which to the HTTP
service will bind. It is recommended that the service be bound to
<Term type="expression">localhost</Term>
and a reverse proxy such as <LinkExternal target="https://www.nginx.org">nginx</LinkExternal> be used to
provide <LinkExternal target="https://en.wikipedia.org/wiki/Transport_Layer_Security">TLS</LinkExternal>
<LinkFootnote target="dffb7f36-a515-46c7-9ae1-f476b2257ce2"/>.
service will bind.
</Paragraph>
<Paragraph>
The <Term type="expression">ExternalAddress</Term> attribute specifies the external address that clients will
Expand All @@ -167,6 +186,15 @@
<Paragraph>
By convention, the User API should listen on TCP port <Term type="constant">50000</Term>.
</Paragraph>
<Paragraph>
The <Term type="expression">HTTPServiceUserAPI</Term> element must contain either a
<Term type="expression">TLSEnabled</Term> or <Term type="expression">TLSDisabled</Term> element specifying
whether TLS should be enabled or disabled, respectively. The <Term type="expression">TLSEnabled</Term>
element describes the key store and trust store. The <Term type="package">idstore</Term> server
automatically reloads certificates periodically in order to work well in environments using the
<LinkExternal target="https://datatracker.ietf.org/doc/html/rfc8555">ACME</LinkExternal> protocol to
issue certificates.
</Paragraph>
</Subsection>
<Subsection title="HTTPServiceUserView">
<Paragraph>
Expand All @@ -175,11 +203,7 @@
<Paragraph>
The <Term type="expression">ListenAddress</Term>
and <Term type="expression">ListenPort</Term> attributes specify the address and port to which to the HTTP
service will bind. It is recommended that the service be bound to
<Term type="expression">localhost</Term>
and a reverse proxy such as <LinkExternal target="https://www.nginx.org">nginx</LinkExternal> be used to
provide <LinkExternal target="https://en.wikipedia.org/wiki/Transport_Layer_Security">TLS</LinkExternal>
<LinkFootnote target="dffb7f36-a515-46c7-9ae1-f476b2257ce2"/>.
service will bind.
</Paragraph>
<Paragraph>
The <Term type="expression">ExternalAddress</Term> attribute specifies the external address that clients will
Expand All @@ -189,6 +213,15 @@
<Paragraph>
By convention, the User API should listen on TCP port <Term type="constant">50001</Term>.
</Paragraph>
<Paragraph>
The <Term type="expression">HTTPServiceUserView</Term> element must contain either a
<Term type="expression">TLSEnabled</Term> or <Term type="expression">TLSDisabled</Term> element specifying
whether TLS should be enabled or disabled, respectively. The <Term type="expression">TLSEnabled</Term>
element describes the key store and trust store. The <Term type="package">idstore</Term> server
automatically reloads certificates periodically in order to work well in environments using the
<LinkExternal target="https://datatracker.ietf.org/doc/html/rfc8555">ACME</LinkExternal> protocol to
issue certificates.
</Paragraph>
</Subsection>
<Subsection title="Example">
<Paragraph>
Expand All @@ -199,13 +232,28 @@
<HTTPServices>
<HTTPServiceAdminAPI ListenAddress="localhost"
ListenPort="51000"
ExternalURI="http://localhost:51000/"/>
ExternalURI="http://localhost:51000/">
<it:TLSEnabled>
<it:KeyStore Type="CANONMILL"
Provider="CANONMILL"
Password="changeit"
File="keystore.xml"/>
<it:TrustStore Type="CANONMILL"
Provider="CANONMILL"
Password="changeit"
File="truststore.xml"/>
</it:TLSEnabled>
</HTTPServiceAdminAPI>
<HTTPServiceUserAPI ListenAddress="localhost"
ListenPort="50000"
ExternalURI="http://localhost:50000/"/>
ExternalURI="http://localhost:50000/">
<it:TLSDisabled/>
</HTTPServiceUserAPI>
<HTTPServiceUserView ListenAddress="localhost"
ListenPort="50001"
ExternalURI="http://localhost:50001/"/>
ExternalURI="http://localhost:50001/">
<it:TLSDisabled/>
</HTTPServiceUserView>
</HTTPServices>
]]></Verbatim>
</FormalItem>
Expand Down Expand Up @@ -646,21 +694,18 @@
<Paragraph>
The XSD schema for the configuration file is as follows:
</Paragraph>
<FormalItem title="Schema">
<FormalItem title="Configuration Schema">
<Verbatim>
<xi:include href="com/io7m/idstore/server/service/configuration/configuration.xsd"
<xi:include href="com/io7m/idstore/server/service/configuration/configuration-1.xsd"
parse="text"/>
</Verbatim>
</FormalItem>
<FormalItem title="TLS Schema">
<Verbatim>
<xi:include href="com/io7m/idstore/server/service/configuration/tls-1.xsd"
parse="text"/>
</Verbatim>
</FormalItem>
</Subsection>

<Footnote id="dffb7f36-a515-46c7-9ae1-f476b2257ce2">
Note: It is extremely important that any reverse proxy used provides the correct
<LinkExternal target="https://datatracker.ietf.org/doc/html/rfc7239">RFC 7239</LinkExternal>
headers in order to tell the <Term type="expression">idstore</Term> server that a reverse proxy is present.
Otherwise, the <Term type="expression">idstore</Term> server will apply
<Link target="1d55d366-5883-4418-a61a-eef7a88eaca7">rate-limiting decisions</Link> to the address of the proxy
as opposed to the address of the user connecting through the proxy, as intended.
</Footnote>

</Section>
8 changes: 8 additions & 0 deletions com.io7m.idstore.main/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,14 @@
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
</dependency>
<dependency>
<groupId>com.io7m.anethum</groupId>
<artifactId>com.io7m.anethum.slf4j</artifactId>
</dependency>
<dependency>
<groupId>com.io7m.anethum</groupId>
<artifactId>com.io7m.anethum.api</artifactId>
</dependency>

<dependency>
<groupId>org.osgi</groupId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,13 @@

package com.io7m.idstore.main.internal;

import com.io7m.anethum.slf4j.ParseStatusLogging;
import com.io7m.idstore.model.IdEmail;
import com.io7m.idstore.model.IdName;
import com.io7m.idstore.model.IdRealName;
import com.io7m.idstore.server.api.IdServerConfigurations;
import com.io7m.idstore.server.api.IdServerFactoryType;
import com.io7m.idstore.server.service.configuration.IdServerConfigurationFiles;
import com.io7m.idstore.server.service.configuration.IdServerConfigurationParsers;
import com.io7m.quarrel.core.QCommandContextType;
import com.io7m.quarrel.core.QCommandMetadata;
import com.io7m.quarrel.core.QCommandStatus;
Expand All @@ -30,6 +31,8 @@
import com.io7m.quarrel.core.QParameterNamedType;
import com.io7m.quarrel.core.QStringType.QConstant;
import com.io7m.quarrel.ext.logback.QLogback;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.bridge.SLF4JBridgeHandler;

import java.nio.file.Path;
Expand All @@ -49,6 +52,9 @@

public final class IdMCmdInitialAdmin implements QCommandType
{
private static final Logger LOG =
LoggerFactory.getLogger(IdMCmdInitialAdmin.class);

private static final QParameterNamed1<Path> CONFIGURATION_FILE =
new QParameterNamed1<>(
"--configuration",
Expand Down Expand Up @@ -157,9 +163,13 @@ public QCommandStatus onExecute(
final var configurationFile =
context.parameterValue(CONFIGURATION_FILE);

final var parsers =
new IdServerConfigurationParsers();
final var configFile =
new IdServerConfigurationFiles()
.parse(configurationFile);
parsers.parseFile(
configurationFile,
status -> ParseStatusLogging.logWithAll(LOG, status)
);

final var configuration =
IdServerConfigurations.ofFile(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,10 @@

package com.io7m.idstore.main.internal;

import com.io7m.anethum.slf4j.ParseStatusLogging;
import com.io7m.idstore.server.api.IdServerConfigurations;
import com.io7m.idstore.server.api.IdServerFactoryType;
import com.io7m.idstore.server.service.configuration.IdServerConfigurationFiles;
import com.io7m.idstore.server.service.configuration.IdServerConfigurationParsers;
import com.io7m.quarrel.core.QCommandContextType;
import com.io7m.quarrel.core.QCommandMetadata;
import com.io7m.quarrel.core.QCommandStatus;
Expand All @@ -27,6 +28,8 @@
import com.io7m.quarrel.core.QParameterNamedType;
import com.io7m.quarrel.core.QStringType;
import com.io7m.quarrel.ext.logback.QLogback;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.bridge.SLF4JBridgeHandler;

import java.nio.file.Path;
Expand All @@ -45,6 +48,9 @@

public final class IdMCmdServer implements QCommandType
{
private static final Logger LOG =
LoggerFactory.getLogger(IdMCmdServer.class);

private static final QParameterNamed1<Path> CONFIGURATION_FILE =
new QParameterNamed1<>(
"--configuration",
Expand Down Expand Up @@ -98,9 +104,16 @@ public QCommandStatus onExecute(

QLogback.configure(context);

final var configurationFile =
context.parameterValue(CONFIGURATION_FILE);

final var parsers =
new IdServerConfigurationParsers();
final var configFile =
new IdServerConfigurationFiles()
.parse(context.parameterValue(CONFIGURATION_FILE));
parsers.parseFile(
configurationFile,
status -> ParseStatusLogging.logWithAll(LOG, status)
);

final var configuration =
IdServerConfigurations.ofFile(
Expand Down
2 changes: 2 additions & 0 deletions com.io7m.idstore.main/src/main/java/module-info.java
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,8 @@
requires com.io7m.idstore.shell.admin;
requires com.io7m.idstore.strings;

requires com.io7m.anethum.api;
requires com.io7m.anethum.slf4j;
requires com.io7m.quarrel.core;
requires com.io7m.quarrel.ext.logback;
requires com.io7m.repetoir.core;
Expand Down
Loading

0 comments on commit e36cf08

Please sign in to comment.