Today: May 3, 2024 11:45 pm
A collection of Software and Cloud patterns with a focus on the Enterprise

Generate TLS Secret for kubernetes

Often in development or when working on proofs of concept (PoC), I need working SSL to protect an endpoint. If I controlled the domain, I would use Lets Encrypt to generate a certificate. When I don’t control the domain, I often use self signed certificates. Below is how I create them and then use them to create a Secret in kubernetes.

Choosing a domain (common name)

When I don’t control the domain, that usually means I can’t setup a subdomain with appropriate name resolution for my project. In this case I use a wildcard DNS provider, like nip.io. In my kubernetes clusters, there is usually a gateway that facilitates ingress to pods running on the cluster. The example below assumes that my gateway is running at 192.168.99.6.

Create the key and certificate

I use openssl to create the key and certificate in one command

$ openssl req -newkey rsa:2048 -nodes -keyout onboard.192.168.99.6.nip.io.key -x509 -days 365 -out onboard.192.168.99.6.nip.io.crt
Generating a 2048 bit RSA private key
.............................................................................+++
......................................................+++
writing new private key to 'onboard.192.168.99.6.nip.io.key'
-----
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) [AU]:US
State or Province Name (full name) [Some-State]:Texas
Locality Name (eg, city) []:Houston
Organization Name (eg, company) [Internet Widgits Pty Ltd]:My Company
Organizational Unit Name (eg, section) []:Engineering
Common Name (e.g. server FQDN or YOUR name) []:onboard.192.168.99.6.nip.io
Email Address []:
$ ll onboard.192.168.99.6.nip.io.*
-rw-r--r--  1 dwatrous  2104653700   1.5K Oct 30 08:27 onboard.192.168.99.6.nip.io.crt
-rw-r--r--  1 dwatrous  2104653700   1.6K Oct 30 08:27 onboard.192.168.99.6.nip.io.key

Create the kubernetes resource

Now that I have the key and crt file, I’m ready to create a kubernetes Secret using these files. Kubernetes stores these files as a base64 string, so the first step is to encode them.

