diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index dedcde0..b152dad 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -6,6 +6,7 @@ jobs: matrix: os: - ubuntu-latest + - macos-latest runs-on: ${{ matrix.os }} steps: - name: Checkout diff --git a/.goreleaser.macos-latest.yml b/.goreleaser.macos-latest.yml new file mode 100644 index 0000000..55ed199 --- /dev/null +++ b/.goreleaser.macos-latest.yml @@ -0,0 +1,13 @@ +archives: +- files: + - deploy/* + - LICENSE + - README.md +builds: +- dir: cmd/piv-agent + goos: + - darwin + goarch: + - amd64 +checksum: + name_template: "{{ .ProjectName }}_{{ .Version }}_darwin_checksums.txt" diff --git a/cmd/piv-agent/serve.go b/cmd/piv-agent/serve.go index df8f8d8..df1f9a6 100644 --- a/cmd/piv-agent/serve.go +++ b/cmd/piv-agent/serve.go @@ -7,10 +7,10 @@ import ( "path/filepath" "time" - "github.com/coreos/go-systemd/activation" "github.com/smlx/piv-agent/internal/keyservice/piv" "github.com/smlx/piv-agent/internal/pinentry" "github.com/smlx/piv-agent/internal/server" + "github.com/smlx/piv-agent/internal/sockets" "github.com/smlx/piv-agent/internal/ssh" "go.uber.org/zap" "golang.org/x/sync/errgroup" @@ -50,8 +50,8 @@ func (cmd *ServeCmd) Run(log *zap.Logger) error { log.Info("startup", zap.String("version", version), zap.String("build date", date)) p := piv.New(log) - // use systemd socket activation - ls, err := activation.Listeners() + // use FDs passed via socket activation + ls, err := sockets.Get(validAgents) if err != nil { return fmt.Errorf("cannot retrieve listeners: %w", err) } diff --git a/deploy/launchd/com.github.smlx.piv-agent.plist b/deploy/launchd/com.github.smlx.piv-agent.plist new file mode 100644 index 0000000..8fac9ea --- /dev/null +++ b/deploy/launchd/com.github.smlx.piv-agent.plist @@ -0,0 +1,26 @@ + + + + + Label + com.github.smlx.piv-agent + ProgramArguments + + /usr/local/bin/piv-agent + serve + + Sockets + + ssh + + SecureSocketWithKey + SSH_AUTH_SOCK + + gpg + + SockPathName + /Users/ExampleUserName/.gnupg/S.gpg-agent + + + + diff --git a/deploy/piv-agent.service b/deploy/systemd/piv-agent.service similarity index 100% rename from deploy/piv-agent.service rename to deploy/systemd/piv-agent.service diff --git a/deploy/piv-agent.socket b/deploy/systemd/piv-agent.socket similarity index 100% rename from deploy/piv-agent.socket rename to deploy/systemd/piv-agent.socket diff --git a/docs/content/en/docs/install.md b/docs/content/en/docs/install.md index 7edcaec..760caa5 100644 --- a/docs/content/en/docs/install.md +++ b/docs/content/en/docs/install.md @@ -23,20 +23,26 @@ sudo apt install libpcsclite1 ## Install piv-agent +### Linux + Download the latest [release](https://github.com/smlx/piv-agent/releases), and extract it to a temporary location. Copy the `piv-agent` binary into your `$PATH`, and the `systemd` unit files to the correct location: ``` sudo cp piv-agent /usr/local/bin/ -cp deploy/piv-agent.{socket,service} ~/.config/systemd/user/ +cp deploy/systemd/piv-agent.{socket,service} ~/.config/systemd/user/ systemctl --user daemon-reload ``` +### macOS + +Similarly to Linux, copy `piv-agent` to `/usr/local/bin/`, edit the `.plist` file with the correct home directory, and drop it in `~/Library/LaunchAgents`. + ### Socket activation `piv-agent` relies on [socket activation](https://0pointer.de/blog/projects/socket-activated-containers.html), and is currently only tested with `systemd`. It doesn't listen to any sockets directly, and instead requires the init system to pass file descriptors to the `piv-agent` process after it is running. This requirement makes it possible to exit the process when not in use. -`ssh-agent` and `gpg-agent` functionality are enabled by default in `piv-agent.service` and `piv-agent.socket`. -The index of the sockets listed in `piv-agent.socket` are indicated by the arguments to `--agent-types`. +`ssh-agent` and `gpg-agent` functionality are enabled by default in the systemd and launchd configuration files. +On Linux, index of the sockets listed in `piv-agent.socket` are indicated by the arguments to `--agent-types`. diff --git a/go.mod b/go.mod index b28010e..ce68e10 100644 --- a/go.mod +++ b/go.mod @@ -11,6 +11,7 @@ require ( github.com/golang/mock v1.6.0 github.com/gopasspw/gopass v1.10.2-0.20201105185611-36c5888f3a49 github.com/smlx/fsm v0.2.0 + github.com/x13a/go-launch v0.0.0-20210715084817-fd409384939b go.uber.org/zap v1.19.1 golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a golang.org/x/sync v0.0.0-20210220032951-036812b2e83c diff --git a/go.sum b/go.sum index a3f286c..634f8a5 100644 --- a/go.sum +++ b/go.sum @@ -113,6 +113,8 @@ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/tadvi/systray v0.0.0-20190226123456-11a2b8fa57af h1:6yITBqGTE2lEeTPG04SN9W+iWHCRyHqlVYILiSXziwk= github.com/tadvi/systray v0.0.0-20190226123456-11a2b8fa57af/go.mod h1:4F09kP5F+am0jAwlQLddpoMDM+iewkxxt6nxUQ5nq5o= github.com/urfave/cli/v2 v2.2.0/go.mod h1:SE9GqnLQmjVa0iPEY0f1w3ygNIYcIJ0OKPMoW2caLfQ= +github.com/x13a/go-launch v0.0.0-20210715084817-fd409384939b h1:rpNT9cyxH8nsCM8htO1SLhrehyt74GFczE9s/O6WkfE= +github.com/x13a/go-launch v0.0.0-20210715084817-fd409384939b/go.mod h1:kfVYr1hMcmOVxZt+2kFzCXf/YRX9Cz+F1QkijZQMaMM= github.com/xrash/smetrics v0.0.0-20170218160415-a3153f7040e9/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8= github.com/xrash/smetrics v0.0.0-20200730060457-89a2a8a1fb0b/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= diff --git a/internal/sockets/get_darwin.go b/internal/sockets/get_darwin.go new file mode 100644 index 0000000..10610c7 --- /dev/null +++ b/internal/sockets/get_darwin.go @@ -0,0 +1,34 @@ +package sockets + +import ( + "fmt" + "net" + "os" + + "github.com/x13a/go-launch" +) + +// Get returns the sockets passed to the process from launchd socket +// activation. +func Get(names []string) ([]net.Listener, error) { + var listeners []net.Listener + // get the FDs + for _, name := range names { + nameFDs, err := launch.ActivateSocket(name) + if err != nil { + return nil, err + } + for _, fd := range nameFDs { + f := os.NewFile(uintptr(fd), name) + if f == nil { + return nil, fmt.Errorf("couldn't create file from FD") + } + l, err := net.FileListener(f) + if err != nil { + return nil, err + } + listeners = append(listeners, l) + } + } + return listeners, nil +} diff --git a/internal/sockets/get_linux.go b/internal/sockets/get_linux.go new file mode 100644 index 0000000..a931ffc --- /dev/null +++ b/internal/sockets/get_linux.go @@ -0,0 +1,13 @@ +package sockets + +import ( + "net" + + "github.com/coreos/go-systemd/activation" +) + +// Get returns the sockets passed to the process from systemd socket +// activation. +func Get(_ []string) ([]net.Listener, error) { + return activation.Listeners() +}