diff --git a/cmd/saml-server/main.go b/cmd/saml-server/main.go new file mode 100644 index 0000000000000000000000000000000000000000..bddc3355baa9c2af40b6f0bcdb47189e0ddf0e5f --- /dev/null +++ b/cmd/saml-server/main.go @@ -0,0 +1,81 @@ +package main + +import ( + "context" + "flag" + "io/ioutil" + "log" + "net/http" + "os" + "os/signal" + "strings" + "syscall" + "time" + + "git.autistici.org/id/go-sso/saml" + + "gopkg.in/yaml.v2" +) + +var ( + addr = flag.String("addr", ":5004", "address to listen on") + configFile = flag.String("config", "/etc/sso/saml.yml", "`path` of config file") +) + +func loadConfig() (*saml.Config, error) { + // Read YAML config. + data, err := ioutil.ReadFile(*configFile) + if err != nil { + return nil, err + } + var config saml.Config + if err := yaml.Unmarshal(data, &config); err != nil { + return nil, err + } + return &config, nil +} + +// Set defaults for command-line flags using variables from the environment. +func setFlagDefaultsFromEnv() { + flag.VisitAll(func(f *flag.Flag) { + envVar := "SAML_" + strings.ToUpper(strings.Replace(f.Name, "-", "_", -1)) + if value := os.Getenv(envVar); value != "" { + f.DefValue = value + f.Value.Set(value) + } + }) +} + +func main() { + setFlagDefaultsFromEnv() + flag.Parse() + + config, err := loadConfig() + if err != nil { + log.Fatal(err) + } + + s, err := saml.NewSAMLIDP(config) + if err != nil { + log.Fatal(err) + } + + srv := &http.Server{ + Addr: *addr, + Handler: s, + } + + sigCh := make(chan os.Signal, 1) + go func() { + <-sigCh + ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) + defer cancel() + _ = srv.Shutdown(ctx) + _ = srv.Close() + }() + signal.Notify(sigCh, syscall.SIGTERM, syscall.SIGINT) + + if err := srv.ListenAndServe(); err != nil { + log.Fatal(err) + } +} diff --git a/cmd/saml-server/saml-server b/cmd/saml-server/saml-server new file mode 100755 index 0000000000000000000000000000000000000000..f2f0075ac6d0e7456880a8c9cb733d408d0fbe2d Binary files /dev/null and b/cmd/saml-server/saml-server differ diff --git a/debian/control b/debian/control index d011af6a3c72a2943b684b9c52dd9653dcf4ec36..96d4fa68d21b82d1145e14a1fc8fce4db0a123be 100644 --- a/debian/control +++ b/debian/control @@ -16,3 +16,9 @@ Architecture: any Depends: ${shlibs:Depends}, ${misc:Depends} Description: Single-Sign-On HTTP proxy. Single-Sign-On HTTP proxy. + +Package: saml-server +Architecture: any +Depends: ${shlibs:Depends}, ${misc:Depends} +Description: SAML Single-Sign-On bridge + SAML Single-Sign-On bridge. diff --git a/debian/saml-server.default b/debian/saml-server.default new file mode 100644 index 0000000000000000000000000000000000000000..8536d1352c917777d87a51ff0db887ffbec3f839 --- /dev/null +++ b/debian/saml-server.default @@ -0,0 +1 @@ +ADDR=:5004 diff --git a/debian/saml-server.install b/debian/saml-server.install new file mode 100644 index 0000000000000000000000000000000000000000..87af1cd06b395c016b36b11ba515c57f88c7d402 --- /dev/null +++ b/debian/saml-server.install @@ -0,0 +1 @@ +usr/bin/saml-server diff --git a/debian/saml-server.postinst b/debian/saml-server.postinst new file mode 100755 index 0000000000000000000000000000000000000000..73768438ac8522fd4dbf496e61d47f5f3dd66ae6 --- /dev/null +++ b/debian/saml-server.postinst @@ -0,0 +1,16 @@ +#!/bin/sh + +set -e + +case "$1" in +configure) + addgroup --system --quiet sso-proxy + adduser --system --no-create-home --home /run/sso-proxy \ + --disabled-password --disabled-login \ + --quiet --ingroup sso-proxy sso-proxy + ;; +esac + +#DEBHELPER# + +exit 0 diff --git a/debian/saml-server.service b/debian/saml-server.service new file mode 100644 index 0000000000000000000000000000000000000000..25b2a91be3a19b1036cf9aeea8abb0891747d7ad --- /dev/null +++ b/debian/saml-server.service @@ -0,0 +1,13 @@ +[Unit] +Description=SAML SSO Bridge + +[Service] +User=saml-server +Group=saml-server +EnvironmentFile=-/etc/default/saml-server +ExecStart=/usr/bin/saml-server --addr $ADDR +Restart=always + +[Install] +WantedBy=multi-user.target + diff --git a/saml/saml.go b/saml/saml.go index 40713e8f9d21da7fe056fb8623fa96480a0deb1d..6d5fe2d42786075891b57b2228a7483ad928eb9e 100644 --- a/saml/saml.go +++ b/saml/saml.go @@ -5,6 +5,7 @@ import ( "crypto/tls" "encoding/base64" "encoding/hex" + "encoding/xml" "errors" "fmt" "io/ioutil" @@ -22,13 +23,6 @@ import ( "git.autistici.org/id/go-sso/httpsso" ) -type ServiceProviderConfig struct { -} - -func (p *ServiceProviderConfig) toEntity() *saml.EntityDescriptor { - return nil -} - type Config struct { BaseURL string `yaml:"base_url"` @@ -46,7 +40,8 @@ type Config struct { SSODomain string `yaml:"sso_domain"` // Service provider config. - ServiceProviders map[string]*ServiceProviderConfig `yaml:"service_providers"` + ServiceProviders []string `yaml:"service_providers"` + parsedServiceProviders map[string]*saml.EntityDescriptor } // Sanity checks for the configuration. @@ -74,12 +69,28 @@ func (c *Config) check() error { return nil } +func (c *Config) loadServiceProviders() error { + c.parsedServiceProviders = make(map[string]*saml.EntityDescriptor) + for _, path := range c.ServiceProviders { + data, err := ioutil.ReadFile(path) + if err != nil { + return err + } + var ent saml.EntityDescriptor + if err := xml.Unmarshal(data, &ent); err != nil { + return err + } + c.parsedServiceProviders[ent.EntityID] = &ent + } + return nil +} + func (c *Config) GetServiceProvider(r *http.Request, serviceProviderID string) (*saml.EntityDescriptor, error) { - srv, ok := c.ServiceProviders[serviceProviderID] + srv, ok := c.parsedServiceProviders[serviceProviderID] if !ok { return nil, os.ErrNotExist } - return srv.toEntity(), nil + return srv, nil } // Read users from a YAML-encoded file, in a format surprisingly @@ -142,6 +153,9 @@ func NewSAMLIDP(config *Config) (http.Handler, error) { if err := config.check(); err != nil { return nil, err } + if err := config.loadServiceProviders(); err != nil { + return nil, err + } cert, err := tls.LoadX509KeyPair(config.CertificateFile, config.PrivateKeyFile) if err != nil {