Talking to FreeIPA with python-requests

The code that Rich M gave me a while back has bit rotted. At some point, I need to get an updated version, but until then, I can continue to talk to the FreeIPA server using Python and the Requests library. In the future, I can get a session cookie, but for now, python3-request-gssapi will work to authenticate me, provided I have a valid TGT.

I pulled the requests-gssapi library from Koji, as it does not currently ship in any of the RHEL8 repos. Here is the one I installed.

https://koji.fedoraproject.org/koji/buildinfo?buildID=1371255

Note that this quick-and-dirty code runs on the IPA server itself. A better approach would be to read the Server name out of /etc/ipa/default.conf.

#!/bin/python3
import requests
from requests_gssapi import HTTPSPNEGOAuth
import socket
hostname = socket.gethostname()
url = "https://%s/ipa/json" % hostname
referer =  "https://%s/ipa" % hostname
body = {"method":"user_find","params":[[""],{}],"id":0}
 
r = requests.post(url,
                  json = body,
                  auth=HTTPSPNEGOAuth(),
                  headers = {
                    'Content-Type': 'application/json',
                    'Accept': 'applicaton/json',
                    'referer': referer})
print(r.status_code)
if r.status_code  == 200:
    print(r.text)

Network Policy to Explicitly Allow access from all Namespaces

The Default network policy in OpenShift allows all access from all pods in all namespaces via the cluster IP. However, once you start enforcing policy on a project, all policy decision need to be made explicit. If you want to still allow access from all projects, you can use the following policy file.

kind: NetworkPolicy
apiVersion: networking.k8s.io/v1
metadata:
  name: allow-all-namespaces
spec:
  ingress:
  - from:
    - namespaceSelector: {}

Testing OpenShift Network Policy

The first principle is that you must not fool yourself and you are the easiest person to fool.

Richard P. Feynman

Test before you deploy. Treat configuration as code. These are precepts of DevOps that we want to make real. When dealing with network policy, we want to test it out in a development context before deploying it in production.

When one service calls another, it can avoid the overhead of passing through the external load balancer by using the cluster IP assigned to the other service. If both services are in the same project, the default policies allows this with no changes.

If we need to cross project boundaries, we need to customize policy. Specifically, we need to create policies that will mirror the default, while also allowing access from another project.

For my example, I am going to create two projects. One is my demo-servicet, that has the app ab-deploy running in it. This application is the target. We want to show we can get access to it from services both inside and outside demo-project. The second project is called check-project and exists solely to run the code to check the cross-project call.

I am going to use the simple Flask service I wrote about in my previous post. I will have two deployments of this code, one running in demo-service and one running in check-project. The one running in demo-project is part of our control; it shows that we can access the ab-deploy application. The one running in check-project will show what happens when we make cross-project calls.

I added three policies to the demo-service project.

  • allow-from-openshift-ingress
  • allow-same-namespace
  • allow-from-openshift-global

The first two mimic default policy: they allow access from the ingress controller and from services in the same namespace. There are others, specifically monitoring, that I should add as well, but this is enough to prove my point.

The last policy allows access from any namespace with a special label on it. That policy looks like this:

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  creationTimestamp: "2020-07-31T20:12:12Z"
  generation: 1
  name: allow-from-openshift-global
  namespace: demo-service
  resourceVersion: "9196451"
  selfLink: /apis/networking.k8s.io/v1/namespaces/demo-service/networkpolicies/allow-from-openshift-global
  uid: 144e751e-f71f-416b-8d7e-3938e52cb9b9
spec:
  ingress:
  - from:
    - namespaceSelector:
        matchLabels:
          network.openshift.io/policy-group: global
  podSelector: {}
  policyTypes:
  - Ingress

The line to take note of here is:

network.openshift.io/policy-group: global

This is what I want to test: If a project has that label, it should be able to access the ab-service. If it does not, I should not be able to access the service.

For my first round of testing, I am going to hard code values. In the future, I will be able to look these up. The values I am going to hard code are the Cluster IP of the ab-deploy service and the URLs of the ckaccess service that calls them. I’ve collected them all into this script:

#!/bin/python3
 
import requests
import os
 
services = ["http://172.21.193.8:8080", "https://www.redhat.com"]
checkers={
    'ckaccess1-demo-service':
    'http://ckaccess1-demo-service.ayoung-rhfsi-f2c6cdc6801be85fd188b09d006f13e3-0000.us-south.containers.appdomain.cloud/',
     'ckaccess-check-project':
    'http://ckaccess-check-project.ayoung-rhfsi-f2c6cdc6801be85fd188b09d006f13e3-0000.us-south.containers.appdomain.cloud/'
    }
 
 
