Skip to content

Commit

Permalink
update
Browse files Browse the repository at this point in the history
  • Loading branch information
fbiering committed Jun 30, 2023
1 parent 4522689 commit 5aad7ad
Show file tree
Hide file tree
Showing 20 changed files with 1,203 additions and 557 deletions.
78 changes: 78 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
# goipa - FreeIPA client library
===============================================================================

[![GoDoc](https://img.shields.io/badge/godoc-reference-blue)](https://pkg.go.dev/github.com/ubccr/goipa)

goipa is a [FreeIPA](http://www.freeipa.org/) client library written in Go.
It interfaces with the FreeIPA JSON [api](https://github.com/freeipa/freeipa/blob/master/API.txt)
over HTTPS.

## Usage

Install using go tools:

```
$ go get github.com/ubccr/goipa
```

Example calling FreeIPA user-show:

```go
package main

import (
"fmt"

"github.com/ubccr/goipa"
)

func main() {
client := ipa.NewDefaultClient()

err := client.LoginWithKeytab("/path/to/user.keytab", "username")
if err != nil {
panic(err)
}

rec, err := client.UserShow("username")
if err != nil {
panic(err)
}

fmt.Println("%s - %s", rec.Username, rec.Uid)
}
```

## Hacking

Development and testing goipa uses docker-compose. The scripts to spin up a
FreeIPA test server in docker were copied/adopted from [this great repository](https://github.com/adelton/webauthinfra).
Most of the scripts in `container/` directory are written by Jan Pazdziora and
licensed under Apache 2.0 and modified for use with goipa.

NOTE: The containers are NOT meant to be run in production and used solely for
development.

To get started hacking on goipa and running the test suite:

```
$ cp .env.sample .env
[edit to taste. add passwords and ssh key]
$ docker-compose build
$ docker-compose up -d
$ ssh -p 9022 localhost
$ kinit admin
$ cd /app
$ go test
```

To run a specific test with trace debugging:

```
$ go test -v -run UserShow
```

## License

goipa is released under a BSD style License. See the LICENSE file.
33 changes: 33 additions & 0 deletions container/Dockerfile.client
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
FROM centos:centos7
RUN yum swap -y -- remove fakesystemd -- install systemd systemd-libs && yum clean all
RUN yum install -y ipa-client openssh-server openssh-clients sudo vim vim-go wget dbus-python perl 'perl(Data::Dumper)' 'perl(Time::HiRes)' && yum clean all
COPY init-data ipa-client-enroll populate-data-volume /usr/sbin/
RUN sed -i 's/^#AddressFamily any/AddressFamily inet/' /etc/ssh/sshd_config
COPY ipa-client-enroll.service populate-data-volume.service /usr/lib/systemd/system/
RUN ln -s /usr/lib/systemd/system/ipa-client-enroll.service /usr/lib/systemd/system/default.target.wants/
RUN ln -s /usr/lib/systemd/system/sshd.service /usr/lib/systemd/system/default.target.wants/
RUN ln -s /usr/lib/systemd/system/populate-data-volume.service /usr/lib/systemd/system/default.target.wants/
COPY volume-data-list /etc/

ARG DEV_SSH_KEY
ARG GO_VERSION
ARG USER
ARG USER_ID

RUN useradd -m --uid=${USER_ID} ${USER} \
&& mkdir /home/${USER}/.ssh \
&& echo "$DEV_SSH_KEY" > /home/${USER}/.ssh/authorized_keys \
&& chmod 600 /home/${USER}/.ssh/authorized_keys \
&& echo 'export PATH=$PATH:/usr/local/go/bin' >> /home/${USER}/.bashrc \
&& chown -R ${USER}:${USER} /home/${USER}

RUN install -o ${USER_ID} -g ${USER_ID} -m 0755 -d /app
RUN wget https://go.dev/dl/go${GO_VERSION}.linux-amd64.tar.gz
RUN tar -C /usr/local -xzf go${GO_VERSION}.linux-amd64.tar.gz

RUN echo "${USER} ALL=(ALL) NOPASSWD:ALL" > /etc/sudoers.d/90-dev-user \
&& chmod 440 /etc/sudoers.d/90-dev-user

ENV container docker
VOLUME [ "/tmp", "/run", "/data" ]
ENTRYPOINT /usr/sbin/init-data
12 changes: 12 additions & 0 deletions container/Dockerfile.ipa
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
FROM freeipa/freeipa-server:centos-7
ARG IPA_ADMIN_PASS
ARG IPA_DS_PASS
COPY init-ipa-server-install-options ipa-precreate-hosts /usr/sbin/
COPY ipa-precreate-hosts.service /usr/lib/systemd/system/
RUN mkdir /usr/lib/systemd/system/container-ipa.target.wants \
&& ln -s /usr/lib/systemd/system/ipa-precreate-hosts.service /usr/lib/systemd/system/container-ipa.target.wants/ipa-precreate-hosts.service
RUN ln -s /data /var/www/html/pub

ENV IPA_ADMIN_PASS $IPA_ADMIN_PASS
ENV IPA_DS_PASS $IPA_DS_PASS
ENTRYPOINT [ "/usr/sbin/init-ipa-server-install-options" ]
83 changes: 83 additions & 0 deletions container/init-data
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
#!/bin/bash

# Copyright 2016--2017 Jan Pazdziora
#
# Licensed under the Apache License, Version 2.0 (the "License").

# Populates data in the image from the /data volume. Also
# waits for the IPA server to be ready.

set -e

UNAME=$( uname -n )
if [ "$HOSTNAME" != "$UNAME" -a "$HOSTNAME" == "${UNAME%%.*}" ] ; then
HOSTNAME="$UNAME"
fi

for i in /run/* /tmp/* ; do
if [ -e "$i" -a "$i" != '/run/secrets' ] ; then
rm -rf "$i"
fi
done

if [ -f /data/volume-version ] ; then
sed 's%^/%%' /etc/volume-data-list | while read f ; do [ -e /data/$f -o -L /data/$f ] && echo $f ; done | ( cd /data && xargs cp --parents -rp -t / )
fi

DOMAIN=mokey.local
IPA=ipa.$DOMAIN

IPA_IP_FROM_DOCKER=$( host $IPA | awk '/has address (.+)/ { print $4; exit}' )

i=0
while ! curl -fs http://$IPA/ipa/config/ca.crt &> /dev/null ; do
if [ "$(( i % 20 ))" -eq 0 ] ; then
echo "Waiting for FreeIPA server (HTTP Server) ..."
fi
i=$(( i + 1 ))
sleep 1
done
i=0
while ! dig NS mokey.local &> /dev/null ; do
if [ "$(( i % 20 ))" -eq 0 ] ; then
echo "Waiting for FreeIPA server (DNS) ..."
fi
i=$(( i + 1 ))
sleep 1
done

echo "nameserver $IPA_IP_FROM_DOCKER" > /etc/resolv.conf
echo "Pointing resolv.conf at $HOSTNAME to $IPA_IP_FROM_DOCKER"

i=0
while true ; do
IPA_IP_FROM_IPA=$( host $IPA | awk '/has address (.+)/ { print $4; exit}' )
if [ "$IPA_IP_FROM_DOCKER" == "$IPA_IP_FROM_IPA" ] ; then
break
fi
if [ "$(( i % 20 ))" -eq 0 ] ; then
echo "Waiting for FreeIPA server (its IP address in DNS) ..."
fi
i=$(( i + 1 ))
sleep 1
done

echo "FreeIPA server is ready."

while ! ( set -x ; curl -o /etc/pki/ca-trust/source/anchors/ipa-ca.crt -fs http://$IPA/ipa/config/ca.crt ) ; do
sleep 1
done
(
set -x
update-ca-trust
)

(
trap '' SIGHUP
rm -rf /run/docker-console
mkdir -p /run/docker-console
(sleep infinity) &
ln -s /proc/$!/fd /run/docker-console/
)

exec /usr/sbin/init
52 changes: 52 additions & 0 deletions container/init-ipa-server-install-options
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
#!/bin/bash

# Copyright 2016--2017 Jan Pazdziora
#
# Licensed under the Apache License, Version 2.0 (the "License").

# If the FreeIPA server does not contain ipa-server-install-options
# and the server was not configured yet, produce some default
# configuration for the developer setup.

set -e

cd /

UNAME=$( uname -n )
if [ "$HOSTNAME" != "$UNAME" -a "$HOSTNAME" == "${UNAME%%.*}" ] ; then
HOSTNAME="$UNAME"
fi

DATA=/data
if ! [ -f /etc/ipa/ca.crt -a -f $DATA/ipa-server-install-options ] ; then
echo "Configuring $HOSTNAME ..."
echo $IPA_ADMIN_PASS > $DATA/admin-password
echo $IPA_DS_PASS > $DATA/ds-master-password
DOMAIN=${HOSTNAME#*.}
REALM=${DOMAIN^^}
cat > $DATA/ipa-server-install-options <<EOS
--admin-password='$( cat $DATA/admin-password )'
--ds-password='$( cat $DATA/ds-master-password )'
--realm $REALM
--setup-dns
--no-forwarders
--no-dnssec-validation
--no-ntp
-U
EOS
fi

(
trap '' SIGHUP
rm -rf /run/docker-console
mkdir -p /run/docker-console
(sleep infinity) &
ln -s /proc/$!/fd /run/docker-console/
)

export SHOW_LOG=1
if [ -f /usr/sbin/init-data ] ; then
exec /usr/sbin/init-data
else
exec /usr/local/sbin/init
fi
62 changes: 62 additions & 0 deletions container/ipa-client-enroll
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
#!/bin/bash

# Copyright 2016--2018 Jan Pazdziora
#
# Licensed under the Apache License, Version 2.0 (the "License").

# If the machine is not yet IPA-enrolled, fetch its OTP from the IPA
# server and IPA-enroll the machine. In container with Apache HTTP
# server, also get keytab for the HTTP/ service and SSL certificate.

set -e

exec >> /run/docker-console/fd/1 2>> /run/docker-console/fd/2

workaround_1708275 () {
# Workaround 1708275
if ! grep -q dyndns_refresh_interval /etc/sssd/sssd.conf ; then
sed -i '/^\[domain/adyndns_refresh_interval = 999999' /etc/sssd/sssd.conf
fi
}

if [ -f /etc/ipa/default.conf ] ; then
echo "$HOSTNAME is already IPA-enrolled."
workaround_1708275
exit
fi

DOMAIN=mokey.local
IPA=ipa.$DOMAIN

i=0
while ! curl -fs https://$IPA/pub/$HOSTNAME-otp &> /dev/null ; do
if [ "$(( i % 20 ))" -eq 0 ] ; then
echo "Waiting for my host record and OTP ..."
fi
i=$(( i + 1 ))
sleep 1
done

(
set -x
curl -o /tmp/otp-password -fs https://$IPA/pub/$HOSTNAME-otp
ipa-client-install --server $IPA --domain $DOMAIN --password $( cat /tmp/otp-password ) --enable-dns-updates --no-ntp --no-nisdomain --no-ssh --no-sshd --no-sudo --no-dns-sshfp --unattended
)

workaround_1708275

if id apache &> /dev/null ; then
HTTP_CERT=/etc/pki/tls/certs/localhost.crt
rm -f $HTTP_CERT

(
set -x

kinit -k
ipa-getkeytab -k /etc/http.keytab -s $IPA -p HTTP/$HOSTNAME
chown apache /etc/http.keytab

ipa-getcert request -k /etc/pki/tls/private/localhost.key -f $HTTP_CERT -N $HOSTNAME -K HTTP/$HOSTNAME -w
chown apache $HTTP_CERT
)
fi
7 changes: 7 additions & 0 deletions container/ipa-client-enroll.service
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
[Unit]
Description=IPA-enroll the machine to the FreeIPA server

[Service]
Type=oneshot
ExecStart=/usr/sbin/ipa-client-enroll
RemainAfterExit=true
28 changes: 28 additions & 0 deletions container/ipa-precreate-hosts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
#!/bin/bash

# Copyright 2016--2018 Jan Pazdziora
#
# Licensed under the Apache License, Version 2.0 (the "License").

# Prepare HBAC rules for services webapp and idp, and OTPs for IPA clients.

set -e

exec >> /var/log/ipa-server-run.log 2>&1

HOSTS="client.mokey.local"
for i in $HOSTS ; do
if ! [ -f /data/$i-otp ] ; then
echo "Creating host record for $i"
klist > /dev/null || kinit admin < /data/admin-password
(
set -x
ipa host-find $i > /dev/null && ipa host-del $i
ipa host-add --random $i --force --raw | awk '/randompassword:/ { print $2 }' > /data/$i-otp.1
ipa service-add --force HTTP/$i
mv /data/$i-otp.1 /data/$i-otp
)
fi
done

kdestroy -A
13 changes: 13 additions & 0 deletions container/ipa-precreate-hosts.service
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
[Unit]
Description=Create IPA client host records and OTPs, and HBAC rules
After=ipa.service
After=ipa-server-configure-first.service
After=ipa-server-update-self-ip-address.service

[Service]
Type=oneshot
ExecStart=/usr/sbin/ipa-precreate-hosts
RemainAfterExit=true

[Install]
WantedBy=container-ipa.target
21 changes: 21 additions & 0 deletions container/populate-data-volume
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
#!/bin/bash

# Copyright 2016 Jan Pazdziora
#
# Licensed under the Apache License, Version 2.0 (the "License").

# Upon the first run, populates the /data directory, to be used during
# subsequent startups.

set -e

exec >> /run/docker-console/fd/1 2>> /run/docker-console/fd/2

if [ -f /data/volume-version ] ; then
echo "The data volume is already populated on $HOSTNAME."
exit
fi

cat /etc/volume-data-list | while read f ; do [ -e $f ] && echo $f ; done | ( set -x ; xargs cp --parents -rp -t /data )
set -x
echo 0.1 > /data/volume-version
Loading

0 comments on commit 5aad7ad

Please sign in to comment.