ブログを購読する

A lot of system administrators within the Department of Defense already use the Advanced Intrusion Detection Environment (AIDE). This is mainly because of a Security Technical Implementation Guide (STIG) that states that a file integrity checker must be configured to verify extended file attributes. There are a lot of features to AIDE, and the combination of using AIDE with Red Hat Ansible Automation Platform gives you the ability to automate important corrections to your system configuration.

Telling AIDE what to check

AIDE can be configured to check multiple file and folder attributes. For this article, I concentrate on the permissions (p), user (u), group (g), SELinux context (selinux) and SHA512 checksum (sha512). To create a rule that applies those attribute checks, define a name for that rule, then set it equal to each of the attributes separated by a plus sign (+). For example:

MYRULE = p+u+g+selinux+sha512

Then set different files or folders to the newly created file, separated by a dollar sign ($) and a space. For example:

/some/file/or/folder$ MYRULE

Of course, this isn’t all that's needed to create a working configuration file for AIDE, but the rest can be found in the aide.conf man pages. 

Here's an example of an aide.conf file that locks down the attributes, as seen above, to a file named other.txt in the /tmp directory:

@@define DBDIR /var/lib/aide
@@define LOGDIR /var/log/aide

# The location of the database to be read.
database=file:@@{DBDIR}/exampleaide.db.gz

# The location of the database to be written.
database_out=file:@@{DBDIR}/exampleaide.db.new.gz

#Whether to gzip the output to database
gzip_dbout=yes

# Default.
verbose=5

report_url=file:@@{LOGDIR}/exampleaide.log
report_url=stdout

MYRULE = p+u+g+selinux+sha512

/tmp/other.txt$ MYRULE

Running AIDE

Between STIG and your custom rules, aide.conf can get unwieldy. AIDE, by default, uses the /etc/aide.conf file for its rules. But you can pass in the --config (-c for short) option to specify an alternate configuration file. For instance, you could run aide --check --config=/etc/alternateAIDE.conf to check the state of our other.txt file.

AIDE tells you when there's an error in a file:

Start timestamp: 1970-01-01 00:00:00 +0000 (AIDE 0.16)
AIDE found differences between database and filesystemvi aide.conf

Summary:
  Total number of entries: 3
  Added entries: 0
  Removed entries: 0
  Changed entries: 1

------------------------------------------------------------------------------
Changed entries:
------------------------------------------------------------------------------

f pug C S : /tmp//other.txt

------------------------------------------------------------------------------
Detailed information about changes:
------------------------------------------------------------------------------

File: /tmp/other.txt
  Perm :  -rw-r--r– |  ---Sr-xrwt
  Uid :  0 |  2
  Gid :  0 |  2
  SHA512 :  dKZ3fe187ScU81Guyni0jGCsf |  ziTsUMaYngwi6fafR5G1mCMm1
  mX9/T9EZau2unU1+Nk7B5JKad |  hr/Hi6LTHY8Pcd5esWdwCyu3jK
  YIKUi5kV5605/YhvRYDGWJGkx |  eU2JAYR6qiDWpeIjsP7o7Yb3UE
  MHNTZp91sQ== |  mnCzeUVtw==
  SELinux :  unconfined_u:object_r:admin_ | unconfined_u:object_r:mnt_t:s0
  home_t:s0

------------------------------------------------------------------------------
The attributes of the (uncompressed) database(s):
------------------------------------------------------------------------------

/var/lib/aide/exampleaide.db.gz
  SHA1 :  KCr1BWRHAuZRkD4TgxR9RadnBto=
  SHA256 :  pbAUuhUnpyqfHt/OVAytX37hco1QuBC1
   keg107hsWEk=
  SHA512 :  ur3bt+wGZAizlbtIdINVNmdYN4U/EzZq
   50Ryzj8kUutkCVIBEPoQ3apOCBOYMY2p
   eGfeqK3QtmdjFHsy1cUKuw==

