- All Implemented Interfaces:
Serializable, Servlet, ServletConfig
- See Also:
Field Summary
FieldsModifier and TypeFieldDescriptionprivate static final LogLogger for the login servlet.private static final ObjectSerializes the read-modify-write of the persisted-session list.Languages offered by the login page flag bar (login.jsp langToCountry).Themes the login page accepts.private static final PatternSplits a stored session record on colons.private static final SecureRandomRandom source for the CSRF tokens.private static final StringDefault login page theme.(package private) static final intHow many "remember me" sessions router.config holds.private static final StringForm parameter carrying the password.private static final StringForm parameter carrying the user name.private static final PatternSplits the list of stored session records.private static final StringConfig key for the persisted session count.private static final StringSet to true only when the console really does sit behind a reverse proxy that rewrites the forwarding headers.private static final StringThe authentication realm name.Constructor Summary
ConstructorsMethod Summary
Modifier and TypeMethodDescription(package private) static StringaddPersistedSession(String existing, String token, String username, long expiresAt, long now) Rebuild the persisted-session list with one session added.(package private) static StringChoose the address the login blocklist keys on.protected voiddoGet(HttpServletRequest req, HttpServletResponse resp) protected voiddoPost(HttpServletRequest req, HttpServletResponse resp) (package private) static StringfirstForwarded(String header) The client address from a forwarding header: the leftmost entry of an X-Forwarded-For chain, since later entries are appended by proxies further along and the first is the one the nearest proxy saw.private StringGenerates a random CSRF token.private StringReturns the client address of the request.private StringReturns the configured login page theme.private StringReturns the persisted session cookie value.private booleanChecks the password files for any usable password.voidinit()private booleanChecks whether any console password is configured.private voidLoads the saved sessions from the config.private longparseDuration(String duration) Parses a session duration like 7d.private voidpersistSession(String token, String username, long expiryMs) Saves a session to survive a restart.private static StringsanitizeLang(String lang) Keeps only a well formed language tag.private static StringsanitizeTheme(String theme) Keeps only the known theme names.private voidupdatePreference(String key, String value) Writes one console preference to the config.private booleanverifyPassword(String username, String password) Checks the submitted password against the stored hashes.Methods inherited from class HttpServlet
doDelete, doHead, doOptions, doPut, doTrace, getLastModified, service, serviceMethods inherited from class GenericServlet
destroy, getInitParameter, getInitParameterNames, getServletConfig, getServletContext, getServletInfo, getServletName, init, log, log
Field Details
_log
Logger for the login servlet.PARAM_USERNAME
Form parameter carrying the user name.- See Also:
PARAM_PASSWORD
Form parameter carrying the password.- See Also:
REALM
DEFAULT_THEME
PROP_PERSISTED_SESSIONS
Config key for the persisted session count.- See Also:
_SESSION_LIST_LOCK
Serializes the read-modify-write of the persisted-session list. Two concurrent logins otherwise read the same list and the later saveConfig() overwrites the earlier one's entry, so a session that was already issued silently stops working.MAX_PERSISTED_SESSIONS
static final int MAX_PERSISTED_SESSIONSHow many "remember me" sessions router.config holds. Every login used to append one, with no expiry pruning, so the value grew without bound.- See Also:
PROP_TRUST_FORWARDED_FOR
Set to true only when the console really does sit behind a reverse proxy that rewrites the forwarding headers. Left false, the login blocklist keys on the peer address of the connection, which a client cannot forge.- See Also:
COLON_SPLIT
Splits a stored session record on colons.PIPE_SPLIT
Splits the list of stored session records.CSRF_RANDOM
Random source for the CSRF tokens.ALLOWED_THEMES
ALLOWED_LANGS
Constructor Details
LoginServlet
public LoginServlet()Constructor, used by the servlet container. The container callsinit()afterwards, which is where the persisted sessions are loaded.
Method Details
init
- Overrides:
initin classGenericServlet- Throws:
ServletException
doGet
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException - Overrides:
doGetin classHttpServlet- Throws:
ServletExceptionIOException
isPasswordConfigured
private boolean isPasswordConfigured()Checks whether any console password is configured.- Returns:
- true if a password exists
hasAnyPassword
Checks the password files for any usable password.- Parameters:
ctx- the router context- Returns:
- true if a password exists
getLoginTheme
sanitizeTheme
sanitizeLang
doPost
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException - Overrides:
doPostin classHttpServlet- Throws:
ServletExceptionIOException
parseDuration
Parses a session duration like 7d.- Parameters:
duration- the text from the form- Returns:
- the duration, in ms
getClientIP
Returns the client address of the request.- Parameters:
req- the request to read the client address from- Returns:
- the address text
clientIP
static String clientIP(String forwardedFor, String realIp, String remoteAddr, boolean trustForwarded) Choose the address the login blocklist keys on.The peer address of the connection is the default. The forwarding headers are attacker-controlled on a directly-reachable console, and honouring them let every attempt with a fresh X-Forwarded-For value look like a new client, so the blocklist never engaged and its map grew one entry per spoofed address. They are read only when an operator has opted in via
PROP_TRUST_FORWARDED_FOR, having put a real proxy in front.That proxy must OVERWRITE the forwarding header with the address it saw - nginx
X-Real-IPfrom$remote_addr, for example. A proxy that appends instead (nginx$proxy_add_x_forwarded_for, Apache's default) leaves the client's own text as the leftmost entry, which is exactly the fresh-identity bypass the header is not trusted for by default. A value that is not an IP literal is ignored outright, so a hostile header can neither key the blocklist on text of the sender's choosing nor reach the log line verbatim.- Parameters:
forwardedFor- the X-Forwarded-For header, a comma-separated chain, or nullrealIp- the X-Real-IP header, or nullremoteAddr- the peer address of the connection, never null in practicetrustForwarded- true only when a trusted reverse proxy fronts the console- Returns:
- the address to rate-limit, never null or empty
firstForwarded
The client address from a forwarding header: the leftmost entry of an X-Forwarded-For chain, since later entries are appended by proxies further along and the first is the one the nearest proxy saw.- Parameters:
header- the raw header value, or null- Returns:
- the trimmed address, or null if the header is absent, blank, or the literal "unknown" some proxies send
verifyPassword
getSessionCookie
Returns the persisted session cookie value.- Parameters:
req- the request to read the cookie from- Returns:
- the cookie value, null if absent
persistSession
addPersistedSession
static String addPersistedSession(String existing, String token, String username, long expiresAt, long now) Rebuild the persisted-session list with one session added.The list used to be append-only: every login wrote a new entry, nothing ever removed one, and the whole value was rewritten each time - so a router left running accumulated a session token per login in router.config for ever, including ones that had expired and were never loadable again. This drops expired and unreadable entries, replaces an existing entry for the same token instead of adding a second, and caps the list so it cannot grow without bound.
Pure and static so a test can drive a hostile stored value without a router.
- Parameters:
existing- the stored value, or nulltoken- the session token being addedusername- who the session belongs toexpiresAt- when it expires in ms, or -1 for no expirynow- current time in ms- Returns:
- the value to store, entries joined with '|'
loadPersistedSessions
private void loadPersistedSessions()Loads the saved sessions from the config.generateCSRFToken
updatePreference