Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add JWT support #85

Merged
merged 36 commits into from
Dec 12, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
b7dcf65
Adds jwt update to posix.
JU4N98 Jul 17, 2023
a20e66e
Adds jwt support for windows, fixes lint errors.
JU4N98 Jul 18, 2023
ae88282
Improves some messages.
JU4N98 Jul 18, 2023
7f337e4
Improves context handling.
JU4N98 Jul 19, 2023
fa59c9e
Fixes typo.
JU4N98 Oct 6, 2023
e386261
Adds jsonFilename config and its warning and error.
JU4N98 Oct 13, 2023
1e9d2ac
adds suggested changes
JU4N98 Oct 19, 2023
f24d177
Replaces errorf for fatalf.
JU4N98 Oct 23, 2023
1085103
Uses expiry time to renew JWT svid.
JU4N98 Oct 24, 2023
dd13164
Creates separates files for JWT SVID and Bundles, updates README.
JU4N98 Oct 25, 2023
12d855a
Fixes lint error.
JU4N98 Oct 25, 2023
d01e840
Fixes bug in config.go validations, changes JWT files structure, upda…
JU4N98 Nov 7, 2023
9f2b6a6
Adds suggested changes.
JU4N98 Nov 8, 2023
d4b0706
Refactors util files.
JU4N98 Nov 10, 2023
cb236cb
Updates README.md.
JU4N98 Nov 10, 2023
825d138
Updates README.md.
JU4N98 Nov 13, 2023
44080f3
Updates README.md.
JU4N98 Nov 13, 2023
298d6a0
Updates README.md.
JU4N98 Nov 13, 2023
616bc0d
Moves RunDeamon into sidecar.go, refactors updateJWTSVID method.
JU4N98 Nov 13, 2023
80d1919
Moves RunDaemon method just after New method.
JU4N98 Nov 13, 2023
8bdb162
Refactors createRetryIntervalFunc and updateJWTSVID.
JU4N98 Nov 13, 2023
18d00eb
Updates logs and README.md.
JU4N98 Nov 14, 2023
bfb6ccc
Updates README.md.
JU4N98 Nov 14, 2023
6b3629b
Corrects misplaced log.
JU4N98 Nov 14, 2023
ba89caf
Updates pkg/sidecar/sidecar.go
JU4N98 Nov 15, 2023
09c35b6
Updates pkg/sidecar/sidecar.go
JU4N98 Nov 15, 2023
d690410
Updates README.md
JU4N98 Nov 15, 2023
7c076da
Updates README.md
JU4N98 Nov 15, 2023
4c277c3
Adds tests for new conf fields.
JU4N98 Nov 15, 2023
d6aab07
Adds suggested validations.
JU4N98 Nov 17, 2023
752f20d
Adds suggested changes.
JU4N98 Nov 21, 2023
6f92a7d
Adds suggested changes.
JU4N98 Nov 21, 2023
45aa340
Adds flexibility to JWT.
JU4N98 Nov 22, 2023
bec5d88
Updates writeJSON and error message.
JU4N98 Nov 29, 2023
d544c46
Re use jwtSource, solve identation, add blank line.
JU4N98 Dec 4, 2023
cd61f89
Improves identation.
JU4N98 Dec 5, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 20 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,17 +16,20 @@ If `-config` is not specified, the default value `helper.conf` is assumed.
## Configuration
The configuration file is an [HCL](https://github.com/hashicorp/hcl) formatted file that defines the following configurations:

|Configuration | Description | Example Value |
|--------------------------|------------------------------------------------------------------------------------------------| ------------- |
|`agent_address` | Socket address of SPIRE Agent. | `"/tmp/agent.sock"` |
|`cmd` | The path to the process to launch. | `"ghostunnel"` |
|`cmd_args` | The arguments of the process to launch. | `"server --listen localhost:8002 --target localhost:8001--keystore certs/svid_key.pem --cacert certs/svid_bundle.pem --allow-uri-san spiffe://example.org/Database"` |
|`cert_dir` | Directory name to store the fetched certificates. This directory must be created previously. | `"certs"` |
|`add_intermediates_to_bundle`| Add intermediate certificates into Bundle file instead of SVID file. | `true` |
|`renew_signal` | The signal that the process to be launched expects to reload the certificates. It is not supported on Windows. | `"SIGUSR1"` |
|`svid_file_name` | File name to be used to store the X.509 SVID public certificate in PEM format. | `"svid.pem"` |
|`svid_key_file_name` | File name to be used to store the X.509 SVID private key and public certificate in PEM format. | `"svid_key.pem"` |
|`svid_bundle_file_name` | File name to be used to store the X.509 SVID Bundle in PEM format. | `"svid_bundle.pem"` |
| Configuration | Description | Example Value |
|-----------------------------|----------------------------------------------------------------------------------------------------------------| -------------------- |
|`agent_address` | Socket address of SPIRE Agent. | `"/tmp/agent.sock"` |
|`cmd` | The path to the process to launch. | `"ghostunnel"` |
|`cmd_args` | The arguments of the process to launch. | `"server --listen localhost:8002 --target localhost:8001--keystore certs/svid_key.pem --cacert certs/svid_bundle.pem --allow-uri-san spiffe://example.org/Database"` |
|`cert_dir` | Directory name to store the fetched certificates. This directory must be created previously. | `"certs"` |
|`add_intermediates_to_bundle`| Add intermediate certificates into Bundle file instead of SVID file. | `true` |
|`renew_signal` | The signal that the process to be launched expects to reload the certificates. It is not supported on Windows. | `"SIGUSR1"` |
|`svid_file_name` | File name to be used to store the X.509 SVID public certificate in PEM format. | `"svid.pem"` |
|`svid_key_file_name` | File name to be used to store the X.509 SVID private key and public certificate in PEM format. | `"svid_key.pem"` |
|`svid_bundle_file_name` | File name to be used to store the X.509 SVID Bundle in PEM format. | `"svid_bundle.pem"` |
|`jwt_audience` | JWT SVID audience. | `"your-audience"` |
|`jwt_svid_file_name` | File name to be used to store JWT SVID in Base64-encoded string. | `"jwt_svid.token"` |
|`jwt_bundle_file_name` | File name to be used to store JWT Bundle in JSON format. | `"jwt_bundle.json"` |

### Configuration example
```
Expand All @@ -38,6 +41,9 @@ renew_signal = "SIGUSR1"
svid_file_name = "svid.pem"
svid_key_file_name = "svid_key.pem"
svid_bundle_file_name = "svid_bundle.pem"
jwt_audience = "your-audience"
jwt_svid_file_name = "jwt.token"
jwt_bundle_file_name = "bundle.json"
```

### Windows example
Expand All @@ -47,4 +53,7 @@ cert_dir = "certs"
svid_file_name = "svid.pem"
svid_key_file_name = "svid_key.pem"
svid_bundle_file_name = "svid_bundle.pem"
jwt_audience = "your-audience"
jwt_svid_file_name = "jwt.token"
jwt_bundle_file_name = "bundle.json"
```
40 changes: 31 additions & 9 deletions pkg/sidecar/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,12 @@ type Config struct {
SvidBundleFileNameDeprecated string `hcl:"svidBundleFileName"`
RenewSignal string `hcl:"renew_signal"`
RenewSignalDeprecated string `hcl:"renewSignal"`

// JWT configuration
JWTAudience string `hcl:"jwt_audience"`
JWTSvidFilename string `hcl:"jwt_svid_file_name"`
JWTBundleFilename string `hcl:"jwt_bundle_file_name"`

// TODO: is there a reason for this to be exposed? and inside of config?
ReloadExternalProcess func() error
// TODO: is there a reason for this to be exposed? and inside of config?
Expand Down Expand Up @@ -114,18 +120,34 @@ func ValidateConfig(c *Config) error {
c.RenewSignal = c.RenewSignalDeprecated
}

switch {
case c.SvidFileName == "":
return errors.New("svid_file_name is required")
case c.SvidKeyFileName == "":
return errors.New("svid_key_file_name is required")
case c.SvidBundleFileName == "":
return errors.New("svid_bundle_file_name is required")
default:
return nil
x509EmptyCount := countEmpty(c.SvidFileName, c.SvidBundleFileName, c.SvidKeyFileName)
jwtSVIDEmptyCount := countEmpty(c.JWTSvidFilename, c.JWTAudience)
jwtBundleEmptyCount := countEmpty(c.SvidBundleFileName)
if x509EmptyCount == 3 && jwtSVIDEmptyCount == 2 && jwtBundleEmptyCount == 1 {
return errors.New("at least one of the sets ('svid_file_name', 'svid_key_file_name', 'svid_bundle_file_name'), ('jwt_file_name', 'jwt_audience'), or ('jwt_bundle_file_name') must be fully specified")
}

if x509EmptyCount != 0 && x509EmptyCount != 3 {
return errors.New("all or none of 'svid_file_name', 'svid_key_file_name', 'svid_bundle_file_name' must be specified")
}

if jwtSVIDEmptyCount != 0 && jwtSVIDEmptyCount != 2 {
return errors.New("all or none of 'jwt_file_name', 'jwt_audience' must be specified")
}

return nil
faisal-memon marked this conversation as resolved.
Show resolved Hide resolved
}

func getWarning(s1 string, s2 string) string {
return s1 + " will be deprecated, should be used as " + s2
}

func countEmpty(configs ...string) int {
cnt := 0
for _, config := range configs {
if config == "" {
cnt++
}
}
return cnt
}
45 changes: 27 additions & 18 deletions pkg/sidecar/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@ func TestParseConfig(t *testing.T) {
expectedSvidFileName := "svid.pem"
expectedKeyFileName := "svid_key.pem"
expectedSvidBundleFileName := "svid_bundle.pem"
expectedJWTSVIDFileName := "jwt_svid.token"
expectedJWTBundleFileName := "jwt_bundle.json"
expectedJWTAudience := "your-audience"

assert.Equal(t, expectedAgentAddress, c.AgentAddress)
assert.Equal(t, expectedCmd, c.Cmd)
Expand All @@ -31,6 +34,9 @@ func TestParseConfig(t *testing.T) {
assert.Equal(t, expectedSvidFileName, c.SvidFileName)
assert.Equal(t, expectedKeyFileName, c.SvidKeyFileName)
assert.Equal(t, expectedSvidBundleFileName, c.SvidBundleFileName)
assert.Equal(t, expectedJWTSVIDFileName, c.JWTSvidFilename)
assert.Equal(t, expectedJWTBundleFileName, c.JWTBundleFilename)
assert.Equal(t, expectedJWTAudience, c.JWTAudience)
assert.True(t, c.AddIntermediatesToBundle)
}

Expand All @@ -51,33 +57,37 @@ func TestValidateConfig(t *testing.T) {
},
},
{
name: "no SVID file",
name: "no error",
config: &Config{
AgentAddress: "path",
SvidKeyFileName: "key.pem",
SvidBundleFileName: "bundle.pem",
AgentAddress: "path",
JWTAudience: "your-audience",
JWTSvidFilename: "jwt.token",
JWTBundleFilename: "bundle.json",
},
expectError: "svid_file_name is required",
},
{
name: "no key file",
name: "no set specified",
config: &Config{
AgentAddress: "path",
SvidFileName: "cert.pem",
SvidBundleFileName: "bundle.pem",
AgentAddress: "path",
},
expectError: "svid_key_file_name is required",
expectError: "at least one of the sets ('svid_file_name', 'svid_key_file_name', 'svid_bundle_file_name'), ('jwt_file_name', 'jwt_audience'), or ('jwt_bundle_file_name') must be fully specified",
},
{
name: "no bundle file",
name: "missing svid config",
config: &Config{
AgentAddress: "path",
SvidFileName: "cert.pem",
},
expectError: "all or none of 'svid_file_name', 'svid_key_file_name', 'svid_bundle_file_name' must be specified",
},
{
name: "missing jwt config",
config: &Config{
AgentAddress: "path",
SvidFileName: "cert.pem",
SvidKeyFileName: "key.pem",
JWTSvidFilename: "cert.pem",
},
expectError: "svid_bundle_file_name is required",
expectError: "all or none of 'jwt_file_name', 'jwt_audience' must be specified",
},

// Duplicated field error:
{
name: "Both agent_address & agentAddress in use",
Expand All @@ -88,7 +98,7 @@ func TestValidateConfig(t *testing.T) {
SvidKeyFileName: "key.pem",
SvidBundleFileName: "bundle.pem",
},
expectError: "use of agent_address and AgentAddress found, use only agent_address",
expectError: "use of agent_address and agentAddress found, use only agent_address",
},
{
name: "Both cmd_args & cmdArgs in use",
Expand Down Expand Up @@ -159,7 +169,6 @@ func TestValidateConfig(t *testing.T) {
},
expectError: "use of renew_signal and renewSignal found, use only renew_signal",
},

// Deprecated field warning:
{
name: "Using AgentAddressDeprecated",
Expand Down Expand Up @@ -276,7 +285,7 @@ func TestValidateConfig(t *testing.T) {
require.ElementsMatch(t, tt.expectLogs, getShortEntries(hook.AllEntries()))

if tt.expectError != "" {
require.Error(t, err, tt.expectError)
require.EqualError(t, err, tt.expectError)
return
}

Expand Down
Loading
Loading