koji: add config files to configure kerberos settings

Kerberos keytabs and principals are configured per koji server both in
composer and in the worker.

Signed-off-by: Tom Gundersen <teg@jklm.no>
This commit is contained in:
Tom Gundersen 2020-09-14 01:58:55 +01:00
parent 9666be2891
commit c6cf9de85d
6 changed files with 129 additions and 17 deletions

View file

@ -9,11 +9,13 @@ import (
"os"
"path"
"github.com/BurntSushi/toml"
"github.com/osbuild/osbuild-composer/internal/distro/fedora31"
"github.com/osbuild/osbuild-composer/internal/distro/fedora32"
"github.com/osbuild/osbuild-composer/internal/distro/rhel8"
"github.com/osbuild/osbuild-composer/internal/jobqueue/fsjobqueue"
"github.com/osbuild/osbuild-composer/internal/kojiapi"
"github.com/osbuild/osbuild-composer/internal/upload/koji"
"github.com/osbuild/osbuild-composer/internal/common"
"github.com/osbuild/osbuild-composer/internal/distro"
@ -25,6 +27,8 @@ import (
"github.com/coreos/go-systemd/activation"
)
const configFile = "/etc/osbuild-composer/osbuild-composer.toml"
type connectionConfig struct {
CACertFile string
ServerKeyFile string
@ -55,10 +59,30 @@ func createTLSConfig(c *connectionConfig) (*tls.Config, error) {
}
func main() {
var config struct {
KojiServers map[string]struct {
Kerberos *struct {
Principal string `toml:"principal"`
KeyTab string `toml:"keytab"`
} `toml:"kerberos,omitempty"`
} `toml:"koji"`
}
var verbose bool
flag.BoolVar(&verbose, "v", false, "Print access log")
flag.Parse()
_, err := toml.DecodeFile(configFile, &config)
if err == nil {
log.Println("Composer configuration:")
encoder := toml.NewEncoder(log.Writer())
err := encoder.Encode(&config)
if err != nil {
log.Fatalf("Could not print config: %v", err)
}
} else if !os.IsNotExist(err) {
log.Fatalf("Could not load config file '%s': %v", configFile, err)
}
stateDir, ok := os.LookupEnv("STATE_DIRECTORY")
if !ok {
log.Fatal("STATE_DIRECTORY is not set. Is the service file missing StateDirectory=?")
@ -151,7 +175,19 @@ func main() {
// Optionally run Koji API
if kojiListeners, exists := listeners["osbuild-composer-koji.socket"]; exists {
kojiServer := kojiapi.NewServer(workers, rpm, distros)
kojiServers := make(map[string]koji.GSSAPICredentials)
for server, creds := range config.KojiServers {
if creds.Kerberos == nil {
// For now we only support Kerberos authentication.
continue
}
kojiServers[server] = koji.GSSAPICredentials{
Principal: creds.Kerberos.Principal,
KeyTab: creds.Kerberos.KeyTab,
}
}
kojiServer := kojiapi.NewServer(workers, rpm, distros, kojiServers)
tlsConfig, err := createTLSConfig(&connectionConfig{
CACertFile: "/etc/osbuild-composer/ca-crt.pem",

View file

@ -10,10 +10,12 @@ import (
"io/ioutil"
"log"
"net/http"
"net/url"
"os"
"path"
"time"
"github.com/BurntSushi/toml"
"github.com/google/uuid"
"github.com/osbuild/osbuild-composer/internal/common"
@ -26,6 +28,8 @@ import (
"github.com/osbuild/osbuild-composer/internal/worker"
)
const configFile = "/etc/osbuild-worker/osbuild-worker.toml"
type connectionConfig struct {
CACertFile string
ClientKeyFile string
@ -92,7 +96,7 @@ func osbuildStagesToRPMs(stages []osbuild.StageResult) []koji.RPM {
return rpms
}
func RunJob(job worker.Job, store string) (*osbuild.Result, error) {
func RunJob(job worker.Job, store string, kojiServers map[string]koji.GSSAPICredentials) (*osbuild.Result, error) {
outputDirectory, err := ioutil.TempDir("/var/tmp", "osbuild-worker-*")
if err != nil {
return nil, fmt.Errorf("error creating temporary output directory: %v", err)
@ -201,7 +205,13 @@ func RunJob(job worker.Job, store string) (*osbuild.Result, error) {
Renegotiation: tls.RenegotiateOnceAsClient,
}
k, err := koji.NewFromGSSAPI(options.Server, &koji.GSSAPICredentials{}, transport)
kojiServer, _ := url.Parse(options.Server)
creds, exists := kojiServers[kojiServer.Hostname()]
if !exists {
r = append(r, fmt.Errorf("Koji server has not been configured: %s", kojiServer.Hostname()))
}
k, err := koji.NewFromGSSAPI(options.Server, &creds, transport)
if err != nil {
r = append(r, err)
continue
@ -294,7 +304,7 @@ func RunJob(job worker.Job, store string) (*osbuild.Result, error) {
return result, nil
}
func FailJob(job worker.Job) {
func FailJob(job worker.Job, kojiServers map[string]koji.GSSAPICredentials) {
_, targets, err := job.OSBuildArgs()
if err != nil {
panic(err)
@ -310,7 +320,14 @@ func FailJob(job worker.Job) {
Renegotiation: tls.RenegotiateOnceAsClient,
}
k, err := koji.NewFromGSSAPI(options.Server, &koji.GSSAPICredentials{}, transport)
kojiServer, _ := url.Parse(options.Server)
creds, exists := kojiServers[kojiServer.Hostname()]
if !exists {
log.Printf("Koji server has not been configured: %s", kojiServer.Hostname())
return
}
k, err := koji.NewFromGSSAPI(options.Server, &creds, transport)
if err != nil {
log.Printf("koji login failed: %v", err)
return
@ -357,6 +374,14 @@ func WatchJob(ctx context.Context, job worker.Job) {
}
func main() {
var config struct {
KojiServers map[string]struct {
Kerberos *struct {
Principal string `toml:"principal"`
KeyTab string `toml:"keytab"`
} `toml:"kerberos,omitempty"`
} `toml:"koji"`
}
var unix bool
flag.BoolVar(&unix, "unix", false, "Interpret 'address' as a path to a unix domain socket instead of a network address")
@ -373,12 +398,36 @@ func main() {
flag.Usage()
}
_, err := toml.DecodeFile(configFile, &config)
if err == nil {
log.Println("Composer configuration:")
encoder := toml.NewEncoder(log.Writer())
err := encoder.Encode(&config)
if err != nil {
log.Fatalf("Could not print config: %v", err)
}
} else if !os.IsNotExist(err) {
log.Fatalf("Could not load config file '%s': %v", configFile, err)
}
cacheDirectory, ok := os.LookupEnv("CACHE_DIRECTORY")
if !ok {
log.Fatal("CACHE_DIRECTORY is not set. Is the service file missing CacheDirectory=?")
}
store := path.Join(cacheDirectory, "osbuild-store")
kojiServers := make(map[string]koji.GSSAPICredentials)
for server, creds := range config.KojiServers {
if creds.Kerberos == nil {
// For now we only support Kerberos authentication.
continue
}
kojiServers[server] = koji.GSSAPICredentials{
Principal: creds.Kerberos.Principal,
KeyTab: creds.Kerberos.KeyTab,
}
}
var client *worker.Client
if unix {
client = worker.NewClientUnix(address)
@ -411,13 +460,13 @@ func main() {
go WatchJob(ctx, job)
var status common.ImageBuildState
result, err := RunJob(job, store)
result, err := RunJob(job, store, kojiServers)
if err != nil {
log.Printf(" Job failed: %v", err)
status = common.IBFailed
// Fail the jobs in any targets that expects it
FailJob(job)
FailJob(job, kojiServers)
// If the error comes from osbuild, retrieve the result
if osbuildError, ok := err.(*OSBuildError); ok {

View file

@ -10,6 +10,7 @@ import (
"log"
"net"
"net/http"
"net/url"
"github.com/google/uuid"
"github.com/osbuild/osbuild-composer/internal/blueprint"
@ -26,14 +27,16 @@ type Server struct {
workers *worker.Server
rpmMetadata rpmmd.RPMMD
distros *distro.Registry
kojiServers map[string]koji.GSSAPICredentials
}
// NewServer creates a new koji server
func NewServer(workers *worker.Server, rpmMetadata rpmmd.RPMMD, distros *distro.Registry) *Server {
func NewServer(workers *worker.Server, rpmMetadata rpmmd.RPMMD, distros *distro.Registry, kojiServers map[string]koji.GSSAPICredentials) *Server {
server := &Server{
workers: workers,
rpmMetadata: rpmMetadata,
distros: distros,
kojiServers: kojiServers,
}
return server
}
@ -71,6 +74,18 @@ func (server *Server) PostCompose(w http.ResponseWriter, r *http.Request) {
return
}
kojiServer, err := url.Parse(request.Koji.Server)
if err != nil {
http.Error(w, fmt.Sprintf("Invalid Koji server: %s", request.Koji.Server), http.StatusBadRequest)
return
}
creds, exists := server.kojiServers[kojiServer.Hostname()]
if !exists {
http.Error(w, fmt.Sprintf("Koji server has not been configured: %s", kojiServer.Hostname()), http.StatusBadRequest)
return
}
type imageRequest struct {
manifest distro.Manifest
filename string
@ -137,7 +152,7 @@ func (server *Server) PostCompose(w http.ResponseWriter, r *http.Request) {
Renegotiation: tls.RenegotiateOnceAsClient,
}
k, err := koji.NewFromGSSAPI(request.Koji.Server, &koji.GSSAPICredentials{}, transport)
k, err := koji.NewFromGSSAPI(request.Koji.Server, &creds, transport)
if err != nil {
http.Error(w, fmt.Sprintf("Could not log into Koji: %v", err), http.StatusBadRequest)
return

View file

@ -36,31 +36,37 @@ fi
greenprint "Starting containers"
sudo ./internal/upload/koji/run-koji-container.sh start
greenprint "Copying custom composer/worker config"
sudo mkdir -p /etc/osbuild-composer
sudo cp test/image-tests/osbuild-composer.toml \
/etc/osbuild-composer/
sudo mkdir -p /etc/osbuild-worker
sudo cp test/image-tests/osbuild-worker.toml \
/etc/osbuild-worker/
greenprint "Adding kerberos config"
sudo cp \
/tmp/osbuild-composer-koji-test/client.keytab \
/etc/krb5.keytab
/etc/osbuild-composer/client.keytab
sudo cp \
/tmp/osbuild-composer-koji-test/client.keytab \
/etc/osbuild-worker/client.keytab
sudo cp \
test/image-tests/krb5-local.conf \
/etc/krb5.conf.d/local
greenprint "Initializing Kerberos"
kinit osbuild-krb@LOCAL -k
sudo -u _osbuild-composer kinit osbuild-krb@LOCAL -k
greenprint "Adding generated CA cert for Koji"
sudo cp \
/tmp/osbuild-composer-koji-test/ca-crt.pem \
/etc/pki/ca-trust/source/anchors/koji-ca-crt.pem
sudo update-ca-trust
greenprint "Restarting composer to pick up new certs"
greenprint "Restarting composer to pick up new config"
sudo systemctl restart osbuild-composer
sudo systemctl restart osbuild-worker\@1
greenprint "Testing Koji"
koji --server=http://localhost/kojihub --user=osbuild --password=osbuildpass --authtype=password hello
koji --server=http://localhost/kojihub hello
sudo -u _osbuild-composer koji --server=http://localhost/kojihub hello
greenprint "Creating Koji task"
koji --server=http://localhost/kojihub --user kojiadmin --password kojipass --authtype=password make-task image

View file

@ -0,0 +1,3 @@
[koji.localhost.kerberos]
principal = "osbuild-krb@LOCAL"
keytab = "/etc/osbuild-composer/client.keytab"

View file

@ -0,0 +1,3 @@
[koji.localhost.kerberos]
principal = "osbuild-krb@LOCAL"
keytab = "/etc/osbuild-worker/client.keytab"