$ cat onboard.192.168.99.6.nip.io.key | base64
LS0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVktLS0tLQpNSUlFcFFJQkFBS0NBUUVBc09yNjVWNmpnR0pacWxZOFNYSitBZlFONitaUzRMc1ozcldYbjZ6V3J5WlNlc0drCktZRHVSQytEKzYyQ3JVWjZCTEVkNmxkcmdqMmVFRXFuc2hDNjk5ODRGTnF1c1Jjd1k2eVRHaGFwdXhFak1ONHAKZU4vWk1oeUVEb0pNQXJIUS8yZG9FdFlST0xjU2k1VWk4K1V4bzJ0M2F5dkQ0QlpZb3NvdCtoRjg0TUtVNTZuWApQYnRuVlk0L2theHZPbXZmUmh5eno3K2Zkam1nTldnMTJGcVp4NFBzMnBPd2hFUGJzakZxVVUzYXYxTjg1bk1zCmZ0b0xHRkhGdGlac21pRVZBalVjMVUrZmJqaUt5VVNrczlDYzIza1NhOXN6ZklqRW5YNnB2NzdQOERvYjBFaUwKbGVTR3pIbXlnMnN4c3dydlVBdmMxdHNMM0w1Y1ZWWWcvQmt6WndJREFRQUJBb0lCQVFDdUdqQlo5SzVXTTNNbAo2OE5jVEMydzRtbW8wbnFRNmM5bG1iTGkwZ1piU3cwei9NZitpQUR5WFFnM2J1TUFmakNwMlZzbE9HNTJOd2tMCjQwdndNZ0tzMTZDcTlTR2c2TDhYOThhemo4WHNiOHIvMWtGZDBIdy9ra3hxc3RMMmUrSysxUlpZSXczRGtIWFIKb3l4SWRDNUxNZ0ROR0w2a0VabWNhZmFnOXdDN2RWMHlQNnpOTzg2dUlXbWpsdnRMSVJVMHhNWmh1VXBrOEtoOQo0QkpEemJBUUREQlFMbUU2WEtySHVzYncxQldWZUNBWEhBdlMvamNJalNxVWV4djlNUDM2OE5Xb3BkNFRRRk82CmVXRnZ6cmJlQWhZcUk1K2RrUVRLb3MvbmZOcFY3clFQSG0vM1ZPcWtEaWtpTTllcXU1bGUvQWk2cjhFM2NCclIKZzJ1OW9sYkJBb0dCQU9iSGt2Y2Y0aFZScDVTUHZ4UmJlSklqaFE2NDAxMDQ5aU1wcjFEdlpVbVZUZHUvQlk4agpTYTRtTkNDcWRvR1VaOTR2Z3EyQmJ2Q1E4RysvdVdxbmpuSHZNQ0JPL3ZISHRkVzBna2s1WWs3U1lrajNNeTBSCkIyUEZ5MmM0YVpaZnpnYTRzWm04UENEbjZBL25HMmE3L2YrMitRNk12YTNvdmxMNWVLaW5sa3R4QW9HQkFNUkEKaWhZQ1RKTTVZa1JTK0p2TEdCcVpDY1o4VzNTNmlsb012bjJNWG41eXZITzhveGRKSjFBZVQwek1DYm5PdUtHZApndHZNSW5iV1ZvVE5iblZpSWJ4Y0FUNnlLNmc5WGxnWGRqL0lnL3VZME5pYUZKQjBKL1NCRXcvdkdOSGZsVUN0CkswaU15SjBNRjdValhJTGMvNkZWOENONEdoQ2Ivb0RjcVhsdjlwQlhBb0dCQUtCT2VaUVlIWi9aZktNQnh5V0kKOUpQdkFIcGRnTlQ4d0Yxa2sxZVJNN2FOYyt2MUlSTncyN21RNkJ6WXpFRHVxY2Y1RUxrZGM4YS9wNFM0bFQrMAo3SW5RTUlvQTFhOTFucVc0SWRoamVCcHdvYjAxbWVMd05VWGxHNG16OGdSMndGS0M4VHR0T2dkUmtDMmJ4N2p4ClZWclA2dWxrY2szZm9uVll2YXZKM2VqQkFvR0FQMG9NL2lJSnJlVHdvdFliSktydmdBeGdrYWtqR1ZiYkxVVTAKM1dvNlF3OGZaVGV0Wk9JTGtwUGp3UUdBRlhMc0tmcVE2KzgrSWhMblhmYWVLNjdVOEZpL2NnZWxlcUVuM3NMYQpPS0dpcHEzV2xEUEVjLzF4c1RFN0E0VjQvSjNkRlRtRFh1Yy8veDJhTzZsR3VWRXFBMDZpbmQycWtqYXNjY1EzCngxanJMQXNDZ1lFQWluOWRGY0NCUkRuNjFxUkg4TkE2NUdiZEgrblFZN1VVL3V0S21YOXV2OEc3ZXhTeW56S1YKMVBmNTFOeUFOVzlaZ2N0UjJtVWNxcno4cW5taEFMeFgzS05oeDRjaS9yRTAwdTFrNVVpTGhiUnNISzVtQ3NaOQplbTNzS240WTVuM1dhTUI5RFlGd1NwL0tRZlRRU1FUdmw4N05FSWx5NTdHeGdpLy9DQXg2dVMwPQotLS0tLUVORCBSU0EgUFJJVkFURSBLRVktLS0tLQo=
$ cat onboard.192.168.99.6.nip.io.crt | base64
LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUViakNDQTFhZ0F3SUJBZ0lKQU82dk5NZEcwcG5TTUEwR0NTcUdTSWIzRFFFQkJRVUFNSUdBTVFzd0NRWUQKVlFRR0V3SlZVekVPTUF3R0ExVUVDQk1GVkdWNFlYTXhFREFPQmdOVkJBY1RCMGh2ZFhOMGIyNHhFekFSQmdOVgpCQW9UQ2sxNUlFTnZiWEJoYm5reEZEQVNCZ05WQkFzVEMwVnVaMmx1WldWeWFXNW5NU1F3SWdZRFZRUURFeHR2CmJtSnZZWEprTGpFNU1pNHhOamd1T1RrdU5pNXVhWEF1YVc4d0hoY05NVGN4TURNd01UTTFNekkxV2hjTk1UZ3gKTURNd01UTTFNekkxV2pDQmdERUxNQWtHQTFVRUJoTUNWVk14RGpBTUJnTlZCQWdUQlZSbGVHRnpNUkF3RGdZRApWUVFIRXdkSWIzVnpkRzl1TVJNd0VRWURWUVFLRXdwTmVTQkRiMjF3WVc1NU1SUXdFZ1lEVlFRTEV3dEZibWRwCmJtVmxjbWx1WnpFa01DSUdBMVVFQXhNYmIyNWliMkZ5WkM0eE9USXVNVFk0TGprNUxqWXVibWx3TG1sdk1JSUIKSWpBTkJna3Foa2lHOXcwQkFRRUZBQU9DQVE4QU1JSUJDZ0tDQVFFQXNPcjY1VjZqZ0dKWnFsWThTWEorQWZRTgo2K1pTNExzWjNyV1huNnpXcnlaU2VzR2tLWUR1UkMrRCs2MkNyVVo2QkxFZDZsZHJnajJlRUVxbnNoQzY5OTg0CkZOcXVzUmN3WTZ5VEdoYXB1eEVqTU40cGVOL1pNaHlFRG9KTUFySFEvMmRvRXRZUk9MY1NpNVVpOCtVeG8ydDMKYXl2RDRCWllvc290K2hGODRNS1U1Nm5YUGJ0blZZNC9rYXh2T212ZlJoeXp6NytmZGptZ05XZzEyRnFaeDRQcwoycE93aEVQYnNqRnFVVTNhdjFOODVuTXNmdG9MR0ZIRnRpWnNtaUVWQWpVYzFVK2ZiamlLeVVTa3M5Q2MyM2tTCmE5c3pmSWpFblg2cHY3N1A4RG9iMEVpTGxlU0d6SG15ZzJzeHN3cnZVQXZjMXRzTDNMNWNWVllnL0Jrelp3SUQKQVFBQm80SG9NSUhsTUIwR0ExVWREZ1FXQkJTcTJiblRaMGRjKzVFNlZWTWwrZmJIcGhsaEFUQ0J0UVlEVlIwagpCSUd0TUlHcWdCU3EyYm5UWjBkYys1RTZWVk1sK2ZiSHBobGhBYUdCaHFTQmd6Q0JnREVMTUFrR0ExVUVCaE1DClZWTXhEakFNQmdOVkJBZ1RCVlJsZUdGek1SQXdEZ1lEVlFRSEV3ZEliM1Z6ZEc5dU1STXdFUVlEVlFRS0V3cE4KZVNCRGIyMXdZVzU1TVJRd0VnWURWUVFMRXd0RmJtZHBibVZsY21sdVp6RWtNQ0lHQTFVRUF4TWJiMjVpYjJGeQpaQzR4T1RJdU1UWTRMams1TGpZdWJtbHdMbWx2Z2drQTdxODB4MGJTbWRJd0RBWURWUjBUQkFVd0F3RUIvekFOCkJna3Foa2lHOXcwQkFRVUZBQU9DQVFFQXJtdUVUeHp0YzZLdmpxbjJMY0Z5TGZxMWRqQTJWZXpJU2FmUHhxWWUKd1pJcnc2SkxOaUI3MVRibk1YWVp3UGpNTXVBOGJYc0ZHR0wzQzRGRzl2NGIvYzd0V2hsWks3R1ZucjYyQ0Q2dgpsS3pLeG5ZT2E0MVN4UzdRdG9RN0tDYkc5K0ZPeWhaUjIzS2hpV0UrQ0plT09MVE5XUW1QVlRkRWhuWmVEaWlBClhDWE1DNHE1a252UmNTK1ZXRWVUTjRhd2R2bUVseSs2bXlTNkszQTl1VTQvajQ5dlRwcWNUVThOLzI4SzhTQUMKeWx1QkF3RG1pRURFU1JzUmVDWVBHanZ2Tmx2c2xvUElJVWwxTTAvN2VIbjZkaU96R3ZueGFKN0ltZ29Ody9hNgpsQm5pYW1OaXNCejViU25GWlpLbXNDWVloN25teFJsODNBZWRoN3VCNW05SlhBPT0KLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQo=

