Konfiguracja PKI dla strongswan

Errata 22.05.2026r. - Kilka mniejszych poprawek + korekta dotycząca nazwy przedstawionej tu konfiguracji, to jest konfiguracja typu “jumphost”, a nie jak została tu wcześniej błędnie przytoczona jako “Road Warrior”. Podziękowania dla recenzenta Piotra :)

Wpis o tym jak skonfigurować bardzo podstawowe PKI (public key infrastrucutre) dla Strongswan IPSec. Nie jest do dogłębna analiza anie rozłożenia na czynniki pierwsze, a jedynie podręczny tutorial na przyszłość, żebym znów nie musiał poświęcać więcej niż 5 minut na zadanie, które jest w zasadzie trywialne… Natomiast może być to ewentualnie punkt zaczepienia do PKI jako takiego.

Na początek należy pamiętać, że Strongswan wymaga, aby w PKI identyfikatory zawierały się w polach SAN (subject alternative name[s]) lub DN (Distinguished Name). Bez tego, przy próbach nawiązania połączenia przez service charon-systemd (za pośrednictwem swanctl) będą prawdopodobnie widoczne takie oto błędy:

(...) charon-systemd (...) no trusted rsa public key found for "example.com"

Gdzie example.com to identyfikator hosta próbującego nawiązać połączenie. W komunikacie błędu mogą być też widoczne nazwy innych algorytmów szyfrujących, w zależności od tego co zostało użyte przy generowaniu certyfikatów. A w takim razie jak przygotować działający zestaw certyfikatów dla wybranej konfiguracji połączenia?

Przypadek “jumphost”

Wybrana przeze mnie konfiguracja to tzw. “jumphost”. Konfigurację oparłem na wariancie typu “Road Warrior” opisanym w dokumentacji Strongswan. Natomiast moją intencją było aby pomiędzy dwoma hostami, jednym widocznym publicznie (Peregrin), drugim będącym routerem dla pewnej podsieci przebywającym za NATem (Meriadoc), nawiązywane zostało połączenie szyfrowane (za pomocą IPSec), po czym aby każdy z hostów z grupy w podsieci mógł z tego szyfrowanego połączenia swobodnie (albo w sposób kontrolowany) korzystać. W rezultacie umożliwia to aby dowolne hosty znajdujące się za NATem były dostępne z poziomu Internetu. Zamierzona kofiguracja została zobrazowana poniżej.

Konfiguracja typu "jumphost" dla dwóch hostów połączonych
punkt-do-punktu, Meriadoc i
Peregrin.

Zatem potrzebuje łącznie dwóch “entity” certyfikatów dla każdego z hostów biorących udział w połączeniu IPSec, to jest dla Meriadoca i Peregrina. Ponieważ uznaniowo stwierdziłem, że jestem w stanie samodzielnie zapewnić bezpieczeństwo swoich danych, to dodatkowo wygeneruję tzw. root certificate (self signed), którym te “entity” certyfikaty podpiszę. Innymi słowy, sam dla siebie zostanę CA (Certificate Authority).

Generowanie Root Certificate - CA

Bazuję tutaj na tutorialu generowania certyfikatów znalezionych na tej stronie. Natomiast w internecie można znaleźć wiele takich tutoriali. W moim przypadku korzystam z dostępnego pod Linuxem narzędzia openssl. Natomiast warto wziąć pod uwagę, że Strongswan również dołączył do swojego zestawu narzędzie dedykowane właśnie do generowania odpowiednich certyfikatów, pki.

openssl req -newkey rsa:4096 \
            -x509 \
            -sha256 \
            -days 3650 \
            -nodes \
            -out CA.pem \
            -keyout CA.key

Z powyższej komendy uzyskuję root certyfikat CA.pem wraz z jego kluczem prywatnym CA.key[1].

Generowanie entity certificates

Następnie przystępują do generowania “entity” certyfikatów - tylu ile potrzebuję, czyli w moim przypadku dwóch. Tutaj bazuję na tutorialu z tej strony. Nazwy “entities” jakie tutaj wykorzystam to Meriadoc i Peregrin - posłużą one też później jako identyfikatory dla Strongswan.

W pierwszej kolejności przygotowuję plik konfiguracyjny zawierający informacje o każdym z “entities”. Na przykład, dla “entity” Meriadoc będzie to:

# ./meriadoc.conf

[ req ]
default_bits = 4096
prompt = no
default_md = sha256
distinguished_name = dn
req_extensions = v3_req

[ dn ]
C  = PL
ST = Mazowieckie
L = Warszawa
O  = Durins Gate Company
CN = Meriadoc

[ v3_req ]
subjectAltName = @alt_names

[ alt_names ]
DNS.1 = meriadoc
DNS.2 = meriadoc.durinsgate.com
IP.1 = 10.1.0.1

Nie będę tutaj omawiał szczegółowo każdego z pól pliku konfiguracyjnego, natomiast zwracam szczególną uwagę na ostatnią strukturę alt_names, czyli “SAN”. Jak wspomniałem na samym początku, są one niezbędne dla strongswan (wystarczy jedno).

Następnie wygeneruję klucz prywatny dla certyfikatu dla Meriadoca.

openssl genrsa -out meriadoc_key.pem 4096

Po czym przygotuję tzw. CSR, czyli certificate signing request. Bazuje on na informacjach zawartych już w pliku konfiguracyjnym przygotowanym dla Meriadoca.

