blob: 5272b2de0d3c9f3dc2c0ba1697fc9cbc84ab5b77 [file] [log] [blame]
Sergiusz Bazanski4d61d202019-07-21 16:56:41 +02001# Deploy a Docker Registry in a cluster.
2
3# This needs an oauth2 secret provisioned, create with:
4# kubectl -n registry create secret generic auth --from-literal=oauth2_secret=...
5# kubectl get secrets rook-ceph-object-user-<ceph-pool>-object-registry -n <ceph-namespace> -o yaml --export | kubectl replace -f - -n registry
6
7local kube = import "../../../kube/kube.libsonnet";
8local cm = import "cert-manager.libsonnet";
9
10{
11 Environment: {
12 local env = self,
13 local cfg = env.cfg,
14 cfg:: {
15 namespace: "registry",
16 domain: error "domain must be set",
17 storageClassName: error "storageClassName must be set",
Sergiusz Bazanskid07861b2019-08-08 17:48:25 +020018 objectStoreName: error "objectStoreName must be set",
Sergiusz Bazanski4d61d202019-07-21 16:56:41 +020019 },
20
21 metadata(component):: {
22 namespace: cfg.namespace,
23 labels: {
24 "app.kubernetes.io/name": "registry",
25 "app.kubernetes.io/managed-by": "kubecfg",
26 "app.kubernetes.io/component": component,
27 },
28 },
29
30 namespace: kube.Namespace(cfg.namespace),
31
32 registryIssuer: cm.Issuer("registry-issuer") {
33 metadata+: env.metadata("registry-issuer"),
34 spec: {
35 selfSigned: {},
36 },
37 },
38 authCertificate: cm.Certificate("auth") {
39 metadata+: env.metadata("auth"),
40 spec: {
41 secretName: "auth-internal",
42 duration: "43800h0m0s", // 5 years
43 issuerRef: {
44 name: env.registryIssuer.metadata.name,
45 },
46 commonName: "auth.registry",
47 },
48 },
49 registryCertificate: cm.Certificate("registry") {
50 metadata+: env.metadata("registry"),
51 spec: {
52 secretName: "registry-internal",
53 duration: "43800h0m0s", // 5 years
54 issuerRef: {
55 name: env.registryIssuer.metadata.name,
56 },
57 commonName: "registry.registry",
58 },
59 },
60
61 registryConfig: kube.ConfigMap("registry-config") {
62 metadata+: env.metadata("registry-config"),
63 data: {
64 "config.yml": std.manifestYamlDoc({
65 version: "0.1",
66 log: {
Sergiusz Bazanski5f3a5e02019-09-25 02:51:51 +020067 level: "debug",
Sergiusz Bazanski4d61d202019-07-21 16:56:41 +020068 fields: {
69 service: "registry",
70 },
71 },
72 storage: {
73 cache: {
74 blobdescriptor: "inmemory",
75 },
76 s3: {
Sergiusz Bazanskid07861b2019-08-08 17:48:25 +020077 regionendpoint: "https://object.ceph-waw2.hswaw.net",
Sergiusz Bazanski4d61d202019-07-21 16:56:41 +020078 bucket: "registry",
Sergiusz Bazanskid07861b2019-08-08 17:48:25 +020079 region: "waw-hdd-redunant-2-object:default-placement",
Sergiusz Bazanski4d61d202019-07-21 16:56:41 +020080 },
81 },
82 http: {
83 addr: ":5000",
84 headers: {
85 "X-Content-Type-Options": ["nosniff"],
86 },
87 tls: {
88 certificate: "/certs/tls.crt",
89 key: "/certs/tls.key",
90 },
91 debug: {
92 addr: "localhost:5001",
93 },
94 },
95 health: {
96 storagedriver: {
97 enabled: true,
98 interval: "10s",
99 threshold: 3,
100 },
101 },
102 auth: {
103 token: {
104 realm: "https://%s/auth" % [cfg.domain],
105 service: "my.docker.registry",
106 issuer: "%s auth server" % [cfg.domain],
107 rootcertbundle: "/authcerts/tls.crt",
108 },
109 },
110 }),
111 },
112 },
113
114 authVolumeClaim: kube.PersistentVolumeClaim("auth-token-storage") {
115 metadata+: env.metadata("auth-token-storage"),
116 spec+: {
117 storageClassName: cfg.storageClassName,
118 accessModes: [ "ReadWriteOnce" ],
119 resources: {
120 requests: {
121 storage: "1Gi",
122 },
123 },
124 },
125 },
126
127 authConfig: kube.ConfigMap("auth-config") {
128 metadata+: env.metadata("auth-config"),
129 data: {
130 "auth_config.yml": std.manifestYamlDoc({
131 server: {
132 addr: ":5001",
133 certificate: "/certs/tls.crt",
134 key: "/certs/tls.key",
135 },
136 token: {
137 issuer: "%s auth server" % [cfg.domain],
138 expiration: 900,
139 },
140 oauth2: {
141 client_id: "registry",
142 client_secret_file: "/secrets/oauth2_secret",
143 authorize_url: "https://sso.hackerspace.pl/oauth/authorize",
144 access_token_url: "https://sso.hackerspace.pl/oauth/token",
145 profile_url: "https://sso.hackerspace.pl/api/1/profile",
146 redirect_url: "https://registry.k0.hswaw.net/oauth2",
147 username_key: "username",
148 token_db: "/data/oauth2_tokens.ldb",
149 registry_url: "https://registry.k0.hswaw.net",
150 },
151 users: {
152 [""]: {}, // '' user are anonymous users.
153 },
154 local data = self,
155 pushers:: [
Sergiusz Bazanskib13b7ff2019-08-29 20:12:24 +0200156 { who: ["q3k", "informatic"], what: "vms/*" },
157 { who: ["q3k", "informatic"], what: "app/*" },
158 { who: ["q3k", "informatic"], what: "go/svc/*" },
Sergiusz Bazanskid07861b2019-08-08 17:48:25 +0200159 { who: ["q3k"], what: "bgpwtf/*" },
160 { who: ["q3k"], what: "devtools/*" },
Sergiusz Bazanskib13b7ff2019-08-29 20:12:24 +0200161 { who: ["q3k", "informatic"], what: "cluster/*" },
Sergiusz Bazanski4d61d202019-07-21 16:56:41 +0200162 ],
163 acl: [
164 {
165 match: {account: "/.+/", name: "${account}/*"},
166 actions: ["*"],
167 comment: "Logged in users have full access to images that are in their 'namespace'",
168 },
169 {
170 match: {account: "/.+/", type: "registry", name: "catalog"},
171 actions: ["*"],
172 comment: "Logged in users can query the catalog.",
173 },
174 {
175 match: {account: ""},
176 actions: ["pull"],
177 comment: "Anyone can pull all images.",
178 },
179 ] + [
180 {
181 match: {
182 account: "/(%s)/" % std.join("|", p.who),
183 name: p.what,
184 },
185 actions: ["*"],
186 comment: "%s can push to %s" % [std.join(", ", p.who), p.what],
187 }
188 for p in data.pushers
189 ],
190 }),
191 }
192 },
193
194 authDeployment: kube.Deployment("auth") {
195 metadata+: env.metadata("auth"),
196 spec+: {
197 replicas: 1,
198 template+: {
199 spec+: {
200 volumes_: {
201 data: kube.PersistentVolumeClaimVolume(env.authVolumeClaim),
202 config: kube.ConfigMapVolume(env.authConfig),
203 certs: {
204 secret: { secretName: env.authCertificate.spec.secretName },
205 },
206 secrets: {
207 secret: { secretName: "auth" },
208 },
209 },
210 containers_: {
211 auth: kube.Container("auth") {
212 image: "informatic/docker_auth:2019040307",
213 volumeMounts_: {
214 config: { mountPath: "/config" },
215 certs: { mountPath: "/certs" },
216 secrets: { mountPath: "/secrets" },
217 data: { mountPath: "/data" },
218 },
219 },
220 },
221 },
222 },
223 },
224 },
225 authService: kube.Service("auth") {
226 metadata+: env.metadata("auth"),
227 target_pod:: env.authDeployment.spec.template,
228 spec+: {
229 type: "ClusterIP",
230 ports: [
231 { name: "auth", port: 5001, targetPort: 5001, protocol: "TCP" },
232 ],
233 }
234 },
235 registryDeployment: kube.Deployment("docker-registry") {
236 metadata+: env.metadata("docker-registry"),
237 spec+: {
238 replicas: 1,
239 template+: {
240 spec+: {
241 volumes_: {
242 config: kube.ConfigMapVolume(env.registryConfig),
243 certs: {
244 secret: { secretName: env.registryCertificate.spec.secretName },
245 },
246 authcerts: {
247 secret: { secretName: env.authCertificate.spec.secretName },
248 },
249 },
250 containers_: {
251 registry: kube.Container("docker-registry") {
Sergiusz Bazanski5f3a5e02019-09-25 02:51:51 +0200252 image: "registry:2.7.1",
Sergiusz Bazanski4d61d202019-07-21 16:56:41 +0200253 args: ["/config/config.yml"],
254 volumeMounts_: {
255 config: { mountPath: "/config" },
256 certs: { mountPath: "/certs" },
257 authcerts: { mountPath: "/authcerts" },
258 },
259 env_: {
260 REGISTRY_STORAGE_S3_ACCESSKEY: { secretKeyRef: {
Sergiusz Bazanskid07861b2019-08-08 17:48:25 +0200261 name: "rook-ceph-object-user-%(objectStorageName)s-registry" % {objectStorageName: cfg.objectStorageName},
Sergiusz Bazanski4d61d202019-07-21 16:56:41 +0200262 key: "AccessKey"
263 }},
264 REGISTRY_STORAGE_S3_SECRETKEY: { secretKeyRef: {
Sergiusz Bazanskid07861b2019-08-08 17:48:25 +0200265 name: "rook-ceph-object-user-%(objectStorageName)s-registry" % {objectStorageName: cfg.objectStorageName},
Sergiusz Bazanski4d61d202019-07-21 16:56:41 +0200266 key: "SecretKey",
267 }},
268 },
269 },
270 },
271 },
272 },
273 },
274 },
275 registryService: kube.Service("docker-registry") {
276 metadata+: env.metadata("docker-registry"),
277 target_pod:: env.registryDeployment.spec.template,
278 spec+: {
279 type: "ClusterIP",
280 ports: [
281 { name: "registry", port: 5000, targetPort: 5000, protocol: "TCP" },
282 ],
283 }
284 },
285 registryIngress: kube.Ingress("registry") {
286 metadata+: env.metadata("registry") {
287 annotations+: {
288 "kubernetes.io/tls-acme": "true",
289 "certmanager.k8s.io/cluster-issuer": "letsencrypt-prod",
290 "nginx.ingress.kubernetes.io/backend-protocol": "HTTPS",
291 "nginx.ingress.kubernetes.io/proxy-body-size": "0",
292 },
293 },
294 spec+: {
295 tls: [
296 {
297 hosts: [cfg.domain],
298 secretName: "registry-tls",
299 },
300 ],
301 rules: [
302 {
303 host: cfg.domain,
304 http: {
305 paths: [
306 { path: "/auth", backend: env.authService.name_port },
307 { path: "/", backend: env.authService.name_port },
308 { path: "/v2/", backend: env.registryService.name_port },
309 ]
310 },
311 }
312 ],
313 },
314 },
315
316 registryStorageUser: kube._Object("ceph.rook.io/v1", "CephObjectStoreUser", "registry") {
317 metadata+: {
Sergiusz Bazanskid07861b2019-08-08 17:48:25 +0200318 namespace: "ceph-waw2",
Sergiusz Bazanski4d61d202019-07-21 16:56:41 +0200319 },
320 spec: {
Sergiusz Bazanskid07861b2019-08-08 17:48:25 +0200321 store: cfg.objectStorageName,
Sergiusz Bazanski4d61d202019-07-21 16:56:41 +0200322 displayName: "docker-registry user",
323 },
324 },
325 }
326}