Skip to content

Commit 4d2b5c3

Browse files
authored
Merge pull request #1985 from isodude/systemd-socket
Add support for systemd socket
2 parents 5ec03ab + 6743a9c commit 4d2b5c3

File tree

10 files changed

+435
-4
lines changed

10 files changed

+435
-4
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010

1111
- [#2800](https://github.com/oauth2-proxy/oauth2-proxy/pull/2800) Add some opencontainer labels to docker image (@halkeye)
1212
- [#2755](https://github.com/oauth2-proxy/oauth2-proxy/pull/2755) feat: add X-Envoy-External-Address as supported header (@bjencks)
13+
- [#1985](https://github.com/oauth2-proxy/oauth2-proxy/pull/1985) Add support for systemd socket (@isodude)
1314

1415
# V7.7.1
1516

@@ -58,6 +59,7 @@
5859
- [#2790](https://github.com/oauth2-proxy/oauth2-proxy/pull/2790) chore(deps): update all golang dependencies (@tuunit)
5960
- [#2607](https://github.com/oauth2-proxy/oauth2-proxy/pull/2607) fix(csrf): fix possible infinite loop (@Primexz)
6061

62+
6163
# V7.6.0
6264

6365
## Release Highlights

docs/docs/configuration/overview.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -219,7 +219,7 @@ Provider specific options can be found on their respective subpages.
219219

220220
| Flag / Config Field | Type | Description | Default |
221221
| ------------------------------------------------------------------- | -------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------ |
222-
| flag: `--http-address`<br/>toml: `http_address` | string | `[http://]<addr>:<port>` or `unix://<path>` to listen on for HTTP clients. Square brackets are required for ipv6 address, e.g. `http://[::1]:4180` | `"127.0.0.1:4180"` |
222+
| flag: `--http-address`<br/>toml: `http_address` | string | `[http://]<addr>:<port>` or `unix://<path>` or `fd:<int>` (case insensitive) to listen on for HTTP clients. Square brackets are required for ipv6 address, e.g. `http://[::1]:4180` | `"127.0.0.1:4180"` |
223223
| flag: `--https-address`<br/>toml: `https_address` | string | `[https://]<addr>:<port>` to listen on for HTTPS clients. Square brackets are required for ipv6 address, e.g. `https://[::1]:443` | `":443"` |
224224
| flag: `--metrics-address`<br/>toml: `metrics_address` | string | the address prometheus metrics will be scraped from | `""` |
225225
| flag: `--metrics-secure-address`<br/>toml: `metrics_secure_address` | string | the address prometheus metrics will be scraped from if using HTTPS | `""` |
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
---
2+
id: systemd_socket
3+
title: Systemd Socket Activation
4+
---
5+
6+
Pass an existing listener created by systemd.socket to oauth2-proxy.
7+
8+
To do this create a socket:
9+
10+
oauth2-proxy.socket
11+
```
12+
[Socket]
13+
ListenStream=%t/oauth2.sock
14+
SocketGroup=www-data
15+
SocketMode=0660
16+
```
17+
18+
Now it's possible to call this socket from e.g. nginx:
19+
```
20+
server {
21+
location /oauth2/ {
22+
proxy_pass http://unix:/run/oauth2-proxy/oauth2.sock;
23+
}
24+
```
25+
26+
The oauth2-proxy should have `--http-address=fd:3` as a parameter.
27+
Here fd is case insensitive and means file descriptor. The number 3 refers to the first non-stdin/stdout/stderr file descriptor,
28+
systemd-socket-activate (which is what systemd.socket uses), listens to what it is told and passes
29+
the listener it created onto the process, starting with file descriptor 3.
30+
31+
```
32+
./oauth2-proxy \
33+
--http-address="fd:3" \
34+
--email-domain="yourcompany.com" \
35+
--upstream=http://127.0.0.1:8080/ \
36+
--cookie-secret=... \
37+
--cookie-secure=true \
38+
--provider=... \
39+
--client-id=... \
40+
--client-secret=...
41+
```
42+
43+
Currently TLS is not supported (but it's doable).

docs/docs/installation.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,3 +29,4 @@ title: Installation
2929
2. [Select a Provider and Register an OAuth Application with a Provider](configuration/providers/index.md)
3030
3. [Configure OAuth2 Proxy using config file, command line options, or environment variables](configuration/overview.md)
3131
4. [Configure SSL or Deploy behind an SSL endpoint](configuration/tls.md) (example provided for Nginx)
32+
5. [Configure OAuth2 Proxy using systemd.socket](configuration/systemd_socket.md) (example provided for Nginx/Systemd)

go.mod

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ require (
1111
github.com/bitly/go-simplejson v0.5.1
1212
github.com/bsm/redislock v0.9.4
1313
github.com/coreos/go-oidc/v3 v3.11.0
14+
github.com/coreos/go-systemd v0.0.0-20191104093116-d3cd4ed1dbcf
1415
github.com/fsnotify/fsnotify v1.7.0
1516
github.com/ghodss/yaml v1.0.1-0.20220118164431-d8423dcdf344
1617
github.com/go-jose/go-jose/v3 v3.0.1

go.sum

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,8 @@ github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDk
4242
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
4343
github.com/coreos/go-oidc/v3 v3.11.0 h1:Ia3MxdwpSw702YW0xgfmP1GVCMA9aEFWu12XUZ3/OtI=
4444
github.com/coreos/go-oidc/v3 v3.11.0/go.mod h1:gE3LgjOgFoHi9a4ce4/tJczr0Ai2/BoDhf0r5lltWI0=
45+
github.com/coreos/go-systemd v0.0.0-20191104093116-d3cd4ed1dbcf h1:iW4rZ826su+pqaw19uhpSCzhj44qo35pNgKFGqzDKkU=
46+
github.com/coreos/go-systemd v0.0.0-20191104093116-d3cd4ed1dbcf/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
4547
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
4648
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
4749
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=

oauthproxy.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -606,7 +606,9 @@ func (p *OAuthProxy) isAPIPath(req *http.Request) bool {
606606

607607
// isTrustedIP is used to check if a request comes from a trusted client IP address.
608608
func (p *OAuthProxy) isTrustedIP(req *http.Request) bool {
609-
if p.trustedIPs == nil {
609+
// RemoteAddr @ means unix socket
610+
// https://github.com/golang/go/blob/0fa53e41f122b1661d0678a6d36d71b7b5ad031d/src/syscall/syscall_linux.go#L506-L511
611+
if p.trustedIPs == nil && req.RemoteAddr != "@" {
610612
return false
611613
}
612614

pkg/apis/options/legacy_options.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -470,7 +470,7 @@ func legacyServerFlagset() *pflag.FlagSet {
470470
flagSet.String("metrics-secure-address", "", "the address /metrics will be served on for HTTPS clients (e.g. \":9100\")")
471471
flagSet.String("metrics-tls-cert-file", "", "path to certificate file for secure metrics server")
472472
flagSet.String("metrics-tls-key-file", "", "path to private key file for secure metrics server")
473-
flagSet.String("http-address", "127.0.0.1:4180", "[http://]<addr>:<port> or unix://<path> to listen on for HTTP clients")
473+
flagSet.String("http-address", "127.0.0.1:4180", "[http://]<addr>:<port> or unix://<path> or fd:<int> (case insensitive) to listen on for HTTP clients")
474474
flagSet.String("https-address", ":443", "<addr>:<port> to listen on for HTTPS clients")
475475
flagSet.String("tls-cert-file", "", "path to certificate file")
476476
flagSet.String("tls-key-file", "", "path to private key file")

pkg/http/server.go

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,27 @@ import (
77
"fmt"
88
"net"
99
"net/http"
10+
"os"
11+
"strconv"
1012
"strings"
1113
"time"
1214

15+
"github.com/coreos/go-systemd/activation"
1316
"github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/options"
1417
"github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/options/util"
1518
"github.com/oauth2-proxy/oauth2-proxy/v7/pkg/logger"
1619
"golang.org/x/sync/errgroup"
1720
)
1821

22+
// listenFdsStart corresponds to `SD_LISTEN_FDS_START`.
23+
// Since the 3 first file descriptors in every linux process is
24+
// stdin, stdout and stderr. The first usable file descriptor is 3.
25+
// systemd-socket-activate will always assume that the first socket will be
26+
// 3 and the rest follow.
27+
const (
28+
listenFdsStart = 3
29+
)
30+
1931
// Server represents an HTTP or HTTPS server.
2032
type Server interface {
2133
// Start blocks and runs the server.
@@ -35,13 +47,21 @@ type Opts struct {
3547

3648
// TLS is the TLS configuration for the server.
3749
TLS *options.TLS
50+
51+
// Let testing infrastructure circumvent parsing file descriptors
52+
fdFiles []*os.File
3853
}
3954

4055
// NewServer creates a new Server from the options given.
4156
func NewServer(opts Opts) (Server, error) {
4257
s := &server{
4358
handler: opts.Handler,
4459
}
60+
61+
if len(opts.fdFiles) > 0 {
62+
s.fdFiles = opts.fdFiles
63+
}
64+
4565
if err := s.setupListener(opts); err != nil {
4666
return nil, fmt.Errorf("error setting up listener: %v", err)
4767
}
@@ -58,6 +78,30 @@ type server struct {
5878

5979
listener net.Listener
6080
tlsListener net.Listener
81+
82+
// ensure activation.Files are called once
83+
fdFiles []*os.File
84+
}
85+
86+
// convert a string filedescriptor to an actual listener
87+
func (s *server) fdToListener(bindAddress string) (net.Listener, error) {
88+
fd, err := strconv.Atoi(bindAddress)
89+
if err != nil {
90+
return nil, fmt.Errorf("listen failed: fd with name is not implemented yet")
91+
}
92+
fdIndex := fd - listenFdsStart
93+
94+
if len(s.fdFiles) == 0 {
95+
s.fdFiles = activation.Files(true)
96+
}
97+
98+
l := len(s.fdFiles)
99+
100+
if fdIndex < 0 || fdIndex >= l || l == 0 {
101+
return nil, fmt.Errorf("listen failed: fd outside of range of available file descriptors")
102+
}
103+
104+
return net.FileListener(s.fdFiles[fdIndex])
61105
}
62106

63107
// setupListener sets the server listener if the HTTP server is enabled.
@@ -69,6 +113,22 @@ func (s *server) setupListener(opts Opts) error {
69113
return nil
70114
}
71115

116+
// Use fd: as a prefix for systemd socket activation, it's generic
117+
// enough and short.
118+
// The most common usage would be --http-address fd:3.
119+
// This causes oauth2-proxy to just assume that the third fd passed
120+
// to the program is indeed a net.Listener and starts using it
121+
// without setting up a new listener.
122+
if strings.HasPrefix(strings.ToLower(opts.BindAddress), "fd:") {
123+
listenAddr := opts.BindAddress[3:]
124+
listener, err := s.fdToListener(listenAddr)
125+
if err != nil {
126+
err = fmt.Errorf("listen (%s, %s) failed: %v", "file", listenAddr, err)
127+
}
128+
s.listener = listener
129+
return err
130+
}
131+
72132
networkType := getNetworkScheme(opts.BindAddress)
73133
listenAddr := getListenAddress(opts.BindAddress)
74134

0 commit comments

Comments
 (0)