While setting up a headless Fedora Server, I encountered a common problem on institutional networks: Internet access required authentication through a web-based firewall portal.
The server had a valid network connection and DNS resolution, but HTTPS requests failed because the firewall intercepted unauthenticated traffic. Since the machine had no graphical browser, I needed to authenticate entirely from the command line.
Finding the Captive Portal
A useful trick is to request a deliberately HTTP-only website:
curl -v http://neverssl.com/
Instead of the expected page, the network returned JavaScript redirecting the browser to a FortiGate authentication URL:
window.location="http://192.168.31.4:1000/fgtauth?<TOKEN>";
The token is important because it identifies the current authentication session.
Opening that URL with curl revealed an HTML login form containing fields similar to:
<form action="/" method="post">
<input type="hidden" name="4Tredir" value="http://neverssl.com/">
<input type="hidden" name="magic" value="<TOKEN>">
<input name="username" type="text">
<input name="password" type="password">
</form>
This tells us exactly what a browser normally submits.
Authenticating with curl
Rather than putting credentials directly into shell history, they can be entered interactively:
read -p "Username: " USERNAME
read -s -p "Password: " PASSWORD
echo
Then submit the form:
curl -v \
--data-urlencode "username=$USERNAME" \
--data-urlencode "password=$PASSWORD" \
--data-urlencode "magic=<TOKEN>" \
--data-urlencode "4Tredir=http://neverssl.com/" \
"http://192.168.31.4:1000/"
In my case, successful authentication produced:
HTTP/1.1 303 See Other
Location: https://...
The 303 redirect was the important part: the firewall had accepted the credentials.
There is no need to follow the redirect with curl -L just to determine whether authentication succeeded. In fact, avoiding it makes troubleshooting cleaner because any TLS problem at the redirected website cannot be confused with a firewall authentication failure.
Verify Internet Access Separately
Finally, test a normal HTTPS connection:
curl -I https://google.com/
After authentication, the connection should proceed normally with a valid TLS handshake.
The overall process is therefore quite simple:
curl HTTP site
↓
Discover captive-portal redirect
↓
Extract session/magic token
↓
POST username + password + token with curl
↓
Receive HTTP 303
↓
Verify normal HTTPS access
This approach is particularly useful for headless Linux servers, Raspberry Pis, remote compute nodes, and other systems connected to networks that assume every device has a graphical web browser.
One important caveat: the magic token is session-specific and should not be hard-coded into a permanent script. A robust login script should first request an HTTP page, extract the current FortiGate token, and then perform the authentication POST. Credentials should likewise not be stored as plaintext in the script.
You can use the following bash script on my github page here.
As you may guess, AI helped me inspecting the login page and writing correct curl code to follow the page. But it was my idea to make curl follow the login page with my credentials (he was pointing me other directions interestingly, and I was not sure he would find this solution, hence it's still worth publishing on my page).
Yorumlar