End timestamp:  1970-01-01  00:00:00 +0000 (run time: 0m 0s) 

Understanding the AIDE output

A summary of incorrect attributes to the file is in the Changed entries section:

f   pug    C  S  : /tmp/other.txt
  • f: a file
  • pug: permissions, user, and group are incorrect
  • C: checksum mismatch
  • S: SELinux context is incorrect

If one of the attributes is correct, a dot (.) is displayed instead of the character.

The Detailed information about changes section provides a full explanation of differences in the file.

AIDE maintains a database, specified in aide.conf, so that it knows what the attributes are supposed to be. In my example, you can look at the contents of the database with the zcat command. 

@@begin_db
# This file was generated by Aide, version 0.16
# Time of generation was 2023-06-29 00:27:36
@@db_spec name name attr perm inode uid gid sha512 selinux
/tmp/other.txt 0 6442450973 100644 138 0 0  2a0d941f74139a069df6e1df2533159b46875e1eb4cc815f600ea22e1526552556e03adb4b3ae668398f1b5fb60471ebd1c80c6686fa3507e5561adcfcb87==

All the information that the aide.conf could want is stored within the database file. 

Making use of the AIDE output and database

Now you've gotten the output of running the aide command and viewed the contents of the database that AIDE utilizes. So what can you do with this information? You can fix these files, when AIDE shows them as being incorrect, and you can do all of this with Ansible. All you have to do is write a playbook to run your aide command, interpret the errors, get the corrections from the AIDE database file, and then apply these fixes to the files. All of this is IF the checksums aren’t different. If the checksum ends up being different than expected, well then you have to replace the file with the correct one. But Ansible can handle replacing files with ease.

First step is to check for errors. You must use the cmd module to run the aide command and collect the output as seen here:

   - name: Check aide for Errors
     ansible.builtin.shell:
       cmd: "/usr/sbin/aide -V5 -C -c {{ aideconf }} | egrep '^d|^f'"
     register: values
     when: true
     failed_when: values.rc > 1

Now that you can detect errors, you need to get the proper attributes prior to fixing the attributes to what they are. Once again, you have to defer to the cmd module to run the zcat command to view the contents of the database file.

     - name: Get UID Index
       ansible.builtin.shell:
         cmd: AIDEDB=$(/bin/zcat {{ aidedb }} | grep @@db_spec); echo ${AIDEDB[@]/uid//} | cut -d/ -f1 | wc -w
       register: uidpos
       when: true

     - name: Set UID
       ansible.builtin.shell:
         cmd: /usr/bin/id -n -u $(/bin/zcat {{ aidedb }} | grep "{{ afile }} " | cut -d' ' -f"{{ uidpos.stdout }}")
       register: auid
       when: true

Because the database stores all possible attributes that AIDE can handle, you must set the index for the position that the attribute is in, then get that value from the database. In the above example, you can see how to do this for the UID. This must be done for each of the attributes that are checked.

Once the correct attribute is found, you can ensure that the file gets that correct attribute. First, ensure that the file needs to be fixed:

​​     - name: Fix the File(s)
       ansible.builtin.include_tasks:
         file: fix.yml
       vars:
         afile: "{{ item.split(':')[1].split(' ')[1] }}"
       loop: "{{ values.stdout_lines }}"
       when: (item is defined) and (not "C" in item.split(':')[0]) and (item.split()[0] == 'f') and ('p' or 'u' or 'g' or 'S' in item.split(':')[0])

Then make this change:

- name: Fix {{ afile }}
  ansible.builtin.file:
    path: "{{ afile }}"
    owner: "{{ auid.stdout }}"

When the checksum is different

Earlier, I said you could fix the file as long as the checksum is the same. But what do you do when the checksum has changed? In this situation, you replace the incorrect file with the proper one. This can be done with the Ansible modules copy or template. You can still use all the other attributes that were pulled from the database with the module. But the whole file needs to be replaced if the checksum differs.

Automate using Red Hat Ansible Automation Platform

Now that you have some working Ansible playbooks established, it would be best to utilize Ansible Automation Platform to execute them. The platform also enables you to use the REST (REpresentational State Transfer) API. But before you can do this, you must make a few changes to how the playbook operates.

First, ensure that there are credentials that the playbook can use to access the host system. This allows the playbook to run unassisted, meaning the REST API kicks it off and no other interactions are needed. Next, require the variables Prompt at launch. Here is where you need to allow changes to the inventory because you don’t want to run this playbook against all systems just because one system is out of configuration.

Once this is done, you can craft your REST API call to Ansible Automation Platform:

$ curl -k -X POST -u USERNAME:PASSWORD \
https://{ AAP }/api/v2/job_templates/{ Template Number }/launch/  \
-H "Content-Type: application/json" \
-d '{"extra_vars": "{host: '$(hostname)'}"}'

This command sends a POST to the platform website to execute the REST API you want to call. To execute a playbook, curl must be able to log in to Ansible Automation Platform. This is where the -u USERNAME:PASSWORD comes into play. For this to work, you must set up a user account within the platform that only has access to execute this one playbook. 

Also, you can obfuscate the password to comply compliant to the STIG against displaying clear text passwords. First, create a password hash:

$ echo mypassword | base64 > /root/passFile.txt

Decode that password in your curl command with $(cat /root/passFile.txt | base64 --decode) in place of PASSWORD.

The Template Number is a number assigned to the job template based on when it was put into the Ansible platform. To get this number, go to Red Hat Ansible Automation Platform > Templates, then click on the job template name. Afterwards, the Job Template ID appears within the URL of your site. For example, if a Job Template is 9, then your URL might be https://myaap/#/templates/job_template/9/details.

The Content-Type section is where you tell the API, through your POST action, that you have more applicable content to add to the call. Specify that you are inserting the arguments as JSON, and then input your code. In this example, you're saying that the variable you want to reassign is called host and that you want to reassign it to $(hostname). The $(hostname) parameter tells the system to run the hostname command. If this isn’t the same as what you have in your Ansible Automation Platform inventory, then you must make them match. For example, if you have the condensed hostname in Ansible Automation Platform, but on the host you have the FQDN, then you would pass in something like $(hostname | cut -d\. -f1).

Now that you have the REST API command, you can run this on the system to start your playbook automatically. Better yet, you can insert this command as a cron job to automatically return your system to the desired configuration.

AIDE and Ansible Automation Platform

AIDE is a useful and flexible tool that helps you stay compliant with STIG requirements. Combined with Red Hat Ansible Automation Platform, the most important configuration for your systems is automatic.

 


執筆者紹介

Jonathon is a Senior Solution Architect working with Department of Defense. He started with Red Hat in 2022, but has spent 15+ years within the DoD. He has spent the bulk of his career concentrating on automation. The past few years has been spent working on CI/CD Pipelines and utilizing new technologies.

Read full bio

チャンネル別に見る

automation icon

自動化

テクノロジー、チームおよび環境に関する IT 自動化の最新情報

AI icon

AI (人工知能)

お客様が AI ワークロードをどこでも自由に実行することを可能にするプラットフォームについてのアップデート

open hybrid cloud icon

オープン・ハイブリッドクラウド

ハイブリッドクラウドで柔軟に未来を築く方法をご確認ください。

security icon

セキュリティ

環境やテクノロジー全体に及ぶリスクを軽減する方法に関する最新情報

edge icon

エッジコンピューティング

エッジでの運用を単純化するプラットフォームのアップデート

Infrastructure icon

インフラストラクチャ

世界有数のエンタープライズ向け Linux プラットフォームの最新情報

application development icon

アプリケーション

アプリケーションの最も困難な課題に対する Red Hat ソリューションの詳細

Original series icon

オリジナル番組

エンタープライズ向けテクノロジーのメーカーやリーダーによるストーリー