upload/koji: add support for GSSAPI/Kerberos auth
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.
This commit is contained in:
parent
ecc7340570
commit
05fd221bd4
21 changed files with 1637 additions and 31 deletions
|
|
@ -4,14 +4,17 @@ import (
|
|||
"bytes"
|
||||
"crypto/md5"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"hash/adler32"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
|
||||
"github.com/kolo/xmlrpc"
|
||||
"github.com/ubccr/kerby/khttp"
|
||||
)
|
||||
|
||||
type Koji struct {
|
||||
|
|
@ -106,26 +109,17 @@ type CGImportResult struct {
|
|||
BuildID int `xmlrpc:"build_id"`
|
||||
}
|
||||
|
||||
func Login(server, user, password string, transport http.RoundTripper) (*Koji, error) {
|
||||
// Create a temporary xmlrpc client.
|
||||
// The API doesn't require sessionID, sessionKey and callnum yet,
|
||||
// so there's no need to use the custom Koji RoundTripper,
|
||||
// let's just use the one that the called passed in.
|
||||
loginClient, err := xmlrpc.NewClient(server, http.DefaultTransport)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
type GSSAPICredentials struct {
|
||||
Principal string
|
||||
KeyTab string
|
||||
}
|
||||
|
||||
args := []interface{}{user, password}
|
||||
var reply struct {
|
||||
SessionID int64 `xmlrpc:"session-id"`
|
||||
SessionKey string `xmlrpc:"session-key"`
|
||||
}
|
||||
err = loginClient.Call("login", args, &reply)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
type loginReply struct {
|
||||
SessionID int64 `xmlrpc:"session-id"`
|
||||
SessionKey string `xmlrpc:"session-key"`
|
||||
}
|
||||
|
||||
func newKoji(server string, transport http.RoundTripper, reply loginReply) (*Koji, error) {
|
||||
// Create the final xmlrpc client with our custom RoundTripper handling
|
||||
// sessionID, sessionKey and callnum
|
||||
kojiTransport := &Transport{
|
||||
|
|
@ -134,7 +128,6 @@ func Login(server, user, password string, transport http.RoundTripper) (*Koji, e
|
|||
callnum: 0,
|
||||
transport: transport,
|
||||
}
|
||||
|
||||
client, err := xmlrpc.NewClient(server, kojiTransport)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
@ -147,6 +140,55 @@ func Login(server, user, password string, transport http.RoundTripper) (*Koji, e
|
|||
}, nil
|
||||
}
|
||||
|
||||
// NewFromPlain creates a new Koji sessions =authenticated using the plain
|
||||
// username/password method. If you want to speak to a public koji instance,
|
||||
// you probably cannot use this method.
|
||||
func NewFromPlain(server, user, password string, transport http.RoundTripper) (*Koji, error) {
|
||||
// Create a temporary xmlrpc client.
|
||||
// The API doesn't require sessionID, sessionKey and callnum yet,
|
||||
// so there's no need to use the custom Koji RoundTripper,
|
||||
// let's just use the one that the called passed in.
|
||||
loginClient, err := xmlrpc.NewClient(server, http.DefaultTransport)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
args := []interface{}{user, password}
|
||||
var reply loginReply
|
||||
err = loginClient.Call("login", args, &reply)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return newKoji(server, transport, reply)
|
||||
}
|
||||
|
||||
// NewFromGSSAPI creates a new Koji session authenticated using GSSAPI.
|
||||
// Principal and keytab used for the session is passed using credentials
|
||||
// parameter.
|
||||
func NewFromGSSAPI(server string, credentials *GSSAPICredentials, transport http.RoundTripper) (*Koji, error) {
|
||||
// Create a temporary xmlrpc client with kerberos transport.
|
||||
// The API doesn't require sessionID, sessionKey and callnum yet,
|
||||
// so there's no need to use the custom Koji RoundTripper,
|
||||
// let's just use the one that the called passed in.
|
||||
loginClient, err := xmlrpc.NewClient(server+"/ssllogin", &khttp.Transport{
|
||||
KeyTab: credentials.KeyTab,
|
||||
Principal: credentials.Principal,
|
||||
Next: transport,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var reply loginReply
|
||||
err = loginClient.Call("sslLogin", nil, &reply)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return newKoji(server, transport, reply)
|
||||
}
|
||||
|
||||
// GetAPIVersion gets the version of the API of the remote Koji instance
|
||||
func (k *Koji) GetAPIVersion() (int, error) {
|
||||
var version int
|
||||
|
|
@ -309,3 +351,17 @@ func (rt *Transport) RoundTrip(req *http.Request) (*http.Response, error) {
|
|||
|
||||
return rt.transport.RoundTrip(rClone)
|
||||
}
|
||||
|
||||
func GSSAPICredentialsFromEnv() (*GSSAPICredentials, error) {
|
||||
principal, principalExists := os.LookupEnv("OSBUILD_COMPOSER_KOJI_PRINCIPAL")
|
||||
keyTab, keyTabExists := os.LookupEnv("OSBUILD_COMPOSER_KOJI_KEYTAB")
|
||||
|
||||
if !principalExists || !keyTabExists {
|
||||
return nil, errors.New("Both OSBUILD_COMPOSER_KOJI_PRINCIPAL and OSBUILD_COMPOSER_KOJI_KEYTAB must be set")
|
||||
}
|
||||
|
||||
return &GSSAPICredentials{
|
||||
Principal: principal,
|
||||
KeyTab: keyTab,
|
||||
}, nil
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue