Skip to content

Commit

Permalink
Merge branch 'main' of https://github.com/Azure/azure-osconfig into a…
Browse files Browse the repository at this point in the history
…hbenmes/fix_clobbered_artifacts_logs
  • Loading branch information
MariusNi committed Jun 4, 2024
2 parents 5f6918e + 7f2ad01 commit 18912aa
Show file tree
Hide file tree
Showing 5 changed files with 20 additions and 95 deletions.
6 changes: 1 addition & 5 deletions docs/architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -321,8 +321,4 @@ The Universal NRP is used with the SecurityBaseline module to audit and remediat

<img src="assets/5_guestconfig.png" alt="OSConfig NRP" width=70%/>

For more information see [src/adapters/mc/README.md](../src/adapters/mc/README.md) and [src/modules/securitybaseline/README.md](../src/modules/securitybaseline/README.md).

A fallback execution exists for policies that apply to more distros than OSConfig has binary packages for. Per each check, if OSConfig is present, the NRP invokes it over the MPI REST API (case A) and if OSConfig is not present, the NRP executes the fallback case B where the NRP executes itself the respective audit and remediation checks:

<img src="assets/6_fallback.png" alt="OSConfig NRP with fallback" width=70%/>
For more information see [src/adapters/mc/README.md](../src/adapters/mc/README.md) and [src/modules/securitybaseline/README.md](../src/modules/securitybaseline/README.md).
18 changes: 10 additions & 8 deletions src/common/asb/Asb.c
Original file line number Diff line number Diff line change
Expand Up @@ -511,6 +511,7 @@ static const char* g_home = "/home";
static const char* g_devShm = "/dev/shm";
static const char* g_tmp = "/tmp";
static const char* g_varTmp = "/var/tmp";
static const char* g_varLogJournal = "/var/log/journal";
static const char* g_media = "/media/";
static const char* g_nodev = "nodev";
static const char* g_nosuid = "nosuid";
Expand Down Expand Up @@ -571,6 +572,7 @@ static const char* g_ipv4ll = "ipv4ll";
static const char* g_sysCtlA = "sysctl -a";
static const char* g_fileCreateMode = "$FileCreateMode";
static const char* g_logrotate = "logrotate";
static const char* g_logrotateTimer = "logrotate.timer";
static const char* g_telnet = "telnet";
static const char* g_rcpSocket = "rcp.socket";
static const char* g_rshSocket = "rsh.socket";
Expand Down Expand Up @@ -1681,7 +1683,7 @@ static char* AuditEnsureSystemdJournaldServicePersistsLogMessages(void* log)
{
char* reason = NULL;
RETURN_REASON_IF_NOT_ZERO(CheckPackageInstalled(g_systemd, &reason, log));
CheckDirectoryAccess("/var/log/journal", 0, -1, 2775, false, &reason, log);
CheckDirectoryAccess(g_varLogJournal, 0, -1, 2775, false, &reason, log);
return reason;
}

Expand Down Expand Up @@ -1758,7 +1760,7 @@ static char* AuditEnsureSyslogRotaterServiceIsEnabled(void* log)
RETURN_REASON_IF_NOT_ZERO(CheckPackageInstalled(g_logrotate, &reason, log));
RETURN_REASON_IF_NOT_ZERO(CheckFileExists(g_etcCronDailyLogRotate, &reason, log));
RETURN_REASON_IF_NOT_ZERO(CheckFileAccess(g_etcCronDailyLogRotate, 0, 0, 755, &reason, log));
CheckDaemonActive(g_logrotate, &reason, log);
CheckDaemonActive(g_logrotateTimer, &reason, log);
return reason;
}

Expand Down Expand Up @@ -2951,10 +2953,10 @@ static int RemediateEnsureDefaultDenyFirewallPolicyIsSet(char* value, void* log)
static int RemediateEnsurePacketRedirectSendingIsDisabled(char* value, void* log)
{
UNUSED(value);
return ((0 == ExecuteCommand(NULL, "sysctl -w net.ipv4.conf.all.accept_redirects=0", true, false, 0, 0, NULL, NULL, log)) &&
(0 == ExecuteCommand(NULL, "sysctl -w net.ipv4.conf.default.accept_redirects=0", true, false, 0, 0, NULL, NULL, log)) &&
(0 == ReplaceMarkedLinesInFile(g_etcSysctlConf, "net.ipv4.conf.all.accept_redirects", "net.ipv4.conf.all.accept_redirects = 0\n", '#', log)) &&
(0 == ReplaceMarkedLinesInFile(g_etcSysctlConf, "net.ipv4.conf.default.accept_redirects", "net.ipv4.conf.default.accept_redirects = 0\n", '#', log))) ? 0 : ENOENT;
return ((0 == ExecuteCommand(NULL, "sysctl -w net.ipv4.conf.all.send_redirects=0", true, false, 0, 0, NULL, NULL, log)) &&
(0 == ExecuteCommand(NULL, "sysctl -w net.ipv4.conf.default.send_redirects=0", true, false, 0, 0, NULL, NULL, log)) &&
(0 == ReplaceMarkedLinesInFile(g_etcSysctlConf, "net.ipv4.conf.all.send_redirects", "net.ipv4.conf.all.send_redirects = 0\n", '#', log)) &&
(0 == ReplaceMarkedLinesInFile(g_etcSysctlConf, "net.ipv4.conf.default.send_redirects", "net.ipv4.conf.default.send_redirects = 0\n", '#', log))) ? 0 : ENOENT;
}

static int RemediateEnsureIcmpRedirectsIsDisabled(char* value, void* log)
Expand Down Expand Up @@ -3227,7 +3229,7 @@ static int RemediateEnsureSystemdJournaldServicePersistsLogMessages(char* value,
{
UNUSED(value);
return ((0 == InstallPackage(g_systemd, log)) &&
(0 == SetDirectoryAccess("/var/log/journal", 0, -1, 2775, log))) ? 0 : ENOENT;
(0 == SetDirectoryAccess(g_varLogJournal, 0, -1, 2775, log))) ? 0 : ENOENT;
}

static int RemediateEnsureALoggingServiceIsEnabled(char* value, void* log)
Expand Down Expand Up @@ -3306,7 +3308,7 @@ static int RemediateEnsureSyslogRotaterServiceIsEnabled(char* value, void* log)
{
UNUSED(value);
return ((0 == InstallPackage(g_logrotate, log)) && FileExists(g_etcCronDailyLogRotate) &&
(0 == SetFileAccess(g_etcCronDailyLogRotate, 0, 0, 755, log)) && EnableAndStartDaemon(g_logrotate, log)) ? 0 : ENOENT;
(0 == SetFileAccess(g_etcCronDailyLogRotate, 0, 0, 755, log)) && EnableAndStartDaemon(g_logrotateTimer, log)) ? 0 : ENOENT;
}

static int RemediateEnsureTelnetServiceIsDisabled(char* value, void* log)
Expand Down
2 changes: 1 addition & 1 deletion src/common/commonutils/UserUtils.c
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,7 @@ static char* EncryptionName(int type)

static bool IsNoLoginUser(SIMPLIFIED_USER* user)
{
const char* noLoginShell[] = {"/usr/sbin/nologin", "/sbin/nologin", "/bin/false", "/bin/true", "/usr/bin/true", "/dev/null", ""};
const char* noLoginShell[] = {"/usr/sbin/nologin", "/sbin/nologin", "/bin/false", "/bin/true", "/usr/bin/true", "/usr/bin/false", "/dev/null", ""};

int index = ARRAY_SIZE(noLoginShell);
bool noLogin = false;
Expand Down
8 changes: 6 additions & 2 deletions src/common/tests/CommonUtilsUT.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2172,6 +2172,10 @@ TEST_F(CommonUtilsTest, ReplaceEscapeSequencesInString)
EXPECT_NE(nullptr, value = ReplaceEscapeSequencesInString("This\\xis\\xa\\ftest", "xf", 2, ' ', nullptr));
EXPECT_STREQ(value, "This is a test");
FREE_MEMORY(value);

EXPECT_NE(nullptr, value = ReplaceEscapeSequencesInString("This\\.is\\.a\\:test\\", ".:", 2, ' ', nullptr));
EXPECT_STREQ(value, "This is a test\\");
FREE_MEMORY(value);
}

TEST_F(CommonUtilsTest, RemoveEscapeSequencesFromFile)
Expand All @@ -2181,14 +2185,14 @@ TEST_F(CommonUtilsTest, RemoveEscapeSequencesFromFile)
"Test line\\ytwo\n"
"Test\\sLine\\+3+\n"
"Test\\mLine\\l4\n"
"Test\\yLine\\f5";
"Test\\yLine\\f5\\";

const char* targetContents =
" Test line one\n"
"Test line two\n"
"Test Line 3+\n"
"Test Line 4\n"
"Test Line 5";
"Test Line 5\\";

const char* escapes = "xysmlf+";
unsigned int numEscapes = 7;
Expand Down
81 changes: 2 additions & 79 deletions src/modules/securitybaseline/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

## Introduction

SecurityBaseline is an [OSConfig Management Module](../../../docs/modules.md) that audits and remediates the [Linux Security Baseline](https://learn.microsoft.com/en-us/azure/governance/policy/samples/guest-configuration-baseline-linux) from the Azure Compute Security Baselines.
SecurityBaseline is an [OSConfig Management Module](../../../docs/modules.md) that audits and remediates the [Azure Security Baseline for Linux](https://learn.microsoft.com/en-us/azure/governance/policy/samples/guest-configuration-baseline-linux).

The Module Interface Model (MIM) for Security Baseline is at [src/modules/mim/securitybaseline.json](../mim/securitybaseline.json). This MIM implements a single MIM component, `SecurityBaseline`. This component contains sets (pairs or triplets) of reported and desired MIM objects for the individual baseline checks with names that follow the respective check descriptions. For example:

Expand All @@ -11,25 +11,6 @@ The Module Interface Model (MIM) for Security Baseline is at [src/modules/mim/se
Ensure nodev option set on home partition | `auditEnsureNodevOptionOnHomePartition` | `remediateEnsureNodevOptionOnHomePartition`
Ensure users own their home directories | `auditEnsureUsersOwnTheirHomeDirectories` | `remediatesEnsureUsersOwnTheirHomeDirectories`

The SecurityBaseline module implementation is done for all Linux distributions that OSConfig targets today: Ubuntu 18.04, Ubuntu 20.04, Debian 10, Debian 11. In a future release the implementation can be expanded to other distros.

The full set of reported MIM objects for audit is fully implemented.

Remediation is incomplete for 102 remaining desired MIM objects. All these remaining objects are already plugged into unit-tests, functional tests with test recipe and to all management channels OSConfig supports: [Azure Policy](https://learn.microsoft.com/en-us/azure/governance/policy/overview) via [AutoManage Machine Configuration](https://learn.microsoft.com/en-us/azure/governance/machine-configuration/) and the [Universal NRP](../../adapters/mc/), GitOps, Digital Twins via [IoT Hub](https://learn.microsoft.com/en-us/azure/iot-hub/), Local Management, etc. All that remains are implementations for these checks.

The implementation of the checks follows a rule where there are general utility check functions added to [commonutils](../../common/commonutils/) libraries which are then simply invoked from the [SecurityBaseline module implementation](src/lib/). This will allow us to reuse those checks for other security baseline imlementations in the future.

For example there are functions in [commonutils](../../common/commonutils/) that check and set file access:

```C
int CheckFileAccess(const char* fileName, int desiredOwnerId, int desiredGroupId, unsigned int desiredAccess, char** reason, void* log);
int SetFileAccess(const char* fileName, unsigned int desiredOwnerId, unsigned int desiredGroupId, unsigned int desiredAccess, void* log);
```
which then get invoked from several check implementations in [src/lib/securitybaseline.c](src/lib/SecurityBaseline.c), such as for example `AuditEnsurePermissionsOnEtcIssue` and `RemediateEnsurePermissionsOnEtcIssue`.
Note that for the remaining remediation checks there are missing set counterparts to the check functions in [commonutils](../../common/commonutils/) such as for example `CheckFileSystemMountingOption`, `CheckSystemAccountsAreNonLogin`, etc.
## Building the module

Follow the instructions in the main [README.md](../../../README.md) how to install prerequisites and how to build OSConfig. The SecurityBaseline module is built with the rest of OSConfig.
Expand Down Expand Up @@ -70,62 +51,4 @@ sudo modules/test/moduletest ../src/modules/test/recipes/SecurityBaselineTests.j

See instructions in the main [README.md](../../../README.md) on how to enable local management.

When local management is enabled we can use the desired configuration (DC) file at `/etc/osconfig/osconfig_desired.json` and the reported configuration (RC) file at `/etc/osconfig/osconfig_reported.json` to test all desired and reported objects for Security Baseline.

## Continuing implementation

The remediation checks that remain to be fully implemented can be found in [src/lib/SecurityBaseline.c](src/lib/SecurityBaseline.c).

There the MIM object names constants are listed:

```C
static const char* g_remediateEnsureAllAccountsHavePasswordsObject = "remediateEnsureAllAccountsHavePasswords";
...
static const char* g_remediateEnsureUsersOwnTheirHomeDirectoriesObject = "remediateEnsureUsersOwnTheirHomeDirectories";
```

And then later the placeholder check functions that need to be completed:

```C
static int RemediateEnsureAllAccountsHavePasswords(char* value)
{
UNUSED(value);
return 0; //TODO: add remediation respecting all existing patterns
}
...
static int RemediateEnsureUsersOwnTheirHomeDirectories(char* value)
{
UNUSED(value);
return 0; //TODO: add remediation respecting all existing patterns
}
```
By returning 0 (success) these empty placeholder checks do not flag any error in the functional recipe tests. Try turning one to a non-zero value (error) and the respective functional test recipe check will fail, etc.
### Example
An example of a completed check, `auditEnsureAuditdServiceIsRunning` and `remediateEnsureAuditdServiceIsRunning` in [src/lib/SecurityBaseline.c](src/lib/SecurityBaseline.c):
```C
static char* AuditEnsureAuditdServiceIsRunning(void)
{
char* reason = NULL;
CheckDaemonActive(g_auditd, &reason SecurityBaselineGetLog());
return reason;
}
```

```C
static int RemediateEnsureAuditdServiceIsRunning(char* value)
{
UNUSED(value);
return (0 == InstallPackage(g_auditd, SecurityBaselineGetLog()) &&
EnableAndStartDaemon(g_auditd, SecurityBaselineGetLog())) ? 0 : ENOENT;
}
```
These simple functions invoke functions like `CheckDaemonActive` and `InstallPackage` that are implemented in [commonutils](../../common/commonutils/).
Remember, we want to separate the bulk of generic check implementations from this security baseline so that they could be reused in the future for the implementations of other baselines.
Last but not least, make sure to follow [CONTRIBUTING](../../../CONTRIBUTING.md).
When local management is enabled we can use the desired configuration (DC) file at `/etc/osconfig/osconfig_desired.json` and the reported configuration (RC) file at `/etc/osconfig/osconfig_reported.json` to test all desired and reported objects for Security Baseline.

0 comments on commit 18912aa

Please sign in to comment.