Python foreman_scap_client

Hey everybody,

I am new here and I have a client that requested that I remove the ruby dependency from the foreman_scap_client and replace it with python, since they are an ansible shop. I started the conversion by creating a YAML parser, to avoid installing pyyaml on the target hosts.

I chose python over bash because I am worried about parsing the yaml config file in bash. Also, it isn’t as easy for me to write unittest, like I can with python.

That’s an interesting requirement.

Did you consider using Go instead of Python? Then you’d have a static binary that doesn’t have any dependencies at all.

They really do not want ruby installed, for some reason.

I am not too familiar with Go. After that comment, I googled it and I found out that it may be better to do it in Go. Do you want me to submit a PR, if it is in GO, and not python?

I can’t speak for the maintainers of the current rubygem-foreman_scap_client, but I see no reason why we would be against an alternative implementation.

cc @Marek_Hulan @Ron_Lavi

Thanks @theredgreek for raising it here and opening this topic. I think @ofedoren and @aruzicka may be interested in this, when they have a bit of time. I have a feeling even pure shell script could do, but whatever has lower amount of dependencies the better. I also heard about experiments with pull provider in other language, perhaps it would be useful to have the same stack for it.

You are right. I should just do it in bash, even though all the cool kids write everything in Go and Rust. I guess I would have to develop a bash yaml parser…

Well I think Python is available almost everywhere either, go lang has benefit of being statically compiled. I think it needs a broader discussion on what’s the best fit (shell was just suggestion for consideration, I don’t feel knowledgeable enough to push for a specific technology)

The only reason we write down a YAML config is our Ansible/Puppet code deploys it that way. There’s little reason it needs to be that way. If you look at https://github.com/theforeman/ansible-foreman_scap_client/blob/master/tasks/main.yml then it really is small. Writing some other file (like bash env vars) is easy. Probably easier than trying to parse YAML in bash.

Well, if you really want bash, I can create a simple ini file, instead of yaml. Something like this will work just fine:

[config]
server = foreman_proxy.example.com
port = 8443
timeout = 60
fetch_remote_resources = true
http_proxy_server =
http_proxy_port =
ca_file = /var/lib/puppet/ssl/certs/ca.pem
host_certificate = /var/lib/puppet/ssl/certs/client.example.com.pem
host_private_key = /var/lib/puppet/ssl/private_keys/client.example.com.pem
ciphers = AES256-SHA:AES128-SHA:DES-CBC3-SHA

[policy1]
content_path = /usr/share/xml/scap/ssg/content/ssg-fedora-ds.xml

[policy2]
profile = xccdf_org.ssgproject.content_profile_common
content_path = /usr/share/xml/scap/ssg/content/ssg-fedora-ds.xml

[oval3]
content_path = /var/lib/openscap/oval_content/ansible-2.9.oval.xml.bz2```
[root@idm bash_scap_client]# cat config.ini 
  [config]  
server= foreman_proxy.example.com
port =8443
timeout = 60
fetch_remote_resources = true
http_proxy_server =
http_proxy_port =
ca_file = /var/lib/puppet/ssl/certs/ca.pem
host_certificate = /var/lib/puppet/ssl/certs/client.example.com.pem
host_private_key = /var/lib/puppet/ssl/private_keys/client.example.com.pem
ciphers = AES256-SHA:AES128-SHA:DES-CBC3-SHA

   [1]
content_path = /usr/share/xml/scap/ssg/content/ssg-fedora-ds.xml

[2]
profile=xccdf_org.ssgproject.content_profile_common
content_path=/usr/share/xml/scap/ssg/content/ssg-fedora-ds.xml

[33]
content_path = /var/lib/openscap/oval_content/ansible-2.9.oval.xml.bz2

[root@idm bash_scap_client]# 
[root@idm bash_scap_client]# 
[root@idm bash_scap_client]# cat bb.sh 
#!/bin/bash

ini_file="./config.ini"

get_config_value() {
    local key="$1"
    while IFS= read -r line; do
        if [[ "$line" =~ ^$key[[:space:]]*=[[:space:]]*(.*) ]]; then
            echo "${BASH_REMATCH[1]}"
            return
        fi
    done < "$ini_file"
}

get_section_content() {
    local sec="$1"
    local in_section=0
    local found_section=0
    while IFS= read -r line; do
        # The modified condition
        if [[ "$line" =~ ^[[:space:]]*\[$sec\][[:space:]]*$ ]]; then
            in_section=1
            found_section=1
        elif [[ "$line" =~ ^[[:space:]]*\[.*\][[:space:]]*$ ]]; then
            in_section=0
        elif [[ $in_section -eq 1 && "$line" ]]; then
            echo "  $line"
        fi
    done < "$ini_file"

    if [[ $found_section -eq 0 ]]; then
        echo "Section [$sec] not found in $ini_file."
    fi
}

# Print config[server]
server_val=$(get_config_value "server")
echo "[config][server] = $server_val"

# Print section content
echo -e "\n[$1]"
get_section_content "$1"

Why write a parser for ini files? If you care about configuration, you can simply source a file with variables

$ cat config 
PROFILE=xccdf_org.ssgproject.content_profile_common
$ cat test 
#!/bin/sh

. ./config

echo $PROFILE
$ ./test 
xccdf_org.ssgproject.content_profile_common