Ansible vs Puppet, why I chose Puppet

First off let me make it clear I looked at options for automating configuration quite a while ago and chose puppet as the most secure option. I still believe that to be the case.

The issue I have with options such as Ansible and Chef is that they need ssh keys lying about the place, where the CE release of puppet uses only agents. I have not really looked at Chef as I do not have the computing resources available to run it but I have taken another look at ansible, which is what this post is about.

My reference for quickly installing ansible was https://www.redhat.com/sysadmin/configuring-ansible

It is also important to note that this is my first serious look at ansible, as such many of the issues I have with it may have solutions I have not found yet.

This post is in no way meant to identify one solution as better that the other as there are probably many ansible (and puppet) plugins and tools to emulate each others functionality that I have not had the need to find yet.

Ansible notes quick overview

Ansible requires as well as the ‘ansible’ package installed on the control server

  1. the ansible user defined an all servers to be managed
  2. ssh keys generated and the public key of the ansible control server(s) to be installed on all servers to be managed
  3. an entry in the sudoers file to allow the ansible user on all servers to be managed to run commands as root without password prompting
  4. the sftp service an all servers to be managed to be available

The issues I see with this are

  1. no issues, a normal user
  2. anyone with access to logon to the userid on any of the control servers can logon to any managed server without authentication. Normally not much of an issue as most users setup ssh keys to allow that, but for all servers in the environment is not normal
  3. and once on any server can issue any command they want with root authority
  4. and normally (and the default) is that the sftp service is disabled in /etc/ssh/sshd_config as most users use ‘scp’. This last is probably not a risk but why require services disabled by default when there are alternatives that are enabled by default

Anyway, I created a new class in puppet to define the new group and user on all servers I have puppet managing. I explicitly defined a group, then added the new user to that group, to ensure it was the same on all servers (well away from existing user number ranges rather than defaulting to the next free gid/uid as that would have resulted in different numerical ids on my servers). This new puppet class also copied the public key for the ansible user to all servers being managed.

At that point I had to manually (disclaimer: where I refer to ‘all server’s I only chose a subset of servers to add to a group, as it was getting to be a lot of work for a simple test)

  1. from the control server ssh to all the servers to be managed to reply ‘y’ to the new fingerprint message; I now know that could have been avoided by running the ansible ping command against a ‘group’ containing all servers with the –ssh-common-args=”-o StrictHostKeyChecking=no” option to the ansible command would which have ssh automatically accept the fingerprint
  2. manually on every server to be managed add a new sudoers entry to allow the ansible group to issue any damn command it wants with root authority without needing password authentication (I used the new group rather than adding the new user to an existing group as some tutorials suggest simply because I want existing groups to still have to enter a password when using ‘su’)
  3. manually on all servers to be managed uncomment the sftp entry in the sshd_config file and restart sshd

Only then would a simple ansible ping of the servers work.

Ansible allows groupings of servers so commands can be issued against groups, it also allows for multiple inventories to be used as long as you remeber to use the ‘-i’ option to select the correct one. Output from commands appears to be returned in JSON format so if you want to script handling responses there will be a few scripting tool packages you would need to install.

By default ansible works on a ‘push’ model when changes are pushed out from the control server; however documentation at https://docs.ansible.com/ansible/latest/user_guide/playbooks_intro.html#id16 describes an “ansible-pull” utility that can alter that to an environment where your managed nodes query the control node instead which is apparently best for large environments. I have not tried that and presumably it would require more messing about with ssh keys.

Puppet notes quick overview

To obtain a working puppet CE environment is simply a case of

  1. ensure your DNS can resolve the server name ‘puppet’ to the server you will install ‘puppetserver’ on
  2. firewall ports, agents poll the master on port 8140 so that needs to be open on the master server
  3. installing ‘puppetserver’ on the control node and starting it
  4. installing the ‘puppet’ (agent) package on each server to be managed and starting it

At this point on the ‘puppet’ master a ‘puppet cert list’ will show all your servers waiting for you to ‘puppet cert sign hostname’ to allow them to use the master. It should also be noted that there is a puppet configuration option to permit ‘autosigning’ which I switched on when first adding all my servers before switching off again that makes it easer to enroll all your agent servers when first installing a puppet solution.

What the puppet CE solution does not provide is an equivalent of the ansible ‘–become’ option that allows anyone logged on to the ansible user on the control node to issue any command they desire as root without authentication on any of the managed server nodes… I personally think not being able to do so is a good thing.

However if you really wanted that facility you could configure sshd to permitrootlogin on all your puppet managed nodes and setup ssh keys from the puppet master and simply use ‘ssh hostname anycommand’ to issue commands as root on any managed server, so if you want to open the gaping hole ansible opens you can do so anyway… although I would suggest adding a new user and allowing it to su without a password exactly like ansible does; so you don’t need ansible for that dangerous feature.

Puppets equivalent of the ansible playbook groupings by function are puppet profiles and roles, and a server may have multiple of each. It also supports multiple environments (ie: a test/dev as well as the default ‘production’), there is no reason it could not also contain environments such as webservers, databases etc but application specific grouping is probably better left to puppets use of role and profile functions to group application configurations.

Its grouping of servers themselves is done in the manifest/site.pp where groups of servers can be defined by wildcard host name or selected as lists of individual hosts and given the roles/modules associated with them.

Puppet works on a ‘pull’ model, the puppet agents on each managed note poll the master for any updates.

Usage differences

Ansible uses YAML syntax, which is very dependant on correct indenting. The latest versions of puppet have their own syntax although still support ruby for backward compatibility and puppet class files do not care about indenting as long as you have the correct number of braces and brackets. I also find puppet configurations easier to read.

An example of a ansible playbook to install httpd

---
- hosts: webservers
  remote_user: ansible
  become: yes
  tasks:
  - name: Installing apache
    yum:
      name: httpd
      state: latest
  - name: Enabling httpd service
    service:
      name: httpd
      enabled: yes
    notify:
      - name: restart httpd
  handlers:
  - name: restart httpd
    service:
      name: httpd
      state: restarted

Then run the command "ansible-playbook -i idenityfile playbookname.yaml"

The puppet equavilent

class httpd {
   package { 'httpd':
     ensure => installed,
   }
   service { 'httpd':
     ensure => running,
     enable => true,
   }
} # end httpd class

Then ensure the new class is added to the site manifest

node 'somewebserver','anotherwebserver' {
   ...some stuff
   include httpd
}

Deployment is automatic although dependant upon the agents poll interval; and immediate refesh can be done from the agent side if impatient.

Both ansible and puppet provide a way to publish configuration files and restart services when a file changes

Ansible is a bit confusing, I am not sure if the below will work or even where it belongs in a playbook.

tasks:
  - name: Copy ansible inventory file to client
    copy: src=/some/path/to/a/file/httpd.conf dest=/etc/httpd/httpd.conf
            owner=root group=root mode=0644
    notify:
         - restart apache
handlers:
    - name: restart apache
      service:
        name: apache
        state: restarted

In Puppet there is no ‘handler’ required to be defined as the ‘notify’ can be part of the copy statement. And I personally bundle configuration files for an application within the application class to make them easy to find.

class httpd {
   package { 'httpd':
      ensure => installed,
   }
   service { 'httpd':
      ensure => running,
      enable => true,
   }
   file { '/etc/httpd/httpd.conf': # file resource name, standard is use the name
      path => '/etc/httpd/httpd.conf', # destination path
      ensure => file,
      owner => 'root',
      group => 'root',
      mode => '0644',
      source => 'puppet:///modules/httpd/httpd.conf', # source of file to be copied
      notify => Service['httpd'],
   }
} # end httpd class

Additional puppet features I use

One very usefull feature is managing logs. While I am sure most sites have implemented  log maintenance scripts I found using puppet to manage its own logs easier than creating new scripts, an example is below.

node 'puppet' {
   ... some roles and classes being used
   tidy { "/opt/puppetlabs/server/data/puppetserver/reports":
      age => "1w",
      recurse => true,
   }
}

There is also templating which allows files to be customised for each server, it is used to read a template file from which substitutions are made to provide the input contents for a file to be copied to an agent node. This means that configuration files that would be identical in all but a few key parameters can when being copied to an agent be built on the fly with the correct values set rather than having to have a seperate file for each agent to handle those small differences; and the values can also be set using ‘facter’ information.

The below example I use to install bacula-fd (bacula-client package) on all my servers and ensure the FD name is unique by using the hostname as part of the FD name, and to bind it to the default ip-address rather than the default of listen on all interfaces… the one template creates a unique configuration for all my servers as soon as the puppet agent starts.

For example a snippet from a template (epp) file may be

<%- | String $target_hostname,
String $target_ipaddr
| -%>
# Bacula File Daemon Configuration file
# FileDaemon name is set to agent hostname-fd
...lots of stuff
# "Global" File daemon configuration specifications
FileDaemon { # this is me
   Name = <%= $target_hostname %>-fd
   FDport = 9102 # where we listen for the director
   FDAddress = <%= $target_ipaddr %>
   WorkingDirectory = /var/spool/bacula
   Pid Directory = /var/run
   Maximum Concurrent Jobs = 20
}
...lots more stuff


And the class file would contain the below to get the facter
hostname and use it in creating the file contents

class bacula_fd (
  $target_hostname = $facts['hostname'],
  $target_ipaddr = $facts['networking']['ip'],
){
   ...lots of stuff
   # Use a template to create the FD configuration file as it uses
   # the hostname to customise the file.
   $template_hash = {
     target_hostname => $target_hostname,
     target_ipaddr => $target_ipaddr,
   }
   file { '/etc/bacula/bacula-fd.conf':           # file resource name
       path => '/etc/bacula/bacula-fd.conf',      # destination path
       ensure => file,
       owner => 'root',
       group => 'bacula',
       mode => '0640',
       content => epp('bacula_fd/bacula-fd.conf.epp', $template_hash),
       notify  => Service['bacula-fd'],
     }
   ...lots more stuff
} # end class

It should be noted that ansible documentation makes reference to templates. From what I can see that term is used in a different way for ansible as I can’t see how they can interact with a ansible copy task. I have found an example of ansible using variables in a similar way as below so I assume it is possible, just hard to find documentation on.

   - name: create default page content
     copy:
       content: "Welcome to {{ ansible_fqdn}} on {{ ansible_default_ipv4.address }}"
       dest: /var/www/html/index.html
       owner: webadm
       group: web
       mode: u=rw,g=rw,o=r

One other ability of puppet I make heavy use of is its ability to query facter information, one class file can with use of if/else statements run blocks of code depending on OS version so an application class file can install the correct packages for CentOS7, the correct but different packages for CentOS8, completely different packages for each of Fedora30/31/32, to result in the application installed and running (or skipped if the OS does not support it). I have not seen any ansible yaml files that provide that so assume multiple inventory files are needed, one for each OS type.

For servers with firewalld I can use a single class file with all common services and ports and if/else to provide all customisation for different servers in one place using rich firewalld rules (note: ansible seems to have only the normal rules for services and ports but not rich rules, but it may just be another case of ansible documentation/examples being hard to find). Looks like for something similar in ansible you would have seperate yaml files (playbooks) for each application type, or in other words not possible to contain all firewall rules for the entire infrastructure in one file if using ansible.

The above two paragraphs highlight an issue for me, as I believe one of the key reasons for using a configuration product is that configuration information can be easily accessed in one place and that one place can be deployed, if multiple files are used you may as well just have those multiple files managed on thier multiple servers as ansible is effectively just a backup copy doing pushes, if you have to maintain multiple files exactly the same file placement can be achieved by editing the files on their individual servers and keeping copies on a backup server; or pointless as you of course backup your servers.

Puppet examples of a class using if/else (now how would you put this into a single ansible yaml file ?; you don’t you create lots of server groups based on OS I would assume with seperate playbooks)

   if $facts['hostname'] == 'phoenix' {
      ...do something unique for this server
   }
   # note: htmldoc is not available on CentOS8 except from snap, so need a check here
   if ( $facts['os']['name'] == "CentOS" and $facts[os][release][major] < 8 ) { package { 'htmldoc': ensure => installed,
      }
   } else {
      if ( $facts['os']['name'] == "Fedora" ) {
         package { 'htmldoc':
           ensure => installed,
         }
      } # else package not available so do nothing
   }

