Prior this commit we only had support for username/password authentication in the koji integration. This wasn't particularly useful because this auth type isn't used in any production instance. This commit adds the support for GSSAPI/Kerberos authentication. The implementation uses kerby library which is very lightweight wrapper around C gssapi library. Also, the koji unit test and the run-koji-container script were modified so the GSSAPI auth is fully tested.
97 lines
2.5 KiB
Go
97 lines
2.5 KiB
Go
// Copyright 2015 Andrew E. Bruno
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
// Package khttp is a transport that authenticates all outgoing requests using
|
|
// SPNEGO (negotiate authentication) http://tools.ietf.org/html/rfc4559.
|
|
package khttp
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"net"
|
|
"net/http"
|
|
"os"
|
|
"strings"
|
|
|
|
"github.com/ubccr/kerby"
|
|
)
|
|
|
|
var (
|
|
negotiateHeader = "Negotiate"
|
|
wwwAuthenticateHeader = "WWW-Authenticate"
|
|
authorizationHeader = "Authorization"
|
|
)
|
|
|
|
// HTTP client transport that authenticates all outgoing
|
|
// requests using SPNEGO. Implements the http.RoundTripper interface
|
|
type Transport struct {
|
|
// keytab file to use
|
|
KeyTab string
|
|
// principal
|
|
Principal string
|
|
// Next specifies the next transport to be used or http.DefaultTransport if nil.
|
|
Next http.RoundTripper
|
|
}
|
|
|
|
// RoundTrip executes a single HTTP transaction performing SPNEGO negotiate
|
|
// authentication.
|
|
func (t *Transport) RoundTrip(req *http.Request) (*http.Response, error) {
|
|
if len(t.KeyTab) > 0 {
|
|
os.Setenv("KRB5_CLIENT_KTNAME", t.KeyTab)
|
|
}
|
|
host, _, err := net.SplitHostPort(req.URL.Host)
|
|
if err != nil {
|
|
host = req.URL.Host
|
|
}
|
|
service := fmt.Sprintf("HTTP@%s", host)
|
|
kc := new(kerby.KerbClient)
|
|
err = kc.Init(service, t.Principal)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer kc.Clean()
|
|
|
|
err = kc.Step("")
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
req.Header.Set(authorizationHeader, negotiateHeader+" "+kc.Response())
|
|
|
|
tr := t.Next
|
|
if tr == nil {
|
|
tr = http.DefaultTransport
|
|
if tr == nil {
|
|
return nil, errors.New("khttp: no Next transport or DefaultTransport")
|
|
}
|
|
}
|
|
|
|
resp, err := tr.RoundTrip(req)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
authReply := strings.Split(resp.Header.Get(wwwAuthenticateHeader), " ")
|
|
if len(authReply) != 2 || strings.ToLower(authReply[0]) != strings.ToLower(negotiateHeader) {
|
|
return nil, errors.New("khttp: server replied with invalid www-authenticate header")
|
|
}
|
|
|
|
// Authenticate the reply from the server
|
|
err = kc.Step(authReply[1])
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return resp, nil
|
|
}
|