mirror of
https://github.com/kemko/nomad.git
synced 2026-01-05 18:05:42 +03:00
The OIDC provider cache is used by the RPC handler as the OIDC implementation keeps long lived processes running. These process include connections to the remote OIDC provider. The Callback server is used by the CLI and starts when the login command is triggered. This callback server includes success HTML which is displayed when the user successfully logs into the remote OIDC provider.
245 lines
12 KiB
Go
245 lines
12 KiB
Go
package oidc
|
|
|
|
import (
|
|
"fmt"
|
|
"net"
|
|
"net/http"
|
|
|
|
"github.com/hashicorp/cap/oidc"
|
|
"github.com/hashicorp/nomad/api"
|
|
)
|
|
|
|
// CallbackServer is started with NewCallbackServer and creates an HTTP
|
|
// server for handling loopback OIDC auth redirects.
|
|
type CallbackServer struct {
|
|
ln net.Listener
|
|
url string
|
|
clientNonce string
|
|
errCh chan error
|
|
successCh chan *api.ACLOIDCCompleteAuthRequest
|
|
}
|
|
|
|
// NewCallbackServer creates and starts a new local HTTP server for
|
|
// OIDC authentication to redirect to. This is used to capture the
|
|
// necessary information to complete the authentication.
|
|
func NewCallbackServer(addr string) (*CallbackServer, error) {
|
|
// Generate our nonce
|
|
nonce, err := oidc.NewID()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
ln, err := net.Listen("tcp", addr)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// Initialize our callback server
|
|
srv := &CallbackServer{
|
|
url: fmt.Sprintf("http://%s/oidc/callback", ln.Addr().String()),
|
|
ln: ln,
|
|
clientNonce: nonce,
|
|
errCh: make(chan error, 5),
|
|
successCh: make(chan *api.ACLOIDCCompleteAuthRequest, 5),
|
|
}
|
|
|
|
// Register our HTTP route and start the server
|
|
mux := http.NewServeMux()
|
|
mux.Handle("/oidc/callback", srv)
|
|
go func() {
|
|
httpServer := &http.Server{Handler: mux}
|
|
if err := httpServer.Serve(ln); err != nil {
|
|
srv.errCh <- err
|
|
}
|
|
}()
|
|
|
|
return srv, nil
|
|
}
|
|
|
|
// Close cleans up and shuts down the server. On close, errors may be
|
|
// sent to ErrorCh and should be ignored.
|
|
func (s *CallbackServer) Close() error { return s.ln.Close() }
|
|
|
|
// RedirectURI is the redirect URI that should be provided for the auth.
|
|
func (s *CallbackServer) RedirectURI() string { return s.url }
|
|
|
|
// Nonce returns a generated nonce that can be used for the request.
|
|
func (s *CallbackServer) Nonce() string { return s.clientNonce }
|
|
|
|
// ErrorCh returns a channel where any errors are sent. Errors may be
|
|
// sent after Close and should be disregarded.
|
|
func (s *CallbackServer) ErrorCh() <-chan error { return s.errCh }
|
|
|
|
// SuccessCh returns a channel that gets sent a partially completed
|
|
// request to complete the OIDC auth with the Waypoint server.
|
|
func (s *CallbackServer) SuccessCh() <-chan *api.ACLOIDCCompleteAuthRequest { return s.successCh }
|
|
|
|
// ServeHTTP implements http.Handler and handles the callback request. This
|
|
// isn't usually used directly; use the server address instead.
|
|
func (s *CallbackServer) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
|
q := req.URL.Query()
|
|
|
|
// Build our result
|
|
result := &api.ACLOIDCCompleteAuthRequest{
|
|
State: q.Get("state"),
|
|
ClientNonce: s.clientNonce,
|
|
Code: q.Get("code"),
|
|
}
|
|
|
|
// Send our result. We don't block here because the channel should be
|
|
// buffered, and otherwise we're done.
|
|
select {
|
|
case s.successCh <- result:
|
|
default:
|
|
}
|
|
|
|
w.WriteHeader(http.StatusOK)
|
|
_, _ = w.Write([]byte(serverSuccessHTMLResponse))
|
|
}
|
|
|
|
// serverSuccessHTMLResponse is the HTML response the OIDC callback server uses
|
|
// when the user has successfully logged in via the OIDC provider.
|
|
const serverSuccessHTMLResponse = `
|
|
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
<title>OIDC Authentication Succeeded</title>
|
|
<style>
|
|
body {
|
|
font-size: 14px;
|
|
font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI",
|
|
"Roboto", "Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans",
|
|
"Helvetica Neue", sans-serif;
|
|
}
|
|
hr {
|
|
border-color: #fdfdfe;
|
|
margin: 24px 0;
|
|
}
|
|
.container {
|
|
display: flex;
|
|
justify-content: center;
|
|
align-items: center;
|
|
height: 70vh;
|
|
}
|
|
svg.logo {
|
|
display: block;
|
|
margin: 0 0 20px;
|
|
}
|
|
.message {
|
|
display: flex;
|
|
min-width: 40vw;
|
|
background: #f0f5ff;
|
|
border: 1px solid #1563ff;
|
|
margin-bottom: 12px;
|
|
padding: 12px 16px 16px 12px;
|
|
position: relative;
|
|
border-radius: 2px;
|
|
font-size: 14px;
|
|
color: #0a2d74;
|
|
}
|
|
.message-content {
|
|
margin-left: 4px;
|
|
}
|
|
.message #checkbox {
|
|
fill: #1563ff;
|
|
}
|
|
.message .message-title {
|
|
font-size: 16px;
|
|
font-weight: 700;
|
|
line-height: 1.25;
|
|
}
|
|
.message .message-body {
|
|
border: 0;
|
|
margin-top: 4px;
|
|
}
|
|
.message p {
|
|
font-size: 12px;
|
|
margin: 0;
|
|
padding: 0;
|
|
}
|
|
a {
|
|
display: block;
|
|
margin: 8px 0;
|
|
color: #1563ff;
|
|
text-decoration: none;
|
|
font-weight: 600;
|
|
}
|
|
a:hover {
|
|
color: black;
|
|
}
|
|
a svg {
|
|
fill: currentcolor;
|
|
}
|
|
.icon {
|
|
align-items: center;
|
|
display: inline-flex;
|
|
justify-content: center;
|
|
height: 21px;
|
|
width: 21px;
|
|
vertical-align: middle;
|
|
}
|
|
h1 {
|
|
font-size: 17.5px;
|
|
font-weight: 700;
|
|
margin-bottom: 0;
|
|
}
|
|
h1 + p {
|
|
margin: 8px 0 16px 0;
|
|
}
|
|
</style>
|
|
</head>
|
|
<body translate="no" >
|
|
<div class="container">
|
|
<div>
|
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="20 0 342 107" id="LOGOS" height="50">
|
|
<defs><style>.cls-1{fill:#00ca8e;}</style></defs>
|
|
<path d="M125,61.42V92h-7.44V51.68h10.16L143,82.29V51.68h7.44V92H140.28Z"></path>
|
|
<path d="M168.58,92.58c-10.1,0-12.82-5.57-12.82-11.62V73.52c0-6,2.72-11.61,12.82-11.61s12.83,5.56,12.83,11.61V81C181.41,87,178.69,92.58,168.58,92.58Zm0-24.38c-3.93,0-5.44,1.75-5.44,5.08V81.2c0,3.33,1.51,5.09,5.44,5.09S174,84.53,174,81.2V73.28C174,70,172.52,68.2,168.58,68.2Z"></path>
|
|
<path d="M203.85,92V71.4c0-1.57-.67-2.36-2.36-2.36s-5,1.09-7.68,2.48V92h-7.38V62.51h5.62l.73,2.48a29.59,29.59,0,0,1,11.8-3.08c2.84,0,4.59,1.15,5.56,3.14A29,29,0,0,1,222,61.91c4.9,0,6.65,3.44,6.65,8.71V92h-7.38V71.4c0-1.57-.66-2.36-2.36-2.36a19.55,19.55,0,0,0-7.68,2.48V92Z"></path>
|
|
<path d="M256.54,92h-6.05l-.55-2a16.15,16.15,0,0,1-8.77,2.6c-5.38,0-7.68-3.69-7.68-8.77,0-6,2.6-8.29,8.59-8.29h7.08V72.43c0-3.26-.91-4.41-5.63-4.41a41.55,41.55,0,0,0-8.17.9l-.9-5.62a38.26,38.26,0,0,1,10.1-1.39c9.25,0,12,3.26,12,10.64Zm-7.38-11.13h-5.45c-2.42,0-3.08.67-3.08,2.91,0,2,.66,3,3,3a11.69,11.69,0,0,0,5.57-1.51Z"></path>
|
|
<path d="M261.07,72.31c0-6.53,2.9-10.4,9.74-10.4a41.09,41.09,0,0,1,7.87.84V50.47l7.38-1V92h-5.87l-.73-2.48a15.46,15.46,0,0,1-9.31,3.09c-5.93,0-9.08-3.51-9.08-10.23ZM278.68,69a33.06,33.06,0,0,0-6.54-.78c-2.66,0-3.69,1.27-3.69,3.93V82.54c0,2.42.91,3.75,3.63,3.75a10.43,10.43,0,0,0,6.6-2.67Z"></path>
|
|
<path class="cls-1" d="M61.14,38.19,32,55V88.63l29.12,16.81L90.26,88.63V55Zm13,37-7.76,4.48L57,74.54V85.26l-8.81,5.59V68.45l7-4.28,9.7,5.11V58.34l9.25-5.56Z"></path>
|
|
<path d="M125.42,41.81V36.15h-5.17v5.66h-2.64V28.22h2.64v5.7h5.17v-5.7h2.65V41.81Zm12.33,0h-2.1l-.19-.67a5.69,5.69,0,0,1-3,.87c-1.86,0-2.66-1.23-2.66-2.92,0-2,.9-2.76,3-2.76h2.45v-1c0-1.09-.31-1.47-1.95-1.47a14.94,14.94,0,0,0-2.83.3l-.31-1.87a13.9,13.9,0,0,1,3.5-.46c3.2,0,4.15,1.08,4.15,3.54Zm-2.56-3.7H133.3c-.83,0-1.06.22-1.06,1s.23,1,1,1a4,4,0,0,0,1.93-.51ZM143.1,42a12.59,12.59,0,0,1-3.52-.56l.36-1.88a11.5,11.5,0,0,0,3,.43c1.13,0,1.3-.24,1.3-1s-.13-.9-1.78-1.29c-2.5-.58-2.79-1.18-2.79-3.08s.9-2.83,3.81-2.83a14,14,0,0,1,3.06.34l-.25,2a18.71,18.71,0,0,0-2.81-.28c-1.11,0-1.3.24-1.3.84,0,.79.07.85,1.45,1.19,2.85.73,3.12,1.09,3.12,3.1S146.18,42,143.1,42Zm11.71-.2V35c0-.53-.23-.79-.82-.79a7.12,7.12,0,0,0-2.66.83v6.8h-2.56V28l2.56.38v4.34a9.34,9.34,0,0,1,3.73-.94c1.7,0,2.31,1.14,2.31,2.89v7.11Zm4.7-11.19v-2.4h2.56v2.4Zm0,11.19V32h2.56v9.8Zm4.6-9.72c0-2.46,1.49-3.88,5-3.88a16.47,16.47,0,0,1,3.79.44l-.29,2.19a21.49,21.49,0,0,0-3.42-.34c-1.82,0-2.41.6-2.41,2v5.15c0,1.43.59,2,2.41,2a21.57,21.57,0,0,0,3.42-.35l.29,2.2a16.47,16.47,0,0,1-3.79.44c-3.48,0-5-1.43-5-3.88ZM178.52,42c-3.5,0-4.44-1.85-4.44-3.86V35.67c0-2,.94-3.86,4.44-3.86S183,33.66,183,35.67v2.48C183,40.16,182,42,178.52,42Zm0-8.11c-1.36,0-1.89.58-1.89,1.69v2.64c0,1.1.53,1.69,1.89,1.69s1.89-.59,1.89-1.69V35.59C180.41,34.48,179.88,33.9,178.52,33.9Zm11.64.16a20.53,20.53,0,0,0-2.7,1.43v6.32H184.9V32h2.16l.17,1.08a11.54,11.54,0,0,1,2.68-1.28Zm10.22,4.49c0,2.17-1,3.46-3.37,3.46a14.85,14.85,0,0,1-2.73-.28v4l-2.55.38V32h2l.25.82a5.54,5.54,0,0,1,3.23-1c2,0,3.14,1.16,3.14,3.4Zm-6.1,1.11a12,12,0,0,0,2.27.26c.92,0,1.28-.43,1.28-1.31V35.15c0-.81-.32-1.25-1.26-1.25a3.68,3.68,0,0,0-2.29.89Z"></path>
|
|
</svg>
|
|
<div class="message">
|
|
<svg id="checkbox" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 16 16" fill="currentColor">
|
|
<path fill-rule="evenodd" d="M8 16A8 8 0 108 0a8 8 0 000 16zm.93-9.412l-2.29.287-.082.38.45.083c.294.07.352.176.288.469l-.738 3.468c-.194.897.105 1.319.808 1.319.545 0 1.178-.252 1.465-.598l.088-.416c-.2.176-.492.246-.686.246-.275 0-.375-.193-.304-.533L8.93 6.588zM8 5.5a1 1 0 100-2 1 1 0 000 2z" clip-rule="evenodd"/>
|
|
</svg>
|
|
<div class="message-content">
|
|
<div class="message-title">
|
|
Signed in via your OIDC provider
|
|
</div>
|
|
<p class="message-body">
|
|
You can now close this window and start using Nomad.
|
|
</p>
|
|
</div>
|
|
</div>
|
|
<hr />
|
|
<h1>Not sure how to get started?</h1>
|
|
<p class="learn">
|
|
Check out beginner and advanced guides on HashiCorp Nomad at the HashiCorp Learn site or read more in the official documentation.
|
|
</p>
|
|
<a href="https://developer.hashicorp.com/nomad/tutorials" rel="noreferrer noopener">
|
|
<span class="icon">
|
|
<svg width="16" height="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg">
|
|
<path d="M8.338 2.255a.79.79 0 0 0-.645 0L.657 5.378c-.363.162-.534.538-.534.875 0 .337.171.713.534.875l1.436.637c-.332.495-.638 1.18-.744 2.106a.887.887 0 0 0-.26 1.559c.02.081.03.215.013.392-.02.205-.074.43-.162.636-.186.431-.45.64-.741.64v.98c.651 0 1.108-.365 1.403-.797l.06.073c.32.372.826.763 1.455.763v-.98c-.215 0-.474-.145-.71-.42-.111-.13-.2-.27-.259-.393a1.014 1.014 0 0 1-.06-.155c-.01-.036-.013-.055-.013-.058h-.022a2.544 2.544 0 0 0 .031-.641.886.886 0 0 0-.006-1.51c.1-.868.398-1.477.699-1.891l.332.147-.023.746v2.228c0 .115.04.22.105.304.124.276.343.5.587.677.297.217.675.396 1.097.54.846.288 1.943.456 3.127.456 1.185 0 2.281-.168 3.128-.456.422-.144.8-.323 1.097-.54.244-.177.462-.401.586-.677a.488.488 0 0 0 .106-.304V8.218l2.455-1.09c.363-.162.534-.538.534-.875 0-.337-.17-.713-.534-.875L8.338 2.255zm-.34 2.955L3.64 7.38l4.375 1.942 6.912-3.069-6.912-3.07-6.912 3.07 1.665.74 4.901-2.44.328.657zM14.307 1H12.5a.5.5 0 1 1 0-1h3a.499.499 0 0 1 .5.65V3.5a.5.5 0 1 1-1 0V1.72l-1.793 1.774a.5.5 0 0 1-.713-.701L14.307 1zm-2.368 7.653v2.383a.436.436 0 0 0-.007.021c-.017.063-.084.178-.282.322-.193.14-.473.28-.836.404-.724.247-1.71.404-2.812.404-1.1 0-2.087-.157-2.811-.404a3.188 3.188 0 0 1-.836-.404c-.198-.144-.265-.26-.282-.322a.437.437 0 0 0-.007-.02V8.983l.01-.338 3.617 1.605a.791.791 0 0 0 .645 0l3.6-1.598z" fill-rule="evenodd"></path>
|
|
</svg>
|
|
</span>
|
|
Get started with Nomad
|
|
</a>
|
|
<a href="https://developer.hashicorp.com/nomad/docs" rel="noreferrer noopener">
|
|
<span class="icon">
|
|
<svg width="16" height="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg">
|
|
<path d="M13.307 1H11.5a.5.5 0 1 1 0-1h3a.499.499 0 0 1 .5.65V3.5a.5.5 0 1 1-1 0V1.72l-1.793 1.774a.5.5 0 0 1-.713-.701L13.307 1zM12 14V8a.5.5 0 1 1 1 0v6.5a.5.5 0 0 1-.5.5H.563a.5.5 0 0 1-.5-.5v-13a.5.5 0 0 1 .5-.5H8a.5.5 0 0 1 0 1H1v12h11zM4 6a.5.5 0 0 1 0-1h3a.5.5 0 0 1 0 1H4zm0 2.5a.5.5 0 0 1 0-1h5a.5.5 0 0 1 0 1H4zM4 11a.5.5 0 1 1 0-1h5a.5.5 0 1 1 0 1H4z"/>
|
|
</svg>
|
|
</span>
|
|
View the official Nomad documentation
|
|
</a>
|
|
</div>
|
|
</div>
|
|
</body>
|
|
</html>
|
|
`
|