And of course a puppet class has case statements, which can either do actions or set variables to be used later in the class.

   # These rules below are specific to OS releases where the command syntax is different
   # note: 'facter -p' run on a server provides factor details
   case $facts['os']['name'] {
      'RedHat', 'CentOS': {
                             case $facts['networking']['hostname'] {
                                'region1server1': { $fname = "centos_openstack_controller.cfg" }
                                'region1server2': { $fname = "centos_openstack_compute.cfg" }
                                default:            { $fname = "centos.cfg" }
                             }
                          }
      'Fedora':           { $fname = "fedora.cfg" }
      default:            { $fname = "fedora.cfg" }
   }
   file { 'nrpe_os_specific':
      path => "/etc/nrpe.d/${fname}",
      ensure => file,
      owner => 'root',
      group => 'root',
      mode => '0644',
      source => "puppet:///modules/nrpe/${fname}",
      notify  => Service['nrpe'],
    }

As a puppet class is effectively the equivalent of an ansible yaml file playbook I consider puppet to be better for self documenting, as a single class can contain all the login needed to deploy an application on multiple OSs where I believe ansible may require a playbook per OS; although I may later find I am incorrect in that assumption.

Features Ansible provides that Puppet CE does not

The most glaring difference is the way that the ansible command line can be used to issue commands to multiple hosts at once. I have never had a need to simultaneously shutdown multiple databases or webservers on multiple hosts at once although I can see the power of it.

The most useful thing I can think of to do which such power is to have a script that runs a playbook on each server to do lots of netstats, pings, nmap etc and have a script process the results to build a map of you network and its responsiveness. But then there are probably existing tools for that.

Ansible also has a ‘schedule’ tag that can be used in tasks to add crontab entries. I can see how that would be usefull when deploying new software.

Where I can see it being useful is the ability to ad-hoc copy a file to multiple servers with one command, although for config files that need managing puppet does that well.

The documentation says that playbooks can orchestrate steps even if different steps must bounce between machines in a particular order. This will be useful to me as openstack yaml stack files are limited in the information they can pass to the instances they are creating so ansible could replace some of my custom scripts… the damb ssh fingerprint prompt the first time a server is connected to by ansible which totally breaks any automation can be suppressed with the option ‘ –ssh-common-args=”-o StrictHostKeyChecking=no” ‘ used the first time a command is run to allow that.

Complience and consistency and sumary

Ansibles ‘push’ method requires user interaction, although I am sure a cron job could be setup to run every hour across all servers to ensure configuration files and software packages are in an expected state. It is entirely possible RedHat have a commercial dashboard and scheduling system to do just that, but you don’t get that from installing ansible.

Puppet on the other hand will poll the master at regular intervals and replace any configuration file it finds changed with the one from the puppet master; ensuring that is miscreants are modifying critical configuration files all their changes are undone in a totally hands-off way.
Puppet also at the agent poll interval starts services that should be running that were stopped which is nice to have done automatically rather than having to issue ansible commands periodically to see if state needs changing. It is also ‘not-nice’ when you want something stopped and find stopping the puppet agent before stopping an app does not have the desired effect when nagios event handlers restart puppet which restarts the app which… is a reason to document every automation tool in place and what they manage in a pretty flow diagram.

An example of what not to do, in my environment if I want to modify iptables I need to stop docker; if I stop docker a nagios/nrpe event handler will see it is down and reload the iptables and start docker; so I have to stop docker and nrpe; then puppet pokes its nose in and expects nrpe up so starts it resulting in nrpe loading iptables and starting docker again; so I have to stop puppet, nrpe and docker to make a change; do I want a stray ansible command issued to start things again as well ?, no. Si iseally there should only be one tool to manage application persistence in use at a time, on the other hand if nrpe crashed I would want it automatically restarted… where do you draw a line ?, well in a flow diagram so you know what applications to stop in order to keep them stopped.

So my summary of the two is that Puppet is best for configuration management, and ansible is best for issuing commands to multiple places at once. I may leave ansible installed along with puppet a while to see if ansible can be of use to me. For ensuring services are running nagios/nrpe monitoring and nagios/nrpe event handlers to try to restart failed tasks is still the best opensource/free application persistence tool and configuration management tools like ansible/puppet/chef should avoid stepping on its toes.

Other notes

I did use ansible to install one package using a ‘lynx.yaml’ file that used ‘hosts:desktops’ which had two hosts defined under it in the hosts_local inventory, which after I updated the sudoers file on the two machines I was testing against worked.

The errors from a failed change (before I updated sudoers) is below… now I ask you how could you automate scripting to check for errors in output like this. Also Fedora 32 is the latest release of Fedora and ‘/usr/bin/python –version’ returns ‘Python 3.8.5’ so ansible really needs its checks updated as not all OS’s have renamed it to python3.

[ansible@puppet desktops]$ ansible-playbook -i /etc/ansible/hosts_local lynx.yaml

PLAY [desktops] ***************************************************************************************************************************

TASK [Gathering Facts] ********************************************************************************************************************
[DEPRECATION WARNING]: Distribution fedora 32 on host vmhost3 should use /usr/bin/python3, but is using /usr/bin/python for backward 
compatibility with prior Ansible releases. A future Ansible release will default to using the discovered platform python for this host. 
See https://docs.ansible.com/ansible/2.9/reference_appendices/interpreter_discovery.html for more information. This feature will be 
removed in version 2.12. Deprecation warnings can be disabled by setting deprecation_warnings=False in ansible.cfg.
ok: [vmhost3]
[DEPRECATION WARNING]: Distribution fedora 32 on host phoenix should use /usr/bin/python3, but is using /usr/bin/python for backward 
compatibility with prior Ansible releases. A future Ansible release will default to using the discovered platform python for this host. 
See https://docs.ansible.com/ansible/2.9/reference_appendices/interpreter_discovery.html for more information. This feature will be 
removed in version 2.12. Deprecation warnings can be disabled by setting deprecation_warnings=False in ansible.cfg.
ok: [phoenix]

TASK [Ensure lynx is installed and updated] ***********************************************************************************************
fatal: [vmhost3]: FAILED! => {"msg": "Missing sudo password"}
fatal: [phoenix]: FAILED! => {"msg": "Missing sudo password"}

PLAY RECAP ********************************************************************************************************************************
phoenix                    : ok=1    changed=0    unreachable=0    failed=1    skipped=0    rescued=0    ignored=0   
vmhost3                    : ok=1    changed=0    unreachable=0    failed=1    skipped=0    rescued=0    ignored=0   
Posted in Automation, Unix | Comments Off on Ansible vs Puppet, why I chose Puppet

Getting AWSTATS running on CentOS8

First let me make it clear I prefer webalizer to awstats; however webalizer is not available in the repositories after CentOS7. It is also worth noting that both webalizer.org and mrunix.net (where webalizer sources/downloads could be manually obtained) appear to be no longer online and there does not appear to be a github source for it either.