def run_tests():
    for project, url in checkers.items():
        for service in services:
            print("--------------------------------------------\n")
            print('CHECKER    : %s' % project) 
            print('SERVICE_URL: %s' % service)
            try:
                r = requests.get(url,
                                 params={'service_url': service},
                )
                print('status=%d' % (r.status_code))
                print("%s" % response.text)
                print("\n")
            except requests.exceptions.RequestException:
                print('Error Calling Service %s!' % url)
 
print("Start\n")
print("make sure the project is unlabeled")
os.system("oc label namespace check-project 'network.openshift.io/policy-group-'")
run_tests()
 
print("Label the project")
os.system("oc label namespace check-project 'network.openshift.io/policy-group=global'")
 
run_tests()
 
print("Unlabel the project")
os.system("oc label namespace check-project 'network.openshift.io/policy-group-'")
run_tests()

Note that I added in a call to redhat.com just to make sure I had a sanity check on my script: I was pretty sure I could call that from inside my cluster.

The output is below. This test requires a human to analyze it, which is not what I would do for an automated test. However, it makes it easier to show the example here.

Note that the places where check_project is unlabeled, the calls from ckaccess-check-project return the message “Could not reach Service http://172.21.193.8:8080!” But the others all succeed.

When the project is labeled, those calls return “Testing Service for http://172.21.193.8:8080! for status 200”

ayoung@ayoungP40 ~]$ ./run_tests.py
Start
 
make sure the project is unlabeled
label "network.openshift.io/policy-group" not found.
namespace/check-project labeled
--------------------------------------------
 
CHECKER    : ckaccess1-demo-service
SERVICE_URL: http://172.21.193.8:8080
status=200
Testing Service for http://172.21.193.8:8080!  for status 200 
 
 
--------------------------------------------
 
CHECKER    : ckaccess1-demo-service
SERVICE_URL: https://www.redhat.com
status=200
Testing Service for https://www.redhat.com!  for status 200 
 
 
--------------------------------------------
 
CHECKER    : ckaccess-check-project
SERVICE_URL: http://172.21.193.8:8080
status=200
Could not reach Service http://172.21.193.8:8080!
 
 
--------------------------------------------
 
CHECKER    : ckaccess-check-project
SERVICE_URL: https://www.redhat.com
status=200
Testing Service for https://www.redhat.com!  for status 200 
 
 
--------------------------------------------
 
Label the project
--------------------------------------------
 
namespace/check-project labeled
--------------------------------------------
 
CHECKER    : ckaccess1-demo-service
SERVICE_URL: http://172.21.193.8:8080
status=200
Testing Service for http://172.21.193.8:8080!  for status 200 
 
 
--------------------------------------------
 
CHECKER    : ckaccess1-demo-service
SERVICE_URL: https://www.redhat.com
status=200
Testing Service for https://www.redhat.com!  for status 200 
 
 
--------------------------------------------
 
CHECKER    : ckaccess-check-project
SERVICE_URL: http://172.21.193.8:8080
status=200
Testing Service for http://172.21.193.8:8080!  for status 200 
 
 
--------------------------------------------
 
CHECKER    : ckaccess-check-project
SERVICE_URL: https://www.redhat.com
status=200
Testing Service for https://www.redhat.com!  for status 200 
 
 
--------------------------------------------
 
Unlabel the project
--------------------------------------------
 
namespace/check-project labeled
--------------------------------------------
 
CHECKER    : ckaccess1-demo-service
SERVICE_URL: http://172.21.193.8:8080
status=200
Testing Service for http://172.21.193.8:8080!  for status 200 
 
 
--------------------------------------------
 
CHECKER    : ckaccess1-demo-service
SERVICE_URL: https://www.redhat.com
status=200
Testing Service for https://www.redhat.com!  for status 200 
 
 
--------------------------------------------
 
CHECKER    : ckaccess-check-project
SERVICE_URL: http://172.21.193.8:8080
status=200
Could not reach Service http://172.21.193.8:8080!
 
 
--------------------------------------------
 
CHECKER    : ckaccess-check-project
SERVICE_URL: https://www.redhat.com
status=200
Testing Service for https://www.redhat.com!  for status 200

Deploying a Minimalistic Flask Application to OpenShift