At this point I can easily create a kubernetes resource definition (YAML) that will create the Secret resource.

apiVersion: v1
kind: Secret
metadata:
  name: onboard.192.168.99.6.nip.io.tls
  namespace: default
type: kubernetes.io/tls
data:
  tls.crt:LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUViakNDQTFhZ0F3SUJBZ0lKQU82dk5NZEcwcG5TTUEwR0NTcUdTSWIzRFFFQkJRVUFNSUdBTVFzd0NRWUQKVlFRR0V3SlZVekVPTUF3R0ExVUVDQk1GVkdWNFlYTXhFREFPQmdOVkJBY1RCMGh2ZFhOMGIyNHhFekFSQmdOVgpCQW9UQ2sxNUlFTnZiWEJoYm5reEZEQVNCZ05WQkFzVEMwVnVaMmx1WldWeWFXNW5NU1F3SWdZRFZRUURFeHR2CmJtSnZZWEprTGpFNU1pNHhOamd1T1RrdU5pNXVhWEF1YVc4d0hoY05NVGN4TURNd01UTTFNekkxV2hjTk1UZ3gKTURNd01UTTFNekkxV2pDQmdERUxNQWtHQTFVRUJoTUNWVk14RGpBTUJnTlZCQWdUQlZSbGVHRnpNUkF3RGdZRApWUVFIRXdkSWIzVnpkRzl1TVJNd0VRWURWUVFLRXdwTmVTQkRiMjF3WVc1NU1SUXdFZ1lEVlFRTEV3dEZibWRwCmJtVmxjbWx1WnpFa01DSUdBMVVFQXhNYmIyNWliMkZ5WkM0eE9USXVNVFk0TGprNUxqWXVibWx3TG1sdk1JSUIKSWpBTkJna3Foa2lHOXcwQkFRRUZBQU9DQVE4QU1JSUJDZ0tDQVFFQXNPcjY1VjZqZ0dKWnFsWThTWEorQWZRTgo2K1pTNExzWjNyV1huNnpXcnlaU2VzR2tLWUR1UkMrRCs2MkNyVVo2QkxFZDZsZHJnajJlRUVxbnNoQzY5OTg0CkZOcXVzUmN3WTZ5VEdoYXB1eEVqTU40cGVOL1pNaHlFRG9KTUFySFEvMmRvRXRZUk9MY1NpNVVpOCtVeG8ydDMKYXl2RDRCWllvc290K2hGODRNS1U1Nm5YUGJ0blZZNC9rYXh2T212ZlJoeXp6NytmZGptZ05XZzEyRnFaeDRQcwoycE93aEVQYnNqRnFVVTNhdjFOODVuTXNmdG9MR0ZIRnRpWnNtaUVWQWpVYzFVK2ZiamlLeVVTa3M5Q2MyM2tTCmE5c3pmSWpFblg2cHY3N1A4RG9iMEVpTGxlU0d6SG15ZzJzeHN3cnZVQXZjMXRzTDNMNWNWVllnL0Jrelp3SUQKQVFBQm80SG9NSUhsTUIwR0ExVWREZ1FXQkJTcTJiblRaMGRjKzVFNlZWTWwrZmJIcGhsaEFUQ0J0UVlEVlIwagpCSUd0TUlHcWdCU3EyYm5UWjBkYys1RTZWVk1sK2ZiSHBobGhBYUdCaHFTQmd6Q0JnREVMTUFrR0ExVUVCaE1DClZWTXhEakFNQmdOVkJBZ1RCVlJsZUdGek1SQXdEZ1lEVlFRSEV3ZEliM1Z6ZEc5dU1STXdFUVlEVlFRS0V3cE4KZVNCRGIyMXdZVzU1TVJRd0VnWURWUVFMRXd0RmJtZHBibVZsY21sdVp6RWtNQ0lHQTFVRUF4TWJiMjVpYjJGeQpaQzR4T1RJdU1UWTRMams1TGpZdWJtbHdMbWx2Z2drQTdxODB4MGJTbWRJd0RBWURWUjBUQkFVd0F3RUIvekFOCkJna3Foa2lHOXcwQkFRVUZBQU9DQVFFQXJtdUVUeHp0YzZLdmpxbjJMY0Z5TGZxMWRqQTJWZXpJU2FmUHhxWWUKd1pJcnc2SkxOaUI3MVRibk1YWVp3UGpNTXVBOGJYc0ZHR0wzQzRGRzl2NGIvYzd0V2hsWks3R1ZucjYyQ0Q2dgpsS3pLeG5ZT2E0MVN4UzdRdG9RN0tDYkc5K0ZPeWhaUjIzS2hpV0UrQ0plT09MVE5XUW1QVlRkRWhuWmVEaWlBClhDWE1DNHE1a252UmNTK1ZXRWVUTjRhd2R2bUVseSs2bXlTNkszQTl1VTQvajQ5dlRwcWNUVThOLzI4SzhTQUMKeWx1QkF3RG1pRURFU1JzUmVDWVBHanZ2Tmx2c2xvUElJVWwxTTAvN2VIbjZkaU96R3ZueGFKN0ltZ29Ody9hNgpsQm5pYW1OaXNCejViU25GWlpLbXNDWVloN25teFJsODNBZWRoN3VCNW05SlhBPT0KLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQo=
  tls.key:LS0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVktLS0tLQpNSUlFcFFJQkFBS0NBUUVBc09yNjVWNmpnR0pacWxZOFNYSitBZlFONitaUzRMc1ozcldYbjZ6V3J5WlNlc0drCktZRHVSQytEKzYyQ3JVWjZCTEVkNmxkcmdqMmVFRXFuc2hDNjk5ODRGTnF1c1Jjd1k2eVRHaGFwdXhFak1ONHAKZU4vWk1oeUVEb0pNQXJIUS8yZG9FdFlST0xjU2k1VWk4K1V4bzJ0M2F5dkQ0QlpZb3NvdCtoRjg0TUtVNTZuWApQYnRuVlk0L2theHZPbXZmUmh5eno3K2Zkam1nTldnMTJGcVp4NFBzMnBPd2hFUGJzakZxVVUzYXYxTjg1bk1zCmZ0b0xHRkhGdGlac21pRVZBalVjMVUrZmJqaUt5VVNrczlDYzIza1NhOXN6ZklqRW5YNnB2NzdQOERvYjBFaUwKbGVTR3pIbXlnMnN4c3dydlVBdmMxdHNMM0w1Y1ZWWWcvQmt6WndJREFRQUJBb0lCQVFDdUdqQlo5SzVXTTNNbAo2OE5jVEMydzRtbW8wbnFRNmM5bG1iTGkwZ1piU3cwei9NZitpQUR5WFFnM2J1TUFmakNwMlZzbE9HNTJOd2tMCjQwdndNZ0tzMTZDcTlTR2c2TDhYOThhemo4WHNiOHIvMWtGZDBIdy9ra3hxc3RMMmUrSysxUlpZSXczRGtIWFIKb3l4SWRDNUxNZ0ROR0w2a0VabWNhZmFnOXdDN2RWMHlQNnpOTzg2dUlXbWpsdnRMSVJVMHhNWmh1VXBrOEtoOQo0QkpEemJBUUREQlFMbUU2WEtySHVzYncxQldWZUNBWEhBdlMvamNJalNxVWV4djlNUDM2OE5Xb3BkNFRRRk82CmVXRnZ6cmJlQWhZcUk1K2RrUVRLb3MvbmZOcFY3clFQSG0vM1ZPcWtEaWtpTTllcXU1bGUvQWk2cjhFM2NCclIKZzJ1OW9sYkJBb0dCQU9iSGt2Y2Y0aFZScDVTUHZ4UmJlSklqaFE2NDAxMDQ5aU1wcjFEdlpVbVZUZHUvQlk4agpTYTRtTkNDcWRvR1VaOTR2Z3EyQmJ2Q1E4RysvdVdxbmpuSHZNQ0JPL3ZISHRkVzBna2s1WWs3U1lrajNNeTBSCkIyUEZ5MmM0YVpaZnpnYTRzWm04UENEbjZBL25HMmE3L2YrMitRNk12YTNvdmxMNWVLaW5sa3R4QW9HQkFNUkEKaWhZQ1RKTTVZa1JTK0p2TEdCcVpDY1o4VzNTNmlsb012bjJNWG41eXZITzhveGRKSjFBZVQwek1DYm5PdUtHZApndHZNSW5iV1ZvVE5iblZpSWJ4Y0FUNnlLNmc5WGxnWGRqL0lnL3VZME5pYUZKQjBKL1NCRXcvdkdOSGZsVUN0CkswaU15SjBNRjdValhJTGMvNkZWOENONEdoQ2Ivb0RjcVhsdjlwQlhBb0dCQUtCT2VaUVlIWi9aZktNQnh5V0kKOUpQdkFIcGRnTlQ4d0Yxa2sxZVJNN2FOYyt2MUlSTncyN21RNkJ6WXpFRHVxY2Y1RUxrZGM4YS9wNFM0bFQrMAo3SW5RTUlvQTFhOTFucVc0SWRoamVCcHdvYjAxbWVMd05VWGxHNG16OGdSMndGS0M4VHR0T2dkUmtDMmJ4N2p4ClZWclA2dWxrY2szZm9uVll2YXZKM2VqQkFvR0FQMG9NL2lJSnJlVHdvdFliSktydmdBeGdrYWtqR1ZiYkxVVTAKM1dvNlF3OGZaVGV0Wk9JTGtwUGp3UUdBRlhMc0tmcVE2KzgrSWhMblhmYWVLNjdVOEZpL2NnZWxlcUVuM3NMYQpPS0dpcHEzV2xEUEVjLzF4c1RFN0E0VjQvSjNkRlRtRFh1Yy8veDJhTzZsR3VWRXFBMDZpbmQycWtqYXNjY1EzCngxanJMQXNDZ1lFQWluOWRGY0NCUkRuNjFxUkg4TkE2NUdiZEgrblFZN1VVL3V0S21YOXV2OEc3ZXhTeW56S1YKMVBmNTFOeUFOVzlaZ2N0UjJtVWNxcno4cW5taEFMeFgzS05oeDRjaS9yRTAwdTFrNVVpTGhiUnNISzVtQ3NaOQplbTNzS240WTVuM1dhTUI5RFlGd1NwL0tRZlRRU1FUdmw4N05FSWx5NTdHeGdpLy9DQXg2dVMwPQotLS0tLUVORCBSU0EgUFJJVkFURSBLRVktLS0tLQo=