Another issue is that webalizer requires, and awstats can use the free geo-ip ‘lite’ country databases from maxmind to resolve ip-addresses to countries; however while still free from December 2019 you must create an account to download them (https://dev.maxmind.com/geoip/geoip2/geolite2/) and we all have far too many internet accounts as it is.

So with webalizer being unavailable and awstats fortunately able to use another free (although out of date) ip lookup database awstats is the logical replacement.

Preparation work

For awstats to use the free ip database you need an additional perl module, you should therefore try to install that first.

perl -MCPAN -e shell
install Geo::IPfree

For awstats to produce PDF reports the ‘htmldoc’ package needs to be installed; that unfortunately is also not in the CentOS8 repositories and would need to be installed using snap. Documentation in installing htmldoc using snap is at https://snapcraft.io/install/htmldoc/centos; however I did not install it as snap is a bloody pain to use and chews up a lot of system resources; the only time I installed snap to get a package I immediately uninstalled it all again. Fortunately if you do not need to generate PDF reports that is no issue. Should the ‘htmldoc’ package ever make it to the CentOS8 repositories I will install it from those at that time.

It is also very important to note that awstats expects all httpd log messages to be in a specific format or it will refuse to process the logs. Anywhere you are defining log files you should use

LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" combined
CustomLog "logs/access_log" combined

You would normally need to set these in a default installation in /etc/httpd/conf/httpd.conf and /etc/httpd/conf.d/ssl.conf; but set anywhere you define log files.

It is also important to note that awstats wants to process only one file, and expects it to be in date order, so if you have seperate access_log and ssl_access_log messages (the default in a CentOS install) you will have issues. What I have done, as I wish to keep seperate files to identify the different traffic for other utilities, is leave those alone but add as an additional entry anywhere that needs logging a new logfile all will share, for example the below line I would have in both the main httpd.conf and ssl.conf files so as well as the unique files all messages from both are also logged to a combined file in the correct LogFormat tagged as ‘combined’.

CustomLog "logs/combined_access_log" combined

This permits the single LogFile name needed by awstats to be used against a file that does contain all the log messages needed.

If your logfiles were not already using the correct format all the existing logfiles cannot be processed by awstats. If you have just configured a new ‘combined_access_log’ as discussed above in the required format restart httpd to start aquiring messages in that file that you can use in later steps.

Installing, configuring, populating

To install awstats it is simple a case of ‘dnf -y install awstats’.

After which there is a bit of editing involved, while it does create a /etc/httpd/conf.d/awstats.conf file all the entries in there refer to /usr/local/awstats but the package files are actually installed in /usr/share/awstats. The best option is to remove everything in that file and replace it with the contents below. The file below is based upon the documentation at https://tecadmin.net/steps-to-configure-awstats-on-centos-and-rhel-system/ but modified to include access to the documentation directory (as being able to browse the documentation is useful) as well as using the latest ‘require ip’ syntax (as ‘allow from’ and ‘require host’ are obsolete and not able to be used without loading compatibility modules).

#
# Content of this file, with correct values, can be automatically added to
# your Apache server by using the AWStats configure.pl tool.
#
# If using Windows and Perl ActiveStat, this is to enable Perl script as CGI.
#ScriptInterpreterSource registry
#
# Directives to add to your Apache conf file to allow use of AWStats as a CGI.
# Note that path "/usr/local/awstats/" must reflect your AWStats install path.
Alias /awstatsclasses "/usr/share/awstats/wwwroot/classes/"
Alias /awstatscss "/usr/share/awstats/wwwroot/css/"
Alias /awstatsicons "/usr/share/awstats/wwwroot/icon/"
ScriptAlias /awstats/ "/usr/share/awstats/wwwroot/cgi-bin/"

Alias /awstatsdoc "/usr/share/doc/awstats/"
<Directory /usr/share/doc/>
   DirectoryIndex index.html
   Require all granted
</Directory>

<Directory "/usr/share/awstats/wwwroot">
   Options +ExecCGI
   AllowOverride None
   Require ip 192.168.1.0/24
</Directory>
<IfModule mod_env.c>
   SetEnv PERL5LIB /usr/share/awstats/lib:/usr/share/awstats/plugins
</IfModule>

You then need to ‘cd /etc/awstats’. You will find a model.conf, awstats.localhost.localdomain.conf, and awstats.HOSTNAME.conf (where HOSTNAME) is the name of your host have been created. Copy any one of them to awstats.your.website.name.conf where your.website.name is your website name, for example if your host is webserver.example.org you need a file named awstats.webserver.example.org.conf; and then open the new file in your favourite editor.

  • set the ‘LogFile’ value; if you followed my recomendation of using a combined_access_log above set it to that, otherwise set it to the only one of the files you want to process (masks such as “*access.log” will not work; also logresolvmerge as documented in template file did not work for me)
  • set the ‘SiteDomain’ value to match your website name
  • set the ‘HostAliases’ values
  • set the ‘DNSLookup’ value to ‘0’ unless you want to do lots of dns lookups
  • review the comments for ‘EnableLockForUpdate’ and decide if you want to set it to ‘0’
  • if using dns lookups set the ‘SkipDNSLookupFor’ list
  • you may want to set ip-addr ranges for clients that can browse the results in the ‘AllowAccessFromWebToFollowingIPAddresses’ list; although you are also limiting by ip-address in the conf.d/awstats.conf file above
  • set the ‘SkipHosts’ value to avoid reporting on internal hosts that would skew the report values (such as nagios/nrpe or other health checking activity)
  • Seach on ‘LoadPlugin=”geoipfree”‘ and uncomment it (if you installed the Geo::IPfree mentioned at the start of this post)
  • set ‘UseFramesWhenCGI=0’, browsers such as FireFox will refuse to display any pages presented in frames aas frames are inherently insecure

You should also note the entries for URL alias references (/awstatscss,/awstatsicons etc) throughout the file, initially leave all those as the defaults as they match what is set in the /etc/httpd/conf.d/awstats.conf file; and they must match.

Before you are able to do anything you need to create some initial data. If you have had to change your log message formats after restarting httpd just wait for a while, you need some log messages available before you are able to initialise awstats.

When you have a reasonably large collection of logged messages simply run the command below replacing ‘webserver.example.org’ with your hostname, which is the part between awstats. and .conf in the /etc/awstats directory; the example below would use the config file ‘awstats.webserver.example.org.conf’.

perl /usr/share/awstats/wwwroot/cgi-bin/awstats.pl -config=webserver.example.org -update

Keeping the statistics up to date

Installing the package creates a file that will perform updates for all the hosts you have configured in seperate conf files for each host in /etc/awstats. That job is /etc/cron.hourly/awstats.

So once you have all the configuration in the steps above done your data is updated automatically.

You may want to create a report job in /etc/cron.daily or /etc/cron.monthly to produce reports that can be archived in case you need to initialise your collected statistics at some point.

Viewing the statistics with a web browser

I suppose after all that work you actually want to look at some of the statistics. If you use the example /etc/httpd/conf.d/awstats.conf file above you would point your web browser at https://yourhostname/awstats/awstats.pl?config=webserver.example.org where the config value matches the configuration file you used.

If you are used to webalizer output you may find it frustrating to hunt down information in. Some information is also incorrect with default settings although I suppose it is possible to correct it with a lot of manual effort in the configuration file and log format statements. But as webalizer is no longer available awstats is a viable replacement; if a lot harder to get working.

The ability to generate static reports may be useful for archiving, but I have not tested that as they would be most useful in PDF form, which as mentioned above awaits the ‘htmldoc’ package reaching the CentOS8 repos one day.

Posted in Unix | Comments Off on Getting AWSTATS running on CentOS8

Setting up and using a local (insecure) Docker registry service container

The first thing to note about this post is that it sets up an insecure local registry facility using the standard registry container. This type of registry is ideal for local ‘internal network’ development use. It is also suitable for standalone docker, no need for a docker swarm.

An important thing to note is that the regsitry server itself is not insecure, it wants TLS traffic by default; however it supports insecure traffic if the client requests it. To use insecure access to the local registry server it is the clients of the registry server that are reconfigured to request insecure communication; and the registry server will permit it.

Configuring an insecure Docker registry allows anyone to ‘push’ images to your registry without authentication, so it must only be used for internal use; never internet facing.

You will notice the official documentation for installing a docker registry container as an insecure installation states that not even basic authentication can be used for an insecure configuration. The may actually be incorrect as the configuration document at https://docs.docker.com/registry/configuration/#htpasswd states basic authentication can be configured without TLS… although the user/password information will be passed in clear text as part of the http header so it seems it is only recomended not to use it.

Installing a local Docker registry

A local registry server can be installed on any server already running Docker or docker-ce. How to setup a local registry server is covered at https://docs.docker.com/registry/deploying/ however it is a little fuzzy on configuring insecure traffic.

The actual document on configuring for insecure use is https://docs.docker.com/registry/insecure/ but it omits the rather import detail that the “insecure-registry” settings must be set on all the clients, not on the docker server running the container. There is a lot of confusion about that easily seen from all the questions about it in forums where everyone assumes it is set on the server providing the registry container; it is set on all the clients. Also note in this document it does state that secure traffic will always be tried first in all cases, the “insecure-registries” entry just allows fallback to insecure traffic, so changing your registry to be secure at a later time is trivial.

It is also important you are aware that by default the container running the registry uses volume persistent storage within the container; this means that while it will survive stopping/starting the container should you delete the container your data will be lost. If you intend to be deleting/reconfiguring the container a lot you probably don’t want to use that default.

I implemented my local registry container to use a directory on the docker host filesystem. The run command I used is below, the importance of the REGISTRY_STORAGE_DELETE_ENABLED environment variable I will discuss later in this post in managing the registry; you would probably normally leave it disabled (the default).

docker run -d \
  -p 5000:5000 \
  --restart=always \
  --name registry \
  -v /home/docker/data:/var/lib/registry \
  -e REGISTRY_STORAGE_DELETE_ENABLED="true" \
  registry:2

Remember to open firewall port 5000 on your registry docker host, and if any client docker hosts have rules blocking outbound traffic ensure the port is opened on those also.

Configuring the Docker client servers for insecure traffic

The “insecure-registries” setting needs to be configured on the servers running Docker that are to be clients of this local registry. On those servers add to (or create if it does not exist) the file /etc/docker/daemon.json and restart the docker service on those clients when you have done so.

{
  "insecure-registries" : [ "hostname-or-ipaddr:5000" ]
}

Of course if you also wish to use the registry on the Docker server running the regisry container set it there also.
If you have a local DNS server you should use the hostname rather than an ip-address.

Pushing and Pulling using your local Docker registry

In the examples here I have the regsitry container running on a host with a DNS entry of docker-local.myexample.org

To push images to your local registry they must be tagged to refer to your local registry. For example if you have an image named busybox:latest you would

docker tag busybox:latest docker-local.myexample.org:5000/busybox:latest
docker push docker-local.myexample.org:5000/busybox:latest

If you get an error along the lines of https expected but got http check your client insecure-registry entries again. On the client the command ‘docker info’ will list the insecure registries configured near the eand of the reponse.

It is also important to note that the tag is a pointer to what is already there, you could in the above example ‘docker image rm busybox:latest’ (which only removed the old pointer) and change your docker run command to run docker-local:5000/busybox:latest instead of busybox:latest which would work perfectly well.

If you have a hostname:port/ in the image name that hostname:port is the registry used, if omitted only the registry at docker.io is used; which you obviously do not have permission to push to.

Once you have images pushed to your local registry you can pull them with the same syntax

docker pull docker-local.myexample.org:5000/busybox:latest

Managing your local registry

Querying your local registry

Useful commands such as ‘docker search’ only apply to the docker.io registry. You obviously need a way of managing your local registry and keeping track of what is in it.

The ‘v2’ interface of the Docker registry container provides a way of looking up what is in your local registry. This can be done with any web browser.

Remembering that we have no certificates and are using an insecure registry the following URLs are useful for determining what is in your registry. The examples use the same example hostname and image.

To see what is in the local registry

    http://docker-local.myexample.org:5000/v2/_catalog
    http://docker-local.myexample.org:5000/v2/_catalog?n=200

Note the second example above, the default response is only the first 100 entries; that can be changed with the n value. If using the default and there are more than 100 entries a link to click on to provide the next 100 entries is provided in the response.

The above URL only displays the image names. You will need to see what tagged versions are stored in the registry. Should yiu have an image names ‘busybox’ and want to see all tagged versions the URL would be

http://docker-local.myexample.org:5000/v2/busybox/tags/list

Of course there are scripts on github that make all that a lot easier and can be run from the command line where most Unix developers work. One is discussed below.

Deleting images from your local registry

The Docker registry ‘v2’ API provides a DELETE facility. As it is possible to corrupt images if you delete incorrect layers it is better to use some of the utilities users have made available on github for that purpose.

I would suggest, and the examples below use, this utility…

cd ~
mkdir git-3rdparty
cd git-3rdparty
git clone https://github.com/byrnedo/docker-reg-tool.git
dnf -y install jp

The below examples of using the script obviously use different examples than busybox above to provide a few more entries to play with; but using the script is fairly simple as seen below. Note that we use “INSECURE_REGISTRY=true” as we have setup an insecure registry; if using TLS there are parameters to provide certs and credentials which are explained on the github page.

[root@gitlab ~]# cd ~git-3rdparty/docker-reg-tool
[root@gitlab docker-reg-tool]# INSECURE_REGISTRY=true ./docker_reg_tool http://docker-local:5000 list
ircserver
mvs38j
[root@gitlab docker-reg-tool]# INSECURE_REGISTRY=true ./docker_reg_tool http://docker-local:5000 list ircserver
f32
f30
[root@gitlab docker-reg-tool]# INSECURE_REGISTRY=true ./docker_reg_tool http://docker-local:5000 delete ircserver f30
DIGEST: sha256:355a3c2bd111b42ea7c1320085c6472ae42bc2de7b13cc1adf54a815ee74fa45
Successfully deleted
[root@gitlab docker-reg-tool]# INSECURE_REGISTRY=true ./docker_reg_tool http://docker-local:5000 list ircserver
f32
[root@gitlab docker-reg-tool]#

However for the delete example above you will most likely get a response as below

[root@gitlab docker-reg-tool]# INSECURE_REGISTRY=true ./docker_reg_tool http://docker-local:5000 delete ircserver f30
DIGEST: sha256:355a3c2bd111b42ea7c1320085c6472ae42bc2de7b13cc1adf54a815ee74fa45
Failed to delete: 405

Refer back to by ‘docker run’ command above, and the parameter I said I would explain later, specifically the environment parameter ‘REGISTRY_STORAGE_DELETE_ENABLED=”true”‘. If that parameter is not set it is not possible to delete entries from the registry… so for a local regsitry you should probably set it unless you intend to keep infinate amounts of images in your local registry.

Reclaiming space in the registry filesystem

Using the registry ‘v2’ API to delete an image/tag from your local registry does not delete anything except the references to the object. This is normally ideal as it preserves layers; for example if you have seven images based on centos:8 they can share the same base layer to minimise space, deleting one of them removes just the pointer. If all are deleted the layers remain as you may push another centos:8 based image in which case a pointer can be re-added rather than the push having to send all layers of the new image.

However there will be occasions when you do want to remove all unused objects from the registry. For example your local development may have been using f30 base images but all development has moved to f32 so you want to reclaim all space used by the old f30 layers.

In order to do so you need to run the registry garbage cleaner to remove the obsolete objects.

The registry garbage cleaner is provided with the docker registry image, and must be run from within the running container.

It should only be run when the registry is not in use; in read-only mode or with user access locked out in some other way. While there is nothing to stop you running it while the registry is in active use any ‘push’ command in progress while the registry garbage cleanup is in progress will result in a corrupt image being stored.

With all that being said, to change to read-only-mode is a case of stopping the regsitry container/deleteing the registry container/redefining-starting the registry in read-only-mode/doing the garbage collect/stopping the registry container/deleting the registry container/redefining starting the registry on write mode again… and if you are going to do all the work you may as well throw in an extra set of stops/deleted/reconfigures to turn on/off the ability to delete entries. Personally I think for a local development registry that is too much complication and I leave registry storage delete enabled and do not switch to read-only-mode (if you need to ensure read-only much simpler to disable the firewall rule allowing access to port 5000 and add it back when done).

To actually perform the garbage reclaimation; on the server hosting Docker container for the registry simply

docker exec -ti regsitry /bin/sh

bin/registry garbage-collect --dry-run /etc/docker/registry/config.yml
exit

Obviously remove the ‘–dry-run’ flag when you are ready to really perform the garbage cleanup.

Some notes on restricting external access, apache proxy

As noted earlier using an insecure registry supposedly prevents any authentication method being used. While easy to switch to TLS that does not magically enable authentication, a lot of extra work is required.

Setting up basic authentication is reasonably easy, and it seems this can be done in clear traffic without TLS if you really want to. However that will the require all your users to authenticate for not just ‘push’ but also for ‘pull’ requests (via ‘docker login’). That limits it’s usability as ideally users should be able to pull anonymously or why bother making it available to them in the first place.

The simpleset way of setting up authentication where users that ‘push’ must authenticate but anyone can ‘pull’ without authentication would seem to be using Apache as a authenticating proxy, by playing with the recipe provided at https://docs.docker.com/registry/recipes/apache/; changing the restriction on GET to allow everyone should do it. And of course create a conf.d file for the virtualhost in your existing web server configuration rather than use the httpd container the example uses. This however still uses basic htaccess authentication although on the web server itself, the registry itself is still insecure using this method but as that would normally run on a seperate machine to the webserver with all public facing traffic having to go via the web server to reach it that is not so much of an issue. Also notice that the example does not really make sense (htpasswd is run in a httpd docker container and the groups created outside the container), but it does at least indicate that all the auth is done by apache in the apache container and not the by the registry container.

One note on the Apache authentication proxy method linked to above is that the document lists as a drawback that the TLS certs must be moved to the webserver as the TLS endpoint instead of on the registry, and the proxy is to the registry at http://servername:5000/v2. Yes, it does mean you must leave your registry container configured as discussed above in that no certificates are configured on the registry itself, but you no longer need to configure the “insecure-hosts” entry on your clients if they pull via the proxy as they will now get https reposnses (provided by the web server).

Also if you already have a public facing Apache webserver with valid certificates an apache proxy setup may be the way to go as yiu do not need to obtain additional certificates. The issues and benefits with the apache proxy approach are

  • issue:if you have not used ‘docker login’ to login to your proxy a ‘push’ request results in ‘no basic auth credentials’, however a ‘pull’ request returns the web servers 400 error page (with ‘require valid user’ for GET even if the option to not proxy apache httpd error pages is commented). However if you do not restrict GET access that is not an issue
  • benefit: after using ‘docker login’ users can push/pull as expected
  • benefit: configuring the GET method rule to “all granted” rather than “valid user” allows any user to ‘pull’ from your registry, which would be the only point of exposing it to the internet. ‘push’ and delete requests are still denied via the proxy if the user has not isued a ‘docker login’ to you local registry
  • issue: the mapping must be to the path /v2 as that is what the docker command requires; an open invite to hackers ?. Exactly what can be done with registry GET requests, are any destructive, thats an unknown
  • benefit: you are not required to obtain any new ssl certificates, if your website already has working certificates you can configure the same certificates your website already uses in the virtual host entry for the registry

Using the apache proxy with “require all” for GET and leaving the other http options unchanged results in all ‘push’ requests being denied unless a user in the ‘pusher’ group you defined has used ‘docker login’ while all ‘pull’ requests are being permitted; which is probably want you want if you expose it to the internet for end users to pull images from.

[root@gitlab docker]# docker tag busybox mywebproxy.myexample.org:5043/busybox
[root@gitlab docker]# docker push mywebproxy.myexample.org:5043/busybox
The push refers to repository [mywebproxy.myexample.org:5043/busybox]
514c3a3e64d4: Preparing 
unauthorized: authentication required

[root@gitlab docker]# docker pull mywebproxy.myexample.org:5043/mvs38j:f30
f30: Pulling from mvs38j
33fd06a469a7: Pull complete 
Digest: sha256:46eb3fb42e4c6ffbd98215ea5d008afc6f19be80d5c1b331f7ba23e07c9d8e46
Status: Downloaded newer image for mywebproxy.myexample.org:5043/mvs38j:f30
mywebproxy.myexample.org:5043/mvs38j:f30

While configuring users in a htpasswd and group file on an apache server providing a proxy service can seperate users that can only use GET operations and those that can perform all operations by requiring them to ‘docker login’ via the proxy, if you do intend to allow external users to pull images from your repository my recomendation would be to allow all users to ‘pull’ and no users to ‘push’ (simply have no users in the pusher group) via a public facing proxy configuration. Any ‘push’ processing should only be done from the trusted internal network anyway.

This is my replacement for the script provided on the docker site to create a working  Apache proxy configuraion on an existing Apache web server.

DOCKER_REGISTRY_HOST="docker-local" # hostname running the registry container, may be localhost if running it there
DOCKER_REGISTRY_PORT="5000" # port name used by the insecure container
APACHE_DOCKER_AUTH_DIR="/var/www/html/registry-auth" # directory to use for proxy vhost htpasswd and group data files
USERNAME_PUSH_AUTH="mark" # user to demo push access
USERNAME_PUSH_PASSWORD="pusherpwd" # password for above
SSL_CERT_PATH="/etc/letsencrypt/live/mywebserver.myexample.org" # where are the certs used by the existing website

# Did we have a valid cert directory ?, change nothing if not
if [ ! -d ${SSL_CERT_PATH} ];
then
echo "${SSL_CERT_PATH} is not a directory"
exit 1
fi

# Ensure the directories exist, create the needed files
if [ ! -d ${APACHE_DOCKER_AUTH_DIR} ];
then
mkdir -p ${APACHE_DOCKER_AUTH_DIR}
chown apache:apache ${APACHE_DOCKER_AUTH_DIR}
done
htpasswd -Bbn ${USERNAME_PUSH_AUTH} ${USERNAME_PUSH_PASSWORD} >> ${APACHE_DOCKER_AUTH_DIR}/httpd.htpasswd
echo "pusher: ${USERNAME_PUSH_AUTH}" >> ${APACHE_DOCKER_AUTH_DIR}/httpd.groups"
chown apache:apache ${APACHE_DOCKER_AUTH_DIR}/httpd.htpasswd
chown apache:apache ${APACHE_DOCKER_AUTH_DIR}/httpd.groups"

# Create the proxy configuration file
cat << EOF > /etc/httpd/conf.d/docker-registry.conf
LoadModule headers_module modules/mod_headers.so
LoadModule authn_file_module modules/mod_authn_file.so
LoadModule authn_core_module modules/mod_authn_core.so
LoadModule authz_groupfile_module modules/mod_authz_groupfile.so
LoadModule authz_user_module modules/mod_authz_user.so
LoadModule authz_core_module modules/mod_authz_core.so
LoadModule auth_basic_module modules/mod_auth_basic.so
LoadModule access_compat_module modules/mod_access_compat.so
LoadModule ssl_module modules/mod_ssl.so
LoadModule proxy_module modules/mod_proxy.so
LoadModule proxy_http_module modules/mod_proxy_http.so
LoadModule unixd_module modules/mod_unixd.so

Listen 5043
<VirtualHost *:5043>
ServerName mywebproxy.myexample.org
SSLEngine on
SSLCertificateFile ${SSL_CERT_PATH}/fullchain.pem
SSLCertificateKeyFile ${SSL_CERT_PATH}/privkey.pem
SSLCertificateChainFile ${SSL_CERT_PATH}/fullchain.pem
SSLCompression off
SSLProtocol all -SSLv2 -SSLv3 -TLSv1
SSLCipherSuite EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH
SSLHonorCipherOrder on
Header always set "Docker-Distribution-Api-Version" "registry/2.0"
Header onsuccess set "Docker-Distribution-Api-Version" "registry/2.0"
RequestHeader set X-Forwarded-Proto "https"
ProxyRequests off
ProxyPreserveHost on
# no proxy for /error/ (Apache HTTPd errors messages)
ProxyPass /error/ !
ProxyPass /v2 http://${DOCKER_REGISTRY_HOST}:${DOCKER_REGISTRY_PORT}/v2
ProxyPassReverse /v2 http://${DOCKER_REGISTRY_HOST}:${DOCKER_REGISTRY_PORT}/v2
<Location /v2>
Order deny,allow
Allow from all
# MUST match realm to the 'basic-realm' used by default in the registry container
# If you change the realm value used by the registry container change this also
AuthName "basic-realm"
AuthType basic
AuthUserFile "${APACHE_DOCKER_AUTH_DIR}/httpd.htpasswd"
AuthGroupFile "${APACHE_DOCKER_AUTH_DIR}/httpd.groups"
# Read access to any users
<Limit GET HEAD>
Require all granted
</Limit>
# Write access to docker-deployer only
<Limit POST PUT DELETE PATCH>
Require group pusher
</Limit>
</Location>
</VirtualHost>
EOF

chown apache:apache /etc/httpd/conf.d/docker-registry.conf

# Implement !
# The below assumes that your apache configuration uses the default of loading all .conf files
# in the conf.d directory; if you selectivy load files from that directory ensure you add
# the new conf file created by this script.
systemctl restart httpd

# What you MUST do
# your webserver host must accept traffic on port 5043
# your webserver mut be able to pass traffic to port 5000 on the remote (or local) registry host
# Then it should all just work with docker images tagged mywebproxy.myexample.org:5043/imagename[:tag]

Have fun.

Posted in Unix | Comments Off on Setting up and using a local (insecure) Docker registry service container

Running remote X-windows applications across ssh from a Fedora server

This post has been created as it may assist others who want to do the same thing. It is not complicated or world shaking.

Often you will run Fedora (or CentOS/RHEL) servers and do not want a GUI desktop installed. However occasionally you may want to run a X-windows GUI application on that server.

This is normally done simply with a normal SSH connection with the remote server allowing X11Forwarding; if the remote server has all the desktop packages installed and boots into GUI mode there is no problem with that approach. However if you SSH into a remote server with “ssh -X user@remotehost” and get a message saying X11 forwarding failed or the command “echo $DISPLAY” returns nothing then congratulations, you have a perfectly normal server install… however you are not able to run GUI applications, which is fortunately easily fixed.

Before you do anything check your /etc/ssh/sshd_config and make sure you have uncommented ‘X11Forwarding yes’ and restarted sshd since doing so. If you had to make that change retry the ssh and echo command above again as while unlikely on a server install you may have had the needed packages all along. Only if you still get the errors do you need to continue reading this post.

It should also be noted that the “X11Forwarding yes” can be set on a per client basis rather than globally if you preffer, an example of that is commented out at the end of the /etc/ssh/sshd_config file on Fedora.

If you have been trying to get this working already you will have already noticed that all fedora server only installs already have a xorg-x11-server-Xorg package installed, for example F32 at the time of writing this post has “xorg-x11-server-Xorg-1.20.8-1.fc32.x86_64” installed by default on a server only install. Obviously this in insuffient to run X-applications across SSH or I would not be writing this post.

To get the required functionality working simply, on the server you wish to remotely connect to using ssh to run GUI apps simply

dnf -y install gdm
systemctl start gdm.service     # note:is enabled by default so will start after reboots

You can then session ssh into the server again, and ‘echo $DISPLAY’ will now have a value.

To test it works, while sshed onto that remote server, simply

sudo dnf -y install xterm
xterm

If you get an xterm window on your client machine with a prompt of the remote server you have it all working.

Important note: on the client machine you must have the X11 forwarding ports open in your firewall, at a minimum have port 6010 open on your client to accept the forwarded X11 session data.

Obviously installing the GDM package installs a few extra required files. Below is a list of all files installed on my server whan I needed to install the GDM package. As the server still boots up in non-GUI mode (unless for some reason you make the changes necessary to change the default runlevel) installed packages used by a desktop session such as pulseaudio and evolution do not start so there is no impact on the server other than the gdm service being started.

[root@xxxx X11]# dnf install gdm
Last metadata expiration check: 1:52:25 ago on Mon 13 Jul 2020 13:44:13.
Dependencies resolved.
=================================================================================================================
 Package                                    Arch        Version                Repository                   Size
=================================================================================================================
Installing:
 gdm                                        x86_64      1:3.36.2-2.fc32        updates                     561 k
Installing dependencies:
 accountsservice                            x86_64      0.6.55-2.fc32          fedora                      118 k
 accountsservice-libs                       x86_64      0.6.55-2.fc32          fedora                       93 k
 bluez-obexd                                x86_64      5.54-1.fc32            fedora                      196 k
 bolt                                       x86_64      0.9-1.fc32             updates                     203 k
 bubblewrap                                 x86_64      0.4.1-1.fc32           fedora                       51 k
 cheese-libs                                x86_64      2:3.34.0-3.fc32        fedora                      814 k
 clutter                                    x86_64      1.26.4-1.fc32          fedora                      1.1 M
 clutter-gst3                               x86_64      3.0.27-3.fc32          fedora                       87 k
 clutter-gtk                                x86_64      1.8.4-7.fc32           fedora                       48 k
 cogl                                       x86_64      1.22.8-1.fc32          updates                     495 k
 colord-gtk                                 x86_64      0.2.0-3.fc32           fedora                       32 k
 cups-pk-helper                             x86_64      0.2.6-9.fc32           fedora                       91 k
 enchant2                                   x86_64      2.2.8-1.fc32           fedora                       63 k
 evolution-data-server                      x86_64      3.36.3-1.fc32          updates                     2.2 M
 evolution-data-server-langpacks            noarch      3.36.3-1.fc32          updates                     1.4 M
 fdk-aac-free                               x86_64      2.0.0-3.fc32           fedora                      401 k
 flatpak-selinux                            noarch      1.6.4-1.fc32           updates                      23 k
 flatpak-session-helper                     x86_64      1.6.4-1.fc32           updates                      77 k
 geoclue2                                   x86_64      2.5.6-1.fc32           fedora                      137 k
 geoclue2-libs                              x86_64      2.5.6-1.fc32           fedora                       51 k
 geocode-glib                               x86_64      3.26.2-1.fc32          fedora                       72 k
 gjs                                        x86_64      1.64.3-3.fc32          updates                     380 k
 gnome-autoar                               x86_64      0.2.4-2.fc32           fedora                       56 k
 gnome-bluetooth                            x86_64      1:3.34.1-1.fc32        fedora                       44 k
 gnome-bluetooth-libs                       x86_64      1:3.34.1-1.fc32        fedora                      318 k
 gnome-control-center                       x86_64      3.36.3-1.fc32          updates                     5.7 M
 gnome-control-center-filesystem            noarch      3.36.3-1.fc32          updates                      12 k
 gnome-desktop3                             x86_64      3.36.3.1-1.fc32        updates                     576 k
 gnome-keyring-pam                          x86_64      3.36.0-1.fc32          fedora                       29 k
 gnome-online-accounts                      x86_64      3.36.0-1.fc32          fedora                      484 k
 gnome-session                              x86_64      3.36.0-2.fc32          fedora                      383 k
 gnome-session-wayland-session              x86_64      3.36.0-2.fc32          fedora                       13 k
 gnome-session-xsession                     x86_64      3.36.0-2.fc32          fedora                       13 k
 gnome-settings-daemon                      x86_64      3.36.1-1.fc32          updates                     1.0 M
 gnome-shell                                x86_64      3.36.4-1.fc32          updates                     1.5 M
 gsound                                     x86_64      1.0.2-11.fc32          fedora                       34 k
 gssdp                                      x86_64      1.0.4-1.fc32           updates                      51 k
 gupnp                                      x86_64      1.0.5-1.fc32           updates                      96 k
 gupnp-av                                   x86_64      0.12.11-3.fc32         fedora                       92 k
 gupnp-dlna                                 x86_64      0.10.5-12.fc32         fedora                       89 k
 harfbuzz-icu                               x86_64      2.6.4-3.fc32           fedora                       16 k
 hyphen                                     x86_64      2.8.8-13.fc32          fedora                       29 k
 ibus                                       x86_64      1.5.22-7.fc32          updates                     7.4 M
 ibus-gtk2                                  x86_64      1.5.22-7.fc32          updates                      28 k
 ibus-gtk3                                  x86_64      1.5.22-7.fc32          updates                      29 k
 ibus-libs                                  x86_64      1.5.22-7.fc32          updates                     262 k
 ibus-setup                                 noarch      1.5.22-7.fc32          updates                      61 k
 iio-sensor-proxy                           x86_64      3.0-1.fc32             fedora                       57 k
 libappindicator-gtk3                       x86_64      12.10.0-28.fc32        updates                      42 k
 libcanberra                                x86_64      0.30-22.fc32           fedora                       87 k
 libcanberra-gtk3                           x86_64      0.30-22.fc32           fedora                       32 k
 libdbusmenu                                x86_64      16.04.0-15.fc32        fedora                      136 k
 libdbusmenu-gtk3                           x86_64      16.04.0-15.fc32        fedora                       41 k
 libgdata                                   x86_64      0.17.12-1.fc32         fedora                      472 k
 libgee                                     x86_64      0.20.3-1.fc32          fedora                      289 k
 libgnomekbd                                x86_64      3.26.1-3.fc32          fedora                      163 k
 libgtop2                                   x86_64      2.40.0-3.fc32          fedora                      150 k
 libgweather                                x86_64      3.36.1-1.fc32          updates                     2.9 M
 libhandy                                   x86_64      0.0.13-4.fc32          updates                     162 k
 libical-glib                               x86_64      3.0.8-1.fc32           fedora                      187 k
 libimobiledevice                           x86_64      1.2.1-0.3.fc32         fedora                       79 k
 libindicator-gtk3                          x86_64      12.10.1-17.fc32        fedora                       67 k
 libmediaart                                x86_64      1.9.4-9.fc32           fedora                       44 k
 libnma                                     x86_64      1.8.28-1.fc32          updates                     302 k
 libnotify                                  x86_64      0.7.9-1.fc32           fedora                       43 k
 libplist                                   x86_64      2.1.0-3.fc32           fedora                       78 k
 libsbc                                     x86_64      1.4-5.fc32             fedora                       44 k
 libusbmuxd                                 x86_64      2.0.0-2.fc32           fedora                       38 k
 libvncserver                               x86_64      0.9.11-11.fc32         fedora                      272 k
 libwpe                                     x86_64      1.6.0-1.fc32           fedora                       26 k
 libxklavier                                x86_64      5.4-15.fc32            fedora                       67 k
 low-memory-monitor                         x86_64      2.0-4.fc32             fedora                       34 k
 mesa-vulkan-drivers                        x86_64      20.1.2-1.fc32          updates                     3.3 M
 mobile-broadband-provider-info             noarch      20190618-3.fc32        fedora                       67 k
 mozjs68                                    x86_64      68.10.0-1.fc32         updates                     6.8 M
 mutter                                     x86_64      3.36.4-1.fc32          updates                     2.4 M
 network-manager-applet                     x86_64      1.16.0-2.fc32          updates                     208 k
 nm-connection-editor                       x86_64      1.16.0-2.fc32          updates                     864 k
 ostree-libs                                x86_64      2020.3-5.fc32          updates                     398 k
 pipewire                                   x86_64      0.3.6-1.fc32           updates                     110 k
 pipewire-libs                              x86_64      0.3.6-1.fc32           updates                     708 k
 pipewire0.2-libs                           x86_64      0.2.7-2.fc32           fedora                      354 k
 pulseaudio                                 x86_64      13.99.1-4.fc32         updates                     1.0 M
 pulseaudio-module-bluetooth-freeworld      x86_64      1.4-1.fc32             rpmfusion-free-updates      100 k
 python3-cairo                              x86_64      1.18.2-4.fc32          fedora                       94 k
 python3-gobject                            x86_64      3.36.1-1.fc32          updates                      17 k
 rtkit                                      x86_64      0.11-23.fc32           fedora                       58 k
 sound-theme-freedesktop                    noarch      0.8-13.fc32            fedora                      378 k
 speexdsp                                   x86_64      1.2.0-1.fc32           updates                     453 k
 startup-notification                       x86_64      0.12-19.fc32           fedora                       42 k
 switcheroo-control                         x86_64      2.2-1.fc32             updates                      38 k
 upower                                     x86_64      0.99.11-3.fc32         fedora                      176 k
 vulkan-loader                              x86_64      1.2.135.0-1.fc32       updates                     126 k
 webkit2gtk3                                x86_64      2.28.3-1.fc32          updates                      15 M
 webkit2gtk3-jsc                            x86_64      2.28.3-1.fc32          updates                     6.0 M
 webrtc-audio-processing                    x86_64      0.3.1-4.fc32           fedora                      313 k
 woff2                                      x86_64      1.0.2-8.fc32           fedora                       61 k
 wpebackend-fdo                             x86_64      1.6.0-1.fc32           fedora                       36 k
 xdg-dbus-proxy                             x86_64      0.1.2-2.fc32           fedora                       43 k
 xdg-desktop-portal                         x86_64      1.7.2-2.fc32           updates                     434 k
 xdg-desktop-portal-gtk                     x86_64      1.7.1-1.fc32           fedora                      239 k
 xorg-x11-server-Xwayland                   x86_64      1.20.8-1.fc32          fedora                      988 k
 xorg-x11-xauth                             x86_64      1:1.1-3.fc32           fedora                       36 k
 xorg-x11-xinit                             x86_64      1.4.0-6.fc32           fedora                       56 k
 zenity                                     x86_64      3.32.0-3.fc32          fedora                      4.3 M
Installing weak dependencies:
 flatpak                                    x86_64      1.6.4-1.fc32           updates                     1.5 M
 gnome-remote-desktop                       x86_64      0.1.8-2.fc32           updates                      69 k
 libldac                                    x86_64      2.0.2.3-5.fc32         fedora                       41 k
 p11-kit-server                             x86_64      0.23.20-1.fc32         fedora                      189 k
 pinentry-gtk                               x86_64      1.1.0-7.fc32           fedora                       48 k
 rygel                                      x86_64      0.36.2-5.fc32          fedora                      1.0 M
 vino                                       x86_64      3.22.0-17.fc32         fedora                      453 k

Transaction Summary
=================================================================================================================
Install  113 Packages

Total download size: 81 M
Installed size: 344 M
Is this ok [y/N]: y
Posted in Unix | Comments Off on Running remote X-windows applications across ssh from a Fedora server

Installing airgraph-ng on Kali Linux

There are many tutorials on using airgraph-ng on youtube; most omit the small detail that it cannot be installed using a package manager.

The airodump-ng utility within the aircrack-ng toolkit is used to scan wireless activity near your location, including not just wireless hot spots but also the devices connected to or trying to connect to those hot spots. As it only scans activity happening in the air and cannot be considered a hacking tool it can be considered legal to use anywhere. It can save results to a CSV file, which id not easily human readable.

The airgraph-ng utility covered here is one of the experimental scripts available in the aircrack-ng suite of tools. It’s purpose is to take the CSV file generated by the airodump-ng utility and display the data captured as a pretty diagram showing the association between the devices captured. This is useful in determining what wireless devices are connected to what access points at a glance without having to write your own tools to parse the file.

As the airgraph-ng script is in the ‘experimental’ category it is not shipped as part of aircrack-ng packages that are available for most Linux distributions so needs to be installed from source. The aircrack-ng sources can be obtained with “git clone https://github.com/aircrack-ng/aircrack-ng.git”.

The airgraph-ng tool requires a bit of manual fiddling to get it to work after installation, which may be why it is bundled in the experimental category; but the fiddling required is covered in this post.

This post covers installing airgraph-ng (actually installing the entire package) on the normal full Kali OS server install, plus covers additional steps needed if you installed your Kali system from the Live DVD media rather than the server media.

The live DVD used was

Linux kali 4.19.0-kali3-amd64 #1 SMP Debian 4.19.20-1kali1 (2019-02-14) x86_64 GNU/Linux
gcc (Debian 8.2.0-14) 8.2.0

The server install used was

Linux kali 5.7.0-kali1-amd64 #1 SMP Debian 5.7.6-1kali2 (2020-07-01) x86_64 GNU/Linux
gcc (Debian 9.3.0-14) 9.3.0

Requirements:

  • A GCC compiler version below 10, as of July 18 2020 it is not possible to compile the aircrack-ng tools using GCC 10
  • Any Linux OS with an older version of the GCC Compiler available (Fedora 32 for example uses GCC 10 so cannot be used). This post uses the Kali OS
  • The OS must have python 2 installed (2.7 works OK); the airgraph-ng python script will not run under python3. Kali Linux includes both python and python3 so this is not an issue if you are using Kali Linux

The issue:

Most OS’s have the package aircrack-ng available in their repositories, Kali even comes with the package installed; this provides the standard airmon-ng and airodump-ng utilities. The issue is that the supplied packages do not include optional utilities such as airgraph-ng. If you want experimental features you must install from source.

This post was written on July 18 2020; issues with the source on github not compiling using GCC 10 may have been resolved by the time you read this.

Pre-requisites

If you installed from the live DVD:

If you installed a VM from the ‘live DVD’ after the install you will have no repositories configured, even the DVD media will have been commented out of the available repository list after installation. You need to be able to access repositories to install packages required to compile the source. This is easily fixed with the below command.

cat << EOF >> /etc/apt/sources.list
deb http://http.kali.org/kali kali-rolling main non-free contrib
# For source package access, uncomment the following line
# deb-src http://http.kali.org/kali kali-rolling main non-free contrib
EOF

Additional packages required:

In order to compile the aircrack-ng utilities you need at a minimum the following additional packages installed in addition to the base Kali install.

apt-get clean
apt-get autoclean
apt update
apt upgrade
apt install build-essential
apt-get install automake autoconf autotools-dev 
apt install libtool pkg-config m4
apt install libssl-dev
apt install libnl-3-200 libnl-3-dev
apt install libpcap-dev
apt install libnl-genl-3-200 libnl-genl-3-dev

# The below are only needed if you installed Kali from the live DVD
apt install libstdc++-dev
apt install python3-graphviz graphviz
apt install python-pygraphviz
apt install python3-pydot python3-pydot-ng python3-pydotplus

Obtaining aitcrack-ng, and Compiling

After all the pre-requisites have been satisfied you are ready to actually compile it. Do not just blindly copy/paste the below commands, run each one by one and fix errors as needed before running the next.

cd ~
mkdir git
cd git
git clone https://github.com/aircrack-ng/aircrack-ng.git
cd aircrack-ng
autoreconf -i
./configure --with-ext-scripts --disable-shared --enable-static
make
make install

If all went well you will have updated versions of the software in /usr/local/bin and /usr/local/sbin. The files installed by the default Kali install package reside in /usr/bin and /usr/sbin so both can co-exist at the same time. It is important to note however that on Kali the /usr/local directories are by default searched first so your newly compiled files will be chosen by default.

I would recomend you do not uninstall the aircrack-ng package and actually use the packaged versions of the utilities. The reason for that is that is that if you were paying attention during the configure step you will have noticed that many facilities were not implemented (for example pcap is available as libpcap-dev was one of the packages I stated you need to download, pcre was not available as I could not locate a libpcre-dev package to obtain it). It can be assumed that the packaged utilites have all features available so are probably the better ones to use. Remember this post is about obtaining the airgraph-ng command, however if you want to obtain all the features and use the latest source you need to hunt down all the development libraries and header files needed to provide all the features.

airgraph-ng is now available, but it will not yet run

So, you think you are now reasy to run airgraph-ng ?. Bad news, the script is broken. Fortunately not too badly, the issues are

  1. it requires a ‘support’ directory under your current working directory, which you need to manually create
  2. it wants to download a device list file from http://standards-oui.ieee.org/oui.txt into that directory, and fails to do so

The fix is simply, in your work directory

mkdir support
cd support
wget http://standards-oui.ieee.org/oui.txt
cd ..

and run the airgraph-ng command again, with the ‘support’ directory and required file within it existing it will finally work.

Other important things to note: are

  • The airmon-ng script runs using python 2 (fortunately Kali has 2.7 ‘python command’ as well as python 3.x ‘python3 command’ so that is not an issue
  • The airgraph-ng script will not run under python 3.x, if you try to install it on a server with only python 3.x forget it (at the current time) as the graphviz libraries are for python2 and if airmon-ng is run with ‘python3’ it cannot find the graphviz libraries (this will obviously change in later releases, but for now it is a stopper)

An example usage, assuming wireless device is wlp1s0

--- On Terminal 1
airmon-ng start wlp1s0

--- On Terminal 2
airodump-ng wlp1s0mon -w /root/osint/data/airodump_scan_location_date
^C (control-C) when run for a while to capture data, at least 5mins

# take the wireless adapter out of monitor mode, should free up terminal 1 again
airmon-ng stop wlp1s0mon

# Then use the collected data
   # map as a png the wireless devices actually connected to the networks located
   airgraph-ng -i airodump_scan_location_date.csv -o 'airodump_scan_location_date_CAPR' -g CAPR
   # map as png devices trying to connect to networks, this can show what networks they
   # connected to in the past that they are trying to re-connect to.
   airgraph-ng -i airodump_scan_location_date.csv -o 'airodump_scan_location_date_CPG' -g CPG

The first graph allows connected wireless devices nearby to be mapped to the wireless hot spots they are connected to which is useful for penetration testers, especially if ‘open’ unsecured ssid’s are found.

The details in second graph can be used by hackers, to use ssid spoofing to obtain the first two parts of a key handshake from the device trying to connect the the fake ssid; while the device will not connect as the fake ssid does not have a valid key there are tools that allow a key to be determined from those first two parts of the handshake the device used; this allows a fully operational fake ssid to be created and route all the traffic from the device connecting to it through that fake ssid hot spot… so ensure your wireless devices are never setup to try to autoconnect (most phones do) and instead manually connect to networks you know are expected to be near you when needed, as all connection attempts are broadcast to every wireless hot spot within range including those mapping other peoples networks.

Posted in Penetration Testing, Unix | Comments Off on Installing airgraph-ng on Kali Linux

Generating a new puppet client certificate

This is for the community edition of puppetserver, although will work for all recent puppet releases.

I use puppetserver as it is supposed to be able to manage up to 100 agents without having to hook it up to a web-server such as apache without any issues, and I have nowhere near that many servers or VMs in my home lab. At the time of this post it is running on a CentOS7 VM with agents running on CentOS7, CentOS8, Fedora32 and Fedora31.

It should also be noted this has been posted primarily for my use, as I needed the info again and it took me a while to remeber where I had placed the documentation; so posting it so I can just search my blog as I need it.

I initially investigated this as I had an interesting issue where on a puppet master server a “puppet cert list –all” did not show a cert for one of the agent nodes, but the agent node had no problem updating from the puppet master server. It turns out that ‘cleaning’ a cert does not stop an agent node updating, as the node still has a signed cert. You have to revoke it on the puppet master and leave it there rather than clean (remove) it from the puppet master to stop an agent updating.

So to stop a node updating just revoke.

For my initial problem I just think the agent server in question had another nodes certificate somehow; but thats another issue.

Anyway I was looking for the documentation a second time because after a full OS upgrade of an agent the agent was unable to retrieve files from the puppet master; it seems to connect OK and handshake OK but the errors were simply that it was unable to access file xxx where xxx was all the files that were relevant to that agent server. The solution in most cases where agent/master communication hits bumps is to generate a new certificate for the agent server.

It took me almost 10mins to find the documentation (after searching my blog first and not finding it). Therefore this blog post so I can find it easily next time by simply searching the blog, and there will be a next time.

To get a new certiificate for an agent follow the steps below

To generate a new certificate for an agent node you must delete all existing certificates for the agent on the agent and master.

  1. On the puppet agent as the root user, see the paragraph below the list of steps
    • systemctl stop puppet
    • puppet config print | grep ssldir
    • cd to the directory the ssl certificates are stored in identified by the ssldir entry
    • rm -rf *
  2. On the puppet master
    • puppet cert clean youragenthostname
  3. On the puppet agent
    • systemctl start puppet
  4. On the puppet master
    • use ‘puppet cert list’ occasionally to wait for the signing request, and ‘puppetcert sign youragenthostname’ when it eventually appears

The reason the work on the agent must be done as the root user is simply because that is the correct environment to work in. If you were to run the ‘puppet print config’ command as a non-root user you would see all the directory paths as $HOME/username/.puppetlabs’ instead of the directories used by the puppet agent itself, in most cases paths under the user home directory would not exist.

If you have a complex environment where you do run copies of the puppet agent under multiple userids (pointless as only the copy running under root can do updates to system files), presumably overriding the hostname, as a way of targeted testing; the you have missed the point of having the puppet master able to serve multiple environments (production, test1, test2) etc. and I would not try to do such a thing. So as this post is to remind me how to fix my environment you are on your own sorting that out should you have gone down the path of running agents under non-root userids.

Special notes on cleaning up the master

Puppet does a good job of preventing you from messing up the certificates on the master, but if you try hard enough you can. Resolving that is similar.

  1. On the puppet master as root
    • systemctl stop puppetserver
    • puppet config print | grep ssldir
    • cd to the directory the ssl certificates are stored in
    • rm -rf *
    • systemctl start puppetserver
  2. On every agent server follow the steps to be taken on the agent server mentioned in obtaining a new certificate above

When the puppetserver is started again it will recreate the keys it needs to function. The reason you must generate new certificates for every agent is simply because you will have deleted the signing key from the puppet master and it will have created a new one when it started, so all agent certificates are invalid and will error when trying to use the puppet master, they must be recreated so they can be signed with the new key.

It is certainly possible to selectively delete certificates on the puppet master to ensure the signing key remains, do not do that. If there is an issue on your puppet master severe enough to require manually deleting ssl keys go for the ‘big bang’ and wipe them all, trying to fiddle about selectively could leave you in a worse unknown position, simpler to regain a consistent environment by deleting them all and effectively starting from scratch.

Depending on how many agents you have you can configure the master to ‘autosign’ tne new certificate requests coming in from the agents; however as you will be manually working through deleting old certificated from the agent servers one by one I see no benefit in that.

UPDATE: 8Jan2022 – a bit of a late update as the change was a wile ago, but please note that more recent versions of puppet have retired the ‘puppet cert’ facility on the puppetserver/master and the puppet command is no longer used to manage puppetserver certificates, you must use puppetserver; you now need to use ‘puppetserver ca list [–all]’ and ‘puppetserver ca clean –certname=xxxx’ or ‘puppetserver ca sign –certname=xxxx’ on the puppetserver machine rather than the ‘puppet cert’ commands from the origional post. All else remains the same.

Posted in Automation, Unix | Comments Off on Generating a new puppet client certificate

Installing a local GitLab instance using the supplied container

There are lots of complicated ways to install a local GitLab instance, however for small environments it is easiest to simply use the supplied container image. Instructions on installing that are at https://docs.gitlab.com/omnibus/docker/ and are simple to follow.

Install, start, configure

For my home lab environment installing the GitLab container was simply a case of

  1. create a new KVM based on CentOS-8 with 4Gb of memory, 2 vcpus, and a 50Gb qcow2 disk
  2. install docker
    dnf config-manager --add-repo=https://download.docker.com/linux/centos/docker-ce.repo
    dnf list docker-ce
    dnf install docker-ce --nobest
    usermod -aG docker root
    usermod -aG docker mark
    systemctl enable docker
    systemctl start docker
    
  3. in /etc/profile add the required ‘export GITLAB_HOME=”/srv”‘ line (it does not have to be in the global profile but I did not want to have to add it in multiple places)
  4. docker pull gitlab/gitlab-ce:latest
  5. start it
    docker run --detach \
       --hostname gitlab-local.xxx.xxx.org \
       --publish 443:443 --publish 80:80 --publish 5522:22 \
       --name gitlab \
       --restart always \
       --volume $GITLAB_HOME/gitlab/config:/etc/gitlab:Z \
       --volume $GITLAB_HOME/gitlab/logs:/var/log/gitlab:Z \
       --volume $GITLAB_HOME/gitlab/data:/var/opt/gitlab:Z \
       gitlab/gitlab-ce:latest
    

    note that I use port 5522 for ssh as the server running the container is already using port 22 for it’s ssh server, also the :Z appended to the volume lines are because selinux is on my server

Then point a web browser at the ip-address of the server (http://xxx.xxx.xxx.xxx rather than https) and using the userid root create a new password for the GitLab root user. At that point you can configure a few of the options, such as not letting new users create their own accounts.

Create a normal non-admin user, and setup ssh keys

You will then want to create a non-root/non-admin user for day to day use for your projects.

When defining a new user GitLab wants to send an email to the new user to allow them to create their new password, by default the GitLab container does not support outbound email. If that is an issue for you documentation on setting up email is at https://docs.gitlab.com/omnibus/settings/smtp.html#example-configuration, however it is not required (see next paragraph).

After defining a new user you just “edit” the user, which allows you to set an initial password for the user, when they use it at first logon they are required to change it; so having email is not actually required to define a new user. If you are going to run the container in your home lab you probably do not want emails out of GitLab enabled anyway.

Once done log out from the root user.

Setup a ssh key for the new user, fully documented at https://docs.gitlab.com/ee/ssh/, but briefly covered in the following paragraphs.

Logon to the GitLab interface using the new user you created using the password set when you edited the user above. You will be required to change the password, and then logon again with the new password.
Once logged on go to the user settings and select ssh keys.

On a terminal window on your Linux development desktop use ssh-keygen -t ed25519 -C “some meaningful comment” to create a new ssh key, note that I changed the name of the default file when prompted from id_ed25519 to id_ed25519_local_gitlab so I can clearly identify it, this raises issues also covered below.

Once you have generated the key done copy the contents of the .pub file created for the key to your clipboard.

Paste the ssh key in your clipboard into the ‘Key’ field provided, give it a meaningfull title and add the new key.

Back in the terminal window test the key works with “ssh -T git@xxx.xxx.xxx.xxx”, or in my case as I mapped port 5522 “ssh -p 5522 -T git@xxx.xxx.xxx.xxx”; reply yes to accept the fingerprint; you should see a ‘Welcome to GitLab @username!’ message before being returned to your own hosts shell. Repeat to ensure the fingerprint has been accepted and prompts will not interfere with workflow anymore.

For those used to using ssh to jump around machines it is worth pointing out that the command is in fact git@xxx.xxx.xxx.xxx and not the username of the GitLab account; that can be confusing at a first glance but the userid used must be git, and it magicaly determines what username to map to based on the key used.

It should also be noted that by default one of the key files ssh looks for is ~/.ssh/id_ed25519 whch is the default filename used when generating the key. If like me you have many idendity key files discretely seperated the command for me is “ssh -p 5522 -i ~/.ssh/id_ed25519_local_gitlab -T git@xxx.xxx.xxx.xxx” to select the key I want to use.

Create a project as the new user

While logged on as the newly created user you may want to create groups, but if it is for a home lab environment you probably want to jump directly to starting a new project.

The main reason new users should create a new project via the browser interface is that when a new project is created a ‘help page’ for the new project is displayed showing the git commands needed to setup the git environment and create a new local repository on your development desktop by cloning the new empty project or pushing an existing git repository to your new project. It does assume you are using default ssh identity files however.

Workarounds needed for using a container

The documentation recomends mapping port 22:22 which if used means the host running the container would have to provide ssh services on another port; to me that is silly. So as noted in my example of running the container above I mapped 5522:22 so the host running the container can still provide normal ssh services on port 22. It does mean port 5522 (or whatever port you chose) needs to be opened in your firewall).

If you need to use git with multiple ssh identity files as I do there are a few interesting answers at https://superuser.com/questions/232373/how-to-tell-git-which-private-key-to-use.

I prefer the use of ~/.ssh/config myself to provide different keys to different hosts as that also allows the port to be changed. It does mean you need a seperate dns entry for the hostname and gitlab-hostname as you only want to switch ports when accesing the GitLab ssh port and not interfere with the host running the container providing its normal ssh service on port 22.

Assuming a /etc/hosts file entry as below (or dns entry where both hosts map to the same ip-address)

xxx.xxx.xxx.xxx gitlab-local.xxx.xxx.org realservername.xxx.xxx.org

Using the below example of a ~/.ssh/config file any ssh to gitlab-local.xxx.xxx.org will use the specified key in the users .ssh directory instead of the default plus use port 5522 to connect to the remote hosts ssh service. And ssh to any other host (including realservername.xxx.xxx.org) will (as no overrides have been added for the ‘host *’ catch-all entry) use the normal port and all default ssh parameters.

Host gitlab-local.xxx.xxx.org
  IdentityFile ~/.ssh/id_ed25519_local_gitlab
  Port 5522
Host *

Actually using your project from the standard git command line

Then on your development server to push files you are already working on to the project you created earlier is simply a case of following the git instructions that were shown on the web page displayed when your project was created (obviously it would be easier if you added a hostname to your /etc/hosts or dns rather than use the ip-address, but either work).

cd existing_folder
git init
git remote add origin git@xxx.xxxx.xxxx.xxxx:user_name_you_created/project_name_you_created.git
git add .
git commit -m "Initial commit"
git push -u origin master

And your Gitlab browser interface will show the files are in the project now.

To use any of the devops features you will need a bit more work, but for simply installing a local git based software repository users can work on that’s all that is needed.

So excluding the time it takes to download a CentOS-8 install iso, it takes less than 15mins to create a new CentOS-8 VM (remember to asign a static ip-addr), and about another 15mins to get it all working; so give it a try.

Posted in Unix | Comments Off on Installing a local GitLab instance using the supplied container

Re-initialising a bacula database

OK, first on a well managed system you would never need to do this. Despite the fact my system I thought was well managed and had 250gb free in the backup storage pool rotating around quite happily when one of my servers suddenly needed over 500Gb of backup space; oops 100% full backup filesystem. A windy day and the motion activated webcam was going wild from a tree waving about.

The normal activity of pruning jobs could not resolve the issue as multiple servers were using the backup volumes so despite extensive pruning no volumes became free to release space. My only option was to start with a fresh bacula environment.

From previous experience I knew that simply recreating the SQL tables did not create a working environment as flat files (non database files but filesystem files) are also used and a non-working state is achieved by not cleaning up everything. And I discovered I had not documented how I resolved it so many years ago; so for my future reference this is what is needed.

It should be an absolute last resort, as all existing backups are lost. Even if you use multiple storage daemons across multiple servers all backups are lost, re-initialising the database means you must delete all backup volumes across all your distributed storage servers associated with the database used by the bacula director affected.

This post is primarily for my use, as while I hope I will not need it again, I probably will, if only for moving existing servers to new storage pools.

Anyway, out with the sledge hammer; how to start from scratch.

On your bacula director server

systemctl stop bacula-dir

On all storage servers used by that director

systemctl stop bacula-sd
cd /your/storage/pool/dir
rm *

On your bacula director server

# Just drop and recreate the tables. Do not delete the database,
# leaving the database itself in existence means all user/grants
# for the database remain valid.
cd /usr/libexec/bacula
./drop_mysql_tables -u root -p
mysql -u root -p
  use bacula;
  list tables;
  drop table xxx;   # for each table remaining
  \q
./make_mysql_tables -u root -p
# remove everything from /var/spool/bacula, note I do not delete everything as
# I have a custom script in that directory for catalog backups; so this deletes
# everything apart from that script.
# Failure to do this step results in errors as backup details are stored
# here, which would conflict with an empty database.
cd /var/spool/bacula
/bin/rm *bsr
/bin/rm *state
/bin/rm *conmsg
/bin/rm log*

On all of your storage servers

systemctl start bacula-sd

On you bacula director server… after editing the exclude lists to exclude whatever directory cause the blowout

systemctl start bacula-dir

At this point all is well, your scheduled backups will be able to run again. The issue of course is that the first time they are next scheduled to run all incremental backups will now say something like ‘no full backup found, doing a full backup’, which while exactly what you want means your backups will take a lot longer than expected on their next run, plus if you had been staggering your full backups across multiple days bad news as they will now (assuming they all have the same retention period) all want to run on the same day in future.

Use of the ‘bconsole’ interface over a few days to delete/prune and rerun full backups can get the staggering back but it is a bit of a pain.

It is the damb incremental backups that use all the space; the full backups of all my servers only used 22% of the storage pool.

Ideally due to space limitations I should revise my monthly full backup strategy to fortnightly so I can keep two weeks of incrementals rather than four weeks of them; and hope I never need to restore a file over two weeks old. However in the real world if a personal physical server dies it may take over two weeks to be repaired and slotted back in so for now I’ll stick with monthly.

Posted in Automation, Unix | Comments Off on Re-initialising a bacula database

A few very simple Video file manipulation tips for Linux users

This post is obviously aimed at Linux users, as it is my prefred desktop of choice.

Shrinking the size of Video files (linux users only)

Sometimes Video files you have, whether provided by a friend, downloaded from youtube, or even from your own video camera cam be huge. It is not uncommon to see MP4 Video files over 1Gb in size for 30mins of footage while having other MP4 files of high quality footage of the same length using less than 300Mb of space.

The human eye can only see a certain number of video frames a second and many high end video capture devices record a lot more than needed, often at a higher quality than needed as well.

The following command will shrink a 1Gb MP4 file to around 300Mb with minimal loss of quality. But be sure to check the quality of the new file meets you needs before deleting the original as depending on the source file there can be quality loss.

ffmpeg -i input.mp4 -vcodec libx265 -crf 24 output.mp4

It is however important to note that ffmpeg will take a long time to run, for a 30min Video file allow 30mins, and if you are trying to shrink a small file like a 250Mb file the output may be bigger than the original file so only use to shrink huge files.

Also ffmpeg has a lot more options that can be used to control output you should look at, the above is a generic command but there are many other options such as frames per second that could shrink a file even further; but that would be on a individual file bases after checking existing file settings with ffprobe to decide if you want to play with them.

Removing DRM from Video files

This is not about removing DRM from media such as DVDs, but for Video files such as MP4 files you may have.

Sometimes your friends may helpfully record something off TV for you through a subscription service, or you are provided with a video file from a download service you subscribe to that is DRM protected and even though you purchased the download it can only be viewed while connected to that service. This is not about content protection but about inflating their service numbers, for example they can tell their shareholders they had nn connected users on a given date but omit that 90% of them were just re-watching something downloaded months earlier.

In many countries it is illegal to remove DRM; although in most if you have purchased or legally obtained the content it is acceptable to do so for your own use. If you fall into the later case read on.

If you search the internet there are many paid products you can purchase to remove DRM, they should be avoided as they are obviously trying to make money from a dubious legal position, and there are many free alternatives plus you probably already have one installed.

I am referring of course to the VLC media player that most Linux and Windows users already have installed and use in preference to anything bundled with the OS as it is simply better.

As well as VLC helpfully playing DVDs you may have purchased online for out-of-country zones you will have noticed it will also in many cases play DRM protected Video files. As VLC is my preferred player I was astounded one day when a file I was watching via VLC I decided to finish watching in bed (by putting the file on a usb stick) but my JVC TV told me it would not play the file because the file was DRM protected. Astounded ?, actually pissed off because I was halfway through watching it.

Anyway the key thing here is that the file must be playable using VLC. I am sure all VLC users know there is a “record” option in VLC which could do the trick if you are happy to watch the Video file, but most users of VLC will probably never use the streaming functions that VLC provides and not know that one of the stream outputs can be a file (and that video to the screen can be disabled) allowing VLC to batch play/stream-to-file an input Video file it is able to play with the output going to a file… in considerably less time than it would take to watch the Video file.

The below command is for Linux users, but similar syntax can be used for Windoze users.

vlc -I dummy "someinputfile.mp4" \
 --sout="#transcode{vcodec=mpeg4,vb=1024,acodec=mp3,ab=192,channels=2,deinterlace}:std{access=file,mux=ts,dst=someoutputfile.mp4" \
 vlc://quit

There will sometimes be quality loss (observed ‘fuzziness’ in display and occasional freezing on the new output file in a few cases) but in most cases no human observable difference in playback between the two files. You may have to tweak the parameters although I have found the ones above work in most cases.

Extracting audio only

Some video files (such as some audio books) come as MP4 files with a slideshow or static picture for the length of what should just be an audio file.

If like me you consider this a waste of disk space, it is simple to extract the audio on linux servers using the below command (note you may use aac output instead of m4a in this command is preferred).

ffmpeg -i input.mp4 -vn -c:a copy output.m4a

If you wish to use MP3 output instead note that MP3 files are a completely different format so it is not possible to simply copy the input stream to copy the audio, it has to be re-compressed and will in most cases result in a larger file. The command to extract the audio only from a MP4 video file to a MP3 audio file would be

ffmpeg -i input.mp4 -vn -ab 250k output.mp3

And for non-Linux users (or even Linux users) you can of course use VLC. The media -> convert/save dialog options can walk you through saving only the audio stream from a video file as a MP3 file.

Concatenating Video files

Occasionally you may download files from YouTube that came as part-n-of-n files and are a pain to watch as individual pieces. It would be nice to concatenate them into a single file.

For files with identical attributes they can simply be concatenated with video editing tools such as AviDemux; unfortunately while working fine for personally recorded videos with common attributes that and many other video tools will not be able to concatenate files if they have majorly different attributes.

Yet again ffmpeg can come to the rescue. Given a list of video files provided in a file, it can concatenate all the files together even if they have vastly different properties. It is important to note the the filenames in the list should not have spaces in the filename. A quick example…

cp '/some/dir/SS Eat the Fifties Part 1.mp4' input1.mp4
cp '/some/dir/SS Eat the Fifties Part 2.mp4' input2.mp4
cp '/some/dir/SS Eat the Fifties Part 3.mp4' input3.mp4
cp '/some/dir/SS Eat the Fifties Part 4.mp4' input4.mp4
cp '/some/dir/SS Eat the Fifties Part 5.mp4' input5.mp4
cp '/some/dir/SS Eat the Fifties Part 6.mp4' input6.mp4
cat << EOF > filelist.txt
file input1.mp4
file input2.mp4
file input3.mp4
file input4.mp4
file input5.mp4
file input6.mp4
EOF
ffmpeg -f concat -i filelist.txt -c copy Supersizes_Eat_The_Fifties.mp4

For the older format (flv) videos you may have collected will often refuse
to me merged as ffmpeg has dropped FLV from that functionality now. For FLV files you now have to convert them to MP4 first. I use this script where the parameter is simply a string that will find them all by wildcard name…

#!/bin/bash
#
# This script is intended to merge all the 1-of-N files into 1-of-1 merged files.
# MP4 files can be merged as is in most cases
# FLV files have to be converted, then merged
# AVI I am not sure of yet... still doing all the flv ones
#
# WARNING: All mine are n-of-5 and that is used in the 'sed' near
#          the end of the script. You may want to change that
#
# Syntax example : ./merge_files.sh *Balsamic*
#
# DO NOT DELETE THE ORIGINALS UNTIL YOU HAVE TESTED THE MERGED FILES
#

# Merge misc files into one, they should all be exactly the same input
# framerate, video hight/width, audio rate etc or we have to fic that
mask="$1"

# empty eny list left over from a previous run
> filelist.txt

# FLV cannot be merged, if FLV files are provided re-encode each file
# to MP4 first
intype=`ls ${mask}* | head -1 | awk -F\. {'print $2'}`
if [ "${intype}." == "flv." ];
then
   ls ${mask}* | while read xx
   do
      yy=`echo "${xx}" | sed -e's/.flv/.mp4/g'`
      echo "ffmpeg -i ${xx} -vcodec libx264 -acodec copy ${yy}" >> cmdxx.sh
   done
   if [ -f cmdxx.sh ];
   then
      bash cmdxx.sh
      cat cmdxx.sh
      /bin/rm cmdxx.sh
   fi
   ls ${mask}*mp4 | while read xx
   do
      echo "file ${xx}" >> filelist.txt
   done
else
   # else should be mp4 files so should merge fine
   ls ${mask}* | while read xx
   do
      echo "file ${xx}" >> filelist.txt
   done
fi
# merge all the files in the filelist.txt now, append into name ffmpegmerged
# as I do not want to overwrite any origionals until I have tested the
# output file.
outname=`ls ${mask}* | head -1 | sed -e's/1-of-5/1-of-1/g' \
   | awk -F\. {'print $1"-ffmpegmerged.mp4"'}`
ffmpeg -f concat -i filelist.txt -c copy ${outname}
exit 0

Note: I prefer guayadeque for playing music and audio files, it is in the Fedora repositories; and VLC for video files which Fedora/CentOS users should get from rpmfusion.

Posted in Unix | Comments Off on A few very simple Video file manipulation tips for Linux users

OpenStack Magnum Container Infrastructure – My Tests

Summary of results:

While OpenStack deploys all the necessary components to create a working infrastructure the available ‘atomic’ images just do not work. You end up with all the VMs and new private networking required in place, but the OS’s installed are incorrectly configured. I believe this is an atomic image issue rather than an openstack one.

Additional considerations:

The “Atomic Host” project appears end of life. Fedora no longer produce Atomic images, releasing now only ‘bare’ CoreOS images. CoreOS images require a lot of extra configuration steps such as creating an ignition (configuration) file in ‘json’ format for each instance to enable even a simple ssh key insertion to allow you to login and if they ever come out of ‘preview’ may not be supported by OpenStack anyway.

The Good

The OpenStack Magnum Container Infrastructure deployment just works perfectly as far as setting up the environment is concerned

  • it creates a new private network, along with routers between the nodes and to the external network as needed
  • it creates needed security groups for Docker or Kubernetes deployments and attaches the group to the new instances
  • it builds and starts the master and worker noddes needed (number of each defined simply by entering the number required in the horizon dashbord panel)
  • you have a fully running infrastructure

The Irritating, OpenStack tweaks needed

  • To use Atomic images in OpenStack after the images have been loaded you must use the openstack command openstack image set –property os_distro=fedora-atomic Unless that is done the images are not available for clusters and will not be selectable in the horizon cluster deployment panels for magnum deployment (and yes for all OS types the only allowed type is os_distro=fedora-atomic)
  • You also must edit /etc/magnum/magnum.conf to set “default_docker_volume_type = ” to a cinder supported value (use ‘cinder type-list’ to show available options) or all attempts to create a cluster using cinder volumes will fail with volume type not found or invalid volume type () as the default is a blank entry
  • if you elect to assign a floating ip-address during the cluster deployment every instance in the cluster is assigned a floating-ip so you need a lot of them; I would recomend not assigning floating ip and just adding one to the master(s) if needed after deployment as all cluster traffic chatting is done across the provate network

The Bad

  • None of the Atomic Host images I used actually worked, with issues ranging from Docker being installed in a configuration preventing swarm mode (in a docker swarm test) to kubelet looping endlessly on startup (in a kubrernetes cluster test). These are issues with the Atomic Host images and not with OpenStack
  • While OpenStack (stein) requires images for cluster deployment to be Atomic Host images the Atomic project is end-of-life being mergered/replaced with CoreOS (for example the last Fedora Atomic Host image was F29 and the download page says use CoreOS going forward). This is an issue as CoreOS (For Fedora anyway) is still in ‘preview’ so there is no working solution even if OpenStack implements support for CoreOS images

Details of the tests I ran

This post is on attempting to deploy a kubernetes cluster or docker swarm on OpenStack using the publically available atomic cloud images for CentOS and Fedora. Note that no problem resolution or reconfiguartion changes were made to try to get things working as this was an ‘out-of-the-box’ test.

Environment used:

OpenStack release: 'stein'
CentOS7 atomic image: CentOS-Atomic-Host-7.20160130-GenericCloud.qcow2
Fedora29 atomic image: Fedora-AtomicHost-29-20181025.1.x86_64.qcow2
Cluster deployed for each test: One master and one worker VM
VM Sizes: all cluster VMs had 756Mb memory allocated, disk sizes were the minumum 6Gb for F29 and 10Gb for C7

Cluster Templates used:

An example of the cluster templates used. Note C7 needs devicemapper storage and a docker minimum volume size for docker to run as it needs space in clinder storage, Fedora can use overlay allowing docker containers to not depend on cinder storage. You obviously need to update ‘external_network’ withe the name of your own openstack external network, set your own ssh key name, and create flavours based on the values I listed in theVM sizes above.

# rexray service fails to start for C7 atomic,
# but template will not be created unless using driver rexray
openstack coe cluster template create \
 --coe swarm \
 --image C7-Atomic-Host \
 --keypair marks-keypair-stein \
 --server-type vm \
 --external-network external_network \
 --public \
 --network-driver docker \
 --flavor C7-Atomic-Host-min \
 --master-flavor C7-Atomic-Host-min \
 --volume-driver rexray \
 --docker-storage-driver devicemapper \
 --docker-volume-size 3 \
 C7-swarm-cluster-template
# Requires cinder driver, fails to create using rexray
openstack coe cluster template create \
 --coe kubernetes \
 --image C7-Atomic-Host \
 --keypair marks-keypair-stein \
 --server-type vm \
 --external-network external_network \
 --public \
 --network-driver flannel \
 --flavor C7-Atomic-Host-min \
 --master-flavor C7-Atomic-Host-min \
 --volume-driver cinder \
 --docker-storage-driver devicemapper \
 --docker-volume-size 3 \
 C7-kubernetes-cluster-template
# can use overlay for F29 docker
openstack coe cluster template create \
 --coe swarm \
 --image F29-Atomic-Host \
 --keypair marks-keypair-stein \
 --server-type vm \
 --external-network external_network \
 --public \
 --network-driver docker \
 --flavor F29-Atomic-Host-min \
 --master-flavor F29-Atomic-Host-min \
 --volume-driver rexray \
 --docker-storage-driver overlay \
 F29-swarm-cluster-template
openstack coe cluster template create \
 --coe swarm \
 --image F29-Atomic-Host \
 --keypair marks-keypair-stein \
 --server-type vm \
 --external-network external_network \
 --public \
 --network-driver flannel \
 --flavor F29-Atomic-Host-min \
 --master-flavor F29-Atomic-Host-min \
 --volume-driver rexray \
 --docker-storage-driver overlay \
 F29-kubernetes-cluster-template

Observered during the testing

CentOS7 Atomic Image – Docker Swarm

Atomic host image seems to be configured incorrectly. The docker service will not start as the rexray service is not running as docker has a dependancy on docker-storage-setup.service, but the rexray service has a dependance on the docker service running. Simple an endless loop trying/failing to start dervices with everything failing due to unsatisfied dependencies.

As these are atomic images the files simply cannot be edited to resolve the issue. So this atomic image cannot be used for docker swarm deployments.

Fedora 29 Atomic Image – Docker Swarm

Using the defaults (including the ‘flannel’ network driver the cluster builds ok, but docker does not run.

[root@marks-swarm-sstcju3tnss4-master-0 ~]# systemctl start docker
Failed to start docker.service: Unit flanneld.service not found.

Re-doing the cluster deployment using the ‘docker’ driver the cluster builds ok, docker is running OK on both VMs.
However on both master and worker ‘docker info’ shows ‘swarm inactive’ and the way docker has been installed/configured prevents it being used in a docker swarm.

[root@marks-swarm-jv5gugeihibm-master-0 ~]# docker node ls
Error response from daemon: This node is not a swarm manager. Use "docker swarm init" or "docker swarm join" to connect this node to swarm and try again.
[root@marks-swarm-jv5gugeihibm-master-0 ~]# docker swarm init --advertise-addr 10.0.1.138
Error response from daemon: --cluster-store and --cluster-advertise daemon configurations are incompatible with swarm mode

As these are atomic images the configuration files simply cannot be edited to resolve the issue. So this atomic image cannot be used for docker swarm deployments.


CentOS7 Atomic Image – Kubernetes Cluster

On the one occasion the cluster build completed the kubelet service fails to start, hits the spawning/failing too fast limit, no obvious errors, not bothering to debug.
All other attempts to deploy the same cluster timed out; cluster build timeout was set to 3hrs.

Fedora 29 Atomic Image – Kubernetes Cluster

Timed out after 40mins on a ‘wait’ in the install script, cluster build timeout was set to 120mins on that attempt so it was a script timeout. See the additional notes below.

Additional Notes on Kubernetes in the atomic images
It should be noted that the atomic host images do not actually contain kubernetes and in fact have to download them, as seen from activity on the vm servers in the new cluster…

[root@marks-k8-2v4y6qj2weu5-master-0 ~]#  ps -ef | grep install
root      2075  1397 16 00:41 ?        00:00:23 /usr/bin/python3 -Es /usr/bin/atomic install --storage ostree --system --system-package=no --name=kube-proxy docker.io/openstackmagnum/kubernetes-proxy:v1.11.6
[root@marks-k8-2v4y6qj2weu5-master-0 ~]#  ps -ef | grep install
root      2366  2348 30 00:45 ?        00:00:21 /usr/bin/python3 -Es /usr/bin/atomic install --storage ostree --system --system-package no --set REQUESTS_CA_BUNDLE=/etc/pki/tls/certs/ca-bundle.crt --name heat-container-agent docker.io/openstackmagnum/heat-container-agent:stein-dev

The time taken to download the packages may be what causes the timeout failure on the cluster build.

Alternatives, just use your own stack and cloud images

Using an existing private network I manually created a heat stack configuration for a docker swarm of one master and one additional worker (to match the configuration used in tests done above) using a normal cloud-image image. This took under ten minutes to create and test so doing things manually is almost as fast as using Magnum with the advantages that it does not use atomic images but normal cloud images, plus it works.

The only manual step needed after deployment is that the ‘worker’ node (or nodes if you deploy more than one) must be manually joined to the swarm using the token logged on the master as there is no way of in the stack template passing that data between images. The easiest way of doing that would be to install ssh keys for root so each worker instance can scp the file with the token in it from the master and run it (as workers can depend on the master being built); alterately if your stack build is run inside a script on the openstack master you could have that script retrieve the token and push it to each worker and run the command. However for this test I just cut/paste and manually ran the command.

It should also be noted that if you were to have multiple worker nodes you would create a template and just source that into the cluster yaml file, but this is not a heat template post and I want everything visible in one file so I did it this way for this post.

This is the template I used to deploy a stack manually.

Posted in OpenStack | Comments Off on OpenStack Magnum Container Infrastructure – My Tests