Skip to content

Commit

Permalink
Merge pull request #298 from Security-Onion-Solutions/2.4/dev
Browse files Browse the repository at this point in the history
2.4.30
  • Loading branch information
TOoSmOotH authored Nov 13, 2023
2 parents e0c759d + c5518ca commit bf8dac5
Show file tree
Hide file tree
Showing 19 changed files with 345 additions and 87 deletions.
3 changes: 1 addition & 2 deletions cmd/sensoroni.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,11 +61,10 @@ func main() {
"buildTime": cfg.BuildTime,
}).Info("Version Information")

licensing.Init(cfg.LicenseKey)

moduleMgr := module.NewModuleManager()
var srv *server.Server
if cfg.Server != nil {
licensing.Init(cfg.LicenseKey)
srv = server.NewServer(cfg.Server, cfg.Version)
err = moduleMgr.LaunchModules(serverModules.BuildModuleMap(srv), cfg.Server.Modules, cfg.Server.ModuleFailuresIgnored)
if err == nil {
Expand Down
6 changes: 3 additions & 3 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,9 @@ require (
github.com/kennygrant/sanitize v1.2.4
github.com/stretchr/testify v1.8.1
github.com/tidwall/gjson v1.14.4
golang.org/x/crypto v0.8.0
golang.org/x/net v0.9.0 // indirect
golang.org/x/sys v0.7.0 // indirect
golang.org/x/crypto v0.14.0
golang.org/x/net v0.17.0 // indirect
golang.org/x/sys v0.13.0 // indirect
gopkg.in/yaml.v3 v3.0.1
)

Expand Down
12 changes: 6 additions & 6 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -94,25 +94,25 @@ github.com/tj/go-spin v1.1.0/go.mod h1:Mg1mzmePZm4dva8Qz60H2lHwmJ2loum4VIrLgVnKw
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190426145343-a29dc8fdc734/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.8.0 h1:pd9TJtTueMTVQXzk8E2XESSMQDj/U7OUu0PqJqPXQjQ=
golang.org/x/crypto v0.8.0/go.mod h1:mRqEX+O9/h5TFCrQhkgjo2yKi0yYA+9ecGkdQoHrywE=
golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc=
golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4=
golang.org/x/exp v0.0.0-20220303212507-bbda1eaf7a17 h1:3MTrJm4PyNL9NBqvYDSj3DHl46qQakyfqfWo4jgfaEM=
golang.org/x/exp v0.0.0-20220303212507-bbda1eaf7a17/go.mod h1:lgLbSvA5ygNOMpwM/9anMpWVlVJ7Z+cHWq/eFuinpGE=
golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.9.0 h1:aWJ/m6xSmxWBx+V0XRHTlrYrPG56jKsLdTFmsSsCzOM=
golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns=
golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM=
golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.7.0 h1:3jlCCIQZPdOYu1h8BkNvLz8Kgwtae2cagcG/VamtZRU=
golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE=
golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
Expand Down
42 changes: 32 additions & 10 deletions html/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -1201,10 +1201,13 @@ <h4 v-if="!$root.loading">{{ i18n.usersEnabled }} {{ countUsersEnabled() }} / {{
<td v-text="props.item.note" class="d-none d-lg-table-cell"></td>
<td v-text="$root.formatStringArray(props.item.roles)" class="d-none d-lg-table-cell"></td>
<td>
<v-icon v-if="props.item.totpStatus == 'enabled' && props.item.status != 'locked'" color="success" :title="i18n.totpEnabled">fa-user-shield</v-icon>
<v-icon v-if="props.item.totpStatus != 'enabled' && props.item.status != 'locked'" :title="i18n.loginEnabled">fa-user</v-icon>
<v-icon v-if="props.item.status == 'locked'" color="grey" :title="i18n.loginDisabled">fa-user-slash</v-icon>
<v-icon v-if="!props.item.passwordChanged" color="warning" class="px-1" :title="i18n.passwordNeedsChanged">fa-exclamation-triangle</v-icon>
<div v-if="$root.isUserAdmin() || $root.isMyUser(props.item)">
<v-icon v-if="props.item.oidcStatus == 'enabled'" color="info" class="px-1" :title="i18n.oidcLinked">fa-link</v-icon>
<v-icon v-if="props.item.totpStatus == 'enabled'" color="info" :title="i18n.totpActive">fa-user-shield</v-icon>
<v-icon v-if="props.item.webauthnStatus == 'unused-due-to-always-showing-enabled'" color="info" class="px-1" :title="i18n.webauthnActive">fa-fingerprint</v-icon>
<v-icon v-if="props.item.status == 'locked'" color="grey" :title="i18n.loginDisabled">fa-user-slash</v-icon>
<v-icon v-if="!props.item.passwordChanged && props.item.oidcStatus != 'enabled'" color="warning" class="px-1" :title="i18n.passwordNeedsChanged">fa-exclamation-triangle</v-icon>
</div>
</td>
</tr>
</template>
Expand Down Expand Up @@ -1237,6 +1240,10 @@ <h4 v-if="!$root.loading">{{ i18n.usersEnabled }} {{ countUsersEnabled() }} / {{
<v-card>
<v-card-title>{{ i18n.access }}</v-card-title>
<v-card-text>
<v-alert v-if="item.oidcStatus == 'enabled'" class="mt-2" type="warning" icon="fa-shield-halved" text dense>
{{ i18n.oidcResetPasswordHelp }}
</v-alert>

<v-form v-model="form.valid">
<v-text-field id="password-input" v-model="form.password" :placeholder="i18n.passwordChange" :type="showPassword ? 'text' : 'password'"
@click:append="showPassword = !showPassword" :append-icon="showPassword ? 'fa-eye-slash' : 'fa-eye'"
Expand Down Expand Up @@ -3252,7 +3259,22 @@ <h2 v-text="i18n.settingsTitle"></h2>
<v-alert class="mt-2" type="warning" icon="fa-shield-halved" text dense>
{{ i18n.securityInstructions }}
</v-alert>
<v-card outlined class="my-4">
<v-card v-if="oidcEnabled" outlined class="my-4">
<v-card-title>{{ i18n.oidc }}</v-card-title>
<v-card-subtitle v-text="i18n.oidcHelp"></v-card-subtitle>
<v-card-text v-if="passwordEnabled">
<div class="mb-2" v-text="i18n.oidcLinkHelp"></div>
<v-form method="post" :action="authSettingsUrl">
<v-text-field name="csrf_token" v-model="csrfToken" class="d-none"></v-text-field>
<div v-for="oidc in oidcProviders">
<v-btn v-if="oidc.op == 'link'" :name="oidc.op" :value="oidc.id" :id="'settingsOidcLink_' + oidc.id" type="submit" color="primary" v-text="i18n.oidcLink + ' ' + oidc.id"/>
<v-btn v-if="oidc.op == 'unlink'" :name="oidc.op" :value="oidc.id" :id="'settingsOidcUnlink_' + oidc.id" type="submit" color="primary" v-text="i18n.oidcUnlink + ' ' + oidc.id"/>
</div>
</v-form>
</v-card-text>
</v-card>

<v-card v-if="passwordEnabled" outlined class="my-4">
<v-card-title>{{ i18n.password }}</v-card-title>
<v-card-subtitle v-text="i18n.passwordInstructions"></v-card-subtitle>
<v-card-text>
Expand All @@ -3266,7 +3288,7 @@ <h2 v-text="i18n.settingsTitle"></h2>
</v-card-text>
</v-card>

<v-card outlined class="my-4" v-if="totpForm.qr && totpForm.secret">
<v-card v-if="passwordEnabled && passwordSet" outlined class="my-4" v-if="totpForm.qr && totpForm.secret">
<v-alert type="error" border="bottom" colored-border text dense>
{{ i18n.securityInstructionsTotp }}
</v-alert>
Expand All @@ -3288,7 +3310,7 @@ <h2 v-text="i18n.settingsTitle"></h2>
</v-card-text>
</v-card>

<v-card outlined class="my-4" v-if="unlink_totp_available">
<v-card v-if="unlink_totp_available" outlined class="my-4">
<v-card-title>{{ i18n.totp }}</v-card-title>
<v-card-text>
<v-form method="post" :action="authSettingsUrl">
Expand All @@ -3300,7 +3322,7 @@ <h2 v-text="i18n.settingsTitle"></h2>
</v-card-text>
</v-card>

<v-card outlined class="my-4" v-if="webauthnForm.script">
<v-card v-if="webauthnForm.script" outlined class="my-4">
<v-card-title>{{ i18n.webauthn }}</v-card-title>
<v-card-subtitle v-text="i18n.webauthnInstructions"></v-card-subtitle>
<v-card-text>
Expand All @@ -3312,7 +3334,7 @@ <h2 v-text="i18n.settingsTitle"></h2>
</v-form>
</v-card-text>

<div v-if="webauthnForm.existingKeys">
<div v-if="webauthnForm.existingKeys.length">
<v-card-subtitle v-text="i18n.webauthnExistingKeys"></v-card-subtitle>
<v-card-text>
<v-form v-for="key in webauthnForm.existingKeys" method="post" :action="authSettingsUrl">
Expand Down Expand Up @@ -3823,7 +3845,7 @@ <h2 v-text="i18n.gridMembers"></h2>
<script src="js/external/vue-2.7.14.min.js"></script>
<script src="js/external/vue-router-3.5.2.min.js"></script>
<script src="js/external/vuetify-v2.7.0.min.js"></script>
<script src="js/external/axios-1.4.0.min.js"></script>
<script src="js/external/axios-1.6.1.min.js"></script>
<script src="js/external/moment-2.29.4.min.js"></script>
<script src="js/external/moment-timezone-with-data-0.5.43.min.js"></script>
<script src="js/external/chart-3.9.1.min.js"></script>
Expand Down
3 changes: 3 additions & 0 deletions html/js/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -874,6 +874,9 @@ $(document).ready(function() {
isUserAdmin(user = null) {
return this.userHasRole("superuser", user);
},
isMyUser(user) {
return user != null && this.user != null && user.id == this.user.id;
},
userHasRole(role, user = null) {
if (!user) {
user = this.user;
Expand Down
10 changes: 10 additions & 0 deletions html/js/app.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,16 @@ test('isUserAdmin', async () => {
expect(app.isUserAdmin()).toBe(true);
});

test('isMyUser', () => {
app.user = null;
expect(app.isMyUser()).toBe(false);
var user = {id:'123',email:'[email protected]',roles:['nope', 'peon']};
expect(app.isMyUser(user)).toBe(false);
app.user = user;
expect(app.isMyUser(user)).toBe(true);
expect(app.isMyUser()).toBe(false);
});

test('loadServerSettings', async () => {
const fakeInfo = {
srvToken: 'xyz',
Expand Down
1 change: 0 additions & 1 deletion html/js/external/axios-1.4.0.min.js

This file was deleted.

1 change: 1 addition & 0 deletions html/js/external/axios-1.6.1.min.js

Large diffs are not rendered by default.

10 changes: 10 additions & 0 deletions html/js/i18n.js
Original file line number Diff line number Diff line change
Expand Up @@ -434,6 +434,7 @@ const i18n = {
loginEnabled: 'Unlocked',
loginExpired: 'The login session has expired. Refresh, or wait for the page to refresh automatically, and then try again.',
loginInvalid: 'The provided credentials are invalid. Please try again.',
loginOidc: 'Continue with',
loginTitle: 'Login to Security Onion',
logout: 'Logout',
logoutFailure: 'Unable to initiate logout. Ensure server is accessible.',
Expand Down Expand Up @@ -473,6 +474,13 @@ const i18n = {
notFound: 'The selected item no longer exists',
number: 'Num',
numericOps: 'Numeric Ops',
oidc: 'Open ID Connect (OIDC)',
oidcHelp: 'Single Sign-On via an external identity provider has been enabled for SOC. Authentication settings, such as password changes, should be performed in the external identity system unless the Security Onion administrators have enabled local password logins concurrently with SSO.',
oidcLinked: 'OIDC Linked',
oidcLink: 'Link with ',
oidcLinkHelp: 'Users can link to or unlink from the OIDC providers listed below. Be aware that unlinking from all OIDC providers without having a local password set may result in being unable to access this user account. If prompted to login again to verify your identity, choose a login method which is already verified. For example, if you are linking to a new OIDC provider, you cannot use that OIDC provider to confirm your identity.',
oidcResetPasswordHelp: 'Administering authentication settings for OIDC users should normally be conducted in the external provider administration interface. Proceeding may cause the user to be disconnected from the OIDC provider.',
oidcUnlink: 'Unlink from ',
ok: 'OK',
offline: 'Offline',
online: 'Online',
Expand Down Expand Up @@ -665,6 +673,7 @@ const i18n = {
toolTheHiveHelp: 'Case Management',
totp: 'Time-based One-Time Password (TOTP)',
totpActivate: 'Activate TOTP',
totpActive: 'TOTP Active',
totpCodeHelp: 'Enter the code from your authenticator app.',
totpEnabled: 'TOTP (Time-based One-Time Password) enabled',
totpQrInstructions: 'TOTP is a multi-factor authentication (MFA) using an authenticator app, such as Google Authenticator. Using the app on your mobile device, scan the QR code shown below.',
Expand Down Expand Up @@ -710,6 +719,7 @@ const i18n = {
viewCase: 'Case Details',
viewResults: 'View Results',
webauthn: 'Security Keys (WebAuthn / PassKey)',
webauthnActive: 'Webauthn / Security Keys Active',
webauthnAddKey: 'Add New Security Key',
webauthnContinueHelp: 'Prepare your security key (webauthn) device, and press Login when ready.',
webauthnExistingKeys: 'Existing Keys:',
Expand Down
26 changes: 21 additions & 5 deletions html/js/routes/login.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@ routes.push({ path: '*', name: 'login', component: {
script: null,
email: null,
},
passwordEnabled: false,
totpEnabled: false,
oidc: [],
totpCodeLength: 6,
rules: {
required: value => !!value || this.$root.i18n.required,
Expand Down Expand Up @@ -106,14 +109,14 @@ routes.push({ path: '*', name: 'login', component: {

this.csrfToken = flow.data.ui.nodes.find(item => item.attributes && item.attributes.name == 'csrf_token').attributes.value;

// method could be password or totp depending on which phase of login we're in. May be ignored if webauthn is in progress.
this.form.method = flow.data.ui.nodes.find(item => item.attributes && item.attributes.name == 'method' && item.attributes.value == 'password') ? 'password' : 'totp';

this.extractPasswordData(flow);
this.extractTotpData(flow);
this.extractWebauthnData(flow);
this.extractOidcData(flow);
this.$nextTick(function () {
// Wait for next Vue tick to set focus, since at the time of this function call (or even mounted() hook), this element won't be
// loaded, due to v-if's that have yet to process.
if (this.form.method == "totp") {
if (this.totpEnabled) {
const ele = document.getElementById("totp--0");
if (ele) {
ele.focus();
Expand Down Expand Up @@ -165,6 +168,19 @@ routes.push({ path: '*', name: 'login', component: {
},
runWebauthn() {
eval(this.webauthnForm.onclick);
}
},
extractOidcData(response) {
this.oidc = response.data.ui.nodes.filter(item => item.group == "oidc" && item.type == "input" ).map(item => item.attributes.value);
},
extractPasswordData(response) {
if (response.data.ui.nodes.find(item => item.group == "password")) {
this.passwordEnabled = true;
}
},
extractTotpData(response) {
if (response.data.ui.nodes.find(item => item.group == "totp")) {
this.totpEnabled = true;
}
},
},
}});
42 changes: 41 additions & 1 deletion html/js/routes/login.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -192,4 +192,44 @@ test('shouldRunWebauthn', () => {
comp.webauthnForm.onclick = 'this.foo = 123';
comp.runWebauthn();
expect(comp.foo).toBe(123);
});
});

test('shouldExtractPasswordData', () => {
const identifier = {attributes: {name: 'identifier', value: 'some_identifier'}};
const passwordMethod = {group: 'password', attributes: {name: 'method', value: 'password'}};
const nodes = [identifier, passwordMethod];
const response = {data: {ui: {nodes: nodes}}};

expect(comp.passwordEnabled).toBe(false);

comp.extractPasswordData(response);

expect(comp.passwordEnabled).toBe(true);
});

test('shouldExtractTotpData', () => {
const identifier = {attributes: {name: 'identifier', value: 'some_identifier'}};
const totpMethod = {group: 'totp', attributes: {name: 'method', value: 'totp'}};
const nodes = [identifier, totpMethod];
const response = {data: {ui: {nodes: nodes}}};

expect(comp.totpEnabled).toBe(false);

comp.extractTotpData(response);

expect(comp.totpEnabled).toBe(true);
});

test('shouldExtractOidcData', () => {
const identifier = {attributes: {name: 'identifier', value: 'some_identifier'}};
const oidcMethod = {group: 'oidc', type: 'input', attributes: {value: 'SSO'}};
const nodes = [identifier, oidcMethod];
const response = {data: {ui: {nodes: nodes}}};

expect(comp.oidc.length).toBe(0);

comp.extractOidcData(response);

expect(comp.oidc.length).toBe(1);
expect(comp.oidc[0]).toBe('SSO');
});
16 changes: 16 additions & 0 deletions html/js/routes/settings.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,9 @@ routes.push({ path: '/settings', name: 'settings', component: {
script: null,
existingKeys: [],
},
passwordEnabled: false,
oidcEnabled: false,
oidcProviders: [],
rules: {
required: value => !!value || this.$root.i18n.required,
matches: value => (!!value && value == this.passwordForm.password) || this.$root.i18n.passwordMustMatch,
Expand Down Expand Up @@ -85,8 +88,10 @@ routes.push({ path: '/settings', name: 'settings', component: {
this.profileForm.lastName = response.data.identity.traits.lastName;
this.profileForm.note = response.data.identity.traits.note;
}
this.extractPasswordData(response);
this.extractTotpData(response);
this.extractWebauthnData(response);
this.extractOidcData(response);

var errorsMessage = null;
if (response.data.ui.messages && response.data.ui.messages.length > 0) {
Expand Down Expand Up @@ -147,6 +152,17 @@ routes.push({ path: '/settings', name: 'settings', component: {
},
runWebauthn() {
eval(this.webauthnForm.onclick);
},
extractPasswordData(response) {
if (response.data.ui.nodes.find(item => item.group == "password")) {
this.passwordEnabled = true;
}
},
extractOidcData(response) {
response.data.ui.nodes.filter(item => item.group == "oidc").forEach((oidc) => {
this.oidcEnabled = true;
this.oidcProviders.push({op: oidc.attributes.name, id: oidc.attributes.value});
});
}
}
}});
Loading

0 comments on commit bf8dac5

Please sign in to comment.