openssl req -new -key meriadoc_key.pem -out meriadoc.csr -config meriadoc.conf

I wreszcie podpiszę, jako CA, CSR dla Meriadoca przy użyciu mojego zestawu root certificate i root private key.

openssl x509 -req -in meriadoc.csr -CA CA.pem -CAkey CA.key -out meriadoc.crt -days 365 -sha256 -extfile meriadoc.cnf -extensions v3_req

To samo powtarzam dla entity Peregrin odpowiednio wypełniając jego plik konfiguracyjny.

Konfiguracja Strongswan

Na odpowiednie hosty przesyłam odpowiednie certyfikaty w miejsca, gdzie charon-systemd się ich spodziewa, czyli do właściwych podkatalogów w katalogu /etc/swanctl (jest to katalog wykorzystywany pod Debianem i Fedorą; dla innych dystrybucji czy systemów operacyjnych może być to inny katalog, należy na to zwrócić uwagę). Powinno to wyglądać tak:

meriadoc$ tree /etc/swanctl/
/etc/swanctl/
├── conf.d
├── ecdsa
├── pkcs12
├── pkcs8
├── private
│   └── meriadoc_key.pem
├── pubkey
├── rsa
├── swanctl.conf
├── x509
│   └── meriadoc.pem
├── x509aa
├── x509ac
├── x509ca
│   └── CA.pem
├── x509crl
└── x509ocsp

Na danym hoście, do którego należy dany certyfikat, powinny znaleźć się publiczna część certyfikatu tego entity (meriadoc.pem), prywata część certyfikatu (meriadoc_key.pem) oraz publiczna część certyfikatu CA, którym dany “entity” certyfikat został podpisany (CA.pem). Dla Peregrina analogicznie.

Konfiguracja swanctl.conf - przypadek “jumphost”

Do ukończenia konfiguracji niezbędne jest jeszcze przygotowanie plików konfiguracyjnych swanctl.conf. Zakładam tutaj, że “entity” Meriadoc jest niepublicznie dostępne i skrywa za sobą subnet innych hostów, natomiast “entity” Peregrin stanowi publiczny gateway, to jest jumphost, dla Meriadoca i jego podsieci, czyli jest widoczny w Internecie.

Meriadoc swanctl.conf - strona prywatna

Ta strona będzie odpowiedzialna za nawiązanie połączenia IPSec, ponieważ tylko ona może w sposób bezpośredni nawiązać połączenie z drugą stroną. Konfiguracja dla tej strony może wyglądać następująco:

connections {
  home {
    remote_addrs = peregrin.durinsgate.com

    local {
      auth = pubkey
      certs = meriadoc.pem
      id = meriadoc
    }

    remote {
      auth = pubkey
      id = peregrin
    }

    children {
      home {
        start_action = start
        local_ts = 10.1.0.0/16
      }
    }
  }
}

Po szczegóły można przejść do dokumentacji Strongswan.

Peregrin swanctl.conf - strona publiczna

Ta strona ma za zadanie odebrać nawiązywane przez stronę prywatną połączenie i zweryfikować czy może ono zostać ustanowione. Konfiguracja może wyglądać następująco:

connections {
  jh {
    local {
      auth = pubkey
      certs = peregrin.pem
      id = peregrin
    }

    remote {
      auth = pubkey
    }

    children {
      meriadoc-net {
        remote_ts = 10.1.0.0/16
      }
    }
  }
}

Sprawdzenie statusu połączenia

Na koniec, pokrótce weryfikacja czy konfiguracja PKI/swanctl przebiegła pomyślnie. Ze strony prywatnej, tj. Meriadoc, można ręcznie nawiązać połączenie z pomocą komendy swanctl -i -i home, natomiast w pierwszej kolejności powinny zostać przez swanctl załadowane nowa konfiguracja i klucze. Żeby to zapewnić, można zrestartować service strongswan, natomiast to też spowoduje automatyczną próbę nawiązania połączenia.

# systemctl restart strongswan

W celu weryfikacji czy połączenie zostało zestawione, to znaczy czy tzw. SA (security assosiation) zostało utworzone, można wykorzystać komendę swanctl -l. Wynik powinien być podobny jak ten przedstawiony poniżej.

# swanctl -l
jh: #1, ESTABLISHED, IKEv2, (...)
  local  'peregrin' @ <Peregrin public ip address>[4500]
  remote 'meriadoc' @ <Meriadoc NAT ip address>[<random port>]
  (...)
  established 5789s ago, rekeying in 8491s
  meriadoc-net: #1, (...)
    installed 2546s ago, rekeying in 863s, expires in 1414s
    in  c4d35ddc,  85100 bytes,   817 packets,     9s ago
    out c7e44988,  75400 bytes,   910 packets,     9s ago
    local  <Peregrin public ip address>/32
    remote 10.1.0.0/16

  1. Klucz prywatny - jak nazwa wskazuje - należy zachować w prywatności, w przeciwieństwie do klucza publicznego, który będzie widoczny dla każdego. W przypadku klucza prywatnego root certificate sprawa jest dodatkowo na tyle poważna, że utrata prywatności tego klucza powoduje zupełną utratę bezpieczeństwa całego PKI opartego na takim root certificate. “Keep it secret, keep it safe”. ↩︎