Some colleagues and I were discussing the network access policy of OpenShift. I realized it would be very helpful to have a trivial app that I could deploy to OpenShift that would then try to make a call to another service. So I wrote it using Python3 and Flask. Now that I have it working, I want to deploy it in OpenShift, again, in a trivial manner.

I would not deploy a Flask App into production without a Web server to front it. But that is what I am going to do for this test app.

Continue reading

18 Triadic Permutations

I use the term permutations loosely here. But for any given chord inversion, there are 6 variations of the tones in the pitch you can play in order to play each tone once. What makes this an impure use of the term permutations is that the second and third notes of the sequence can go both above the starting note in one variation, and below it in another.

Continue reading

Decisions when playing Chromatic Triadic patterns

George Garzone is the Sax players sax player. He is a teacher that has taught the best of the crop that is out there right now. I had the privilege of studying with George back in high school. I can honestly say that no subject I studied before or since taught me how to think better than Jazz improvisation.

Back in the time of DVDs, George published his method for building a solo that he called the triadic-chromatic approach. A recent video from Moon Hooch’s Patreon reminded me of this approach.

The simple take of the approach is play a triad, move chromatically, play another triad, move chromatically, and continue. Simple, but not easy. Like the game Go, you can learn the rules in 5 minutes, and then spend the rest of your life trying to master it.

What I would like to do is lay out the set of decisions I need to make when playing this approach so that I can build a structure to practice it better. One idea I had was a dice version, where you roll a die for each decision, and then build from there. The first decision would be to chose a key. Since there are 12 Keys, we’ll roll a 12 sided die and use this mapping:

1=A 2=b c=3 4=d…7=G 8=A# 9=C# 10=D# 11=F# 12=G#

That only has to be done once.

OK, now you have your starting note. That does not mean you have your starting chord. First, lets determine what kind of chord it is. We could limit our selves to major and minor, which we would get by flipping a coin….but since there are other decisions to make here, we’ll hold off on that.

We need to figure out the inversion. This is a number 0-2:

0 = root, 1 = first inversion, 2 = second inversion

These 6 combinations can be generated with a 6 sided die.

1=root maj 2 = first major 3 = 2nd maj 4=root min 5=first minor 6=second minor

We also need to come up with the order of the notes. This where the permutations start to add up. If we are playing Root position, we could play 1-3-5, 1-5-3, 5-3-1, 5-1-3.

We also have to decide if we are going up or down between each note. That gives the following options: Up Up , Down Up Up, Down, Down Down. However, this is going to lead to some pretty big jumps. 5 up to 3 is actually jumping up a sixth. 3 up to one is also up a sixth. We can work that in, but it is a lot less common in playing, and we might want to weight things such that those show up less often. The combinations get complex enough quickly enough that we would want a Dungeon Masters Guide (first edition) lookup table to fine the right combination. For now, we’ll leave it equally spaced. 16 options…easier to keep them separate and roll 2 @ 4 sided dice, one for each decision.

For the chromatic runs afterwards, we are going to keep things simple. They are one, two, or three note runs. We need to determine the length of the run and the direction, up or down. 6 combinations. 1-3 is Up, 4-6 is down. divide the roll by three and take the remainder as the length of the run.

Ignoring our first 12 sided die roll, we have a repeated pattern of

6 * 4 * 4 * 6 = 576

options in each sequence. That is a pretty big option space.

What is going to limit you? Here are some ideas:

Habit: You are going to play the notes under your fingers that are most familiar. We are used to going from G down to E down to C more than from C down to E down to G. But it does sound cool.

Chords: You are going to gravitate to the chords of the tune you are playing.

Ease of fingerings: For the most part, it is easier on the Sax to play a natural than an accidental. Certain fingerings are just more comfortable and easy to hit. It is easier to play a C# than a G#.

Size of the Saxophone: You can only play so high and only play so low. have to turn around at some point or you run out of notes

Humans are not random: We are not going to make all of those decisions all at once every time. We are going to fall back on heuristics like : mostly play 135 up or 531 down, but after a while realized we have not played 1 5 3 down and throw that in.

Making a George Garzone simulator would be a fun task.

So I did: https://github.com/admiyo/chooch

The Chooch is one of George’s early tunes. It seemed appropriate.

chooch requires abcMIDI. On Fedora:

sudo yum install abcMIDI

Here’s some sample output.

I suspect there are bugs. It seems to get stuck at the bottom of the staff, althoug I tried to reset it to the middle if it gets too low.