The last thing to do is use kubectl (or the API) to create the actual resource. Assuming the above YAML was in a file onboard.192.168.99.6.nip.io.tls.yaml, the following command would create the resource.

kubectl apply -f /path/to/onboard.192.168.99.6.nip.io.tls.yaml

Use the Secret in a Pod

The Deployment definition below shows how to use the Secret above within a Pod.

apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: onboarding
spec:
  replicas: 1
  template:
    metadata:
      labels:
        app: onboarding
    spec:
      containers:
      - image: username/onboarding
        name: onboarding

        volumeMounts:
        - name: tls
          mountPath: /usr/src/app/tls

      volumes:
      - name: tls
        secret:
          secretName: onboard.192.168.99.6.nip.io.tls

Comments

  1. This is old, now you just do:

    % kubectl create secret tls onboard.192.168.99.6.nip.io.tls –key ./onboard.192.168.99.6.nip.io.key –cert ./onboard.192.168.99.6.nip.io.ctrt

    • There’s a different between creating a yaml file to create a secret, versus creation of secret using command line entry based on referenced certificate/key in a relative path.

  2. I am getting this error, in ingress logs. Any help.

    Updating local copy of SSL certificate “dec-12-28b/tls-secret” with missing intermediate CA certs

  3. Thanks for the instructions, that helped a lot.
    I put all of this in a small bash script which generates the yaml file without any copy-pasting:
    https://gist.github.com/fauberso/f6304adf1213176180aedf70170713d1

  4. Thank you, i have been searching for information a lot.

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.