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:
parent
9666be2891
commit
c6cf9de85d
6 changed files with 129 additions and 17 deletions
|
|
@ -9,11 +9,13 @@ import (
|
||||||
"os"
|
"os"
|
||||||
"path"
|
"path"
|
||||||
|
|
||||||
|
"github.com/BurntSushi/toml"
|
||||||
"github.com/osbuild/osbuild-composer/internal/distro/fedora31"
|
"github.com/osbuild/osbuild-composer/internal/distro/fedora31"
|
||||||
"github.com/osbuild/osbuild-composer/internal/distro/fedora32"
|
"github.com/osbuild/osbuild-composer/internal/distro/fedora32"
|
||||||
"github.com/osbuild/osbuild-composer/internal/distro/rhel8"
|
"github.com/osbuild/osbuild-composer/internal/distro/rhel8"
|
||||||
"github.com/osbuild/osbuild-composer/internal/jobqueue/fsjobqueue"
|
"github.com/osbuild/osbuild-composer/internal/jobqueue/fsjobqueue"
|
||||||
"github.com/osbuild/osbuild-composer/internal/kojiapi"
|
"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/common"
|
||||||
"github.com/osbuild/osbuild-composer/internal/distro"
|
"github.com/osbuild/osbuild-composer/internal/distro"
|
||||||
|
|
@ -25,6 +27,8 @@ import (
|
||||||
"github.com/coreos/go-systemd/activation"
|
"github.com/coreos/go-systemd/activation"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const configFile = "/etc/osbuild-composer/osbuild-composer.toml"
|
||||||
|
|
||||||
type connectionConfig struct {
|
type connectionConfig struct {
|
||||||
CACertFile string
|
CACertFile string
|
||||||
ServerKeyFile string
|
ServerKeyFile string
|
||||||
|
|
@ -55,10 +59,30 @@ func createTLSConfig(c *connectionConfig) (*tls.Config, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
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
|
var verbose bool
|
||||||
flag.BoolVar(&verbose, "v", false, "Print access log")
|
flag.BoolVar(&verbose, "v", false, "Print access log")
|
||||||
flag.Parse()
|
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")
|
stateDir, ok := os.LookupEnv("STATE_DIRECTORY")
|
||||||
if !ok {
|
if !ok {
|
||||||
log.Fatal("STATE_DIRECTORY is not set. Is the service file missing StateDirectory=?")
|
log.Fatal("STATE_DIRECTORY is not set. Is the service file missing StateDirectory=?")
|
||||||
|
|
@ -151,7 +175,19 @@ func main() {
|
||||||
|
|
||||||
// Optionally run Koji API
|
// Optionally run Koji API
|
||||||
if kojiListeners, exists := listeners["osbuild-composer-koji.socket"]; exists {
|
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{
|
tlsConfig, err := createTLSConfig(&connectionConfig{
|
||||||
CACertFile: "/etc/osbuild-composer/ca-crt.pem",
|
CACertFile: "/etc/osbuild-composer/ca-crt.pem",
|
||||||
|
|
|
||||||
|
|
@ -10,10 +10,12 @@ import (
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
"path"
|
"path"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/BurntSushi/toml"
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
|
|
||||||
"github.com/osbuild/osbuild-composer/internal/common"
|
"github.com/osbuild/osbuild-composer/internal/common"
|
||||||
|
|
@ -26,6 +28,8 @@ import (
|
||||||
"github.com/osbuild/osbuild-composer/internal/worker"
|
"github.com/osbuild/osbuild-composer/internal/worker"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const configFile = "/etc/osbuild-worker/osbuild-worker.toml"
|
||||||
|
|
||||||
type connectionConfig struct {
|
type connectionConfig struct {
|
||||||
CACertFile string
|
CACertFile string
|
||||||
ClientKeyFile string
|
ClientKeyFile string
|
||||||
|
|
@ -92,7 +96,7 @@ func osbuildStagesToRPMs(stages []osbuild.StageResult) []koji.RPM {
|
||||||
return rpms
|
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-*")
|
outputDirectory, err := ioutil.TempDir("/var/tmp", "osbuild-worker-*")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("error creating temporary output directory: %v", err)
|
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,
|
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 {
|
if err != nil {
|
||||||
r = append(r, err)
|
r = append(r, err)
|
||||||
continue
|
continue
|
||||||
|
|
@ -294,7 +304,7 @@ func RunJob(job worker.Job, store string) (*osbuild.Result, error) {
|
||||||
return result, nil
|
return result, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func FailJob(job worker.Job) {
|
func FailJob(job worker.Job, kojiServers map[string]koji.GSSAPICredentials) {
|
||||||
_, targets, err := job.OSBuildArgs()
|
_, targets, err := job.OSBuildArgs()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
|
|
@ -310,7 +320,14 @@ func FailJob(job worker.Job) {
|
||||||
Renegotiation: tls.RenegotiateOnceAsClient,
|
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 {
|
if err != nil {
|
||||||
log.Printf("koji login failed: %v", err)
|
log.Printf("koji login failed: %v", err)
|
||||||
return
|
return
|
||||||
|
|
@ -357,6 +374,14 @@ func WatchJob(ctx context.Context, job worker.Job) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
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
|
var unix bool
|
||||||
flag.BoolVar(&unix, "unix", false, "Interpret 'address' as a path to a unix domain socket instead of a network address")
|
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()
|
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")
|
cacheDirectory, ok := os.LookupEnv("CACHE_DIRECTORY")
|
||||||
if !ok {
|
if !ok {
|
||||||
log.Fatal("CACHE_DIRECTORY is not set. Is the service file missing CacheDirectory=?")
|
log.Fatal("CACHE_DIRECTORY is not set. Is the service file missing CacheDirectory=?")
|
||||||
}
|
}
|
||||||
store := path.Join(cacheDirectory, "osbuild-store")
|
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
|
var client *worker.Client
|
||||||
if unix {
|
if unix {
|
||||||
client = worker.NewClientUnix(address)
|
client = worker.NewClientUnix(address)
|
||||||
|
|
@ -411,13 +460,13 @@ func main() {
|
||||||
go WatchJob(ctx, job)
|
go WatchJob(ctx, job)
|
||||||
|
|
||||||
var status common.ImageBuildState
|
var status common.ImageBuildState
|
||||||
result, err := RunJob(job, store)
|
result, err := RunJob(job, store, kojiServers)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf(" Job failed: %v", err)
|
log.Printf(" Job failed: %v", err)
|
||||||
status = common.IBFailed
|
status = common.IBFailed
|
||||||
|
|
||||||
// Fail the jobs in any targets that expects it
|
// Fail the jobs in any targets that expects it
|
||||||
FailJob(job)
|
FailJob(job, kojiServers)
|
||||||
|
|
||||||
// If the error comes from osbuild, retrieve the result
|
// If the error comes from osbuild, retrieve the result
|
||||||
if osbuildError, ok := err.(*OSBuildError); ok {
|
if osbuildError, ok := err.(*OSBuildError); ok {
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@ import (
|
||||||
"log"
|
"log"
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
"github.com/osbuild/osbuild-composer/internal/blueprint"
|
"github.com/osbuild/osbuild-composer/internal/blueprint"
|
||||||
|
|
@ -26,14 +27,16 @@ type Server struct {
|
||||||
workers *worker.Server
|
workers *worker.Server
|
||||||
rpmMetadata rpmmd.RPMMD
|
rpmMetadata rpmmd.RPMMD
|
||||||
distros *distro.Registry
|
distros *distro.Registry
|
||||||
|
kojiServers map[string]koji.GSSAPICredentials
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewServer creates a new koji server
|
// 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{
|
server := &Server{
|
||||||
workers: workers,
|
workers: workers,
|
||||||
rpmMetadata: rpmMetadata,
|
rpmMetadata: rpmMetadata,
|
||||||
distros: distros,
|
distros: distros,
|
||||||
|
kojiServers: kojiServers,
|
||||||
}
|
}
|
||||||
return server
|
return server
|
||||||
}
|
}
|
||||||
|
|
@ -71,6 +74,18 @@ func (server *Server) PostCompose(w http.ResponseWriter, r *http.Request) {
|
||||||
return
|
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 {
|
type imageRequest struct {
|
||||||
manifest distro.Manifest
|
manifest distro.Manifest
|
||||||
filename string
|
filename string
|
||||||
|
|
@ -137,7 +152,7 @@ func (server *Server) PostCompose(w http.ResponseWriter, r *http.Request) {
|
||||||
Renegotiation: tls.RenegotiateOnceAsClient,
|
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 {
|
if err != nil {
|
||||||
http.Error(w, fmt.Sprintf("Could not log into Koji: %v", err), http.StatusBadRequest)
|
http.Error(w, fmt.Sprintf("Could not log into Koji: %v", err), http.StatusBadRequest)
|
||||||
return
|
return
|
||||||
|
|
|
||||||
|
|
@ -36,31 +36,37 @@ fi
|
||||||
greenprint "Starting containers"
|
greenprint "Starting containers"
|
||||||
sudo ./internal/upload/koji/run-koji-container.sh start
|
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"
|
greenprint "Adding kerberos config"
|
||||||
sudo cp \
|
sudo cp \
|
||||||
/tmp/osbuild-composer-koji-test/client.keytab \
|
/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 \
|
sudo cp \
|
||||||
test/image-tests/krb5-local.conf \
|
test/image-tests/krb5-local.conf \
|
||||||
/etc/krb5.conf.d/local
|
/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"
|
greenprint "Adding generated CA cert for Koji"
|
||||||
sudo cp \
|
sudo cp \
|
||||||
/tmp/osbuild-composer-koji-test/ca-crt.pem \
|
/tmp/osbuild-composer-koji-test/ca-crt.pem \
|
||||||
/etc/pki/ca-trust/source/anchors/koji-ca-crt.pem
|
/etc/pki/ca-trust/source/anchors/koji-ca-crt.pem
|
||||||
sudo update-ca-trust
|
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-composer
|
||||||
|
sudo systemctl restart osbuild-worker\@1
|
||||||
|
|
||||||
greenprint "Testing Koji"
|
greenprint "Testing Koji"
|
||||||
koji --server=http://localhost/kojihub --user=osbuild --password=osbuildpass --authtype=password hello
|
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"
|
greenprint "Creating Koji task"
|
||||||
koji --server=http://localhost/kojihub --user kojiadmin --password kojipass --authtype=password make-task image
|
koji --server=http://localhost/kojihub --user kojiadmin --password kojipass --authtype=password make-task image
|
||||||
|
|
|
||||||
3
test/image-tests/osbuild-composer.toml
Normal file
3
test/image-tests/osbuild-composer.toml
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
[koji.localhost.kerberos]
|
||||||
|
principal = "osbuild-krb@LOCAL"
|
||||||
|
keytab = "/etc/osbuild-composer/client.keytab"
|
||||||
3
test/image-tests/osbuild-worker.toml
Normal file
3
test/image-tests/osbuild-worker.toml
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
[koji.localhost.kerberos]
|
||||||
|
principal = "osbuild-krb@LOCAL"
|
||||||
|
keytab = "/etc/osbuild-worker/client.keytab"
|
||||||
Loading…
Add table
Add a link
Reference in a new issue